summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--dashboard/Makefile4
-rw-r--r--dashboard/booking_communication_agent.py8
-rw-r--r--dashboard/config.env.sample5
-rw-r--r--dashboard/config/nginx/pharos_dashboard.conf26
-rw-r--r--dashboard/data/UNH_IOL/HPE_Proliant/hpe13.yaml53
-rw-r--r--dashboard/data/UNH_IOL/HPE_Proliant/hpe14.yaml41
-rw-r--r--dashboard/data/UNH_IOL/HPE_Proliant/hpe15.yaml41
-rw-r--r--dashboard/data/UNH_IOL/HPE_Proliant/hpe16.yaml41
-rw-r--r--dashboard/data/UNH_IOL/HPE_Proliant/hpe17.yaml41
-rw-r--r--dashboard/data/UNH_IOL/HPE_Proliant/hpe18.yaml41
-rw-r--r--dashboard/data/UNH_IOL/HPE_Proliant/hpe19.yaml41
-rw-r--r--dashboard/data/UNH_IOL/HPE_Proliant/hpe20.yaml41
-rw-r--r--dashboard/data/UNH_IOL/hostlist.json14
-rw-r--r--dashboard/docker-compose.yml1
-rw-r--r--dashboard/requirements.txt12
-rw-r--r--dashboard/src/account/admin.py5
-rw-r--r--dashboard/src/account/migrations/0001_initial.py33
-rw-r--r--dashboard/src/account/migrations/0002_auto_20180110_1636.py33
-rw-r--r--dashboard/src/account/migrations/0002_lab_description.py19
-rw-r--r--dashboard/src/account/migrations/0003_auto_20180110_1639.py24
-rw-r--r--dashboard/src/account/migrations/0003_publicnetwork.py25
-rw-r--r--dashboard/src/account/migrations/__init__.py10
-rw-r--r--dashboard/src/account/models.py127
-rw-r--r--dashboard/src/account/tasks.py4
-rw-r--r--dashboard/src/account/urls.py7
-rw-r--r--dashboard/src/account/views.py48
-rw-r--r--dashboard/src/api/admin.py28
-rw-r--r--dashboard/src/api/migrations/0001_initial.py185
-rw-r--r--dashboard/src/api/migrations/__init__.py4
-rw-r--r--dashboard/src/api/models.py713
-rw-r--r--dashboard/src/api/serializers.py53
-rw-r--r--dashboard/src/api/serializers/__init__.py (renamed from dashboard/src/jenkins/__init__.py)4
-rw-r--r--dashboard/src/api/serializers/booking_serializer.py156
-rw-r--r--dashboard/src/api/serializers/old_serializers.py28
-rw-r--r--dashboard/src/api/tests/__init__.py8
-rw-r--r--dashboard/src/api/tests/test_serializers.py213
-rw-r--r--dashboard/src/api/urls.py13
-rw-r--r--dashboard/src/api/views.py101
-rw-r--r--dashboard/src/booking/__init__.py2
-rw-r--r--dashboard/src/booking/admin.py4
-rw-r--r--dashboard/src/booking/forms.py65
-rw-r--r--dashboard/src/booking/migrations/0001_initial.py48
-rw-r--r--dashboard/src/booking/migrations/0002_booking_changeid.py38
-rw-r--r--dashboard/src/booking/migrations/0003_auto_20180108_2024.py25
-rw-r--r--dashboard/src/booking/migrations/0004_booking_ext_count.py27
-rw-r--r--dashboard/src/booking/migrations/__init__.py10
-rw-r--r--dashboard/src/booking/models.py60
-rw-r--r--dashboard/src/booking/stats.py56
-rw-r--r--dashboard/src/booking/tests/__init__.py2
-rw-r--r--dashboard/src/booking/tests/test_models.py216
-rw-r--r--dashboard/src/booking/tests/test_views.py106
-rw-r--r--dashboard/src/booking/urls.py16
-rw-r--r--dashboard/src/booking/views.py138
-rw-r--r--dashboard/src/dashboard/__init__.py2
-rw-r--r--dashboard/src/dashboard/admin.py6
-rw-r--r--dashboard/src/dashboard/context_processors.py12
-rw-r--r--dashboard/src/dashboard/exceptions.py46
-rw-r--r--dashboard/src/dashboard/migrations/0001_initial.py64
-rw-r--r--dashboard/src/dashboard/migrations/0002_auto_20170505_0815.py42
-rw-r--r--dashboard/src/dashboard/migrations/0003_resource_resource_lab.py22
-rw-r--r--dashboard/src/dashboard/models.py90
-rw-r--r--dashboard/src/dashboard/populate_db_iol.py346
-rw-r--r--dashboard/src/dashboard/tasks.py113
-rw-r--r--dashboard/src/dashboard/templatetags/jenkins_filters.py38
-rw-r--r--dashboard/src/dashboard/tests/test_models.py69
-rw-r--r--dashboard/src/dashboard/tests/test_views.py75
-rw-r--r--dashboard/src/dashboard/urls.py17
-rw-r--r--dashboard/src/dashboard/views.py196
-rw-r--r--dashboard/src/jenkins/adapter.py137
-rw-r--r--dashboard/src/jenkins/migrations/0001_initial.py53
-rw-r--r--dashboard/src/jenkins/models.py62
-rw-r--r--dashboard/src/jenkins/tasks.py64
-rw-r--r--dashboard/src/jenkins/tests.py129
-rwxr-xr-x[-rw-r--r--]dashboard/src/manage.py0
-rw-r--r--dashboard/src/notifier/__init__.py8
-rw-r--r--dashboard/src/notifier/admin.py4
-rw-r--r--dashboard/src/notifier/apps.py3
-rw-r--r--dashboard/src/notifier/dispatchers.py4
-rw-r--r--dashboard/src/notifier/manager.py98
-rw-r--r--dashboard/src/notifier/migrations/0001_initial.py25
-rw-r--r--dashboard/src/notifier/models.py33
-rw-r--r--dashboard/src/notifier/tests/test_dispatcher.py (renamed from dashboard/src/jenkins/admin.py)14
-rw-r--r--dashboard/src/notifier/tests/test_models.py29
-rw-r--r--dashboard/src/notifier/urls.py21
-rw-r--r--dashboard/src/notifier/views.py34
-rw-r--r--dashboard/src/pharos_dashboard/settings.py52
-rw-r--r--dashboard/src/pharos_dashboard/urls.py11
-rw-r--r--dashboard/src/resource_inventory/__init__.py (renamed from dashboard/src/jenkins/migrations/__init__.py)4
-rw-r--r--dashboard/src/resource_inventory/admin.py29
-rw-r--r--dashboard/src/resource_inventory/apps.py (renamed from dashboard/src/jenkins/apps.py)5
-rw-r--r--dashboard/src/resource_inventory/migrations/0001_initial.py328
-rw-r--r--dashboard/src/resource_inventory/migrations/0002_auto_20180919_1459.py18
-rw-r--r--dashboard/src/resource_inventory/migrations/0003_vlan_public.py18
-rw-r--r--dashboard/src/resource_inventory/migrations/__init__.py0
-rw-r--r--dashboard/src/resource_inventory/models.py299
-rw-r--r--dashboard/src/resource_inventory/resource_manager.py197
-rw-r--r--dashboard/src/resource_inventory/tests/test_managers.py236
-rw-r--r--dashboard/src/resource_inventory/tests/test_models.py162
-rw-r--r--dashboard/src/resource_inventory/urls.py34
-rw-r--r--dashboard/src/resource_inventory/views.py24
-rw-r--r--dashboard/src/static/css/graph_common.css162
-rw-r--r--dashboard/src/static/css/theme.css6
-rw-r--r--dashboard/src/static/img/mxgraph/add.pngbin0 -> 1564 bytes
-rw-r--r--dashboard/src/static/img/mxgraph/button.gifbin0 -> 137 bytes
-rw-r--r--dashboard/src/static/img/mxgraph/camera.pngbin0 -> 887 bytes
-rw-r--r--dashboard/src/static/img/mxgraph/check.pngbin0 -> 253 bytes
-rw-r--r--dashboard/src/static/img/mxgraph/close.gifbin0 -> 70 bytes
-rw-r--r--dashboard/src/static/img/mxgraph/close.pngbin0 -> 1910 bytes
-rw-r--r--dashboard/src/static/img/mxgraph/collapsed.gifbin0 -> 877 bytes
-rw-r--r--dashboard/src/static/img/mxgraph/connector.gifbin0 -> 954 bytes
-rw-r--r--dashboard/src/static/img/mxgraph/copy.pngbin0 -> 728 bytes
-rw-r--r--dashboard/src/static/img/mxgraph/cut.pngbin0 -> 781 bytes
-rw-r--r--dashboard/src/static/img/mxgraph/delete2.pngbin0 -> 914 bytes
-rw-r--r--dashboard/src/static/img/mxgraph/dot.gifbin0 -> 517 bytes
-rw-r--r--dashboard/src/static/img/mxgraph/error.gifbin0 -> 907 bytes
-rw-r--r--dashboard/src/static/img/mxgraph/expanded.gifbin0 -> 878 bytes
-rw-r--r--dashboard/src/static/img/mxgraph/export1.pngbin0 -> 857 bytes
-rw-r--r--dashboard/src/static/img/mxgraph/fit_to_size.pngbin0 -> 529 bytes
-rw-r--r--dashboard/src/static/img/mxgraph/gradient_background.jpgbin0 -> 6164 bytes
-rw-r--r--dashboard/src/static/img/mxgraph/green-dot.gifbin0 -> 326 bytes
-rw-r--r--dashboard/src/static/img/mxgraph/group.pngbin0 -> 899 bytes
-rw-r--r--dashboard/src/static/img/mxgraph/handle-connect.pngbin0 -> 1300 bytes
-rw-r--r--dashboard/src/static/img/mxgraph/handle-main.pngbin0 -> 379 bytes
-rw-r--r--dashboard/src/static/img/mxgraph/icons48/column.pngbin0 -> 1787 bytes
-rw-r--r--dashboard/src/static/img/mxgraph/icons48/earth.pngbin0 -> 4520 bytes
-rw-r--r--dashboard/src/static/img/mxgraph/icons48/gear.pngbin0 -> 4418 bytes
-rw-r--r--dashboard/src/static/img/mxgraph/icons48/keys.pngbin0 -> 4295 bytes
-rw-r--r--dashboard/src/static/img/mxgraph/icons48/mail_new.pngbin0 -> 3944 bytes
-rw-r--r--dashboard/src/static/img/mxgraph/icons48/server.pngbin0 -> 3556 bytes
-rw-r--r--dashboard/src/static/img/mxgraph/icons48/table.pngbin0 -> 1574 bytes
-rw-r--r--dashboard/src/static/img/mxgraph/key.pngbin0 -> 300 bytes
-rw-r--r--dashboard/src/static/img/mxgraph/loading.gifbin0 -> 7517 bytes
-rw-r--r--dashboard/src/static/img/mxgraph/maximize.gifbin0 -> 843 bytes
-rw-r--r--dashboard/src/static/img/mxgraph/minimize.gifbin0 -> 64 bytes
-rw-r--r--dashboard/src/static/img/mxgraph/navigate_minus.pngbin0 -> 485 bytes
-rw-r--r--dashboard/src/static/img/mxgraph/navigate_plus.pngbin0 -> 709 bytes
-rw-r--r--dashboard/src/static/img/mxgraph/normalize.gifbin0 -> 845 bytes
-rw-r--r--dashboard/src/static/img/mxgraph/paste.pngbin0 -> 783 bytes
-rw-r--r--dashboard/src/static/img/mxgraph/plus.pngbin0 -> 236 bytes
-rw-r--r--dashboard/src/static/img/mxgraph/point.gifbin0 -> 55 bytes
-rw-r--r--dashboard/src/static/img/mxgraph/press32.pngbin0 -> 2261 bytes
-rw-r--r--dashboard/src/static/img/mxgraph/print32.pngbin0 -> 2111 bytes
-rw-r--r--dashboard/src/static/img/mxgraph/printer.pngbin0 -> 896 bytes
-rw-r--r--dashboard/src/static/img/mxgraph/redo.pngbin0 -> 895 bytes
-rw-r--r--dashboard/src/static/img/mxgraph/resize.gifbin0 -> 74 bytes
-rw-r--r--dashboard/src/static/img/mxgraph/separator.gifbin0 -> 146 bytes
-rw-r--r--dashboard/src/static/img/mxgraph/sidebar_bg.gifbin0 -> 80 bytes
-rw-r--r--dashboard/src/static/img/mxgraph/spacer.gifbin0 -> 43 bytes
-rw-r--r--dashboard/src/static/img/mxgraph/submenu.gifbin0 -> 56 bytes
-rw-r--r--dashboard/src/static/img/mxgraph/toolbar_bg.gifbin0 -> 155 bytes
-rw-r--r--dashboard/src/static/img/mxgraph/transparent.gifbin0 -> 90 bytes
-rw-r--r--dashboard/src/static/img/mxgraph/undo.pngbin0 -> 879 bytes
-rw-r--r--dashboard/src/static/img/mxgraph/view_1_1.pngbin0 -> 849 bytes
-rw-r--r--dashboard/src/static/img/mxgraph/view_1_132.pngbin0 -> 2199 bytes
-rw-r--r--dashboard/src/static/img/mxgraph/view_next.pngbin0 -> 918 bytes
-rw-r--r--dashboard/src/static/img/mxgraph/view_previous.pngbin0 -> 912 bytes
-rw-r--r--dashboard/src/static/img/mxgraph/warning.gifbin0 -> 276 bytes
-rw-r--r--dashboard/src/static/img/mxgraph/warning.pngbin0 -> 425 bytes
-rw-r--r--dashboard/src/static/img/mxgraph/window-title.gifbin0 -> 275 bytes
-rw-r--r--dashboard/src/static/img/mxgraph/window.gifbin0 -> 75 bytes
-rw-r--r--dashboard/src/static/img/mxgraph/wires-grid.gifbin0 -> 50 bytes
-rw-r--r--dashboard/src/static/img/mxgraph/zoom_in.pngbin0 -> 858 bytes
-rw-r--r--dashboard/src/static/img/mxgraph/zoom_in32.pngbin0 -> 2184 bytes
-rw-r--r--dashboard/src/static/img/mxgraph/zoom_out.pngbin0 -> 847 bytes
-rw-r--r--dashboard/src/static/img/mxgraph/zoom_out32.pngbin0 -> 2150 bytes
-rw-r--r--dashboard/src/static/js/mxClient.min.js1808
-rw-r--r--dashboard/src/templates/account/booking_list.html52
-rw-r--r--dashboard/src/templates/account/configuration_list.html28
-rw-r--r--dashboard/src/templates/account/details.html9
-rw-r--r--dashboard/src/templates/account/image_list.html27
-rw-r--r--dashboard/src/templates/account/resource_list.html28
-rw-r--r--dashboard/src/templates/account/user_list.html2
-rw-r--r--dashboard/src/templates/base.html118
-rw-r--r--dashboard/src/templates/booking/booking_calendar.html65
-rw-r--r--dashboard/src/templates/booking/booking_detail.html361
-rw-r--r--dashboard/src/templates/booking/booking_grid_item.html11
-rw-r--r--dashboard/src/templates/booking/booking_list.html6
-rw-r--r--dashboard/src/templates/booking/stats.html60
-rw-r--r--dashboard/src/templates/booking/steps/booking_confirm.html25
-rw-r--r--dashboard/src/templates/booking/steps/booking_meta.html66
-rw-r--r--dashboard/src/templates/booking/steps/resource_select.html73
-rw-r--r--dashboard/src/templates/booking/steps/swconfig_select.html73
-rw-r--r--dashboard/src/templates/config_bundle/steps/config_software.html72
-rw-r--r--dashboard/src/templates/config_bundle/steps/define_software.html102
-rw-r--r--dashboard/src/templates/dashboard/ci_pods.html61
-rw-r--r--dashboard/src/templates/dashboard/dev_pods.html70
-rw-r--r--dashboard/src/templates/dashboard/grid.html10
-rw-r--r--dashboard/src/templates/dashboard/host_profile_detail.html71
-rw-r--r--dashboard/src/templates/dashboard/jenkins_slaves.html46
-rw-r--r--dashboard/src/templates/dashboard/lab_detail.html157
-rw-r--r--dashboard/src/templates/dashboard/lab_list.html87
-rw-r--r--dashboard/src/templates/dashboard/landing.html88
-rw-r--r--dashboard/src/templates/dashboard/login.html8
-rw-r--r--dashboard/src/templates/dashboard/multiple_select_filter_widget.html402
-rw-r--r--dashboard/src/templates/dashboard/pdf.yaml95
-rw-r--r--dashboard/src/templates/dashboard/resource.html3
-rw-r--r--dashboard/src/templates/dashboard/resource_all.html5
-rw-r--r--dashboard/src/templates/dashboard/resource_detail.html52
-rw-r--r--dashboard/src/templates/dashboard/searchable_select_multiple.html408
-rw-r--r--dashboard/src/templates/dashboard/table.html1
-rw-r--r--dashboard/src/templates/layout.html10
-rw-r--r--dashboard/src/templates/notifier/inbox.html86
-rw-r--r--dashboard/src/templates/notifier/notification.html34
-rw-r--r--dashboard/src/templates/resource/hosts.html45
-rw-r--r--dashboard/src/templates/resource/mxClient.min.js1808
-rw-r--r--dashboard/src/templates/resource/steps/define_hardware.html37
-rw-r--r--dashboard/src/templates/resource/steps/host_info.html43
-rw-r--r--dashboard/src/templates/resource/steps/pod_definition.html653
-rw-r--r--dashboard/src/templates/resource/uncommon.css162
-rw-r--r--dashboard/src/templates/snapshot_workflow/steps/meta.html27
-rw-r--r--dashboard/src/templates/snapshot_workflow/steps/select_host.html95
-rw-r--r--dashboard/src/templates/workflow/confirm.html125
-rw-r--r--dashboard/src/templates/workflow/no_workflow.html7
-rw-r--r--dashboard/src/templates/workflow/resource_select.html50
-rw-r--r--dashboard/src/templates/workflow/viewport-base.html426
-rw-r--r--dashboard/src/templates/workflow/viewport-element.html69
-rw-r--r--dashboard/src/workflow/__init__.py8
-rw-r--r--dashboard/src/workflow/apps.py15
-rw-r--r--dashboard/src/workflow/booking_workflow.py295
-rw-r--r--dashboard/src/workflow/forms.py446
-rw-r--r--dashboard/src/workflow/models.py508
-rw-r--r--dashboard/src/workflow/resource_bundle_workflow.py427
-rw-r--r--dashboard/src/workflow/snapshot_workflow.py111
-rw-r--r--dashboard/src/workflow/sw_bundle_workflow.py238
-rw-r--r--dashboard/src/workflow/tests/__init__.py (renamed from dashboard/src/dashboard/migrations/__init__.py)4
-rw-r--r--dashboard/src/workflow/tests/constants.py198
-rw-r--r--dashboard/src/workflow/tests/test_steps.py271
-rw-r--r--dashboard/src/workflow/tests/test_steps_render.py36
-rw-r--r--dashboard/src/workflow/tests/test_workflows.py96
-rw-r--r--dashboard/src/workflow/urls.py34
-rw-r--r--dashboard/src/workflow/views.py108
-rw-r--r--dashboard/src/workflow/workflow_factory.py149
-rw-r--r--dashboard/src/workflow/workflow_manager.py251
-rwxr-xr-xdashboard/test.sh12
234 files changed, 15935 insertions, 2004 deletions
diff --git a/dashboard/Makefile b/dashboard/Makefile
index e5f4fb2..31243b0 100644
--- a/dashboard/Makefile
+++ b/dashboard/Makefile
@@ -32,10 +32,10 @@ shell-db:
docker exec -ti ps01 bash
log-nginx:
- docker-compose logs nginx
+ docker-compose logs nginx
log-web:
- docker-compose logs web
+ docker-compose logs web
log-ps:
docker-compose logs postgres
diff --git a/dashboard/booking_communication_agent.py b/dashboard/booking_communication_agent.py
index c52e98b..dc4ea4d 100644
--- a/dashboard/booking_communication_agent.py
+++ b/dashboard/booking_communication_agent.py
@@ -1,3 +1,11 @@
+##############################################################################
+# Copyright (c) 2018 Sawyer Bergeron and others.
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+##############################################################################
from dashboard_notification.notification import Notification
from dashboard_api.api import DashboardAPI
diff --git a/dashboard/config.env.sample b/dashboard/config.env.sample
index 5072779..7677c2f 100644
--- a/dashboard/config.env.sample
+++ b/dashboard/config.env.sample
@@ -2,6 +2,7 @@ DASHBOARD_URL=http://labs.opnfv.org
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG=False
+TEST=False
POSTGRES_DB=sample_name
POSTGRES_USER=sample_user
@@ -26,10 +27,6 @@ JIRA_USER_PASSWORD=sample_jira_pass
RABBITMQ_DEFAULT_USER=opnfv
RABBITMQ_DEFAULT_PASS=opnfvopnfv
-# Cleanup: time is in days
-BOOKING_EXPIRE_TIME=30
-BOOKING_MAXIMUM_NUMBER=10
-
#Jenkins Build Server
JENKINS_URL=https://build.opnfv.org/ci
diff --git a/dashboard/config/nginx/pharos_dashboard.conf b/dashboard/config/nginx/pharos_dashboard.conf
index 87b6f8e..6f32979 100644
--- a/dashboard/config/nginx/pharos_dashboard.conf
+++ b/dashboard/config/nginx/pharos_dashboard.conf
@@ -1,24 +1,24 @@
upstream web {
- ip_hash;
- server web:8000;
+ ip_hash;
+ server web:8000;
}
# portal
-server {
- listen 80;
- server_name localhost;
- charset utf-8;
+server {
+ listen 80;
+ server_name localhost;
+ charset utf-8;
- location /static {
+ location /static {
alias /static;
- }
+ }
- location /media {
+ location /media {
alias /media;
- }
+ }
- location / {
+ location / {
proxy_set_header Host $host;
- proxy_pass http://web/;
- }
+ proxy_pass http://web/;
+ }
}
diff --git a/dashboard/data/UNH_IOL/HPE_Proliant/hpe13.yaml b/dashboard/data/UNH_IOL/HPE_Proliant/hpe13.yaml
new file mode 100644
index 0000000..fd40aff
--- /dev/null
+++ b/dashboard/data/UNH_IOL/HPE_Proliant/hpe13.yaml
@@ -0,0 +1,53 @@
+---
+disk:
+ -
+ name: sda
+ size: 894.2G
+ -
+ name: loop0
+ size: 100G
+ -
+ name: loop1
+ size: 2G
+ -
+ name: loop2
+ size: 792M
+ -
+ name: loop3
+ size: 825M
+cpu:
+ arch: x86_64
+ cpus: 2
+ cores: 88
+memory: 503G
+interface:
+ -
+ name: eno49
+ mac: '48:df:37:1d:54:00'
+ busaddr: '0000:04:00.0'
+ speed: 10000
+ -
+ name: ens1f0
+ mac: '3c:fd:fe:b2:c3:70'
+ busaddr: '0000:05:00.0'
+ speed: 10000
+ -
+ name: ens1f1
+ mac: '3c:fd:fe:b2:c3:71'
+ busaddr: '0000:05:00.1'
+ speed: 10000
+ -
+ name: ens1f2
+ mac: '3c:fd:fe:b2:c3:72'
+ busaddr: '0000:05:00.2'
+ speed: 10000
+ -
+ name: ens4f0
+ mac: '3c:fd:fe:b2:ae:d0'
+ busaddr: '0000:88:00.0'
+ speed: 10000
+ -
+ name: ens4f1
+ mac: '3c:fd:fe:b2:ae:d1'
+ busaddr: '0000:88:00.1'
+ speed: 10000
diff --git a/dashboard/data/UNH_IOL/HPE_Proliant/hpe14.yaml b/dashboard/data/UNH_IOL/HPE_Proliant/hpe14.yaml
new file mode 100644
index 0000000..612f4d0
--- /dev/null
+++ b/dashboard/data/UNH_IOL/HPE_Proliant/hpe14.yaml
@@ -0,0 +1,41 @@
+---
+disk:
+ -
+ name: sda
+ size: 894.2G
+cpu:
+ arch: x86_64
+ cpus: 2
+ cores: 88
+memory: 503G
+interface:
+ -
+ name: eno49
+ mac: '48:df:37:1d:4d:60'
+ busaddr: '0000:04:00.0'
+ speed: 10000
+ -
+ name: ens1f0
+ mac: '3c:fd:fe:b2:b2:00'
+ busaddr: '0000:05:00.0'
+ speed: 10000
+ -
+ name: ens1f1
+ mac: '3c:fd:fe:b2:b2:01'
+ busaddr: '0000:05:00.1'
+ speed: 10000
+ -
+ name: ens1f2
+ mac: '3c:fd:fe:b2:b2:02'
+ busaddr: '0000:05:00.2'
+ speed: 10000
+ -
+ name: ens4f0
+ mac: '3c:fd:fe:b2:b1:d8'
+ busaddr: '0000:88:00.0'
+ speed: 10000
+ -
+ name: ens4f1
+ mac: '3c:fd:fe:b2:b1:d9'
+ busaddr: '0000:88:00.1'
+ speed: 10000
diff --git a/dashboard/data/UNH_IOL/HPE_Proliant/hpe15.yaml b/dashboard/data/UNH_IOL/HPE_Proliant/hpe15.yaml
new file mode 100644
index 0000000..c6799ac
--- /dev/null
+++ b/dashboard/data/UNH_IOL/HPE_Proliant/hpe15.yaml
@@ -0,0 +1,41 @@
+---
+disk:
+ -
+ name: sda
+ size: 894.2G
+cpu:
+ arch: x86_64
+ cpus: 2
+ cores: 88
+memory: 503G
+interface:
+ -
+ name: eno49
+ mac: '48:df:37:22:c5:c0'
+ busaddr: '0000:04:00.0'
+ speed: 10000
+ -
+ name: ens1f0
+ mac: '3c:fd:fe:b2:ae:68'
+ busaddr: '0000:05:00.0'
+ speed: 10000
+ -
+ name: ens1f1
+ mac: '3c:fd:fe:b2:ae:69'
+ busaddr: '0000:05:00.1'
+ speed: 10000
+ -
+ name: ens1f2
+ mac: '3c:fd:fe:b2:ae:6a'
+ busaddr: '0000:05:00.2'
+ speed: 10000
+ -
+ name: ens4f0
+ mac: '3c:fd:fe:b2:ad:90'
+ busaddr: '0000:88:00.0'
+ speed: 10000
+ -
+ name: ens4f1
+ mac: '3c:fd:fe:b2:ad:91'
+ busaddr: '0000:88:00.1'
+ speed: 10000
diff --git a/dashboard/data/UNH_IOL/HPE_Proliant/hpe16.yaml b/dashboard/data/UNH_IOL/HPE_Proliant/hpe16.yaml
new file mode 100644
index 0000000..3148532
--- /dev/null
+++ b/dashboard/data/UNH_IOL/HPE_Proliant/hpe16.yaml
@@ -0,0 +1,41 @@
+---
+disk:
+ -
+ name: sda
+ size: 894.2G
+cpu:
+ arch: x86_64
+ cpus: 2
+ cores: 88
+memory: 503G
+interface:
+ -
+ name: eth0
+ mac: '48:df:37:22:c5:30'
+ busaddr: '0000:04:00.0'
+ speed: 10000
+ -
+ name: eth2
+ mac: '3c:fd:fe:b2:b1:60'
+ busaddr: '0000:05:00.0'
+ speed: 10000
+ -
+ name: eth3
+ mac: '3c:fd:fe:b2:b1:61'
+ busaddr: '0000:05:00.1'
+ speed: 10000
+ -
+ name: eth4
+ mac: '3c:fd:fe:b2:b1:62'
+ busaddr: '0000:05:00.2'
+ speed: 10000
+ -
+ name: eth6
+ mac: '3c:fd:fe:b2:b3:b8'
+ busaddr: '0000:88:00.0'
+ speed: 10000
+ -
+ name: eth7
+ mac: '3c:fd:fe:b2:b3:b9'
+ busaddr: '0000:88:00.1'
+ speed: 10000
diff --git a/dashboard/data/UNH_IOL/HPE_Proliant/hpe17.yaml b/dashboard/data/UNH_IOL/HPE_Proliant/hpe17.yaml
new file mode 100644
index 0000000..4b13ae2
--- /dev/null
+++ b/dashboard/data/UNH_IOL/HPE_Proliant/hpe17.yaml
@@ -0,0 +1,41 @@
+---
+disk:
+ -
+ name: sda
+ size: 894.2G
+cpu:
+ arch: x86_64
+ cpus: 2
+ cores: 88
+memory: 503G
+interface:
+ -
+ name: eno49
+ mac: '48:df:37:1d:48:e0'
+ busaddr: '0000:04:00.0'
+ speed: 10000
+ -
+ name: ens1f0
+ mac: '3c:fd:fe:b2:ad:80'
+ busaddr: '0000:05:00.0'
+ speed: 10000
+ -
+ name: ens1f1
+ mac: '3c:fd:fe:b2:ad:81'
+ busaddr: '0000:05:00.1'
+ speed: 10000
+ -
+ name: ens1f2
+ mac: '3c:fd:fe:b2:ad:82'
+ busaddr: '0000:05:00.2'
+ speed: 10000
+ -
+ name: ens4f0
+ mac: '3c:fd:fe:b2:b2:d0'
+ busaddr: '0000:88:00.0'
+ speed: 10000
+ -
+ name: ens4f1
+ mac: '3c:fd:fe:b2:b2:d1'
+ busaddr: '0000:88:00.1'
+ speed: 10000
diff --git a/dashboard/data/UNH_IOL/HPE_Proliant/hpe18.yaml b/dashboard/data/UNH_IOL/HPE_Proliant/hpe18.yaml
new file mode 100644
index 0000000..aa411f1
--- /dev/null
+++ b/dashboard/data/UNH_IOL/HPE_Proliant/hpe18.yaml
@@ -0,0 +1,41 @@
+---
+disk:
+ -
+ name: sda
+ size: 894.2G
+cpu:
+ arch: x86_64
+ cpus: 2
+ cores: 88
+memory: 503G
+interface:
+ -
+ name: eno49
+ mac: '48:df:37:19:a9:b0'
+ busaddr: '0000:04:00.0'
+ speed: 10000
+ -
+ name: ens1f0
+ mac: '3c:fd:fe:b2:ae:b8'
+ busaddr: '0000:05:00.0'
+ speed: 10000
+ -
+ name: ens1f1
+ mac: '3c:fd:fe:b2:ae:b9'
+ busaddr: '0000:05:00.1'
+ speed: 10000
+ -
+ name: ens1f2
+ mac: '3c:fd:fe:b2:ae:ba'
+ busaddr: '0000:05:00.2'
+ speed: 10000
+ -
+ name: ens4f0
+ mac: '3c:fd:fe:b2:af:50'
+ busaddr: '0000:88:00.0'
+ speed: 10000
+ -
+ name: ens4f1
+ mac: '3c:fd:fe:b2:af:51'
+ busaddr: '0000:88:00.1'
+ speed: 10000
diff --git a/dashboard/data/UNH_IOL/HPE_Proliant/hpe19.yaml b/dashboard/data/UNH_IOL/HPE_Proliant/hpe19.yaml
new file mode 100644
index 0000000..40e538c
--- /dev/null
+++ b/dashboard/data/UNH_IOL/HPE_Proliant/hpe19.yaml
@@ -0,0 +1,41 @@
+---
+disk:
+ -
+ name: sda
+ size: 894.2G
+cpu:
+ arch: x86_64
+ cpus: 2
+ cores: 88
+memory: 503G
+interface:
+ -
+ name: eno49
+ mac: '48:df:37:22:b5:90'
+ busaddr: '0000:04:00.0'
+ speed: 10000
+ -
+ name: ens1f0
+ mac: '3c:fd:fe:b2:ad:20'
+ busaddr: '0000:05:00.0'
+ speed: 10000
+ -
+ name: ens1f1
+ mac: '3c:fd:fe:b2:ad:21'
+ busaddr: '0000:05:00.1'
+ speed: 10000
+ -
+ name: ens1f2
+ mac: '3c:fd:fe:b2:ad:22'
+ busaddr: '0000:05:00.2'
+ speed: 10000
+ -
+ name: ens4f0
+ mac: '3c:fd:fe:b2:b3:10'
+ busaddr: '0000:88:00.0'
+ speed: 10000
+ -
+ name: ens4f1
+ mac: '3c:fd:fe:b2:b3:11'
+ busaddr: '0000:88:00.1'
+ speed: 10000
diff --git a/dashboard/data/UNH_IOL/HPE_Proliant/hpe20.yaml b/dashboard/data/UNH_IOL/HPE_Proliant/hpe20.yaml
new file mode 100644
index 0000000..6c83e79
--- /dev/null
+++ b/dashboard/data/UNH_IOL/HPE_Proliant/hpe20.yaml
@@ -0,0 +1,41 @@
+---
+disk:
+ -
+ name: sda
+ size: 894.2G
+cpu:
+ arch: x86_64
+ cpus: 2
+ cores: 88
+memory: 503G
+interface:
+ -
+ name: eno49
+ mac: '48:df:37:1d:49:a0'
+ busaddr: '0000:04:00.0'
+ speed: 10000
+ -
+ name: ens1f0
+ mac: '3c:fd:fe:b2:b1:c8'
+ busaddr: '0000:05:00.0'
+ speed: 10000
+ -
+ name: ens1f1
+ mac: '3c:fd:fe:b2:b1:c9'
+ busaddr: '0000:05:00.1'
+ speed: 10000
+ -
+ name: ens1f2
+ mac: '3c:fd:fe:b2:b1:ca'
+ busaddr: '0000:05:00.2'
+ speed: 10000
+ -
+ name: ens4f0
+ mac: '3c:fd:fe:b2:ae:a0'
+ busaddr: '0000:88:00.0'
+ speed: 10000
+ -
+ name: ens4f1
+ mac: '3c:fd:fe:b2:ae:a1'
+ busaddr: '0000:88:00.1'
+ speed: 10000
diff --git a/dashboard/data/UNH_IOL/hostlist.json b/dashboard/data/UNH_IOL/hostlist.json
new file mode 100644
index 0000000..cd03859
--- /dev/null
+++ b/dashboard/data/UNH_IOL/hostlist.json
@@ -0,0 +1,14 @@
+{
+ "profiles": {
+ "HPE_Proliant": [
+ "hpe13",
+ "hpe14",
+ "hpe15",
+ "hpe16",
+ "hpe17",
+ "hpe18",
+ "hpe19",
+ "hpe20"
+ ]
+ }
+}
diff --git a/dashboard/docker-compose.yml b/dashboard/docker-compose.yml
index 19e9afd..2f96343 100644
--- a/dashboard/docker-compose.yml
+++ b/dashboard/docker-compose.yml
@@ -1,6 +1,7 @@
---
##############################################################################
# Copyright (c) 2016 Max Breitenfeldt and others.
+# Copyright (c) 2018 Parker Berberian, Sawyer Bergeron, and others.
#
# All rights reserved. This program and the accompanying materials
# are made available under the terms of the Apache License, Version 2.0
diff --git a/dashboard/requirements.txt b/dashboard/requirements.txt
index 19365bc..9ea10a4 100644
--- a/dashboard/requirements.txt
+++ b/dashboard/requirements.txt
@@ -1,11 +1,11 @@
celery==3.1.23
cryptography==2.3.1
-Django==1.10
-django-bootstrap3==7.0.1
-django-crispy-forms==1.6.0
-django-filter==0.14.0
+Django==2.1
+django-bootstrap3==10.0.1
+django-crispy-forms==1.7.2
+django-filter==2.0.0
django-registration==2.1.2
-djangorestframework==3.4.6
+djangorestframework==3.8.2
gunicorn==19.6.0
jira==1.0.7
jsonpickle==0.9.3
@@ -16,3 +16,5 @@ psycopg2==2.6.2
PyJWT==1.4.2
requests==2.11.0
django-fernet-fields==0.5
+pyyaml==3.13
+pytz==2018.5
diff --git a/dashboard/src/account/admin.py b/dashboard/src/account/admin.py
index 6f77122..b4c142c 100644
--- a/dashboard/src/account/admin.py
+++ b/dashboard/src/account/admin.py
@@ -1,5 +1,6 @@
##############################################################################
# Copyright (c) 2016 Max Breitenfeldt and others.
+# Copyright (c) 2018 Parker Berberian, Sawyer Bergeron, and others.
#
# All rights reserved. This program and the accompanying materials
# are made available under the terms of the Apache License, Version 2.0
@@ -10,7 +11,9 @@
from django.contrib import admin
-from account.models import UserProfile, Lab
+from account.models import UserProfile, Lab, VlanManager, PublicNetwork
admin.site.register(UserProfile)
admin.site.register(Lab)
+admin.site.register(VlanManager)
+admin.site.register(PublicNetwork)
diff --git a/dashboard/src/account/migrations/0001_initial.py b/dashboard/src/account/migrations/0001_initial.py
index 591f702..c8b5bdc 100644
--- a/dashboard/src/account/migrations/0001_initial.py
+++ b/dashboard/src/account/migrations/0001_initial.py
@@ -1,6 +1,4 @@
-# -*- coding: utf-8 -*-
-# Generated by Django 1.10 on 2016-11-03 13:33
-from __future__ import unicode_literals
+# Generated by Django 2.1 on 2018-09-14 14:48
import account.models
from django.conf import settings
@@ -18,21 +16,50 @@ class Migration(migrations.Migration):
operations = [
migrations.CreateModel(
+ name='Lab',
+ fields=[
+ ('name', models.CharField(max_length=200, primary_key=True, serialize=False, unique=True)),
+ ('contact_email', models.EmailField(blank=True, max_length=200, null=True)),
+ ('contact_phone', models.CharField(blank=True, max_length=20, null=True)),
+ ('status', models.IntegerField(default=0)),
+ ('location', models.TextField(default='unknown')),
+ ('api_token', models.CharField(max_length=50)),
+ ('lab_user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
+ ],
+ ),
+ migrations.CreateModel(
name='UserProfile',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('timezone', models.CharField(default='UTC', max_length=100)),
('ssh_public_key', models.FileField(blank=True, null=True, upload_to=account.models.upload_to)),
('pgp_public_key', models.FileField(blank=True, null=True, upload_to=account.models.upload_to)),
+ ('email_addr', models.CharField(default='email@mail.com', max_length=300)),
('company', models.CharField(max_length=200)),
('oauth_token', models.CharField(max_length=1024)),
('oauth_secret', models.CharField(max_length=1024)),
('jira_url', models.CharField(default='', max_length=100)),
('full_name', models.CharField(default='', max_length=100)),
+ ('booking_privledge', models.BooleanField(default=False)),
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
options={
'db_table': 'user_profile',
},
),
+ migrations.CreateModel(
+ name='VlanManager',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('vlans', models.TextField()),
+ ('block_size', models.IntegerField()),
+ ('allow_overlapping', models.BooleanField()),
+ ('reserved_vlans', models.TextField()),
+ ],
+ ),
+ migrations.AddField(
+ model_name='lab',
+ name='vlan_manager',
+ field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='account.VlanManager'),
+ ),
]
diff --git a/dashboard/src/account/migrations/0002_auto_20180110_1636.py b/dashboard/src/account/migrations/0002_auto_20180110_1636.py
deleted file mode 100644
index 6170acb..0000000
--- a/dashboard/src/account/migrations/0002_auto_20180110_1636.py
+++ /dev/null
@@ -1,33 +0,0 @@
-# -*- coding: utf-8 -*-
-# Generated by Django 1.10 on 2018-01-10 16:36
-from __future__ import unicode_literals
-
-from django.conf import settings
-from django.db import migrations, models
-import django.db.models.deletion
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- migrations.swappable_dependency(settings.AUTH_USER_MODEL),
- ('account', '0001_initial'),
- ]
-
- operations = [
- migrations.CreateModel(
- name='Lab',
- fields=[
- ('id', models.CharField(max_length=200, primary_key=True, serialize=False)),
- ('name', models.CharField(max_length=200, unique=True)),
- ('contact_email', models.EmailField(blank=True, max_length=200, null=True)),
- ('contact_phone', models.CharField(blank=True, max_length=20, null=True)),
- ('lab_user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
- ],
- ),
- migrations.AddField(
- model_name='userprofile',
- name='email_addr',
- field=models.CharField(default='email@mail.com', max_length=300),
- ),
- ]
diff --git a/dashboard/src/account/migrations/0002_lab_description.py b/dashboard/src/account/migrations/0002_lab_description.py
new file mode 100644
index 0000000..445501a
--- /dev/null
+++ b/dashboard/src/account/migrations/0002_lab_description.py
@@ -0,0 +1,19 @@
+# Generated by Django 2.1 on 2018-09-14 20:22
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('account', '0001_initial'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='lab',
+ name='description',
+ field=models.CharField(default='Lab description default', max_length=240),
+ preserve_default=False,
+ ),
+ ]
diff --git a/dashboard/src/account/migrations/0003_auto_20180110_1639.py b/dashboard/src/account/migrations/0003_auto_20180110_1639.py
deleted file mode 100644
index d0bc4d6..0000000
--- a/dashboard/src/account/migrations/0003_auto_20180110_1639.py
+++ /dev/null
@@ -1,24 +0,0 @@
-# -*- coding: utf-8 -*-
-# Generated by Django 1.10 on 2018-01-10 16:39
-from __future__ import unicode_literals
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('account', '0002_auto_20180110_1636'),
- ]
-
- operations = [
- migrations.RemoveField(
- model_name='lab',
- name='id',
- ),
- migrations.AlterField(
- model_name='lab',
- name='name',
- field=models.CharField(max_length=200, primary_key=True, serialize=False, unique=True),
- ),
- ]
diff --git a/dashboard/src/account/migrations/0003_publicnetwork.py b/dashboard/src/account/migrations/0003_publicnetwork.py
new file mode 100644
index 0000000..71e5caa
--- /dev/null
+++ b/dashboard/src/account/migrations/0003_publicnetwork.py
@@ -0,0 +1,25 @@
+# Generated by Django 2.1 on 2018-09-26 14:41
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('account', '0002_lab_description'),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='PublicNetwork',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('vlan', models.IntegerField()),
+ ('in_use', models.BooleanField(default=False)),
+ ('cidr', models.CharField(default='0.0.0.0/0', max_length=50)),
+ ('gateway', models.CharField(default='0.0.0.0', max_length=50)),
+ ('lab', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='account.Lab')),
+ ],
+ ),
+ ]
diff --git a/dashboard/src/account/migrations/__init__.py b/dashboard/src/account/migrations/__init__.py
index b5914ce..e69de29 100644
--- a/dashboard/src/account/migrations/__init__.py
+++ b/dashboard/src/account/migrations/__init__.py
@@ -1,10 +0,0 @@
-##############################################################################
-# Copyright (c) 2016 Max Breitenfeldt and others.
-#
-# All rights reserved. This program and the accompanying materials
-# are made available under the terms of the Apache License, Version 2.0
-# which accompanies this distribution, and is available at
-# http://www.apache.org/licenses/LICENSE-2.0
-##############################################################################
-
-
diff --git a/dashboard/src/account/models.py b/dashboard/src/account/models.py
index aad4c50..18a8cbb 100644
--- a/dashboard/src/account/models.py
+++ b/dashboard/src/account/models.py
@@ -10,6 +10,14 @@
from django.contrib.auth.models import User
from django.db import models
+import json
+import random
+
+
+class LabStatus(object):
+ UP = 0
+ TEMP_DOWN = 100
+ DOWN = 200
def upload_to(object, filename):
@@ -28,6 +36,7 @@ class UserProfile(models.Model):
jira_url = models.CharField(max_length=100, default='')
full_name = models.CharField(max_length=100, default='')
+ booking_privledge = models.BooleanField(default=False)
class Meta:
db_table = 'user_profile'
@@ -35,11 +44,129 @@ class UserProfile(models.Model):
def __str__(self):
return self.user.username
+
+class VlanManager(models.Model):
+ # list of length 4096 containing either 0 (not available) or 1 (available)
+ vlans = models.TextField()
+ block_size = models.IntegerField()
+ allow_overlapping = models.BooleanField()
+ # list of length 4096 containing either 0 (not rexerved) or 1 (reserved)
+ reserved_vlans = models.TextField()
+
+ def get_vlan(self, count=1):
+ allocated = []
+ vlans = json.loads(self.vlans)
+ for i in range(count):
+ new_vlan = vlans.index(1) # will throw if none available
+ vlans[new_vlan] = 0
+ allocated.append(new_vlan)
+ if count is 1:
+ return allocated[0]
+ return allocated
+
+ def get_public_vlan(self):
+ return PublicNetwork.objects.filter(lab=self.lab_set.first(), in_use=False).first()
+
+ def reserve_public_vlan(self, vlan):
+ net = PublicNetwork.objects.get(lab=self.lab_set.first(), vlan=vlan, in_use=False)
+ net.in_use = True
+ net.save()
+
+ def release_public_vlan(self, vlan):
+ net = PublicNetwork.objects.get(lab=self.lab_set.first(), vlan=vlan, in_use=True)
+ net.in_use = False
+ net.save()
+
+ def public_vlan_is_available(self, vlan):
+ net = PublicNetwork.objects.get(lab=self.lab_set.first(), vlan=vlan)
+ return not net.in_use
+
+
+ def is_available(self, vlans):
+ """
+ 'vlans' is either a single vlan id integer or a list of integers
+ will return true (available) or false
+ """
+ if self.allow_overlapping:
+ return True
+
+ reserved = json.loads(self.reserved_vlans)
+ vlan_master_list = json.loads(self.vlans)
+ try:
+ iter(vlans)
+ except:
+ vlans = [vlans]
+
+ for vlan in vlans:
+ if not vlan_master_list[vlan] or reserved[vlan]:
+ return False
+ return True
+
+ def release_vlans(self, vlans):
+ """
+ 'vlans' is either a single vlan id integer or a list of integers
+ will make the vlans available
+ doesnt return a value
+ """
+ my_vlans = json.loads(self.vlans)
+
+ try:
+ iter(vlans)
+ except:
+ vlans = [vlans]
+
+ for vlan in vlans:
+ my_vlans[vlan] = 1
+ self.vlans = json.dumps(my_vlans)
+ self.save()
+
+ def reserve_vlans(self, vlans):
+ my_vlans = json.loads(self.vlans)
+
+ try:
+ iter(vlans)
+ except:
+ vlans = [vlans]
+
+ vlans = set(vlans)
+
+ for vlan in vlans:
+ if my_vlans[vlan] is 0:
+ raise ValueError("vlan " + str(vlan) + " is not available")
+
+ my_vlans[vlan] = 0
+ self.vlans = json.dumps(my_vlans)
+ self.save()
+
+
+
class Lab(models.Model):
lab_user = models.OneToOneField(User, on_delete=models.CASCADE)
name = models.CharField(max_length=200, primary_key=True, unique=True, null=False, blank=False)
contact_email = models.EmailField(max_length=200, null=True, blank=True)
contact_phone = models.CharField(max_length=20, null=True, blank=True)
+ status = models.IntegerField(default=LabStatus.UP)
+ vlan_manager = models.ForeignKey(VlanManager, on_delete=models.CASCADE, null=True)
+ location = models.TextField(default="unknown")
+ api_token = models.CharField(max_length=50)
+ description = models.CharField(max_length=240)
+
+ @staticmethod
+ def make_api_token():
+ alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
+ key = ""
+ for i in range(45):
+ key += random.choice(alphabet)
+ return key
+
def __str__(self):
return self.name
+
+
+class PublicNetwork(models.Model):
+ vlan = models.IntegerField()
+ lab = models.ForeignKey(Lab, on_delete=models.CASCADE)
+ in_use = models.BooleanField(default=False)
+ cidr = models.CharField(max_length=50, default="0.0.0.0/0")
+ gateway = models.CharField(max_length=50, default="0.0.0.0")
diff --git a/dashboard/src/account/tasks.py b/dashboard/src/account/tasks.py
index bfb865d..fe51974 100644
--- a/dashboard/src/account/tasks.py
+++ b/dashboard/src/account/tasks.py
@@ -1,5 +1,6 @@
##############################################################################
# Copyright (c) 2016 Max Breitenfeldt and others.
+# Copyright (c) 2018 Parker Berberian, Sawyer Bergeron, and others.
#
# All rights reserved. This program and the accompanying materials
# are made available under the terms of the Apache License, Version 2.0
@@ -28,7 +29,6 @@ def sync_jira_accounts():
user.email = user_dict['emailAddress']
user.userprofile.url = user_dict['self']
user.userprofile.full_name = user_dict['displayName']
- print(user_dict)
user.userprofile.save()
- user.save() \ No newline at end of file
+ user.save()
diff --git a/dashboard/src/account/urls.py b/dashboard/src/account/urls.py
index 3962a0c..6ce2115 100644
--- a/dashboard/src/account/urls.py
+++ b/dashboard/src/account/urls.py
@@ -1,5 +1,6 @@
##############################################################################
# Copyright (c) 2016 Max Breitenfeldt and others.
+# Copyright (c) 2018 Parker Berberian, Sawyer Bergeron, and others.
#
# All rights reserved. This program and the accompanying materials
# are made available under the terms of the Apache License, Version 2.0
@@ -27,10 +28,16 @@ from django.conf.urls import url
from account.views import *
+app_name = "account"
urlpatterns = [
url(r'^settings/', AccountSettingsView.as_view(), name='settings'),
url(r'^authenticated/$', JiraAuthenticatedView.as_view(), name='authenticated'),
url(r'^login/$', JiraLoginView.as_view(), name='login'),
url(r'^logout/$', JiraLogoutView.as_view(), name='logout'),
url(r'^users/$', UserListView.as_view(), name='users'),
+ url(r'^my/resources', account_resource_view, name="my-resources"),
+ url(r'^my/bookings', account_booking_view, name="my-bookings"),
+ url(r'^my/images', account_images_view, name="my-images"),
+ url(r'^my/configurations', account_configuration_view, name="my-configurations"),
+ url(r'^my/$', account_detail_view, name="my-account"),
]
diff --git a/dashboard/src/account/views.py b/dashboard/src/account/views.py
index e6a0e5d..04d21b8 100644
--- a/dashboard/src/account/views.py
+++ b/dashboard/src/account/views.py
@@ -1,5 +1,6 @@
##############################################################################
# Copyright (c) 2016 Max Breitenfeldt and others.
+# Copyright (c) 2018 Parker Berberian, Sawyer Bergeron, and others.
#
# All rights reserved. This program and the accompanying materials
# are made available under the terms of the Apache License, Version 2.0
@@ -21,12 +22,15 @@ from django.contrib.auth.models import User
from django.urls import reverse
from django.utils.decorators import method_decorator
from django.views.generic import RedirectView, TemplateView, UpdateView
+from django.shortcuts import render
from jira import JIRA
from rest_framework.authtoken.models import Token
from account.forms import AccountSettingsForm
from account.jira_util import SignatureMethod_RSA_SHA1
from account.models import UserProfile
+from booking.models import Booking
+from resource_inventory.models import GenericResourceBundle, ConfigBundle, Image
@method_decorator(login_required, name='dispatch')
@@ -153,3 +157,47 @@ class UserListView(TemplateView):
context = super(UserListView, self).get_context_data(**kwargs)
context.update({'title': "Dashboard Users", 'users': users})
return context
+
+
+def account_detail_view(request):
+ template = "account/details.html"
+ return render(request, template)
+
+def account_resource_view(request):
+ """
+ gathers a users genericResoureBundles and
+ turns them into displayable objects
+ """
+ if not request.user.is_authenticated:
+ return render(request, "dashboard/login.html", {'title': 'Authentication Required'})
+ template = "account/resource_list.html"
+ resources = list(GenericResourceBundle.objects.filter(owner=request.user))
+ context = {"resources": resources, "title": "My Resources"}
+ return render(request, template, context=context)
+
+def account_booking_view(request):
+ if not request.user.is_authenticated:
+ return render(request, "dashboard/login.html", {'title': 'Authentication Required'})
+ template = "account/booking_list.html"
+ bookings = list(Booking.objects.filter(owner=request.user))
+ collab_bookings = list(request.user.collaborators.all())
+ context = {"title": "My Bookings", "bookings": bookings, "collab_bookings": collab_bookings}
+ return render(request, template, context=context)
+
+def account_configuration_view(request):
+ if not request.user.is_authenticated:
+ return render(request, "dashboard/login.html", {'title': 'Authentication Required'})
+ template = "account/configuration_list.html"
+ configs = list(ConfigBundle.objects.filter(owner=request.user))
+ context = {"title": "Configuration List", "configurations": configs}
+ return render(request, template, context=context)
+
+def account_images_view(request):
+ if not request.user.is_authenticated:
+ return render(request, "dashboard/login.html", {'title': 'Authentication Required'})
+ template = "account/image_list.html"
+ my_images = Image.objects.filter(owner=request.user)
+ public_images = Image.objects.filter(public=True)
+ context = {"title": "Images", "images": my_images, "public_images": public_images }
+ return render(request, template, context=context)
+
diff --git a/dashboard/src/api/admin.py b/dashboard/src/api/admin.py
new file mode 100644
index 0000000..f1bc70a
--- /dev/null
+++ b/dashboard/src/api/admin.py
@@ -0,0 +1,28 @@
+##############################################################################
+# Copyright (c) 2016 Max Breitenfeldt and others.
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+##############################################################################
+
+
+from django.apps import AppConfig
+from django.contrib import admin
+
+from api.models import *
+
+
+class ApiConfig(AppConfig):
+ name = 'apiJobs'
+
+admin.site.register(Job)
+admin.site.register(OpnfvApiConfig)
+admin.site.register(HardwareConfig)
+admin.site.register(NetworkConfig)
+admin.site.register(SoftwareConfig)
+admin.site.register(AccessRelation)
+admin.site.register(SoftwareRelation)
+admin.site.register(HostHardwareRelation)
+admin.site.register(HostNetworkRelation)
diff --git a/dashboard/src/api/migrations/0001_initial.py b/dashboard/src/api/migrations/0001_initial.py
new file mode 100644
index 0000000..abe6f5e
--- /dev/null
+++ b/dashboard/src/api/migrations/0001_initial.py
@@ -0,0 +1,185 @@
+##############################################################################
+# Copyright (c) 2018 Parker Berberian, Sawyer Bergeron, and others.
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+##############################################################################
+# Generated by Django 2.1 on 2018-09-14 14:48
+
+import api.models
+from django.conf import settings
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+ initial = True
+
+ dependencies = [
+ migrations.swappable_dependency(settings.AUTH_USER_MODEL),
+ ('booking', '__first__'),
+ ('resource_inventory', '__first__'),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='AccessRelation',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('status', models.IntegerField(default=0)),
+ ('task_id', models.CharField(default=api.models.get_task_uuid, max_length=37)),
+ ('lab_token', models.CharField(default='null', max_length=50)),
+ ('message', models.TextField(default='')),
+ ],
+ options={
+ 'abstract': False,
+ },
+ ),
+ migrations.CreateModel(
+ name='HostHardwareRelation',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('status', models.IntegerField(default=0)),
+ ('task_id', models.CharField(default=api.models.get_task_uuid, max_length=37)),
+ ('lab_token', models.CharField(default='null', max_length=50)),
+ ('message', models.TextField(default='')),
+ ('host', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='resource_inventory.Host')),
+ ],
+ options={
+ 'abstract': False,
+ },
+ ),
+ migrations.CreateModel(
+ name='HostNetworkRelation',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('status', models.IntegerField(default=0)),
+ ('task_id', models.CharField(default=api.models.get_task_uuid, max_length=37)),
+ ('lab_token', models.CharField(default='null', max_length=50)),
+ ('message', models.TextField(default='')),
+ ('host', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='resource_inventory.Host')),
+ ],
+ options={
+ 'abstract': False,
+ },
+ ),
+ migrations.CreateModel(
+ name='Job',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('status', models.IntegerField(default=0)),
+ ('delta', models.TextField()),
+ ('complete', models.BooleanField(default=False)),
+ ('booking', models.OneToOneField(null=True, on_delete=django.db.models.deletion.CASCADE, to='booking.Booking')),
+ ],
+ ),
+ migrations.CreateModel(
+ name='OpnfvApiConfig',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('installer', models.CharField(max_length=100)),
+ ('scenario', models.CharField(max_length=100)),
+ ('delta', models.TextField()),
+ ('roles', models.ManyToManyField(to='resource_inventory.Host')),
+ ],
+ ),
+ migrations.CreateModel(
+ name='SoftwareRelation',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('status', models.IntegerField(default=0)),
+ ('task_id', models.CharField(default=api.models.get_task_uuid, max_length=37)),
+ ('lab_token', models.CharField(default='null', max_length=50)),
+ ('message', models.TextField(default='')),
+ ('job', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='api.Job')),
+ ],
+ options={
+ 'abstract': False,
+ },
+ ),
+ migrations.CreateModel(
+ name='TaskConfig',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ],
+ ),
+ migrations.CreateModel(
+ name='AccessConfig',
+ fields=[
+ ('taskconfig_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='api.TaskConfig')),
+ ('access_type', models.CharField(max_length=50)),
+ ('revoke', models.BooleanField(default=False)),
+ ('context', models.TextField(default='')),
+ ('delta', models.TextField()),
+ ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
+ ],
+ bases=('api.taskconfig',),
+ ),
+ migrations.CreateModel(
+ name='HardwareConfig',
+ fields=[
+ ('taskconfig_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='api.TaskConfig')),
+ ('image', models.CharField(default='defimage', max_length=100)),
+ ('power', models.CharField(default='off', max_length=100)),
+ ('hostname', models.CharField(default='hostname', max_length=100)),
+ ('ipmi_create', models.BooleanField(default=False)),
+ ('delta', models.TextField()),
+ ],
+ bases=('api.taskconfig',),
+ ),
+ migrations.CreateModel(
+ name='NetworkConfig',
+ fields=[
+ ('taskconfig_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='api.TaskConfig')),
+ ('delta', models.TextField()),
+ ('interfaces', models.ManyToManyField(to='resource_inventory.Interface')),
+ ],
+ bases=('api.taskconfig',),
+ ),
+ migrations.CreateModel(
+ name='SoftwareConfig',
+ fields=[
+ ('taskconfig_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='api.TaskConfig')),
+ ('opnfv', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='api.OpnfvApiConfig')),
+ ],
+ bases=('api.taskconfig',),
+ ),
+ migrations.AddField(
+ model_name='hostnetworkrelation',
+ name='job',
+ field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='api.Job'),
+ ),
+ migrations.AddField(
+ model_name='hosthardwarerelation',
+ name='job',
+ field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='api.Job'),
+ ),
+ migrations.AddField(
+ model_name='accessrelation',
+ name='job',
+ field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='api.Job'),
+ ),
+ migrations.AddField(
+ model_name='softwarerelation',
+ name='config',
+ field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='api.SoftwareConfig'),
+ ),
+ migrations.AddField(
+ model_name='hostnetworkrelation',
+ name='config',
+ field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='api.NetworkConfig'),
+ ),
+ migrations.AddField(
+ model_name='hosthardwarerelation',
+ name='config',
+ field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='api.HardwareConfig'),
+ ),
+ migrations.AddField(
+ model_name='accessrelation',
+ name='config',
+ field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='api.AccessConfig'),
+ ),
+ ]
diff --git a/dashboard/src/api/migrations/__init__.py b/dashboard/src/api/migrations/__init__.py
index b5914ce..e0408fa 100644
--- a/dashboard/src/api/migrations/__init__.py
+++ b/dashboard/src/api/migrations/__init__.py
@@ -1,10 +1,8 @@
##############################################################################
-# Copyright (c) 2016 Max Breitenfeldt and others.
+# Copyright (c) 2018 Parker Berberian, Sawyer Bergeron, and others.
#
# All rights reserved. This program and the accompanying materials
# are made available under the terms of the Apache License, Version 2.0
# which accompanies this distribution, and is available at
# http://www.apache.org/licenses/LICENSE-2.0
##############################################################################
-
-
diff --git a/dashboard/src/api/models.py b/dashboard/src/api/models.py
new file mode 100644
index 0000000..f1e9130
--- /dev/null
+++ b/dashboard/src/api/models.py
@@ -0,0 +1,713 @@
+##############################################################################
+# Copyright (c) 2018 Sawyer Bergeron, Parker Berberian, and others.
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+##############################################################################
+
+
+from django.db import models
+from django.core.exceptions import PermissionDenied
+
+import json
+import uuid
+
+from resource_inventory.models import *
+from booking.models import Booking
+
+
+class JobStatus(object):
+ NEW = 0
+ CURRENT = 100
+ DONE = 200
+ ERROR = 300
+
+
+class LabManagerTracker(object):
+
+ @classmethod
+ def get(cls, lab_name, token):
+ """
+ Takes in a lab name (from a url path)
+ returns a lab manager instance for that lab, if it exists
+ """
+ try:
+ lab = Lab.objects.get(name=lab_name)
+ except:
+ raise PermissionDenied("Lab not found")
+ if lab.api_token == token:
+ return LabManager(lab)
+ raise PermissionDenied("Lab not authorized")
+
+
+class LabManager(object):
+ """
+ This is the class that will ultimately handle all REST calls to
+ lab endpoints.
+ handles jobs, inventory, status, etc
+ may need to create helper classes
+ """
+
+ def __init__(self, lab):
+ self.lab = lab
+
+ def get_profile(self):
+ prof = {}
+ prof['name'] = self.lab.name
+ prof['contact'] = {
+ "phone": self.lab.contact_phone,
+ "email": self.lab.contact_email
+ }
+ prof['host_count'] = []
+ for host in HostProfile.objects.filter(labs=self.lab):
+ count = Host.objects.filter(profile=host, lab=self.lab).count()
+ prof['host_count'].append({
+ "type": host.name,
+ "count": count
+ })
+ return prof
+
+ def get_inventory(self):
+ inventory = {}
+ hosts = Host.objects.filter(lab=self.lab)
+ images = Image.objects.filter(from_lab=self.lab)
+ profiles = HostProfile.objects.filter(labs=self.lab)
+ inventory['hosts'] = self.serialize_hosts(hosts)
+ inventory['images'] = self.serialize_images(images)
+ inventory['host_types'] = self.serialize_host_profiles(profiles)
+ return inventory
+
+ def get_status(self):
+ return {"status": self.lab.status}
+
+ def set_status(self, payload):
+ {}
+
+ def get_current_jobs(self):
+ jobs = Job.objects.filter(booking__lab=self.lab)
+
+ return self.serialize_jobs(jobs, status=JobStatus.CURRENT)
+
+ def get_new_jobs(self):
+ jobs = Job.objects.filter(booking__lab=self.lab)
+
+ return self.serialize_jobs(jobs, status=JobStatus.NEW)
+
+ def get_done_jobs(self):
+ jobs = Job.objects.filter(booking__lab=self.lab)
+
+ return self.serialize_jobs(jobs, status=JobStatus.DONE)
+
+ def get_job(self, jobid):
+ return Job.objects.get(pk=jobid).to_dict()
+
+ def update_job(self, jobid, data):
+ {}
+
+ def serialize_jobs(self, jobs, status=JobStatus.NEW):
+ job_ser = []
+ for job in jobs:
+ jsonized_job = job.get_delta(status)
+ if len(jsonized_job['payload']) < 1:
+ continue
+ job_ser.append(jsonized_job)
+
+ return job_ser
+
+ def serialize_hosts(self, hosts):
+ host_ser = []
+ for host in hosts:
+ h = {}
+ h['interfaces'] = []
+ h['hostname'] = host.name
+ h['host_type'] = host.profile.name
+ for iface in host.interfaces.all():
+ eth = {}
+ eth['mac'] = iface.mac_address
+ eth['busaddr'] = iface.bus_address
+ eth['name'] = iface.name
+ eth['switchport'] = {"switch_name": iface.switch_name, "port_name": iface.port_name}
+ h['interfaces'].append(eth)
+ return host_ser
+
+ def serialize_images(self, images):
+ images_ser = []
+ for image in images:
+ images_ser.append({
+ "name": image.name,
+ "lab_id": image.lab_id,
+ "dashboard_id": image.id
+ })
+ return images_ser
+
+ def serialize_host_profiles(self, profiles):
+ profile_ser = []
+ for profile in profiles:
+ p = {}
+ p['cpu'] = {
+ "cores": profile.cpuprofile.first().cores,
+ "arch": profile.cpuprofile.first().architecture,
+ "cpus": profile.cpuprofile.first().cpus,
+ }
+ p['disks'] = []
+ for disk in profile.storageprofile.all():
+ d = {
+ "size": disk.size,
+ "type": disk.media_type,
+ "name": disk.name
+ }
+ p['disks'].append(d)
+ p['description'] = profile.description
+ p['interfaces'] = []
+ for iface in profile.interfaceprofile.all():
+ p['interfaces'].append({
+ "speed": iface.speed,
+ "name": iface.name
+ })
+
+ p['ram'] = {"amount": profile.ramprofile.first().amount}
+ p['name'] = profile.name
+ profile_ser.append(p)
+ return profile_ser
+
+
+class Job(models.Model):
+ """
+ This is the class that is serialized and put into the api
+ """
+ booking = models.OneToOneField(Booking, on_delete=models.CASCADE, null=True)
+ status = models.IntegerField(default=JobStatus.NEW)
+ complete = models.BooleanField(default=False)
+
+ def to_dict(self):
+ d = {}
+ j = {}
+ j['id'] = self.id
+ for relation in AccessRelation.objects.filter(job=self):
+ if 'access' not in d:
+ d['access'] = {}
+ d['access'][relation.task_id] = relation.config.to_dict()
+ for relation in SoftwareRelation.objects.filter(job=self):
+ if 'software' not in d:
+ d['software'] = {}
+ d['software'][relation.task_id] = relation.config.to_dict()
+ for relation in HostHardwareRelation.objects.filter(job=self):
+ if 'hardware' not in d:
+ d['hardware'] = {}
+ d['hardware'][relation.task_id] = relation.config.to_dict()
+ for relation in HostNetworkRelation.objects.filter(job=self):
+ if 'network' not in d:
+ d['network'] = {}
+ d['network'][relation.task_id] = relation.config.to_dict()
+
+ j['payload'] = d
+
+ return j
+
+ def get_tasklist(self, status="all"):
+ tasklist = []
+ clist = [HostHardwareRelation, AccessRelation, HostNetworkRelation, SoftwareRelation]
+ if status == "all":
+ for cls in clist:
+ tasklist += list(cls.objects.filter(job=self))
+ else:
+ for cls in clist:
+ tasklist += list(cls.objects.filter(job=self).filter(status=status))
+ return tasklist
+
+ def get_delta(self, status):
+ d = {}
+ j = {}
+ j['id'] = self.id
+ for relation in AccessRelation.objects.filter(job=self).filter(status=status):
+ if 'access' not in d:
+ d['access'] = {}
+ d['access'][relation.task_id] = relation.config.get_delta()
+ for relation in SoftwareRelation.objects.filter(job=self).filter(status=status):
+ if 'software' not in d:
+ d['software'] = {}
+ d['software'][relation.task_id] = relation.config.get_delta()
+ for relation in HostHardwareRelation.objects.filter(job=self).filter(status=status):
+ if 'hardware' not in d:
+ d['hardware'] = {}
+ d['hardware'][relation.task_id] = relation.config.get_delta()
+ for relation in HostNetworkRelation.objects.filter(job=self).filter(status=status):
+ if 'network' not in d:
+ d['network'] = {}
+ d['network'][relation.task_id] = relation.config.get_delta()
+
+ j['payload'] = d
+ return j
+
+ def to_json(self):
+ return json.dumps(self.to_dict())
+
+
+class TaskConfig(models.Model):
+ def to_dict(self):
+ pass
+
+ def get_delta(self):
+ pass
+
+ def to_json(self):
+ return json.dumps(self.to_dict())
+
+ def clear_delta(self):
+ self.delta = '{}'
+
+class OpnfvApiConfig(models.Model):
+
+ installer = models.CharField(max_length=100)
+ scenario = models.CharField(max_length=100)
+ roles = models.ManyToManyField(Host)
+ delta = models.TextField()
+
+ def to_dict(self):
+ d = {}
+ if self.installer:
+ d['installer'] = self.installer
+ if self.scenario:
+ d['scenario'] = self.scenario
+
+ hosts = self.roles.all()
+ if hosts.exists():
+ d['roles'] = []
+ for host in self.roles.all():
+ d['roles'].append({host.labid: host.config.opnfvRole.name})
+
+ return d
+
+ def to_json(self):
+ return json.dumps(self.to_dict())
+
+ def set_installer(self, installer):
+ self.installer = installer
+ d = json.loads(self.delta)
+ d['installer'] = installer
+ self.delta = json.dumps(d)
+
+ def set_scenario(self, scenario):
+ self.scenario = scenario
+ d = json.loads(self.delta)
+ d['scenario'] = scenario
+ self.delta = json.dumps(d)
+
+ def add_role(self, host):
+ self.roles.add(host)
+ d = json.loads(self.delta)
+ if 'role' not in d:
+ d['role'] = []
+ d['roles'].append({host.labid: host.config.opnfvRole.name})
+ self.delta = json.dumps(d)
+
+ def clear_delta(self):
+ self.delta = '{}'
+
+ def get_delta(self):
+ if not self.delta:
+ self.delta = self.to_json()
+ self.save()
+ return json.loads(self.delta)
+
+class AccessConfig(TaskConfig):
+ access_type = models.CharField(max_length=50)
+ user = models.ForeignKey(User, on_delete=models.CASCADE)
+ revoke = models.BooleanField(default=False)
+ context = models.TextField(default="")
+ delta = models.TextField()
+
+ def to_dict(self):
+ d = {}
+ d['access_type'] = self.access_type
+ d['user'] = self.user.id
+ d['revoke'] = self.revoke
+ d['context'] = self.context
+ return d
+
+ def get_delta(self):
+ if not self.delta:
+ self.delta = self.to_json()
+ self.save()
+ d = json.loads(self.delta)
+ d["lab_token"] = self.accessrelation.lab_token
+
+ return d
+
+ def to_json(self):
+ return json.dumps(self.to_dict())
+
+ def clear_delta(self):
+ d = {}
+ d["lab_token"] = self.accessrelation.lab_token
+ self.delta = json.dumps(d)
+
+ def set_access_type(self, access_type):
+ self.access_type = access_type
+ d = json.loads(self.delta)
+ d['access_type'] = access_type
+ self.delta = json.dumps(d)
+
+ def set_user(self, user):
+ self.user = user
+ d = json.loads(self.delta)
+ d['user'] = self.user.id
+ self.delta = json.dumps(d)
+
+ def set_revoke(self, revoke):
+ self.revoke = revoke
+ d = json.loads(self.delta)
+ d['revoke'] = revoke
+ self.delta = json.dumps(d)
+
+ def set_context(self, context):
+ self.context = context
+ d = json.loads(self.delta)
+ d['context'] = context
+ self.delta = json.dumps(d)
+
+class SoftwareConfig(TaskConfig):
+ """
+ handled opnfv installations, etc
+ """
+ opnfv = models.ForeignKey(OpnfvApiConfig, on_delete=models.CASCADE)
+
+ def to_dict(self):
+ d = {}
+ if self.opnfv:
+ d['opnfv'] = self.opnfv.to_dict()
+
+ d["lab_token"] = self.softwarerelation.lab_token
+ self.delta = json.dumps(d)
+
+ return d
+
+ def get_delta(self):
+ d = {}
+ d['opnfv'] = self.opnfv.get_delta()
+ d['lab_token'] = self.softwarerelation.lab_token
+
+ return d
+
+ def clear_delta(self):
+ self.opnfv.clear_delta()
+
+ def to_json(self):
+ return json.dumps(self.to_dict())
+
+class HardwareConfig(TaskConfig):
+ """
+ handles imaging, user accounts, etc
+ """
+ image = models.CharField(max_length=100, default="defimage")
+ power = models.CharField(max_length=100, default="off")
+ hostname = models.CharField(max_length=100, default="hostname")
+ ipmi_create = models.BooleanField(default=False)
+ delta = models.TextField()
+
+ def to_dict(self):
+ d = {}
+ d['image'] = self.image
+ d['power'] = self.power
+ d['hostname'] = self.hostname
+ d['ipmi_create'] = str(self.ipmi_create)
+ d['id'] = self.hosthardwarerelation.host.labid
+ return d
+
+ def to_json(self):
+ return json.dumps(self.to_dict())
+
+ def get_delta(self):
+ if not self.delta:
+ self.delta = self.to_json()
+ self.save()
+ d = json.loads(self.delta)
+ d['lab_token'] = self.hosthardwarerelation.lab_token
+ return d
+
+ def clear_delta(self):
+ d = {}
+ d["id"] = self.hosthardwarerelation.host.labid
+ d["lab_token"] = self.hosthardwarerelation.lab_token
+ self.delta = json.dumps(d)
+
+ def set_image(self, image):
+ self.image = image
+ d = json.loads(self.delta)
+ d['image'] = self.image
+ self.delta = json.dumps(d)
+
+ def set_power(self, power):
+ self.power = power
+ d = json.loads(self.delta)
+ d['power'] = power
+ self.delta = json.dumps(d)
+
+ def set_hostname(self, hostname):
+ self.hostname = hostname
+ d = json.loads(self.delta)
+ d['hostname'] = hostname
+ self.delta = json.dumps(d)
+
+ def set_ipmi_create(self, ipmi_create):
+ self.ipmi_create = ipmi_create
+ d = json.loads(self.delta)
+ d['ipmi_create'] = ipmi_create
+ self.delta = json.dumps(d)
+
+
+class NetworkConfig(TaskConfig):
+ """
+ handles network configuration
+ """
+ interfaces = models.ManyToManyField(Interface)
+ delta = models.TextField()
+
+ def to_dict(self):
+ d = {}
+ hid = self.hostnetworkrelation.host.labid
+ d[hid] = {}
+ for interface in self.interfaces.all():
+ d[hid][interface.mac_address] = []
+ for vlan in interface.config.all():
+ d[hid][interface.mac_address].append({"vlan_id": vlan.vlan_id, "tagged": vlan.tagged})
+
+ return d
+
+ def to_json(self):
+ return json.dumps(self.to_dict())
+
+ def get_delta(self):
+ if not self.delta:
+ self.delta = self.to_json()
+ self.save()
+ d = json.loads(self.delta)
+ d['lab_token'] = self.hostnetworkrelation.lab_token
+ return d
+
+ def clear_delta(self):
+ pass
+
+ def add_interface(self, interface):
+ self.interfaces.add(interface)
+ d = json.loads(self.delta)
+ hid = self.hostnetworkrelation.host.labid
+ if hid not in d:
+ d[hid] = {}
+ d[hid][interface.mac_address] = []
+ for vlan in interface.config.all():
+ d[hid][interface.mac_address].append({"vlan_id": vlan.vlan_id, "tagged": vlan.tagged})
+ self.delta = json.dumps(d)
+
+
+def get_task(task_id):
+ for taskclass in [AccessRelation, SoftwareRelation, HostHardwareRelation, HostNetworkRelation]:
+ try:
+ ret = taskclass.objects.get(task_id=task_id)
+ return ret
+ except taskclass.DoesNotExist:
+ pass
+ from django.core.exceptions import ObjectDoesNotExist
+ raise ObjectDoesNotExist("Could not find matching TaskRelation instance")
+
+
+def get_task_uuid():
+ return str(uuid.uuid4())
+
+
+class TaskRelation(models.Model):
+ status = models.IntegerField(default=JobStatus.NEW)
+ job = models.ForeignKey(Job, on_delete=models.CASCADE)
+ config = models.OneToOneField(TaskConfig, on_delete=models.CASCADE)
+ task_id = models.CharField(default=get_task_uuid, max_length=37)
+ lab_token = models.CharField(default="null", max_length=50)
+ message = models.TextField(default="")
+
+ def delete(self, *args, **kwargs):
+ self.config.delete()
+ return super(self.__class__, self).delete(*args, **kwargs)
+
+ def type_str(self):
+ return "Generic Task"
+
+ class Meta:
+ abstract = True
+
+
+class AccessRelation(TaskRelation):
+ config = models.OneToOneField(AccessConfig, on_delete=models.CASCADE)
+
+ def type_str(self):
+ return "Access Task"
+
+ def delete(self, *args, **kwargs):
+ self.config.delete()
+ return super(self.__class__, self).delete(*args, **kwargs)
+
+
+class SoftwareRelation(TaskRelation):
+ config = models.OneToOneField(SoftwareConfig, on_delete=models.CASCADE)
+
+ def type_str(self):
+ return "Software Configuration Task"
+
+ def delete(self, *args, **kwargs):
+ self.config.delete()
+ return super(self.__class__, self).delete(*args, **kwargs)
+
+
+class HostHardwareRelation(TaskRelation):
+ host = models.ForeignKey(Host, on_delete=models.CASCADE)
+ config = models.OneToOneField(HardwareConfig, on_delete=models.CASCADE)
+
+ def type_str(self):
+ return "Hardware Configuration Task"
+
+ def get_delta(self):
+ return self.config.to_dict()
+
+ def delete(self, *args, **kwargs):
+ self.config.delete()
+ return super(self.__class__, self).delete(*args, **kwargs)
+
+
+class HostNetworkRelation(TaskRelation):
+ host = models.ForeignKey(Host, on_delete=models.CASCADE)
+ config = models.OneToOneField(NetworkConfig, on_delete=models.CASCADE)
+
+ def type_str(self):
+ return "Network Configuration Task"
+
+ def delete(self, *args, **kwargs):
+ self.config.delete()
+ return super(self.__class__, self).delete(*args, **kwargs)
+
+
+class JobFactory(object):
+
+ @classmethod
+ def makeCompleteJob(cls, booking):
+ hosts = Host.objects.filter(bundle=booking.resource)
+ job = None
+ try:
+ job = Job.objects.get(booking=booking)
+ except:
+ job = Job.objects.create(status=JobStatus.NEW, booking=booking)
+ cls.makeHardwareConfigs(
+ hosts=hosts,
+ job=job
+ )
+ cls.makeNetworkConfigs(
+ hosts=hosts,
+ job=job
+ )
+ cls.makeSoftware(
+ hosts=hosts,
+ job=job
+ )
+ cls.makeAccessConfig(
+ users=booking.collaborators.all(),
+ access_type="vpn",
+ revoke=False,
+ job=job
+ )
+ cls.makeAccessConfig(
+ users=[booking.owner],
+ access_type="vpn",
+ revoke=False,
+ job=job
+ )
+
+ @classmethod
+ def makeHardwareConfigs(cls, hosts=[], job=Job()):
+ for host in hosts:
+ hardware_config = None
+ try:
+ hardware_config = HardwareConfig.objects.get(relation__host=host)
+ except:
+ hardware_config = HardwareConfig()
+
+ relation = HostHardwareRelation()
+ relation.host = host
+ relation.job = job
+ relation.config = hardware_config
+ relation.config.save()
+ relation.config = relation.config
+ relation.save()
+
+ hardware_config.clear_delta()
+ hardware_config.set_image(host.config.image.lab_id)
+ hardware_config.set_hostname(host.template.resource.name)
+ hardware_config.set_power("on")
+ hardware_config.set_ipmi_create(True)
+ hardware_config.save()
+
+ @classmethod
+ def makeAccessConfig(cls, users, access_type, revoke=False, job=Job()):
+ for user in users:
+ relation = AccessRelation()
+ relation.job = job
+ config = AccessConfig()
+ config.access_type = access_type
+ config.user = user
+ config.save()
+ relation.config = config
+ relation.save()
+ config.clear_delta()
+ config.set_access_type(access_type)
+ config.set_revoke(revoke)
+ config.set_user(user)
+ config.save()
+
+ @classmethod
+ def makeNetworkConfigs(cls, hosts=[], job=Job()):
+ for host in hosts:
+ network_config = None
+ try:
+ network_config = NetworkConfig.objects.get(relation__host=host)
+ except:
+ network_config = NetworkConfig.objects.create()
+
+ relation = HostNetworkRelation()
+ relation.host = host
+ relation.job = job
+ network_config.save()
+ relation.config = network_config
+ relation.save()
+ network_config.clear_delta()
+
+ for interface in host.interfaces.all():
+ network_config.add_interface(interface)
+ network_config.save()
+
+ @classmethod
+ def makeSoftware(cls, hosts=[], job=Job()):
+ def init_config(host):
+ opnfv_config = OpnfvApiConfig()
+ if host is not None:
+ opnfv = host.config.bundle.opnfv_config.first()
+ opnfv_config.installer = opnfv.installer.name
+ opnfv_config.scenario = opnfv.scenario.name
+ opnfv_config.save()
+ return opnfv_config
+
+ try:
+ host = None
+ if len(hosts) > 0:
+ host = hosts[0]
+ opnfv_config = init_config(host)
+
+ for host in hosts:
+ opnfv_config.roles.add(host)
+ software_config = SoftwareConfig.objects.create(opnfv=opnfv_config)
+ software_config.save()
+ software_relation = SoftwareRelation.objects.create(job=job, config=software_config)
+ software_relation.save()
+ return software_relation
+ except:
+ return None
+
+ def makeAccess(cls, user, access_type, revoke):
+ pass
diff --git a/dashboard/src/api/serializers.py b/dashboard/src/api/serializers.py
deleted file mode 100644
index 10e1975..0000000
--- a/dashboard/src/api/serializers.py
+++ /dev/null
@@ -1,53 +0,0 @@
-##############################################################################
-# Copyright (c) 2016 Max Breitenfeldt and others.
-#
-# All rights reserved. This program and the accompanying materials
-# are made available under the terms of the Apache License, Version 2.0
-# which accompanies this distribution, and is available at
-# http://www.apache.org/licenses/LICENSE-2.0
-##############################################################################
-
-
-from rest_framework import serializers
-
-from account.models import UserProfile
-from notifier.models import Notifier
-from booking.models import Booking
-from dashboard.models import Server, Resource, ResourceStatus
-
-class BookingSerializer(serializers.ModelSerializer):
- installer_name = serializers.CharField(source='installer.name')
- scenario_name = serializers.CharField(source='scenario.name')
- opsys_name = serializers.CharField(source='opsys.name')
-
- class Meta:
- model = Booking
- fields = ('id', 'changeid', 'reset', 'user', 'resource_id', 'opsys_name', 'start', 'end', 'installer_name', 'scenario_name', 'purpose')
-
-
-class ServerSerializer(serializers.ModelSerializer):
- class Meta:
- model = Server
- fields = ('id', 'resource_id', 'name', 'model', 'cpu', 'ram', 'storage')
-
-
-class ResourceSerializer(serializers.ModelSerializer):
- class Meta:
- model = Resource
- fields = ('id', 'name', 'description', 'resource_lab', 'url', 'server_set', 'dev_pod')
-
-class ResourceStatusSerializer(serializers.ModelSerializer):
- class Meta:
- model = ResourceStatus
- fields = ('id', 'resource', 'timestamp','type', 'title', 'content')
-
-class NotifierSerializer(serializers.ModelSerializer):
- class Meta:
- model = Notifier
- fields = ('id', 'title', 'content', 'user', 'sender', 'message_type', 'msg_sent')
-
-class UserSerializer(serializers.ModelSerializer):
- username = serializers.CharField(source='user.username')
- class Meta:
- model = UserProfile
- fields = ('user', 'username', 'ssh_public_key', 'pgp_public_key', 'email_addr')
diff --git a/dashboard/src/jenkins/__init__.py b/dashboard/src/api/serializers/__init__.py
index b5914ce..e0408fa 100644
--- a/dashboard/src/jenkins/__init__.py
+++ b/dashboard/src/api/serializers/__init__.py
@@ -1,10 +1,8 @@
##############################################################################
-# Copyright (c) 2016 Max Breitenfeldt and others.
+# Copyright (c) 2018 Parker Berberian, Sawyer Bergeron, and others.
#
# All rights reserved. This program and the accompanying materials
# are made available under the terms of the Apache License, Version 2.0
# which accompanies this distribution, and is available at
# http://www.apache.org/licenses/LICENSE-2.0
##############################################################################
-
-
diff --git a/dashboard/src/api/serializers/booking_serializer.py b/dashboard/src/api/serializers/booking_serializer.py
new file mode 100644
index 0000000..e891de4
--- /dev/null
+++ b/dashboard/src/api/serializers/booking_serializer.py
@@ -0,0 +1,156 @@
+##############################################################################
+# Copyright (c) 2018 Parker Berberian, Sawyer Bergeron, and others.
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+##############################################################################
+
+
+from rest_framework import serializers
+
+from resource_inventory.models import *
+
+class BookingField(serializers.Field):
+
+ def to_representation(self, booking):
+ """
+ Takes in a booking object.
+ Returns a dictionary of primitives representing that booking
+ """
+ ser = {}
+ ser['id'] = booking.id
+ # main loop to grab relevant info out of booking
+ host_configs = {} # mapping hostname -> config
+ networks = {} # mapping vlan id -> network_hosts
+ for host in booking.resource.hosts.all():
+ host_configs[host.name] = HostConfiguration.objects.get(host=host.template)
+ if "jumphost" not in ser and host_configs[host.name].opnfvRole.name.lower() == "jumphost":
+ ser['jumphost'] = host.name
+ #host is a Host model
+ for i in range(len(host.interfaces.all())):
+ interface = host.interfaces.all()[i]
+ #interface is an Interface model
+ for vlan in interface.config.all():
+ #vlan is Vlan model
+ if vlan.id not in networks:
+ networks[vlan.id] = []
+ net_host = {"hostname": host.name, "tagged": vlan.tagged, "interface":i}
+ networks[vlan.id].append(net_host)
+ #creates networking object of proper form
+ networking = []
+ for vlanid in networks:
+ network = {}
+ network['vlan_id'] = vlanid
+ network['hosts'] = networks[vlanid]
+
+ ser['networking'] = networking
+
+ #creates hosts object of correct form
+ hosts = []
+ for hostname in host_configs:
+ host = {"hostname": hostname}
+ host['deploy_image'] = True # TODO?
+ image = host_configs[hostname].image
+ host['image'] = {
+ "name": image.name,
+ "lab_id": image.lab_id,
+ "dashboard_id": image.id
+ }
+ hosts.append(host)
+
+ ser['hosts'] = hosts
+
+ return ser
+
+ def to_internal_value(self, data):
+ """
+ Takes in a dictionary of primitives
+ Returns a booking object
+
+ This is not going to be implemented or allowed.
+ If someone needs to create a booking through the api,
+ they will send a different booking object
+ """
+ return None
+
+class BookingSerializer(serializers.Serializer):
+
+ booking = BookingField()
+
+#Host Type stuff, for inventory
+
+class CPUSerializer(serializers.ModelSerializer):
+ class Meta:
+ model = CpuProfile
+ fields = ('cores', 'architecture', 'cpus')
+
+class DiskSerializer(serializers.ModelSerializer):
+ class Meta:
+ model = DiskProfile
+ fields = ('size', 'media_type', 'name')
+
+class InterfaceProfileSerializer(serializers.ModelSerializer):
+ class Meta:
+ model = InterfaceProfile
+ fields = ('speed', 'name')
+
+class RamSerializer(serializers.ModelSerializer):
+ class Meta:
+ model = RamProfile
+ fields = ('amount', 'channels')
+
+class HostTypeSerializer(serializers.Serializer):
+ name = serializers.CharField(max_length=200)
+ ram = RamSerializer()
+ interface = InterfaceProfileSerializer()
+ description = serializers.CharField(max_length=1000)
+ disks = DiskSerializer()
+ cpu = CPUSerializer()
+
+#the rest of the inventory stuff
+class NetworkSerializer(serializers.Serializer):
+ cidr = serializers.CharField(max_length=200)
+ gateway = serializers.IPAddressField(max_length=200)
+ vlan = serializers.IntegerField()
+
+class ImageSerializer(serializers.ModelSerializer):
+ lab_id = serializers.IntegerField()
+ id = serializers.IntegerField(source="dashboard_id")
+ name = serializers.CharField(max_length=50)
+ description = serializers.CharField(max_length=200)
+ class Meta:
+ model = Image
+
+class InterfaceField(serializers.Field):
+ def to_representation(self, interface):
+ pass
+
+ def to_internal_value(self, data):
+ """
+ takes in a serialized interface and creates an Interface model
+ """
+ mac = data['mac']
+ bus_address = data['busaddr']
+ switch_name = data['switchport']['switch_name']
+ port_name = data['switchport']['port_name']
+ # TODO config??
+ return Interface.objects.create(
+ mac_address=mac,
+ bus_address=bus_address,
+ switch_name=switch_name,
+ port_name=port_name
+ )
+
+class InventoryHostSerializer(serializers.Serializer):
+ hostname = serializers.CharField(max_length=100)
+ host_type = serializers.CharField(max_length=100)
+ interfaces = InterfaceField()
+
+
+class InventorySerializer(serializers.Serializer):
+ hosts = InventoryHostSerializer()
+ networks = NetworkSerializer()
+ images = ImageSerializer()
+ host_types = HostTypeSerializer()
diff --git a/dashboard/src/api/serializers/old_serializers.py b/dashboard/src/api/serializers/old_serializers.py
new file mode 100644
index 0000000..f50b90b
--- /dev/null
+++ b/dashboard/src/api/serializers/old_serializers.py
@@ -0,0 +1,28 @@
+##############################################################################
+# Copyright (c) 2016 Max Breitenfeldt and others.
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+##############################################################################
+
+
+from rest_framework import serializers
+
+from account.models import UserProfile
+from notifier.models import Notifier
+
+
+class NotifierSerializer(serializers.ModelSerializer):
+ class Meta:
+ model = Notifier
+ fields = ('id', 'title', 'content', 'user', 'sender', 'message_type', 'msg_sent')
+
+
+class UserSerializer(serializers.ModelSerializer):
+ username = serializers.CharField(source='user.username')
+
+ class Meta:
+ model = UserProfile
+ fields = ('user', 'username', 'ssh_public_key', 'pgp_public_key', 'email_addr')
diff --git a/dashboard/src/api/tests/__init__.py b/dashboard/src/api/tests/__init__.py
new file mode 100644
index 0000000..fe2a32d
--- /dev/null
+++ b/dashboard/src/api/tests/__init__.py
@@ -0,0 +1,8 @@
+##############################################################################
+# Copyright (c) 2016 Parker Berberian and others.
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+############################################################################## \ No newline at end of file
diff --git a/dashboard/src/api/tests/test_serializers.py b/dashboard/src/api/tests/test_serializers.py
new file mode 100644
index 0000000..c49010c
--- /dev/null
+++ b/dashboard/src/api/tests/test_serializers.py
@@ -0,0 +1,213 @@
+##############################################################################
+# Copyright (c) 2018 Sawyer Bergeron and others.
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+##############################################################################
+from django.test import TestCase
+from booking.models import Booking
+from resource_inventory.models import *
+from account.models import Lab
+from api.serializers.booking_serializer import *
+from datetime import timedelta
+from django.utils import timezone
+from django.contrib.auth.models import Permission, User
+
+
+class BookingSerializerTestCase(TestCase):
+
+ count = 0
+
+ def makeHostConfigurations(self, hosts, config):
+ lab_user = User.objects.create(username="asfasdfasdf")
+ owner = User.objects.create(username="asfasdfasdffffff")
+ lab = Lab.objects.create(
+ lab_user=lab_user,
+ name="TestLab123123",
+ contact_email="mail@email.com",
+ contact_phone=""
+ )
+ jumphost=True
+ for host in hosts:
+ image = Image.objects.create(
+ lab_id=12,
+ from_lab=lab,
+ name="this is a test image",
+ owner=owner
+ )
+ name = "jumphost"
+ if not jumphost:
+ name = "compute"
+ role = OPNFVRole.objects.create(
+ name=name,
+ description="stuff"
+ )
+
+ HostConfiguration.objects.create(
+ host=host,
+ image=image,
+ bundle=config,
+ opnfvRole=role
+ )
+ jumphost=False
+
+
+ def setUp(self):
+ self.serializer = BookingField()
+ lab_user = User.objects.create(username="lab user")
+ lab = Lab.objects.create(name="test lab", lab_user=lab_user)
+ # create hostProfile
+ hostProfile = HostProfile.objects.create(
+ host_type=0,
+ name='Test profile',
+ description='a test profile'
+ )
+ interfaceProfile = InterfaceProfile.objects.create(
+ speed=1000,
+ name='eno3',
+ host=hostProfile
+ )
+ diskProfile = DiskProfile.objects.create(
+ size=1000,
+ media_type="SSD",
+ name='/dev/sda',
+ host=hostProfile
+ )
+ cpuProfile = CpuProfile.objects.create(
+ cores=96,
+ architecture="x86_64",
+ cpus=2,
+ host=hostProfile
+ )
+ ramProfile = RamProfile.objects.create(
+ amount=256,
+ channels=4,
+ host=hostProfile
+ )
+
+ #create GenericResourceBundle
+ genericBundle = GenericResourceBundle.objects.create()
+
+ gres1 = GenericResource.objects.create(
+ bundle=genericBundle,
+ name='generic resource ' + str(self.count)
+ )
+ self.count += 1
+ gHost1 = GenericHost.objects.create(
+ resource=gres1,
+ profile=hostProfile
+ )
+
+ gres2 = GenericResource.objects.create(
+ bundle=genericBundle,
+ name='generic resource ' + str(self.count)
+ )
+ self.count += 1
+ gHost2 = GenericHost.objects.create(
+ resource=gres2,
+ profile=hostProfile
+ )
+ user1 = User.objects.create(username='user1')
+
+ add_booking_perm = Permission.objects.get(codename='add_booking')
+ user1.user_permissions.add(add_booking_perm)
+
+ user1 = User.objects.get(pk=user1.id)
+
+ conf = ConfigBundle.objects.create(owner=user1, name="test conf")
+ self.makeHostConfigurations([gHost1, gHost2], conf)
+
+ #actual resource bundle
+ bundle = ResourceBundle.objects.create(
+ template = genericBundle
+ )
+
+ host1 = Host.objects.create(
+ template=gHost1,
+ booked=True,
+ name='host1',
+ bundle=bundle,
+ profile=hostProfile,
+ lab=lab
+ )
+
+ host2 = Host.objects.create(
+ template=gHost2,
+ booked=True,
+ name='host2',
+ bundle=bundle,
+ profile=hostProfile,
+ lab=lab
+ )
+
+ vlan1 = Vlan.objects.create(vlan_id=300, tagged=False)
+ vlan2 = Vlan.objects.create(vlan_id=300, tagged=False)
+
+ iface1 = Interface.objects.create(
+ mac_address='00:11:22:33:44:55',
+ bus_address='some bus address',
+ switch_name='switch1',
+ port_name='port10',
+ host=host1
+ )
+
+ iface1.config = [vlan1]
+
+ iface2 = Interface.objects.create(
+ mac_address='00:11:22:33:44:56',
+ bus_address='some bus address',
+ switch_name='switch1',
+ port_name='port12',
+ host=host2
+ )
+
+ iface2.config = [vlan2]
+
+ # finally, can create booking
+ self.booking = Booking.objects.create(
+ owner=user1,
+ start = timezone.now(),
+ end = timezone.now() + timedelta(weeks=1),
+ purpose='Testing',
+ resource=bundle,
+ config_bundle=conf
+ )
+
+ serialized_booking = {}
+
+ host1 = {}
+ host1['hostname'] = 'host1'
+ host1['image'] = {} # TODO: Images
+ host1['deploy_image'] = True
+ host2 = {}
+ host2['hostname'] = 'host2'
+ host2['image'] = {} # TODO: Images
+ host2['deploy_image'] = True
+
+ serialized_booking['hosts'] = [host1, host2]
+
+ net = {}
+ net['name'] = 'network_name'
+ net['vlan_id'] = 300
+ netHost1 = {}
+ netHost1['hostname'] = 'host1'
+ netHost1['tagged'] = False
+ netHost1['interface'] = 0
+ netHost2 = {}
+ netHost2['hostname'] = 'host2'
+ netHost2['tagged'] = False
+ netHost2['interface'] = 0
+ net['hosts'] = [netHost1, netHost2]
+
+ serialized_booking['networking'] = [net]
+ serialized_booking['jumphost'] = 'host1'
+
+ self.serialized_booking = serialized_booking
+
+ def test_to_representation(self):
+ keys = ['hosts', 'networking', 'jumphost']
+ serialized_form = self.serializer.to_representation(self.booking)
+ for key in keys:
+ self.assertEquals(serialized_form[key], self.serialized_booking)
diff --git a/dashboard/src/api/urls.py b/dashboard/src/api/urls.py
index c2cd510..94f8279 100644
--- a/dashboard/src/api/urls.py
+++ b/dashboard/src/api/urls.py
@@ -1,5 +1,6 @@
##############################################################################
# Copyright (c) 2016 Max Breitenfeldt and others.
+# Copyright (c) 2018 Sawyer Bergeron, Parker Berberian, and others
#
# All rights reserved. This program and the accompanying materials
# are made available under the terms of the Apache License, Version 2.0
@@ -24,19 +25,25 @@ Including another URLconf
2. Add a URL to urlpatterns: url(r'^blog/', include('blog.urls'))
"""
from django.conf.urls import url, include
+from django.urls import path
from rest_framework import routers
from api.views import *
router = routers.DefaultRouter()
-router.register(r'resources', ResourceViewSet)
-router.register(r'servers', ServerViewSet)
router.register(r'bookings', BookingViewSet)
-router.register(r'resource_status', ResourceStatusViewSet)
router.register(r'notifier', NotifierViewSet)
router.register(r'user', UserViewSet)
urlpatterns = [
url(r'^', include(router.urls)),
+ path('labs/<slug:lab_name>/profile', lab_profile),
+ path('labs/<slug:lab_name>/status', lab_status),
+ path('labs/<slug:lab_name>/inventory', lab_inventory),
+ path('labs/<slug:lab_name>/jobs/<int:job_id>', specific_job),
+ path('labs/<slug:lab_name>/jobs/<int:job_id>/<slug:task_id>', specific_task),
+ path('labs/<slug:lab_name>/jobs/new', new_jobs),
+ path('labs/<slug:lab_name>/jobs/current', current_jobs),
+ path('labs/<slug:lab_name>/jobs/done', done_jobs),
url(r'^token$', GenerateTokenView.as_view(), name='generate_token'),
]
diff --git a/dashboard/src/api/views.py b/dashboard/src/api/views.py
index b873ef1..cefd131 100644
--- a/dashboard/src/api/views.py
+++ b/dashboard/src/api/views.py
@@ -1,5 +1,6 @@
##############################################################################
# Copyright (c) 2016 Max Breitenfeldt and others.
+# Copyright (c) 2018 Parker Berberian, Sawyer Bergeron, and others.
#
# All rights reserved. This program and the accompanying materials
# are made available under the terms of the Apache License, Version 2.0
@@ -8,17 +9,23 @@
##############################################################################
-from django.contrib.auth.decorators import login_required, user_passes_test
+from django.contrib.auth.decorators import login_required
from django.shortcuts import redirect
from django.utils.decorators import method_decorator
from django.views import View
+from django.http.response import JsonResponse
from rest_framework import viewsets
from rest_framework.authtoken.models import Token
+from django.views.decorators.csrf import csrf_exempt
-from api.serializers import *
+import json
+
+from api.serializers.booking_serializer import *
+from api.serializers.old_serializers import NotifierSerializer, UserSerializer
from account.models import UserProfile
from booking.models import Booking
-from dashboard.models import Resource, Server, ResourceStatus
+from notifier.models import Notifier
+from api.models import *
class BookingViewSet(viewsets.ModelViewSet):
@@ -27,29 +34,16 @@ class BookingViewSet(viewsets.ModelViewSet):
filter_fields = ('resource', 'id')
-class ServerViewSet(viewsets.ModelViewSet):
- queryset = Server.objects.all()
- serializer_class = ServerSerializer
- filter_fields = ('resource', 'name')
-
-
-class ResourceViewSet(viewsets.ModelViewSet):
- queryset = Resource.objects.all()
- serializer_class = ResourceSerializer
- filter_fields = ('name', 'id')
-
-class ResourceStatusViewSet(viewsets.ModelViewSet):
- queryset = ResourceStatus.objects.all()
- serializer_class = ResourceStatusSerializer
-
class NotifierViewSet(viewsets.ModelViewSet):
queryset = Notifier.objects.none()
serializer_class = NotifierSerializer
+
class UserViewSet(viewsets.ModelViewSet):
queryset = UserProfile.objects.all()
serializer_class = UserSerializer
+
@method_decorator(login_required, name='dispatch')
class GenerateTokenView(View):
def get(self, request, *args, **kwargs):
@@ -59,3 +53,74 @@ class GenerateTokenView(View):
token.delete()
Token.objects.create(user=user)
return redirect('account:settings')
+
+
+def lab_inventory(request, lab_name=""):
+ lab_token = request.META.get('HTTP_AUTH_TOKEN')
+ lab_manager = LabManagerTracker.get(lab_name, lab_token)
+ return JsonResponse(lab_manager.get_inventory(), safe=False)
+
+
+def lab_status(request, lab_name=""):
+ lab_token = request.META.get('HTTP_AUTH_TOKEN')
+ lab_manager = LabManagerTracker.get(lab_name, lab_token)
+ if request.method == "POST":
+ return JsonResponse(lab_manager.set_status(request.POST), safe=False)
+ return JsonResponse(lab_manager.get_status(), safe=False)
+
+
+def lab_profile(request, lab_name=""):
+ lab_token = request.META.get('HTTP_AUTH_TOKEN')
+ lab_manager = LabManagerTracker.get(lab_name, lab_token)
+ return JsonResponse(lab_manager.get_profile(), safe=False)
+
+
+@csrf_exempt
+def specific_task(request, lab_name="", job_id="", task_id=""):
+ lab_token = request.META.get('HTTP_AUTH_TOKEN')
+ LabManagerTracker.get(lab_name, lab_token) # Authorize caller, but we dont need the result
+
+ if request.method == "POST":
+ task = get_task(task_id)
+ if 'status' in request.POST:
+ task.status = request.POST.get('status')
+ if 'message' in request.POST:
+ task.message = request.POST.get('message')
+ task.save()
+ d = {}
+ d['task'] = task.config.get_delta()
+ m = {}
+ m['status'] = task.status
+ m['job'] = str(task.job)
+ m['message'] = task.message
+ d['meta'] = m
+ response = json.dumps(d)
+ return JsonResponse(response)
+ elif request.method == "GET":
+ return JsonResponse(get_task(task_id).config.get_delta())
+
+
+def specific_job(request, lab_name="", job_id=""):
+ lab_token = request.META.get('HTTP_AUTH_TOKEN')
+ lab_manager = LabManagerTracker.get(lab_name, lab_token)
+ if request.method == "POST":
+ return JsonResponse(lab_manager.update_job(job_id, request.POST), safe=False)
+ return JsonResponse(lab_manager.get_job(job_id), safe=False)
+
+
+def new_jobs(request, lab_name=""):
+ lab_token = request.META.get('HTTP_AUTH_TOKEN')
+ lab_manager = LabManagerTracker.get(lab_name, lab_token)
+ return JsonResponse(lab_manager.get_new_jobs(), safe=False)
+
+
+def current_jobs(request, lab_name=""):
+ lab_token = request.META.get('HTTP_AUTH_TOKEN')
+ lab_manager = LabManagerTracker.get(lab_name, lab_token)
+ return JsonResponse(lab_manager.get_current_jobs(), safe=False)
+
+
+def done_jobs(request, lab_name=""):
+ lab_token = request.META.get('HTTP_AUTH_TOKEN')
+ lab_manager = LabManagerTracker.get(lab_name, lab_token)
+ return JsonResponse(lab_manager.get_done_jobs(), safe=False)
diff --git a/dashboard/src/booking/__init__.py b/dashboard/src/booking/__init__.py
index b5914ce..b6fef6c 100644
--- a/dashboard/src/booking/__init__.py
+++ b/dashboard/src/booking/__init__.py
@@ -6,5 +6,3 @@
# which accompanies this distribution, and is available at
# http://www.apache.org/licenses/LICENSE-2.0
##############################################################################
-
-
diff --git a/dashboard/src/booking/admin.py b/dashboard/src/booking/admin.py
index 51e1031..2beb05b 100644
--- a/dashboard/src/booking/admin.py
+++ b/dashboard/src/booking/admin.py
@@ -1,5 +1,6 @@
##############################################################################
# Copyright (c) 2016 Max Breitenfeldt and others.
+# Copyright (c) 2018 Parker Berberian, Sawyer Bergeron, and others.
#
# All rights reserved. This program and the accompanying materials
# are made available under the terms of the Apache License, Version 2.0
@@ -13,6 +14,3 @@ from django.contrib import admin
from booking.models import *
admin.site.register(Booking)
-admin.site.register(Opsys)
-admin.site.register(Installer)
-admin.site.register(Scenario)
diff --git a/dashboard/src/booking/forms.py b/dashboard/src/booking/forms.py
deleted file mode 100644
index 9d71b42..0000000
--- a/dashboard/src/booking/forms.py
+++ /dev/null
@@ -1,65 +0,0 @@
-##############################################################################
-# Copyright (c) 2016 Max Breitenfeldt and others.
-#
-# All rights reserved. This program and the accompanying materials
-# are made available under the terms of the Apache License, Version 2.0
-# which accompanies this distribution, and is available at
-# http://www.apache.org/licenses/LICENSE-2.0
-##############################################################################
-
-
-import django.forms as forms
-
-from booking.models import Installer, Scenario, Opsys
-from datetime import datetime
-
-class BookingForm(forms.Form):
- fields = ['start', 'end', 'purpose', 'opsys', 'reset', 'installer', 'scenario']
-
- start = forms.DateTimeField()
- end = forms.DateTimeField()
- reset = forms.ChoiceField(choices = ((True, 'Yes'),(False, 'No')), label="Reset System", initial='False', required=False)
- purpose = forms.CharField(max_length=300)
- opsys = forms.ModelChoiceField(queryset=Opsys.objects.all(), required=False)
- opsys.label = "Operating System"
- installer = forms.ModelChoiceField(queryset=Installer.objects.all(), required=False)
- scenario = forms.ModelChoiceField(queryset=Scenario.objects.all(), required=False)
-
-class BookingEditForm(forms.Form):
- fields = ['start', 'end', 'purpose', 'opsys', 'reset', 'installer', 'scenario']
-
- start = forms.DateTimeField()
- end = forms.DateTimeField()
- purpose = forms.CharField(max_length=300)
- opsys = forms.ModelChoiceField(queryset=Opsys.objects.all(), required=False)
- installer = forms.ModelChoiceField(queryset=Installer.objects.all(), required=False)
- scenario = forms.ModelChoiceField(queryset=Scenario.objects.all(), required=False)
- reset = forms.ChoiceField(choices = ((True, 'Yes'),(False, 'No')), label="Reset System", initial='False', required=True)
-
-
- def __init__(self, *args, **kwargs ):
- cloned_kwargs = {}
- cloned_kwargs['purpose'] = kwargs.pop('purpose')
- cloned_kwargs['start'] = kwargs.pop('start')
- cloned_kwargs['end'] = kwargs.pop('end')
- if 'installer' in kwargs:
- cloned_kwargs['installer'] = kwargs.pop('installer')
- if 'scenario' in kwargs:
- cloned_kwargs['scenario'] = kwargs.pop('scenario')
- super(BookingEditForm, self).__init__( *args, **kwargs)
-
- self.fields['purpose'].initial = cloned_kwargs['purpose']
- self.fields['start'].initial = cloned_kwargs['start'].strftime('%m/%d/%Y %H:%M')
- self.fields['end'].initial = cloned_kwargs['end'].strftime('%m/%d/%Y %H:%M')
- try:
- self.fields['installer'].initial = cloned_kwargs['installer'].id
- except KeyError:
- pass
- except AttributeError:
- pass
- try:
- self.fields['scenario'].initial = cloned_kwargs['scenario'].id
- except KeyError:
- pass
- except AttributeError:
- pass
diff --git a/dashboard/src/booking/migrations/0001_initial.py b/dashboard/src/booking/migrations/0001_initial.py
index 6932dae..20415fe 100644
--- a/dashboard/src/booking/migrations/0001_initial.py
+++ b/dashboard/src/booking/migrations/0001_initial.py
@@ -1,6 +1,4 @@
-# -*- coding: utf-8 -*-
-# Generated by Django 1.10 on 2016-11-03 13:33
-from __future__ import unicode_literals
+# Generated by Django 2.1 on 2018-09-14 14:48
from django.conf import settings
from django.db import migrations, models
@@ -12,8 +10,9 @@ class Migration(migrations.Migration):
initial = True
dependencies = [
- ('dashboard', '0001_initial'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
+ ('account', '0001_initial'),
+ ('resource_inventory', '__first__'),
]
operations = [
@@ -23,9 +22,17 @@ class Migration(migrations.Migration):
('id', models.AutoField(primary_key=True, serialize=False)),
('start', models.DateTimeField()),
('end', models.DateTimeField()),
- ('jira_issue_id', models.IntegerField(null=True)),
- ('jira_issue_status', models.CharField(max_length=50)),
+ ('reset', models.BooleanField(default=False)),
+ ('jira_issue_id', models.IntegerField(blank=True, null=True)),
+ ('jira_issue_status', models.CharField(blank=True, max_length=50)),
('purpose', models.CharField(max_length=300)),
+ ('ext_count', models.IntegerField(default=2)),
+ ('project', models.CharField(blank=True, default='', max_length=100, null=True)),
+ ('collaborators', models.ManyToManyField(related_name='collaborators', to=settings.AUTH_USER_MODEL)),
+ ('config_bundle', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='resource_inventory.ConfigBundle')),
+ ('lab', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='account.Lab')),
+ ('owner', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='owner', to=settings.AUTH_USER_MODEL)),
+ ('resource', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='resource_inventory.ResourceBundle')),
],
options={
'db_table': 'booking',
@@ -39,6 +46,14 @@ class Migration(migrations.Migration):
],
),
migrations.CreateModel(
+ name='Opsys',
+ fields=[
+ ('id', models.AutoField(primary_key=True, serialize=False)),
+ ('name', models.CharField(max_length=100)),
+ ('sup_installers', models.ManyToManyField(blank=True, to='booking.Installer')),
+ ],
+ ),
+ migrations.CreateModel(
name='Scenario',
fields=[
('id', models.AutoField(primary_key=True, serialize=False)),
@@ -46,23 +61,8 @@ class Migration(migrations.Migration):
],
),
migrations.AddField(
- model_name='booking',
- name='installer',
- field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.DO_NOTHING, to='booking.Installer'),
- ),
- migrations.AddField(
- model_name='booking',
- name='resource',
- field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='dashboard.Resource'),
- ),
- migrations.AddField(
- model_name='booking',
- name='scenario',
- field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.DO_NOTHING, to='booking.Scenario'),
- ),
- migrations.AddField(
- model_name='booking',
- name='user',
- field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL),
+ model_name='installer',
+ name='sup_scenarios',
+ field=models.ManyToManyField(blank=True, to='booking.Scenario'),
),
]
diff --git a/dashboard/src/booking/migrations/0002_booking_changeid.py b/dashboard/src/booking/migrations/0002_booking_changeid.py
deleted file mode 100644
index 33af8fd..0000000
--- a/dashboard/src/booking/migrations/0002_booking_changeid.py
+++ /dev/null
@@ -1,38 +0,0 @@
-# -*- coding: utf-8 -*-
-# Generated by Django 1.10 on 2017-12-13 15:06
-from __future__ import unicode_literals
-
-from django.db import migrations, models
-import django.db.models.deletion
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('booking', '0001_initial'),
- ]
-
- operations = [
- migrations.CreateModel(
- name='Opsys',
- fields=[
- ('id', models.AutoField(primary_key=True, serialize=False)),
- ('name', models.CharField(max_length=100)),
- ],
- ),
- migrations.AddField(
- model_name='booking',
- name='opsys',
- field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.DO_NOTHING, to='booking.Opsys'),
- ),
- migrations.AddField(
- model_name='booking',
- name='changeid',
- field=models.TextField(default='no change ID'),
- ),
- migrations.AlterField(
- model_name='booking',
- name='changeid',
- field=models.TextField(blank=True, default='no change ID', null=True),
- ),
- ]
diff --git a/dashboard/src/booking/migrations/0003_auto_20180108_2024.py b/dashboard/src/booking/migrations/0003_auto_20180108_2024.py
deleted file mode 100644
index 93cecc2..0000000
--- a/dashboard/src/booking/migrations/0003_auto_20180108_2024.py
+++ /dev/null
@@ -1,25 +0,0 @@
-# -*- coding: utf-8 -*-
-# Generated by Django 1.10 on 2018-01-08 20:24
-from __future__ import unicode_literals
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('booking', '0002_booking_changeid'),
- ]
-
- operations = [
- migrations.AddField(
- model_name='booking',
- name='reset',
- field=models.BooleanField(default=False),
- ),
- migrations.AlterField(
- model_name='booking',
- name='changeid',
- field=models.TextField(blank=True, default='initial', null=True),
- ),
- ] \ No newline at end of file
diff --git a/dashboard/src/booking/migrations/0004_booking_ext_count.py b/dashboard/src/booking/migrations/0004_booking_ext_count.py
deleted file mode 100644
index 6bcc3ce..0000000
--- a/dashboard/src/booking/migrations/0004_booking_ext_count.py
+++ /dev/null
@@ -1,27 +0,0 @@
-##############################################################################
-# Copyright (c) 2018 Sawyer Bergeron and others.
-#
-# All rights reserved. This program and the accompanying materials
-# are made available under the terms of the Apache License, Version 2.0
-# which accompanies this distribution, and is available at
-# http://www.apache.org/licenses/LICENSE-2.0
-##############################################################################
-
-from __future__ import unicode_literals
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('booking', '0003_auto_20180108_2024'),
- ]
-
- operations = [
- migrations.AddField(
- model_name='booking',
- name='ext_count',
- field=models.IntegerField(default=2),
- ),
- ]
diff --git a/dashboard/src/booking/migrations/__init__.py b/dashboard/src/booking/migrations/__init__.py
index b5914ce..e69de29 100644
--- a/dashboard/src/booking/migrations/__init__.py
+++ b/dashboard/src/booking/migrations/__init__.py
@@ -1,10 +0,0 @@
-##############################################################################
-# Copyright (c) 2016 Max Breitenfeldt and others.
-#
-# All rights reserved. This program and the accompanying materials
-# are made available under the terms of the Apache License, Version 2.0
-# which accompanies this distribution, and is available at
-# http://www.apache.org/licenses/LICENSE-2.0
-##############################################################################
-
-
diff --git a/dashboard/src/booking/models.py b/dashboard/src/booking/models.py
index 8762d46..adfc28d 100644
--- a/dashboard/src/booking/models.py
+++ b/dashboard/src/booking/models.py
@@ -1,5 +1,6 @@
##############################################################################
# Copyright (c) 2016 Max Breitenfeldt and others.
+# Copyright (c) 2018 Sawyer Bergeron, Parker Berberian, and others.
#
# All rights reserved. This program and the accompanying materials
# are made available under the terms of the Apache License, Version 2.0
@@ -8,67 +9,61 @@
##############################################################################
+from resource_inventory.models import ResourceBundle, ConfigBundle
+from account.models import Lab
from django.conf import settings
from django.contrib.auth.models import User
from django.db import models
from jira import JIRA
from jira import JIRAError
-from django.utils.crypto import get_random_string
-import hashlib
+import resource_inventory.resource_manager
-from dashboard.models import Resource
-
-class Installer(models.Model):
+class Scenario(models.Model):
id = models.AutoField(primary_key=True)
- name = models.CharField(max_length=30)
+ name = models.CharField(max_length=300)
def __str__(self):
return self.name
-class Scenario(models.Model):
+
+class Installer(models.Model):
id = models.AutoField(primary_key=True)
- name = models.CharField(max_length=300)
+ name = models.CharField(max_length=30)
+ sup_scenarios = models.ManyToManyField(Scenario, blank=True)
def __str__(self):
return self.name
+
class Opsys(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=100)
+ sup_installers = models.ManyToManyField(Installer, blank=True)
def __str__(self):
return self.name
+
class Booking(models.Model):
id = models.AutoField(primary_key=True)
- changeid = models.TextField(default='initial', blank=True, null=True)
- user = models.ForeignKey(User, models.CASCADE) # delete if user is deleted
- resource = models.ForeignKey(Resource, models.PROTECT)
+ owner = models.ForeignKey(User, models.CASCADE, related_name='owner') # delete if user is deleted
+ collaborators = models.ManyToManyField(User, related_name='collaborators')
start = models.DateTimeField()
end = models.DateTimeField()
reset = models.BooleanField(default=False)
- jira_issue_id = models.IntegerField(null=True)
- jira_issue_status = models.CharField(max_length=50)
-
- opsys = models.ForeignKey(Opsys, models.DO_NOTHING, null=True)
- installer = models.ForeignKey(Installer, models.DO_NOTHING, null=True)
- scenario = models.ForeignKey(Scenario, models.DO_NOTHING, null=True)
+ jira_issue_id = models.IntegerField(null=True, blank=True)
+ jira_issue_status = models.CharField(max_length=50, blank=True)
purpose = models.CharField(max_length=300, blank=False)
ext_count = models.IntegerField(default=2)
+ resource = models.ForeignKey(ResourceBundle, on_delete=models.SET_NULL, null=True) #need to decide behavior here on delete
+ config_bundle = models.ForeignKey(ConfigBundle, on_delete=models.SET_NULL, null=True)
+ project = models.CharField(max_length=100, default="", blank=True, null=True)
+ lab = models.ForeignKey(Lab, null=True, on_delete=models.SET_NULL)
class Meta:
db_table = 'booking'
- def get_jira_issue(self):
- try:
- jira = JIRA(server=settings.JIRA_URL,
- basic_auth=(settings.JIRA_USER_NAME, settings.JIRA_USER_PASSWORD))
- issue = jira.issue(self.jira_issue_id)
- return issue
- except JIRAError:
- return None
-
def save(self, *args, **kwargs):
"""
Save the booking if self.user is authorized and there is no overlapping booking.
@@ -83,11 +78,14 @@ class Booking(models.Model):
conflicting_dates = conflicting_dates.filter(start__lt=self.end)
if conflicting_dates.count() > 0:
raise ValueError('This booking overlaps with another booking')
- if not self.changeid:
- self.changeid = self.id
- else:
- self.changeid = hashlib.md5(self.changeid.encode() + get_random_string(length=32).encode()).hexdigest()
return super(Booking, self).save(*args, **kwargs)
+ def delete(self, *args, **kwargs):
+ res = self.resource
+ self.resource = None
+ self.save()
+ resource_inventory.resource_manager.ResourceManager.getInstance().deleteResourceBundle(res)
+ return super(self.__class__, self).delete(*args, **kwargs)
+
def __str__(self):
- return str(self.resource) + ' from ' + str(self.start) + ' until ' + str(self.end)
+ return str(self.purpose) + ' from ' + str(self.start) + ' until ' + str(self.end)
diff --git a/dashboard/src/booking/stats.py b/dashboard/src/booking/stats.py
new file mode 100644
index 0000000..31f7ef1
--- /dev/null
+++ b/dashboard/src/booking/stats.py
@@ -0,0 +1,56 @@
+##############################################################################
+# Copyright (c) 2018 Parker Berberian, Sawyer Bergeron, and others.
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+##############################################################################
+from booking.models import Booking
+import datetime
+import pytz
+
+
+class StatisticsManager(object):
+
+ @staticmethod
+ def getContinuousBookingTimeSeries(span=28):
+ """
+ Will return a dictionary of names and 2-D array of x and y data points.
+ e.g. {"plot1": [["x1", "x2", "x3"],["y1", "y2", "y3]]}
+ x values will be dates in string
+ every change (booking start / end) will be reflected, instead of one data point per day
+ y values are the integer number of bookings/users active at some point in the given date
+ span is the number of days to plot. The last x value will always be the current time
+ """
+ x_set = set()
+ x = []
+ y = []
+ users = []
+ now = datetime.datetime.now(pytz.utc)
+ delta = datetime.timedelta(days=span)
+ end = now-delta
+ bookings = Booking.objects.filter(start__lte=now, end__gte=end)
+ for booking in bookings:
+ x_set.add(booking.start)
+ if booking.end < now:
+ x_set.add(booking.end)
+
+ x_set.add(now)
+ x_set.add(end)
+
+ x_list = list(x_set)
+ x_list.sort(reverse=True)
+ for time in x_list:
+ x.append(str(time))
+ active = Booking.objects.filter(start__lte=time, end__gt=time)
+ booking_count = len(active)
+ users_set = set()
+ for booking in active:
+ users_set.add(booking.owner)
+ for user in booking.collaborators.all():
+ users_set.add(user)
+ y.append(booking_count)
+ users.append(len(users_set))
+
+ return {"booking": [x, y], "user": [x, users]}
diff --git a/dashboard/src/booking/tests/__init__.py b/dashboard/src/booking/tests/__init__.py
index b5914ce..b6fef6c 100644
--- a/dashboard/src/booking/tests/__init__.py
+++ b/dashboard/src/booking/tests/__init__.py
@@ -6,5 +6,3 @@
# which accompanies this distribution, and is available at
# http://www.apache.org/licenses/LICENSE-2.0
##############################################################################
-
-
diff --git a/dashboard/src/booking/tests/test_models.py b/dashboard/src/booking/tests/test_models.py
index b4cd113..a83f817 100644
--- a/dashboard/src/booking/tests/test_models.py
+++ b/dashboard/src/booking/tests/test_models.py
@@ -1,5 +1,6 @@
##############################################################################
# Copyright (c) 2016 Max Breitenfeldt and others.
+# Copyright (c) 2018 Parker Berberian, Sawyer Bergeron, and others.
#
# All rights reserved. This program and the accompanying materials
# are made available under the terms of the Apache License, Version 2.0
@@ -15,29 +16,28 @@ from django.test import TestCase
from django.utils import timezone
from booking.models import *
-from dashboard.models import Resource
-from jenkins.models import JenkinsSlave
-
+from resource_inventory.models import ResourceBundle, GenericResourceBundle, ConfigBundle
class BookingModelTestCase(TestCase):
+ count = 0
def setUp(self):
- self.slave = JenkinsSlave.objects.create(name='test', url='test')
self.owner = User.objects.create(username='owner')
- self.res1 = Resource.objects.create(name='res1', slave=self.slave, description='x',
- url='x',owner=self.owner)
- self.res2 = Resource.objects.create(name='res2', slave=self.slave, description='x',
- url='x',owner=self.owner)
-
+ self.res1 = ResourceBundle.objects.create(
+ template=GenericResourceBundle.objects.create(name="gbundle" + str(self.count))
+ )
+ self.count += 1
+ self.res2 = ResourceBundle.objects.create(
+ template=GenericResourceBundle.objects.create(name="gbundle2" + str(self.count))
+ )
+ self.count += 1
self.user1 = User.objects.create(username='user1')
self.add_booking_perm = Permission.objects.get(codename='add_booking')
self.user1.user_permissions.add(self.add_booking_perm)
self.user1 = User.objects.get(pk=self.user1.id)
-
- self.installer = Installer.objects.create(name='TestInstaller')
- self.scenario = Scenario.objects.create(name='TestScenario')
+ self.config_bundle = ConfigBundle.objects.create(owner=self.user1, name="test config")
def test_start_end(self):
"""
@@ -46,11 +46,25 @@ class BookingModelTestCase(TestCase):
"""
start = timezone.now()
end = start - timedelta(weeks=1)
- self.assertRaises(ValueError, Booking.objects.create, start=start, end=end,
- resource=self.res1, user=self.user1)
+ self.assertRaises(
+ ValueError,
+ Booking.objects.create,
+ start=start,
+ end=end,
+ resource=self.res1,
+ owner=self.user1,
+ config_bundle=self.config_bundle
+ )
end = start
- self.assertRaises(ValueError, Booking.objects.create, start=start, end=end,
- resource=self.res1, user=self.user1)
+ self.assertRaises(
+ ValueError,
+ Booking.objects.create,
+ start=start,
+ end=end,
+ resource=self.res1,
+ owner=self.user1,
+ config_bundle=self.config_bundle
+ )
def test_conflicts(self):
"""
@@ -60,35 +74,153 @@ class BookingModelTestCase(TestCase):
start = timezone.now()
end = start + timedelta(weeks=1)
self.assertTrue(
- Booking.objects.create(start=start, end=end, user=self.user1, resource=self.res1))
-
- self.assertRaises(ValueError, Booking.objects.create, start=start,
- end=end, resource=self.res1, user=self.user1)
- self.assertRaises(ValueError, Booking.objects.create, start=start + timedelta(days=1),
- end=end - timedelta(days=1), resource=self.res1, user=self.user1)
+ Booking.objects.create(
+ start=start,
+ end=end,
+ owner=self.user1,
+ resource=self.res1,
+ config_bundle=self.config_bundle
+ )
+ )
+
+ self.assertRaises(
+ ValueError,
+ Booking.objects.create,
+ start=start,
+ end=end,
+ resource=self.res1,
+ owner=self.user1,
+ config_bundle=self.config_bundle
+ )
+ self.assertRaises(
+ ValueError,
+ Booking.objects.create,
+ start=start + timedelta(days=1),
+ end=end - timedelta(days=1),
+ resource=self.res1,
+ owner=self.user1,
+ config_bundle=self.config_bundle
+ )
+
+ self.assertRaises(
+ ValueError,
+ Booking.objects.create,
+ start=start - timedelta(days=1),
+ end=end,
+ resource=self.res1,
+ owner=self.user1,
+ config_bundle=self.config_bundle
+ )
+
+ self.assertRaises(
+ ValueError,
+ Booking.objects.create,
+ start=start - timedelta(days=1),
+ end=end - timedelta(days=1),
+ resource=self.res1,
+ owner=self.user1,
+ config_bundle=self.config_bundle
+ )
+
+ self.assertRaises(
+ ValueError,
+ Booking.objects.create,
+ start=start,
+ end=end + timedelta(days=1),
+ resource=self.res1,
+ owner=self.user1,
+ config_bundle=self.config_bundle
+ )
+
+ self.assertRaises(
+ ValueError,
+ Booking.objects.create,
+ start=start + timedelta(days=1),
+ end=end + timedelta(days=1),
+ resource=self.res1,
+ owner=self.user1,
+ config_bundle=self.config_bundle
+ )
- self.assertRaises(ValueError, Booking.objects.create, start=start - timedelta(days=1),
- end=end, resource=self.res1, user=self.user1)
- self.assertRaises(ValueError, Booking.objects.create, start=start - timedelta(days=1),
- end=end - timedelta(days=1), resource=self.res1, user=self.user1)
-
- self.assertRaises(ValueError, Booking.objects.create, start=start,
- end=end + timedelta(days=1), resource=self.res1, user=self.user1)
- self.assertRaises(ValueError, Booking.objects.create, start=start + timedelta(days=1),
- end=end + timedelta(days=1), resource=self.res1, user=self.user1)
+ self.assertTrue(
+ Booking.objects.create(
+ start=start - timedelta(days=1),
+ end=start,
+ owner=self.user1,
+ resource=self.res1,
+ config_bundle=self.config_bundle
+ )
+ )
+ self.assertTrue(
+ Booking.objects.create(
+ start=end,
+ end=end + timedelta(days=1),
+ owner=self.user1,
+ resource=self.res1,
+ config_bundle=self.config_bundle
+ )
+ )
- self.assertTrue(Booking.objects.create(start=start - timedelta(days=1), end=start,
- user=self.user1, resource=self.res1))
- self.assertTrue(Booking.objects.create(start=end, end=end + timedelta(days=1),
- user=self.user1, resource=self.res1))
+ self.assertTrue(
+ Booking.objects.create(
+ start=start - timedelta(days=2),
+ end=start - timedelta(days=1),
+ owner=self.user1,
+ resource=self.res1,
+ config_bundle=self.config_bundle
+ )
+ )
self.assertTrue(
- Booking.objects.create(start=start - timedelta(days=2), end=start - timedelta(days=1),
- user=self.user1, resource=self.res1))
+ Booking.objects.create(
+ start=end + timedelta(days=1),
+ end=end + timedelta(days=2),
+ owner=self.user1,
+ resource=self.res1,
+ config_bundle=self.config_bundle
+ )
+ )
+
self.assertTrue(
- Booking.objects.create(start=end + timedelta(days=1), end=end + timedelta(days=2),
- user=self.user1, resource=self.res1))
+ Booking.objects.create(
+ start=start,
+ end=end,
+ owner=self.user1,
+ resource=self.res2,
+ config_bundle=self.config_bundle
+ )
+ )
+
+ def test_extensions(self):
+ """
+ saving a booking with an extended end time is allows to happen twice,
+ and each extension must be a maximum of one week long
+ """
+ start = timezone.now()
+ end = start + timedelta(weeks=1)
self.assertTrue(
- Booking.objects.create(start=start, end=end,
- user=self.user1, resource=self.res2, scenario=self.scenario,
- installer=self.installer)) \ No newline at end of file
+ Booking.objects.create(
+ start=start,
+ end=end,
+ owner=self.user1,
+ resource=self.res1,
+ config_bundle=self.config_bundle
+ )
+ )
+
+ booking = Booking.objects.all().first() # should be only thing in db
+
+ self.assertEquals(booking.ext_count, 2)
+ booking.end = booking.end + timedelta(days=3)
+ try:
+ booking.save()
+ except Exception:
+ self.fail("save() threw an exception")
+ booking.end = booking.end + timedelta(weeks=2)
+ self.assertRaises(ValueError, booking.save)
+ booking.end = booking.end - timedelta(days=8)
+ try:
+ self.assertTrue(booking.save())
+ except Exception:
+ self.fail("save() threw an exception")
+
diff --git a/dashboard/src/booking/tests/test_views.py b/dashboard/src/booking/tests/test_views.py
deleted file mode 100644
index c1da013..0000000
--- a/dashboard/src/booking/tests/test_views.py
+++ /dev/null
@@ -1,106 +0,0 @@
-##############################################################################
-# Copyright (c) 2016 Max Breitenfeldt and others.
-#
-# All rights reserved. This program and the accompanying materials
-# are made available under the terms of the Apache License, Version 2.0
-# which accompanies this distribution, and is available at
-# http://www.apache.org/licenses/LICENSE-2.0
-##############################################################################
-
-
-from datetime import timedelta
-
-from django.test import Client
-from django.test import TestCase
-from django.urls import reverse
-from django.utils import timezone
-from django.utils.encoding import force_text
-from registration.forms import User
-
-from account.models import UserProfile
-from booking.models import Booking
-from dashboard.models import Resource
-from jenkins.models import JenkinsSlave
-
-
-class BookingViewTestCase(TestCase):
- def setUp(self):
- self.client = Client()
- self.slave = JenkinsSlave.objects.create(name='test', url='test')
- self.owner = User.objects.create(username='owner')
- self.res1 = Resource.objects.create(name='res1', slave=self.slave, description='x',
- url='x',owner=self.owner)
- self.user1 = User.objects.create(username='user1')
- self.user1.set_password('user1')
- self.user1profile = UserProfile.objects.create(user=self.user1)
- self.user1.save()
-
- self.user1 = User.objects.get(pk=self.user1.id)
-
-
- def test_resource_bookings_json(self):
- url = reverse('booking:bookings_json', kwargs={'resource_id': 0})
- self.assertEqual(self.client.get(url).status_code, 404)
-
- url = reverse('booking:bookings_json', kwargs={'resource_id': self.res1.id})
- response = self.client.get(url)
- self.assertEqual(response.status_code, 200)
- self.assertJSONEqual(force_text(response.content), {"bookings": []})
- booking1 = Booking.objects.create(start=timezone.now(),
- end=timezone.now() + timedelta(weeks=1), user=self.user1,
- resource=self.res1)
- response = self.client.get(url)
- json = response.json()
- self.assertEqual(response.status_code, 200)
- self.assertIn('bookings', json)
- self.assertEqual(len(json['bookings']), 1)
- self.assertIn('start', json['bookings'][0])
- self.assertIn('end', json['bookings'][0])
- self.assertIn('id', json['bookings'][0])
- self.assertIn('purpose', json['bookings'][0])
-
- def test_booking_form_view(self):
- url = reverse('booking:create', kwargs={'resource_id': 0})
- self.assertEqual(self.client.get(url).status_code, 404)
-
- # authenticated user
- url = reverse('booking:create', kwargs={'resource_id': self.res1.id})
- self.client.login(username='user1',password='user1')
- response = self.client.get(url)
- self.assertEqual(response.status_code, 200)
- self.assertTemplateUsed('booking/booking_calendar.html')
- self.assertTemplateUsed('booking/booking_form.html')
- self.assertIn('resource', response.context)
-
-
- def test_booking_view(self):
- start = timezone.now()
- end = start + timedelta(weeks=1)
- booking = Booking.objects.create(start=start, end=end, user=self.user1, resource=self.res1)
-
- url = reverse('booking:detail', kwargs={'booking_id':0})
- response = self.client.get(url)
- self.assertEqual(response.status_code, 404)
-
- url = reverse('booking:detail', kwargs={'booking_id':booking.id})
- response = self.client.get(url)
- self.assertEqual(response.status_code, 200)
- self.assertTemplateUsed('booking/booking_detail.html')
- self.assertIn('booking', response.context)
-
- def test_booking_list_view(self):
- start = timezone.now() - timedelta(weeks=2)
- end = start + timedelta(weeks=1)
- Booking.objects.create(start=start, end=end, user=self.user1, resource=self.res1)
-
- url = reverse('booking:list')
- response = self.client.get(url)
- self.assertEqual(response.status_code, 200)
- self.assertTemplateUsed('booking/booking_list.html')
- self.assertTrue(len(response.context['bookings']) == 0)
-
- start = timezone.now()
- end = start + timedelta(weeks=1)
- Booking.objects.create(start=start, end=end, user=self.user1, resource=self.res1)
- response = self.client.get(url)
- self.assertTrue(len(response.context['bookings']) == 1) \ No newline at end of file
diff --git a/dashboard/src/booking/urls.py b/dashboard/src/booking/urls.py
index ed3b1d4..88fbb0a 100644
--- a/dashboard/src/booking/urls.py
+++ b/dashboard/src/booking/urls.py
@@ -1,5 +1,6 @@
##############################################################################
# Copyright (c) 2016 Max Breitenfeldt and others.
+# Copyright (c) 2018 Parker Berberian, Sawyer Bergeron, and others.
#
# All rights reserved. This program and the accompanying materials
# are made available under the terms of the Apache License, Version 2.0
@@ -27,20 +28,19 @@ from django.conf.urls import url
from booking.views import *
+app_name = "booking"
urlpatterns = [
- url(r'^(?P<resource_id>[0-9]+)/$', BookingFormView.as_view(), name='create'),
- url(r'^(?P<resource_id>[0-9]+)/edit/(?P<booking_id>[0-9]+)/$', BookingEditFormView.as_view(), name='edit'),
- url(r'^(?P<resource_id>[0-9]+)/bookings_json/$', ResourceBookingsJSON.as_view(),
- name='bookings_json'),
- url(r'^detail/$', BookingView.as_view(), name='detail_prefix'),
- url(r'^detail/(?P<booking_id>[0-9]+)/$', BookingView.as_view(), name='detail'),
+ url(r'^detail/(?P<booking_id>[0-9]+)/$', booking_detail_view, name='detail'),
+ url(r'^(?P<booking_id>[0-9]+)/$', booking_detail_view, name='booking_detail'),
url(r'^delete/$', BookingDeleteView.as_view(), name='delete_prefix'),
url(r'^delete/(?P<booking_id>[0-9]+)/$', BookingDeleteView.as_view(), name='delete'),
url(r'^delete/(?P<booking_id>[0-9]+)/confirm/$', bookingDelete, name='delete_booking'),
- url(r'^list/$', BookingListView.as_view(), name='list')
-] \ No newline at end of file
+ url(r'^list/$', BookingListView.as_view(), name='list'),
+ url(r'^stats/$', booking_stats_view, name='stats'),
+ url(r'^stats/json$', booking_stats_json, name='stats_json'),
+]
diff --git a/dashboard/src/booking/views.py b/dashboard/src/booking/views.py
index a52cfe2..c139b4c 100644
--- a/dashboard/src/booking/views.py
+++ b/dashboard/src/booking/views.py
@@ -1,5 +1,6 @@
##############################################################################
# Copyright (c) 2016 Max Breitenfeldt and others.
+# Copyright (c) 2018 Parker Berberian, Sawyer Bergeron, and others.
#
# All rights reserved. This program and the accompanying materials
# are made available under the terms of the Apache License, Version 2.0
@@ -7,39 +8,38 @@
# http://www.apache.org/licenses/LICENSE-2.0
##############################################################################
-from django.conf import settings
from django.contrib import messages
-from django.contrib.auth.mixins import LoginRequiredMixin
-from django.http import JsonResponse
from django.shortcuts import get_object_or_404
+from django.http import JsonResponse
from django.urls import reverse
from django.utils import timezone
from django.views import View
from django.views.generic import FormView
from django.views.generic import TemplateView
-from jira import JIRAError
-from django.shortcuts import redirect
+from django.shortcuts import redirect, render
+import json
-from account.jira_util import get_jira
from booking.forms import BookingForm, BookingEditForm
-from booking.models import Booking
-from dashboard.models import Resource
-
-def create_jira_ticket(user, booking):
- jira = get_jira(user)
- issue_dict = {
- 'project': 'PHAROS',
- 'summary': str(booking.resource) + ': Access Request',
- 'description': booking.purpose,
- 'issuetype': {'name': 'Task'},
- 'components': [{'name': 'POD Access Request'}],
- 'assignee': {'name': booking.resource.owner.username}
- }
- issue = jira.create_issue(fields=issue_dict)
- jira.add_attachment(issue, user.userprofile.pgp_public_key)
- jira.add_attachment(issue, user.userprofile.ssh_public_key)
- booking.jira_issue_id = issue.id
- booking.save()
+from resource_inventory.models import ResourceBundle
+from resource_inventory.resource_manager import ResourceManager
+from booking.models import Booking, Installer, Opsys
+from booking.stats import StatisticsManager
+
+
+def drop_filter(context):
+ installer_filter = {}
+ for os in Opsys.objects.all():
+ installer_filter[os.id] = []
+ for installer in os.sup_installers.all():
+ installer_filter[os.id].append(installer.id)
+
+ scenario_filter = {}
+ for installer in Installer.objects.all():
+ scenario_filter[installer.id] = []
+ for scenario in installer.sup_scenarios.all():
+ scenario_filter[installer.id].append(scenario.id)
+
+ context.update({'installer_filter': json.dumps(installer_filter), 'scenario_filter': json.dumps(scenario_filter)})
class BookingFormView(FormView):
@@ -47,14 +47,16 @@ class BookingFormView(FormView):
form_class = BookingForm
def dispatch(self, request, *args, **kwargs):
- self.resource = get_object_or_404(Resource, id=self.kwargs['resource_id'])
+ self.resource = get_object_or_404(ResourceBundle, id=self.kwargs['resource_id'])
return super(BookingFormView, self).dispatch(request, *args, **kwargs)
def get_context_data(self, **kwargs):
- title = 'Booking: ' + self.resource.name
+ title = 'Booking: ' + str(self.resource.id)
context = super(BookingFormView, self).get_context_data(**kwargs)
context.update({'title': title, 'resource': self.resource})
- #raise PermissionDenied('check')
+
+ drop_filter(context)
+
return context
def get_success_url(self):
@@ -75,24 +77,16 @@ class BookingFormView(FormView):
booking = Booking(start=form.cleaned_data['start'],
end=form.cleaned_data['end'],
purpose=form.cleaned_data['purpose'],
- opsys=form.cleaned_data['opsys'],
installer=form.cleaned_data['installer'],
scenario=form.cleaned_data['scenario'],
- resource=self.resource, user=user)
+ resource=self.resource,
+ owner=user
+ )
try:
booking.save()
except ValueError as err:
messages.add_message(self.request, messages.ERROR, err)
return super(BookingFormView, self).form_invalid(form)
- try:
- if settings.CREATE_JIRA_TICKET:
- create_jira_ticket(user, booking)
- except JIRAError:
- messages.add_message(self.request, messages.ERROR, 'Failed to create Jira Ticket. '
- 'Please check your Jira '
- 'permissions.')
- booking.delete()
- return super(BookingFormView, self).form_invalid(form)
messages.add_message(self.request, messages.SUCCESS, 'Booking saved')
return super(BookingFormView, self).form_valid(form)
@@ -105,7 +99,7 @@ class BookingEditFormView(FormView):
return True
def dispatch(self, request, *args, **kwargs):
- self.resource = get_object_or_404(Resource, id=self.kwargs['resource_id'])
+ self.resource = get_object_or_404(ResourceBundle, id=self.kwargs['resource_id'])
self.original_booking = get_object_or_404(Booking, id=self.kwargs['booking_id'])
return super(BookingEditFormView, self).dispatch(request, *args, **kwargs)
@@ -113,6 +107,9 @@ class BookingEditFormView(FormView):
title = 'Editing Booking on: ' + self.resource.name
context = super(BookingEditFormView, self).get_context_data(**kwargs)
context.update({'title': title, 'resource': self.resource, 'booking': self.original_booking})
+
+ drop_filter(context)
+
return context
def get_form_kwargs(self):
@@ -120,14 +117,6 @@ class BookingEditFormView(FormView):
kwargs['purpose'] = self.original_booking.purpose
kwargs['start'] = self.original_booking.start
kwargs['end'] = self.original_booking.end
- try:
- kwargs['installer'] = self.original_booking.installer
- except AttributeError:
- pass
- try:
- kwargs['scenario'] = self.original_booking.scenario
- except AttributeError:
- pass
return kwargs
def get_success_url(self):
@@ -145,7 +134,7 @@ class BookingEditFormView(FormView):
'You are not the owner of this booking.')
return super(BookingEditFormView, self).form_invalid(form)
- #Do Conflict Checks
+ # Do Conflict Checks
if self.original_booking.end != form.cleaned_data['end']:
if form.cleaned_data['end'] - self.original_booking.end > timezone.timedelta(days=7):
messages.add_message(self.request, messages.ERROR,
@@ -176,13 +165,12 @@ class BookingEditFormView(FormView):
messages.add_message(self.request, messages.ERROR, err)
return super(BookingEditFormView, self).form_invalid(form)
- user = self.request.user
return super(BookingEditFormView, self).form_valid(form)
+
class BookingView(TemplateView):
template_name = "booking/booking_detail.html"
-
def get_context_data(self, **kwargs):
booking = get_object_or_404(Booking, id=self.kwargs['booking_id'])
title = 'Booking Details'
@@ -190,6 +178,7 @@ class BookingView(TemplateView):
context.update({'title': title, 'booking': booking})
return context
+
class BookingDeleteView(TemplateView):
template_name = "booking/booking_delete.html"
@@ -200,12 +189,14 @@ class BookingDeleteView(TemplateView):
context.update({'title': title, 'booking': booking})
return context
+
def bookingDelete(request, booking_id):
booking = get_object_or_404(Booking, id=booking_id)
booking.delete()
messages.add_message(request, messages.SUCCESS, 'Booking deleted')
return redirect('../../../../')
+
class BookingListView(TemplateView):
template_name = "booking/booking_list.html"
@@ -219,8 +210,47 @@ class BookingListView(TemplateView):
class ResourceBookingsJSON(View):
def get(self, request, *args, **kwargs):
- resource = get_object_or_404(Resource, id=self.kwargs['resource_id'])
- bookings = resource.booking_set.get_queryset().values('id', 'start', 'end', 'purpose',
- 'jira_issue_status', 'opsys__name',
- 'installer__name', 'scenario__name')
+ resource = get_object_or_404(ResourceBundle, id=self.kwargs['resource_id'])
+ bookings = resource.booking_set.get_queryset().values(
+ 'id',
+ 'start',
+ 'end',
+ 'purpose',
+ 'jira_issue_status',
+ 'config_bundle__name'
+ )
return JsonResponse({'bookings': list(bookings)})
+
+
+def booking_detail_view(request, booking_id):
+ user = None
+ if request.user.is_authenticated:
+ user = request.user
+ else:
+ return render(request, "dashboard/login.html", {'title': 'Authentication Required'})
+
+ booking = get_object_or_404(Booking, id=booking_id)
+ return render(request, "booking/booking_detail.html", {
+ 'title': 'Booking Details',
+ 'booking': booking,
+ 'pdf': ResourceManager().makePDF(booking.resource),
+ 'user_id': user.id})
+
+
+def booking_stats_view(request):
+ return render(
+ request,
+ "booking/stats.html",
+ context={
+ "data": StatisticsManager.getContinuousBookingTimeSeries(),
+ "title": "Booking Statistics"
+ }
+ )
+
+
+def booking_stats_json(request):
+ try:
+ span = int(request.GET.get("days", 14))
+ except:
+ span = 14
+ return JsonResponse(StatisticsManager.getContinuousBookingTimeSeries(span), safe=False)
diff --git a/dashboard/src/dashboard/__init__.py b/dashboard/src/dashboard/__init__.py
index b5914ce..b6fef6c 100644
--- a/dashboard/src/dashboard/__init__.py
+++ b/dashboard/src/dashboard/__init__.py
@@ -6,5 +6,3 @@
# which accompanies this distribution, and is available at
# http://www.apache.org/licenses/LICENSE-2.0
##############################################################################
-
-
diff --git a/dashboard/src/dashboard/admin.py b/dashboard/src/dashboard/admin.py
index 0bfdef8..43b5386 100644
--- a/dashboard/src/dashboard/admin.py
+++ b/dashboard/src/dashboard/admin.py
@@ -1,5 +1,6 @@
##############################################################################
# Copyright (c) 2016 Max Breitenfeldt and others.
+# Copyright (c) 2018 Sawyer Bergeron, Parker Berberian, and others.
#
# All rights reserved. This program and the accompanying materials
# are made available under the terms of the Apache License, Version 2.0
@@ -10,11 +11,6 @@
from django.contrib import admin
-from dashboard.models import *
admin.site.site_header = "Pharos Dashboard Administration"
admin.site.site_title = "Pharos Dashboard"
-
-admin.site.register(Resource)
-admin.site.register(Server)
-admin.site.register(ResourceStatus)
diff --git a/dashboard/src/dashboard/context_processors.py b/dashboard/src/dashboard/context_processors.py
new file mode 100644
index 0000000..32c70b8
--- /dev/null
+++ b/dashboard/src/dashboard/context_processors.py
@@ -0,0 +1,12 @@
+##############################################################################
+# Copyright (c) 2018 Sawyer Bergeron, Parker Berberian, and others.
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+##############################################################################
+from django.conf import settings
+
+def debug(context):
+ return {'DEBUG': settings.DEBUG}
diff --git a/dashboard/src/dashboard/exceptions.py b/dashboard/src/dashboard/exceptions.py
new file mode 100644
index 0000000..bc3fcac
--- /dev/null
+++ b/dashboard/src/dashboard/exceptions.py
@@ -0,0 +1,46 @@
+##############################################################################
+# Copyright (c) 2018 Parker Berberian, Sawyer Bergeron, and others.
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+##############################################################################
+
+
+class ResourceProvisioningException(Exception):
+ """
+ Resources could not be provisioned
+ """
+ pass
+
+class ModelValidationException(Exception):
+ """
+ Validation before saving model returned issues
+ """
+ pass
+
+class ResourceAvailabilityException(ResourceProvisioningException):
+ """
+ Requested resources are not *currently* available
+ """
+ pass
+
+class ResourceExistenceException(ResourceAvailabilityException):
+ """
+ Requested resources do not exist or do not match any known resources
+ """
+ pass
+
+
+class NonUniqueHostnameException(Exception):
+ pass
+
+class InvalidHostnameException(Exception):
+ pass
+
+class InvalidVlanConfigurationException(Exception):
+ pass
+
+class NetworkExistsException(Exception):
+ pass
diff --git a/dashboard/src/dashboard/migrations/0001_initial.py b/dashboard/src/dashboard/migrations/0001_initial.py
deleted file mode 100644
index aaf3945..0000000
--- a/dashboard/src/dashboard/migrations/0001_initial.py
+++ /dev/null
@@ -1,64 +0,0 @@
-# -*- coding: utf-8 -*-
-# Generated by Django 1.10 on 2016-11-03 13:33
-from __future__ import unicode_literals
-
-from django.conf import settings
-from django.db import migrations, models
-import django.db.models.deletion
-
-
-class Migration(migrations.Migration):
-
- initial = True
-
- dependencies = [
- migrations.swappable_dependency(settings.AUTH_USER_MODEL),
- ('jenkins', '0001_initial'),
- ]
-
- operations = [
- migrations.CreateModel(
- name='Resource',
- fields=[
- ('id', models.AutoField(primary_key=True, serialize=False)),
- ('name', models.CharField(max_length=100, unique=True)),
- ('description', models.CharField(blank=True, max_length=300, null=True)),
- ('url', models.CharField(blank=True, max_length=100, null=True)),
- ('owner', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='user_lab_owner', to=settings.AUTH_USER_MODEL)),
- ('slave', models.ForeignKey(null=True, on_delete=django.db.models.deletion.DO_NOTHING, to='jenkins.JenkinsSlave')),
- ('vpn_users', models.ManyToManyField(blank=True, related_name='user_vpn_users', to=settings.AUTH_USER_MODEL)),
- ],
- options={
- 'db_table': 'resource',
- },
- ),
- migrations.CreateModel(
- name='ResourceStatus',
- fields=[
- ('id', models.AutoField(primary_key=True, serialize=False)),
- ('timestamp', models.DateTimeField(auto_now_add=True)),
- ('type', models.CharField(max_length=20)),
- ('title', models.CharField(max_length=50)),
- ('content', models.CharField(max_length=5000)),
- ('resource', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='dashboard.Resource')),
- ],
- options={
- 'db_table': 'resource_status',
- },
- ),
- migrations.CreateModel(
- name='Server',
- fields=[
- ('id', models.AutoField(primary_key=True, serialize=False)),
- ('name', models.CharField(blank=True, max_length=100)),
- ('model', models.CharField(blank=True, max_length=100)),
- ('cpu', models.CharField(blank=True, max_length=100)),
- ('ram', models.CharField(blank=True, max_length=100)),
- ('storage', models.CharField(blank=True, max_length=100)),
- ('resource', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='dashboard.Resource')),
- ],
- options={
- 'db_table': 'server',
- },
- ),
- ]
diff --git a/dashboard/src/dashboard/migrations/0002_auto_20170505_0815.py b/dashboard/src/dashboard/migrations/0002_auto_20170505_0815.py
deleted file mode 100644
index 4285b88..0000000
--- a/dashboard/src/dashboard/migrations/0002_auto_20170505_0815.py
+++ /dev/null
@@ -1,42 +0,0 @@
-##############################################################################
-# Copyright (c) 2016 Max Breitenfeldt and others.
-#
-# All rights reserved. This program and the accompanying materials
-# are made available under the terms of the Apache License, Version 2.0
-# which accompanies this distribution, and is available at
-# http://www.apache.org/licenses/LICENSE-2.0
-##############################################################################
-
-
-# -*- coding: utf-8 -*-
-# Generated by Django 1.10 on 2017-05-05 08:15
-from __future__ import unicode_literals
-
-from django.conf import settings
-from django.db import migrations, models
-import django.db.models.deletion
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('dashboard', '0001_initial'),
- ]
-
- operations = [
- migrations.AddField(
- model_name='resource',
- name='dev_pod',
- field=models.BooleanField(default=False),
- ),
- migrations.AlterField(
- model_name='resource',
- name='owner',
- field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='user_lab_owner', to=settings.AUTH_USER_MODEL),
- ),
- migrations.AlterField(
- model_name='resource',
- name='slave',
- field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.DO_NOTHING, to='jenkins.JenkinsSlave'),
- ),
- ]
diff --git a/dashboard/src/dashboard/migrations/0003_resource_resource_lab.py b/dashboard/src/dashboard/migrations/0003_resource_resource_lab.py
deleted file mode 100644
index fff93fd..0000000
--- a/dashboard/src/dashboard/migrations/0003_resource_resource_lab.py
+++ /dev/null
@@ -1,22 +0,0 @@
-# -*- coding: utf-8 -*-
-# Generated by Django 1.10 on 2018-01-10 16:36
-from __future__ import unicode_literals
-
-from django.db import migrations, models
-import django.db.models.deletion
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('account', '0002_auto_20180110_1636'),
- ('dashboard', '0002_auto_20170505_0815'),
- ]
-
- operations = [
- migrations.AddField(
- model_name='resource',
- name='resource_lab',
- field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='lab_resource_set', to='account.Lab'),
- ),
- ]
diff --git a/dashboard/src/dashboard/models.py b/dashboard/src/dashboard/models.py
index ff55232..f9bd07e 100644
--- a/dashboard/src/dashboard/models.py
+++ b/dashboard/src/dashboard/models.py
@@ -1,97 +1,9 @@
##############################################################################
# Copyright (c) 2016 Max Breitenfeldt and others.
+# Copyright (c) 2018 Parker Berberian, Sawyer Bergeron, and others.
#
# All rights reserved. This program and the accompanying materials
# are made available under the terms of the Apache License, Version 2.0
# which accompanies this distribution, and is available at
# http://www.apache.org/licenses/LICENSE-2.0
##############################################################################
-
-
-from datetime import timedelta
-
-from django.contrib.auth.models import User
-from django.db import models
-from django.utils import timezone
-
-from jenkins.models import JenkinsSlave
-from account.models import Lab
-
-
-class Resource(models.Model):
- id = models.AutoField(primary_key=True)
- name = models.CharField(max_length=100, unique=True)
- description = models.CharField(max_length=300, blank=True, null=True)
- url = models.CharField(max_length=100, blank=True, null=True)
- resource_lab = models.ForeignKey(Lab, related_name='lab_resource_set', null=True, blank=True)
- owner = models.ForeignKey(User, related_name='user_lab_owner', null=True, blank=True)
- vpn_users = models.ManyToManyField(User, related_name='user_vpn_users', blank=True)
- slave = models.ForeignKey(JenkinsSlave, on_delete=models.DO_NOTHING, null=True, blank=True)
- dev_pod = models.BooleanField(default=False)
-
- def get_booking_utilization(self, weeks):
- """
- Return a dictionary containing the count of booked and free seconds for a resource in the
- range [now,now + weeks] if weeks is positive,
- or [now-weeks, now] if weeks is negative
- """
-
- length = timedelta(weeks=abs(weeks))
- now = timezone.now()
-
- start = now
- end = now + length
- if weeks < 0:
- start = now - length
- end = now
-
- bookings = self.booking_set.filter(start__lt=start + length, end__gt=start)
-
- booked_seconds = 0
- for booking in bookings:
- booking_start = booking.start
- booking_end = booking.end
- if booking_start < start:
- booking_start = start
- if booking_end > end:
- booking_end = start + length
- total = booking_end - booking_start
- booked_seconds += total.total_seconds()
-
- return {'booked_seconds': booked_seconds,
- 'available_seconds': length.total_seconds() - booked_seconds}
-
- class Meta:
- db_table = 'resource'
-
- def __str__(self):
- return self.name
-
-class Server(models.Model):
- id = models.AutoField(primary_key=True)
- resource = models.ForeignKey(Resource, on_delete=models.CASCADE)
- name = models.CharField(max_length=100, blank=True)
- model = models.CharField(max_length=100, blank=True)
- cpu = models.CharField(max_length=100, blank=True)
- ram = models.CharField(max_length=100, blank=True)
- storage = models.CharField(max_length=100, blank=True)
-
- class Meta:
- db_table = 'server'
-
- def __str__(self):
- return self.name
-
-class ResourceStatus(models.Model):
- id = models.AutoField(primary_key=True)
- resource = models.ForeignKey(Resource, on_delete=models.CASCADE)
- timestamp = models.DateTimeField(auto_now_add=True)
- type = models.CharField(max_length=20)
- title = models.CharField(max_length=50)
- content = models.CharField(max_length=5000)
-
- class Meta:
- db_table = 'resource_status'
-
- def __str__(self):
- return self.resource.name + ': ' + self.title + ' ' + str(self.timestamp)
diff --git a/dashboard/src/dashboard/populate_db_iol.py b/dashboard/src/dashboard/populate_db_iol.py
new file mode 100644
index 0000000..8c8b271
--- /dev/null
+++ b/dashboard/src/dashboard/populate_db_iol.py
@@ -0,0 +1,346 @@
+##############################################################################
+# Copyright (c) 2018 Parker Berberian, Sawyer Bergeron, and others.
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+##############################################################################
+
+
+from django.test import TestCase
+from booking.models import Booking
+from resource_inventory.models import *
+from account.models import *
+from api.serializers.booking_serializer import *
+from datetime import timedelta
+from django.utils import timezone
+from django.contrib.auth.models import Permission, User
+import json
+import yaml
+
+
+class Populator:
+
+ def __init__(self):
+ self.host_profile_count = 0
+ self.generic_host_count = 0
+ self.host_profiles = []
+ self.generic_bundle_count = 0
+ self.booking_count = 0
+
+
+ def make_host_profile(self, lab, data):
+ hostProfile = HostProfile.objects.create(
+ host_type=data['host']['type'],
+ name=data['host']['name'],
+ description=data['host']['description']
+ )
+ hostProfile.save()
+
+ for iface_data in data['interfaces']:
+
+ interfaceProfile = InterfaceProfile.objects.create(
+ speed=iface_data['speed'],
+ name=iface_data['name'],
+ host=hostProfile
+ )
+ interfaceProfile.save()
+
+
+ for disk_data in data['disks']:
+
+ diskProfile = DiskProfile.objects.create(
+ size=disk_data['size'],
+ media_type=disk_data['type'],
+ name=disk_data['name'],
+ host=hostProfile
+ )
+ diskProfile.save()
+
+ cpuProfile = CpuProfile.objects.create(
+ cores=data['cpu']['cores'],
+ architecture=data['cpu']['arch'],
+ cpus=data['cpu']['cpus'],
+ host=hostProfile
+ )
+ cpuProfile.save()
+ ramProfile = RamProfile.objects.create(
+ amount=data['ram']['amount'],
+ channels=data['ram']['channels'],
+ host=hostProfile
+ )
+ ramProfile.save()
+ hostProfile.labs.add(lab)
+ return hostProfile
+
+ def make_users(self):
+ user_pberberian = User.objects.create(username="pberberian")
+ user_pberberian.save()
+ user_pberberian_prof = UserProfile.objects.create(user=user_pberberian)
+ user_pberberian_prof.save()
+
+ user_sbergeron = User.objects.create(username="sbergeron")
+ user_sbergeron.save()
+ user_sbergeron_prof = UserProfile.objects.create(user=user_sbergeron)
+ user_sbergeron_prof.save()
+ return [user_sbergeron, user_pberberian,]
+
+
+ def make_labs(self):
+ unh_iol = User.objects.create(username="unh_iol")
+ unh_iol.save()
+ vlans = []
+ reserved = []
+ for i in range(1,4096):
+ vlans.append(1)
+ reserved.append(0)
+ # TODO: put reserved vlans here
+ iol = Lab.objects.create(
+ lab_user=unh_iol,
+ name="UNH_IOL",
+ vlan_manager=VlanManager.objects.create(
+ vlans = json.dumps(vlans),
+ reserved_vlans = json.dumps(reserved),
+ allow_overlapping = False,
+ block_size = 20,
+ ),
+ api_token = Lab.make_api_token(),
+ contact_email = "nfv-lab@iol.unh.edu",
+ location = "University of New Hampshire, Durham NH, 03824 USA"
+ )
+ return [iol]
+
+
+ def make_configurations(self):
+ #scenarios
+ scen1 = Scenario.objects.create(name="os-nosdn-nofeature-noha")
+ scen2 = Scenario.objects.create(name="os-odl-kvm-ha")
+ scen3 = Scenario.objects.create(name="os-nosdn-nofeature-ha")
+
+ #installers
+ fuel = Installer.objects.create(name="Fuel")
+ fuel.sup_scenarios.add(scen1)
+ fuel.sup_scenarios.add(scen3)
+ fuel.save()
+ joid = Installer.objects.create(name="Joid")
+ joid.sup_scenarios.add(scen1)
+ joid.sup_scenarios.add(scen2)
+ joid.save()
+ apex = Installer.objects.create(name="Apex")
+ apex.sup_scenarios.add(scen2)
+ apex.sup_scenarios.add(scen3)
+ apex.save()
+ daisy = Installer.objects.create(name="Daisy")
+ daisy.sup_scenarios.add(scen1)
+ daisy.sup_scenarios.add(scen2)
+ daisy.sup_scenarios.add(scen3)
+ daisy.save()
+ compass = Installer.objects.create(name="Compass")
+ compass.sup_scenarios.add(scen1)
+ compass.sup_scenarios.add(scen3)
+ compass.save()
+
+ #operating systems
+ ubuntu = Opsys.objects.create(name="Ubuntu")
+ ubuntu.sup_installers.add(compass)
+ ubuntu.sup_installers.add(joid)
+ ubuntu.save()
+ centos = Opsys.objects.create(name="CentOs")
+ centos.sup_installers.add(apex)
+ centos.sup_installers.add(fuel)
+ centos.save()
+ suse = Opsys.objects.create(name="Suse")
+ suse.sup_installers.add(fuel)
+ suse.save()
+
+
+ #opnfv roles
+ compute = OPNFVRole.objects.create(name="Compute", description="Does the heavy lifting")
+ controller = OPNFVRole.objects.create(name="Controller", description="Controls everything")
+ jumphost = OPNFVRole.objects.create(name="Jumphost", description="Entry Point")
+
+ lab = Lab.objects.first()
+ user = UserProfile.objects.first().user
+ image = Image.objects.create(
+ lab_id=23,
+ name="hpe centos",
+ from_lab=lab,
+ owner=user,
+ host_type=HostProfile.objects.get(name="hpe")
+ )
+ image = Image.objects.create(
+ lab_id=25,
+ name="hpe ubuntu",
+ from_lab=lab,
+ owner=user,
+ host_type=HostProfile.objects.get(name="hpe")
+ )
+
+ image = Image.objects.create(
+ lab_id=26,
+ name="hpe suse",
+ from_lab=lab,
+ owner=user,
+ host_type=HostProfile.objects.get(name="hpe")
+ )
+ image = Image.objects.create(
+ lab_id=27,
+ name="arm ubuntu",
+ from_lab=lab,
+ owner=user,
+ host_type=HostProfile.objects.get(name="arm")
+ )
+
+ def make_lab_hosts(self, hostcount, profile, lab, data, offset=1):
+ for i in range(hostcount):
+ name="Host_" + lab.name + "_" + profile.name + "_" + str(i + offset)
+ host = Host.objects.create(
+ name=name,
+ lab=lab,
+ profile=profile,
+ labid=data[i]['labid']
+ )
+ for iface_profile in profile.interfaceprofile.all():
+ iface_data = data[i]['interfaces'][iface_profile.name]
+ Interface.objects.create(
+ mac_address=iface_data['mac'],
+ bus_address=iface_data['bus'],
+ name=iface_profile.name,
+ host=host
+ )
+
+ def make_profile_data(self):
+ """
+ returns a dictionary of data from the yaml files
+ created by inspection scripts
+ """
+ data = []
+ for prof in ["hpe", "arm"]: # TODO
+ profile_dict = {}
+ host = {
+ "name": prof,
+ "type": 0,
+ "description": "some LaaS servers"
+ }
+ profile_dict['host'] = host
+ profile_dict['interfaces'] = []
+ for interface in [{"name": "eno1", "speed": 1000}, {"name": "eno2", "speed": 10000}]: # TODO
+ iface_dict = {}
+ iface_dict["name"] = interface['name']
+ iface_dict['speed'] = interface['speed']
+ profile_dict['interfaces'].append(iface_dict)
+
+ profile_dict['disks'] = []
+ for disk in [{"size": 1000, "type": "ssd", "name": "sda"}]: # TODO
+ disk_dict = {}
+ disk_dict['size'] = disk['size']
+ disk_dict['type'] = disk['type']
+ disk_dict['name'] = disk['name']
+ profile_dict['disks'].append(disk_dict)
+
+ # cpu
+ cpu = {}
+ cpu['cores'] = 4
+ cpu['arch'] = "x86"
+ cpu['cpus'] = 2
+ profile_dict['cpu'] = cpu
+
+ # ram
+ ram = {}
+ ram['amount'] = 256
+ ram['channels'] = 4
+ profile_dict['ram'] = ram
+
+ data.append(profile_dict)
+
+ return data
+
+ def get_lab_data(self, lab):
+ data = {}
+ path = "/pharos_dashboard/data/" + lab.name + "/"
+ host_file = open(path + "hostlist.json")
+ host_structure = json.loads(host_file.read())
+ host_file.close()
+ for profile in host_structure['profiles'].keys():
+ data[profile] = {}
+ prof_path = path + profile
+ for host in host_structure['profiles'][profile]:
+ host_file = open(prof_path + "/" + host + ".yaml")
+ host_data = yaml.load(host_file.read())
+ host_file.close()
+ data[profile][host] = host_data
+ return data
+
+ def make_profiles_and_hosts(self, lab, lab_data):
+ for host_profile_name, host_data_dict in lab_data.items():
+ if len(host_data_dict) < 1:
+ continue
+ host_profile = HostProfile.objects.create(
+ name=host_profile_name,
+ description=""
+ )
+ host_profile.labs.add(lab)
+ example_host_data = list(host_data_dict.values())[0]
+
+ cpu_data = example_host_data['cpu']
+ CpuProfile.objects.create(
+ cores=cpu_data['cores'],
+ architecture=cpu_data['arch'],
+ cpus=cpu_data['cpus'],
+ host=host_profile
+ )
+
+ ram_data = example_host_data['memory']
+ RamProfile.objects.create(
+ amount=int(ram_data[:-1]),
+ channels=1,
+ host=host_profile
+ )
+
+ disks_data = example_host_data['disk']
+ for disk_data in disks_data:
+ size = 0
+ try:
+ size=int(disk_data['size'].split('.')[0])
+ except:
+ size=int(disk_data['size'].split('.')[0][:-1])
+ DiskProfile.objects.create(
+ size=size,
+ media_type="SSD",
+ name=disk_data['name'],
+ host=host_profile
+ )
+
+ ifaces_data = example_host_data['interface']
+ for iface_data in ifaces_data:
+ InterfaceProfile.objects.create(
+ speed=iface_data['speed'],
+ name=iface_data['name'],
+ host=host_profile
+ )
+
+ # all profiles created
+ for hostname, host_data in host_data_dict.items():
+ host = Host.objects.create(
+ name=hostname,
+ labid=hostname,
+ profile=host_profile,
+ lab=lab
+ )
+ for iface_data in host_data['interface']:
+ Interface.objects.create(
+ mac_address=iface_data['mac'],
+ bus_address=iface_data['busaddr'],
+ name=iface_data['name'],
+ host=host
+ )
+
+ def populate(self):
+ self.labs = self.make_labs()
+ # We should use the existing users, not creating our own
+ for lab in self.labs:
+ lab_data = self.get_lab_data(lab)
+ self.make_profiles_and_hosts(lab, lab_data)
+
+ # We will add opnfv info and images as they are created and supported
diff --git a/dashboard/src/dashboard/tasks.py b/dashboard/src/dashboard/tasks.py
index fa2ee9d..827c7c5 100644
--- a/dashboard/src/dashboard/tasks.py
+++ b/dashboard/src/dashboard/tasks.py
@@ -1,5 +1,6 @@
##############################################################################
# Copyright (c) 2016 Max Breitenfeldt and others.
+# Copyright (c) 2018 Parker Berberian, Sawyer Bergeron, and others.
#
# All rights reserved. This program and the accompanying materials
# are made available under the terms of the Apache License, Version 2.0
@@ -7,32 +8,100 @@
# http://www.apache.org/licenses/LICENSE-2.0
##############################################################################
-from datetime import timedelta
from celery import shared_task
from django.utils import timezone
-from django.conf import settings
+from django.db.models import Q
from booking.models import Booking
+from notifier.manager import *
+from notifier.models import *
+from api.models import *
+from resource_inventory.resource_manager import ResourceManager
-from jenkins.models import JenkinsStatistic
@shared_task
-def database_cleanup():
- now = timezone.now()
- JenkinsStatistic.objects.filter(timestamp__lt=now - timedelta(weeks=4)).delete()
-
-def booking_cleanup():
- expire_time = timedelta(days=int(settings.BOOKING_EXP_TIME))
- expire_number = int(settings.BOOKING_MAX_NUM)
- expired_set = Booking.objects.filter(end__lte=timezone.now())
- expired_count = len(expired_set)
-
- for booking in expired_set:
- if timezone.now() - booking.end > expire_time:
- booking.delete()
- expired_count = expired_count - 1
-
- if expired_count > expire_number:
- oldest = expired_set.order_by("end")[:expired_count-expire_number]
- for booking in oldest:
- booking.delete()
+def conjure_aggregate_notifiers():
+ NotifyPeriodic.task()
+
+
+@shared_task
+def booking_poll():
+ def cleanup_hardware(qs):
+ for hostrelation in qs:
+ config = hostrelation.config
+ config.clear_delta()
+ config.set_power("off")
+ config.save()
+ hostrelation.status=JobStatus.NEW
+ hostrelation.save()
+
+ def cleanup_network(qs):
+ for hostrelation in qs:
+ network = hostrelation.config
+ network.interfaces.clear()
+ host = hostrelation.host
+ network.clear_delta()
+ vlans = []
+ for interface in host.interfaces.all():
+ for vlan in interface.config:
+ if vlan.public:
+ try:
+ host.lab.vlan_manager.release_public_vlan(vlan.vlan_id)
+ except: # will fail if we already released in this loop
+ pass
+ else:
+ vlans.append(vlan.vlan_id)
+
+ # release all vlans
+ if len(vlans) > 0:
+ host.lab.vlan_manager.release_vlans(vlans)
+
+ interface.config.clear()
+ network.add_interface(interface)
+ network.save()
+ hostrelation.status=JobStatus.NEW
+ hostrelation.save()
+
+ def cleanup_software(qs):
+ if qs.exists():
+ relation = qs.first()
+ software = relation.config.opnfv
+ software.clear_delta()
+ software.save()
+ relation.status=JobStatus.NEW
+ relation.save()
+
+ def cleanup_access(qs):
+ for relation in qs:
+ pass # TODO
+
+ cleanup_set = Booking.objects.filter(end__lte=timezone.now()).filter(job__complete=False)
+
+ for booking in cleanup_set:
+ if not booking.job.complete:
+ job = booking.job
+ cleanup_software(SoftwareRelation.objects.filter(job=job))
+ cleanup_hardware(HostHardwareRelation.objects.filter(job=job))
+ cleanup_network(HostNetworkRelation.objects.filter(job=job))
+ cleanup_access(AccessRelation.objects.filter(job=job))
+ job.complete = True
+ job.save()
+
+
+@shared_task
+def free_hosts():
+ """
+ gets all hosts from the database that need to be freed and frees them
+ """
+ networks = ~Q(~Q(job__hostnetworkrelation__status=200))
+ hardware = ~Q(~Q(job__hosthardwarerelation__status=200))
+
+ bookings = Booking.objects.filter(
+ networks,
+ hardware,
+ end__lt=timezone.now(),
+ job__complete=True,
+ resource__isnull=False
+ )
+ for booking in bookings:
+ ResourceManager.getInstance().deleteResourceBundle(booking.resource)
diff --git a/dashboard/src/dashboard/templatetags/jenkins_filters.py b/dashboard/src/dashboard/templatetags/jenkins_filters.py
deleted file mode 100644
index e7e1425..0000000
--- a/dashboard/src/dashboard/templatetags/jenkins_filters.py
+++ /dev/null
@@ -1,38 +0,0 @@
-##############################################################################
-# Copyright (c) 2016 Max Breitenfeldt and others.
-#
-# All rights reserved. This program and the accompanying materials
-# are made available under the terms of the Apache License, Version 2.0
-# which accompanies this distribution, and is available at
-# http://www.apache.org/licenses/LICENSE-2.0
-##############################################################################
-
-
-from django.template.defaultfilters import register
-
-
-@register.filter
-def jenkins_job_color(job_result):
- if job_result == 'SUCCESS':
- return '#5cb85c'
- if job_result == 'FAILURE':
- return '#d9534f'
- if job_result == 'UNSTABLE':
- return '#EDD62B'
- return '#646F73' # job is still building
-
-
-@register.filter
-def jenkins_status_color(slave_status):
- if slave_status == 'offline':
- return '#d9534f'
- if slave_status == 'online':
- return '#5cb85c'
- if slave_status == 'online / idle':
- return '#5bc0de'
-
-
-@register.filter
-def jenkins_job_blink(job_result):
- if job_result == '': # job is still building
- return 'class=blink_me'
diff --git a/dashboard/src/dashboard/tests/test_models.py b/dashboard/src/dashboard/tests/test_models.py
deleted file mode 100644
index 3a3aeab..0000000
--- a/dashboard/src/dashboard/tests/test_models.py
+++ /dev/null
@@ -1,69 +0,0 @@
-##############################################################################
-# Copyright (c) 2016 Max Breitenfeldt and others.
-#
-# All rights reserved. This program and the accompanying materials
-# are made available under the terms of the Apache License, Version 2.0
-# which accompanies this distribution, and is available at
-# http://www.apache.org/licenses/LICENSE-2.0
-##############################################################################
-
-
-from datetime import timedelta
-from math import ceil, floor
-
-from django.test import TestCase
-from django.utils import timezone
-
-from booking.models import *
-from dashboard.models import Resource
-from jenkins.models import JenkinsSlave
-
-
-class ResourceModelTestCase(TestCase):
- def setUp(self):
- self.slave = JenkinsSlave.objects.create(name='test', url='test')
- self.owner = User.objects.create(username='owner')
-
- self.res1 = Resource.objects.create(name='res1', slave=self.slave, description='x',
- url='x', owner=self.owner)
-
- def test_booking_utilization(self):
- utilization = self.res1.get_booking_utilization(1)
- self.assertTrue(utilization['booked_seconds'] == 0)
- self.assertTrue(utilization['available_seconds'] == timedelta(weeks=1).total_seconds())
-
- start = timezone.now() + timedelta(days=1)
- end = start + timedelta(days=1)
- booking = Booking.objects.create(start=start, end=end, purpose='test', resource=self.res1,
- user=self.owner)
-
- utilization = self.res1.get_booking_utilization(1)
- booked_seconds = timedelta(days=1).total_seconds()
- self.assertEqual(utilization['booked_seconds'], booked_seconds)
-
- utilization = self.res1.get_booking_utilization(-1)
- self.assertEqual(utilization['booked_seconds'], 0)
-
- booking.delete()
- start = timezone.now() - timedelta(days=1)
- end = start + timedelta(days=2)
- booking = Booking.objects.create(start=start, end=end, purpose='test', resource=self.res1,
- user=self.owner)
- booked_seconds = self.res1.get_booking_utilization(1)['booked_seconds']
- # use ceil because a fraction of the booked time has already passed now
- booked_seconds = ceil(booked_seconds)
- self.assertEqual(booked_seconds, timedelta(days=1).total_seconds())
-
- booking.delete()
- start = timezone.now() + timedelta(days=6)
- end = start + timedelta(days=2)
- booking = Booking.objects.create(start=start, end=end, purpose='test', resource=self.res1,
- user=self.owner)
- booked_seconds = self.res1.get_booking_utilization(1)['booked_seconds']
- booked_seconds = floor(booked_seconds)
- self.assertEqual(booked_seconds, timedelta(days=1).total_seconds())
-
-
-
-
-
diff --git a/dashboard/src/dashboard/tests/test_views.py b/dashboard/src/dashboard/tests/test_views.py
deleted file mode 100644
index f5e17c2..0000000
--- a/dashboard/src/dashboard/tests/test_views.py
+++ /dev/null
@@ -1,75 +0,0 @@
-##############################################################################
-# Copyright (c) 2016 Max Breitenfeldt and others.
-#
-# All rights reserved. This program and the accompanying materials
-# are made available under the terms of the Apache License, Version 2.0
-# which accompanies this distribution, and is available at
-# http://www.apache.org/licenses/LICENSE-2.0
-##############################################################################
-
-
-from django.test import TestCase
-from django.urls import reverse
-
-from dashboard.models import Resource
-from jenkins.models import JenkinsSlave
-
-
-class DashboardViewTestCase(TestCase):
- def setUp(self):
- self.slave_active = JenkinsSlave.objects.create(name='slave_active', url='x', active=True)
- self.slave_inactive = JenkinsSlave.objects.create(name='slave_inactive', url='x',
- active=False)
- self.res_active = Resource.objects.create(name='res_active', slave=self.slave_active,
- description='x', url='x')
- self.res_inactive = Resource.objects.create(name='res_inactive', slave=self.slave_inactive,
- description='x', url='x')
-
- def test_booking_utilization_json(self):
- url = reverse('dashboard:booking_utilization', kwargs={'resource_id': 0, 'weeks': 0})
- self.assertEqual(self.client.get(url).status_code, 404)
-
- url = reverse('dashboard:booking_utilization', kwargs={'resource_id': self.res_active.id,
- 'weeks': 0})
- response = self.client.get(url)
- self.assertEqual(response.status_code, 200)
- self.assertContains(response, 'data')
-
- def test_jenkins_utilization_json(self):
- url = reverse('dashboard:jenkins_utilization', kwargs={'resource_id': 0, 'weeks': 0})
- self.assertEqual(self.client.get(url).status_code, 404)
-
- url = reverse('dashboard:jenkins_utilization', kwargs={'resource_id': self.res_active.id,
- 'weeks': 0})
- response = self.client.get(url)
- self.assertEqual(response.status_code, 200)
- self.assertContains(response, 'data')
-
- def test_jenkins_slaves_view(self):
- url = reverse('dashboard:jenkins_slaves')
- response = self.client.get(url)
- self.assertEqual(response.status_code, 200)
- self.assertIn(self.slave_active, response.context['slaves'])
- self.assertNotIn(self.slave_inactive, response.context['slaves'])
-
- def test_ci_pods_view(self):
- url = reverse('dashboard:ci_pods')
- response = self.client.get(url)
- self.assertEqual(response.status_code, 200)
- self.assertEqual(len(response.context['ci_pods']), 0)
-
- self.slave_active.ci_slave = True
- self.slave_inactive.ci_slave = True
- self.slave_active.save()
- self.slave_inactive.save()
-
- response = self.client.get(url)
- self.assertIn(self.res_active, response.context['ci_pods'])
- self.assertNotIn(self.res_inactive, response.context['ci_pods'])
-
- def test_dev_pods_view(self):
- url = reverse('dashboard:dev_pods')
- response = self.client.get(url)
- self.assertEqual(response.status_code, 200)
- self.assertEqual(len(response.context['dev_pods']), 0)
-
diff --git a/dashboard/src/dashboard/urls.py b/dashboard/src/dashboard/urls.py
index 609e5d6..0d7ee87 100644
--- a/dashboard/src/dashboard/urls.py
+++ b/dashboard/src/dashboard/urls.py
@@ -1,5 +1,6 @@
##############################################################################
# Copyright (c) 2016 Max Breitenfeldt and others.
+# Copyright (c) 2018 Sawyer Bergeron, Parker Berberian, and others.
#
# All rights reserved. This program and the accompanying materials
# are made available under the terms of the Apache License, Version 2.0
@@ -24,18 +25,12 @@ Including another URLconf
2. Add a URL to urlpatterns: url(r'^blog/', include('blog.urls'))
"""
from django.conf.urls import url
-
from dashboard.views import *
+app_name="dashboard"
urlpatterns = [
- url(r'^ci_pods/$', CIPodsView.as_view(), name='ci_pods'),
- url(r'^dev_pods/$', DevelopmentPodsView.as_view(), name='dev_pods'),
- url(r'^jenkins_slaves/$', JenkinsSlavesView.as_view(), name='jenkins_slaves'),
- url(r'^resource/all/$', LabOwnerView.as_view(), name='resources'),
- url(r'^resource/(?P<resource_id>[0-9]+)/$', ResourceView.as_view(), name='resource'),
- url(r'^resource/(?P<resource_id>[0-9]+)/booking_utilization/(?P<weeks>-?\d+)/$',
- BookingUtilizationJSON.as_view(), name='booking_utilization'),
- url(r'^resource/(?P<resource_id>[0-9]+)/jenkins_utilization/(?P<weeks>-?\d+)/$',
- JenkinsUtilizationJSON.as_view(), name='jenkins_utilization'),
- url(r'^$', DevelopmentPodsView.as_view(), name="index"),
+ url(r'^$', landing_view, name='index'),
+ url(r'^lab/$', lab_list_view, name='all_labs'),
+ url(r'^lab/(?P<lab_name>.+)/$', lab_detail_view, name='lab_detail'),
+ url(r'^hosts/$', host_profile_detail_view, name="hostprofile_detail")
]
diff --git a/dashboard/src/dashboard/views.py b/dashboard/src/dashboard/views.py
index 4bab036..2d1f8b2 100644
--- a/dashboard/src/dashboard/views.py
+++ b/dashboard/src/dashboard/views.py
@@ -1,5 +1,6 @@
##############################################################################
# Copyright (c) 2016 Max Breitenfeldt and others.
+# Copyright (c) 2018 Parker Berberian, Sawyer Bergeron, and others.
#
# All rights reserved. This program and the accompanying materials
# are made available under the terms of the Apache License, Version 2.0
@@ -8,139 +9,106 @@
##############################################################################
-from datetime import timedelta
-
-from django.http import JsonResponse
from django.shortcuts import get_object_or_404
-from django.utils import timezone
-from django.views import View
from django.views.generic import TemplateView
+from django.shortcuts import render
+from django.http import HttpResponseRedirect
from booking.models import Booking
-from dashboard.models import Resource
-from jenkins.models import JenkinsSlave
+from account.models import Lab
+from resource_inventory.models import *
+from workflow.views import *
+from workflow.workflow_manager import *
-class JenkinsSlavesView(TemplateView):
- template_name = "dashboard/jenkins_slaves.html"
- def get_context_data(self, **kwargs):
- slaves = JenkinsSlave.objects.filter(active=True)
- context = super(JenkinsSlavesView, self).get_context_data(**kwargs)
- context.update({'title': "Jenkins Slaves", 'slaves': slaves})
- return context
+def lab_list_view(request):
+ labs = Lab.objects.all()
+ context = {"labs": labs}
+ return render(request, "dashboard/lab_list.html", context)
-class CIPodsView(TemplateView):
- template_name = "dashboard/ci_pods.html"
- def get_context_data(self, **kwargs):
- ci_pods = Resource.objects.filter(slave__ci_slave=True, slave__active=True)
- context = super(CIPodsView, self).get_context_data(**kwargs)
- context.update({'title': "CI Pods", 'ci_pods': ci_pods})
- return context
+def lab_detail_view(request, lab_name):
+ user = None
+ if request.user.is_authenticated:
+ user = request.user
+ lab = get_object_or_404(Lab, name=lab_name)
-class DevelopmentPodsView(TemplateView):
- template_name = "dashboard/dev_pods.html"
+ images = Image.objects.filter(from_lab=lab).filter(public=True)
+ if user:
+ images = images | Image.objects.filter(from_lab=lab).filter(owner=user)
- def get_context_data(self, **kwargs):
- resources = Resource.objects.filter(dev_pod=True)
-
- bookings = Booking.objects.filter(start__lte=timezone.now())
- bookings = bookings.filter(end__gt=timezone.now())
-
- dev_pods = []
- for resource in resources:
- booking_utilization = resource.get_booking_utilization(weeks=4)
- total = booking_utilization['booked_seconds'] + booking_utilization['available_seconds']
- try:
- utilization_percentage = "%d%%" % (float(booking_utilization['booked_seconds']) /
- total * 100)
- except (ValueError, ZeroDivisionError):
- return ""
-
- dev_pod = (resource, None, utilization_percentage)
- for booking in bookings:
- if booking.resource == resource:
- dev_pod = (resource, booking, utilization_percentage)
- dev_pods.append(dev_pod)
-
- context = super(DevelopmentPodsView, self).get_context_data(**kwargs)
- context.update({'title': "Development Pods", 'dev_pods': dev_pods})
- return context
+ return render(request, "dashboard/lab_detail.html",
+ {'title': "Lab Overview",
+ 'lab': lab,
+ 'hostprofiles': lab.hostprofiles.all(),
+ 'images': images})
-class ResourceView(TemplateView):
- template_name = "dashboard/resource.html"
+def host_profile_detail_view(request):
- def get_context_data(self, **kwargs):
- resource = get_object_or_404(Resource, id=self.kwargs['resource_id'])
- bookings = Booking.objects.filter(resource=resource, end__gt=timezone.now())
- context = super(ResourceView, self).get_context_data(**kwargs)
- context.update({'title': str(resource), 'resource': resource, 'bookings': bookings})
- return context
+ return render(request, "dashboard/host_profile_detail.html",
+ {'title': "Host Types",
+ })
-class LabOwnerView(TemplateView):
- template_name = "dashboard/resource_all.html"
+def landing_view(request):
+ manager = None
+ manager_detected = False
+ if 'manager_session' in request.session:
- def get_context_data(self, **kwargs):
- resources = Resource.objects.filter(slave__dev_pod=True, slave__active=True)
- pods = []
- for resource in resources:
- utilization = resource.slave.get_utilization(timedelta(days=7))
- bookings = Booking.objects.filter(resource=resource, end__gt=timezone.now())
- pods.append((resource, utilization, bookings))
- context = super(LabOwnerView, self).get_context_data(**kwargs)
- context.update({'title': "Overview", 'pods': pods})
- return context
+ try:
+ manager = ManagerTracker.managers[request.session['manager_session']]
+
+
+ except KeyError as e:
+ pass
+
+ if manager is not None:
+ #no manager detected, don't display continue button
+ manager_detected = True
+ if request.method == 'GET':
+ return render(request, 'dashboard/landing.html', {'manager': manager_detected, 'title': "Welcome!"})
-class BookingUtilizationJSON(View):
- def get(self, request, *args, **kwargs):
- resource = get_object_or_404(Resource, id=kwargs['resource_id'])
- utilization = resource.get_booking_utilization(int(kwargs['weeks']))
- utilization = [
- {
- 'label': 'Booked',
- 'data': utilization['booked_seconds'],
- 'color': '#d9534f'
- },
- {
- 'label': 'Available',
- 'data': utilization['available_seconds'],
- 'color': '#5cb85c'
- },
- ]
- return JsonResponse({'data': utilization})
-
-
-class JenkinsUtilizationJSON(View):
- def get(self, request, *args, **kwargs):
- resource = get_object_or_404(Resource, id=kwargs['resource_id'])
- weeks = int(kwargs['weeks'])
+ if request.method == 'POST':
try:
- utilization = resource.slave.get_utilization(timedelta(weeks=weeks))
- utilization = [
- {
- 'label': 'Offline',
- 'data': utilization['offline'],
- 'color': '#d9534f'
- },
- {
- 'label': 'Online',
- 'data': utilization['online'],
- 'color': '#5cb85c'
- },
- {
- 'label': 'Idle',
- 'data': utilization['idle'],
- 'color': '#5bc0de'
- },
- ]
- jutilization = JsonResponse({'data': utilization})
- except AttributeError:
- return JsonResponse({'data': ''})
- if jutilization:
- return jutilization
+ create = request.POST['create']
+
+ if manager is not None:
+ del manager
+
+ mgr_uuid = create_session(create, request=request,)
+ request.session['manager_session'] = mgr_uuid
+ return HttpResponseRedirect('/wf/')
+
+ except KeyError as e:
+ pass
+
+
+class LandingView(TemplateView):
+ template_name = "dashboard/landing.html"
+
+ def get_context_data(self, **kwargs):
+ context = super(LandingView, self).get_context_data(**kwargs)
+
+ hosts = []
+
+ for host_profile in HostProfile.objects.all():
+ name = host_profile.name
+ description = host_profile.description
+ in_labs = host_profile.labs
+
+ interfaces = host_profile.interfaceprofile
+ storage = host_profile.storageprofile
+ cpu = host_profile.cpuprofile
+ ram = host_profile.ramprofile
+
+ host = (name, description, in_labs, interfaces, storage, cpu, ram)
+ hosts.append(host)
+
+ context.update({'hosts': hosts})
+
+ return context
diff --git a/dashboard/src/jenkins/adapter.py b/dashboard/src/jenkins/adapter.py
deleted file mode 100644
index b48b868..0000000
--- a/dashboard/src/jenkins/adapter.py
+++ /dev/null
@@ -1,137 +0,0 @@
-##############################################################################
-# Copyright (c) 2016 Max Breitenfeldt and others.
-#
-# All rights reserved. This program and the accompanying materials
-# are made available under the terms of the Apache License, Version 2.0
-# which accompanies this distribution, and is available at
-# http://www.apache.org/licenses/LICENSE-2.0
-##############################################################################
-
-
-import logging
-import re
-from django.conf import settings
-
-import requests
-from django.core.cache import cache
-
-logger = logging.getLogger(__name__)
-
-# TODO: implement caching decorator, cache get_* functions
-def get_json(url):
- if cache.get(url) is None:
- try:
- response = requests.get(url)
- json = response.json()
- cache.set(url, json, 180) # cache result for 180 seconds
- return json
- except requests.exceptions.RequestException as e:
- logger.exception(e)
- except ValueError as e:
- logger.exception(e)
- else:
- return cache.get(url)
-
-
-def get_all_slaves():
- url = settings.ALL_SLAVES_URL
- json = get_json(url)
- if json is not None:
- return json['computer'] # return list of dictionaries
- return []
-
-
-
-
-def get_slave(slavename):
- slaves = get_all_slaves()
- for slave in slaves:
- if slave['displayName'] == slavename:
- return slave
- return {}
-
-
-def get_ci_slaves():
- url = settings.CI_SLAVES_URL
- json = get_json(url)
- if json is not None:
- return json['nodes']
- return []
-
-
-def get_all_jobs():
- url = settings.ALL_JOBS_URL
- json = get_json(url)
- if json is not None:
- return json['jobs'] # return list of dictionaries
- return []
-
-
-def get_jenkins_job(slavename):
- jobs = get_all_jobs()
- max_time = 0
- last_job = None
- for job in jobs:
- if job['lastBuild'] is not None:
- if job['lastBuild']['builtOn'] == slavename:
- if job['lastBuild']['building'] is True:
- return job # return active build
- if job['lastBuild']['timestamp'] > max_time:
- last_job = job
- max_time = job['lastBuild']['timestamp']
- return last_job
-
-
-def is_ci_slave(slavename):
- ci_slaves = get_ci_slaves()
- for ci_slave in ci_slaves:
- if ci_slave['nodeName'] == slavename:
- return True
- return False
-
-
-def is_dev_pod(slavename):
- if is_ci_slave(slavename):
- return False
- if slavename.find('pod') != -1:
- return True
- return False
-
-
-def parse_job(job):
- result = parse_job_string(job['lastBuild']['fullDisplayName'])
- result['building'] = job['lastBuild']['building']
- result['result'] = ''
- if not job['lastBuild']['building']:
- result['result'] = job['lastBuild']['result']
- result['url'] = job['url']
- return result
-
-
-def parse_job_string(full_displayname):
- job = {}
- job['scenario'] = ''
- job['installer'] = ''
- job['branch'] = ''
- tokens = re.split(r'[ -]', full_displayname)
- for i in range(len(tokens)):
- if tokens[i] == 'os':
- job['scenario'] = '-'.join(tokens[i: i + 4])
- elif tokens[i] in ['fuel', 'joid', 'apex', 'compass']:
- job['installer'] = tokens[i]
- elif tokens[i] in ['master', 'arno', 'brahmaputra', 'colorado']:
- job['branch'] = tokens[i]
- tokens = full_displayname.split(' ')
- job['name'] = tokens[0]
- return job
-
-def get_slave_url(slave):
- return settings.GET_SLAVE_URL + slave['displayName']
-
-
-def get_slave_status(slave):
- if not slave['offline'] and slave['idle']:
- return 'online / idle'
- if not slave['offline']:
- return 'online'
- return 'offline'
diff --git a/dashboard/src/jenkins/migrations/0001_initial.py b/dashboard/src/jenkins/migrations/0001_initial.py
deleted file mode 100644
index b1c7889..0000000
--- a/dashboard/src/jenkins/migrations/0001_initial.py
+++ /dev/null
@@ -1,53 +0,0 @@
-# -*- coding: utf-8 -*-
-# Generated by Django 1.10 on 2016-11-03 13:33
-from __future__ import unicode_literals
-
-from django.db import migrations, models
-import django.db.models.deletion
-
-
-class Migration(migrations.Migration):
-
- initial = True
-
- dependencies = [
- ]
-
- operations = [
- migrations.CreateModel(
- name='JenkinsSlave',
- fields=[
- ('id', models.AutoField(primary_key=True, serialize=False)),
- ('name', models.CharField(max_length=100, unique=True)),
- ('status', models.CharField(default='offline', max_length=30)),
- ('url', models.CharField(max_length=1024)),
- ('ci_slave', models.BooleanField(default=False)),
- ('dev_pod', models.BooleanField(default=False)),
- ('building', models.BooleanField(default=False)),
- ('last_job_name', models.CharField(default='', max_length=1024)),
- ('last_job_url', models.CharField(default='', max_length=1024)),
- ('last_job_scenario', models.CharField(default='', max_length=50)),
- ('last_job_branch', models.CharField(default='', max_length=50)),
- ('last_job_installer', models.CharField(default='', max_length=50)),
- ('last_job_result', models.CharField(default='', max_length=30)),
- ('active', models.BooleanField(default=False)),
- ],
- options={
- 'db_table': 'jenkins_slave',
- },
- ),
- migrations.CreateModel(
- name='JenkinsStatistic',
- fields=[
- ('id', models.AutoField(primary_key=True, serialize=False)),
- ('offline', models.BooleanField(default=False)),
- ('idle', models.BooleanField(default=False)),
- ('online', models.BooleanField(default=False)),
- ('timestamp', models.DateTimeField(auto_now_add=True)),
- ('slave', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='jenkins.JenkinsSlave')),
- ],
- options={
- 'db_table': 'jenkins_statistic',
- },
- ),
- ]
diff --git a/dashboard/src/jenkins/models.py b/dashboard/src/jenkins/models.py
deleted file mode 100644
index 8254ff3..0000000
--- a/dashboard/src/jenkins/models.py
+++ /dev/null
@@ -1,62 +0,0 @@
-##############################################################################
-# Copyright (c) 2016 Max Breitenfeldt and others.
-#
-# All rights reserved. This program and the accompanying materials
-# are made available under the terms of the Apache License, Version 2.0
-# which accompanies this distribution, and is available at
-# http://www.apache.org/licenses/LICENSE-2.0
-##############################################################################
-
-
-from django.db import models
-from django.utils import timezone
-
-
-class JenkinsSlave(models.Model):
- id = models.AutoField(primary_key=True)
- name = models.CharField(max_length=100, unique=True)
- status = models.CharField(max_length=30, default='offline')
- url = models.CharField(max_length=1024)
- ci_slave = models.BooleanField(default=False)
- dev_pod = models.BooleanField(default=False)
-
- building = models.BooleanField(default=False)
-
- last_job_name = models.CharField(max_length=1024, default='')
- last_job_url = models.CharField(max_length=1024, default='')
- last_job_scenario = models.CharField(max_length=50, default='')
- last_job_branch = models.CharField(max_length=50, default='')
- last_job_installer = models.CharField(max_length=50, default='')
- last_job_result = models.CharField(max_length=30, default='')
-
- active = models.BooleanField(default=False)
-
- def get_utilization(self, timedelta):
- """
- Return a dictionary containing the count of idle, online and offline measurements in the time from
- now-timedelta to now
- """
- utilization = {'idle': 0, 'online': 0, 'offline': 0}
- statistics = self.jenkinsstatistic_set.filter(timestamp__gte=timezone.now() - timedelta)
- utilization['idle'] = statistics.filter(idle=True).count()
- utilization['online'] = statistics.filter(online=True).count()
- utilization['offline'] = statistics.filter(offline=True).count()
- return utilization
-
- class Meta:
- db_table = 'jenkins_slave'
-
- def __str__(self):
- return self.name
-
-
-class JenkinsStatistic(models.Model):
- id = models.AutoField(primary_key=True)
- slave = models.ForeignKey(JenkinsSlave, on_delete=models.CASCADE)
- offline = models.BooleanField(default=False)
- idle = models.BooleanField(default=False)
- online = models.BooleanField(default=False)
- timestamp = models.DateTimeField(auto_now_add=True)
-
- class Meta:
- db_table = 'jenkins_statistic'
diff --git a/dashboard/src/jenkins/tasks.py b/dashboard/src/jenkins/tasks.py
deleted file mode 100644
index ea986c1..0000000
--- a/dashboard/src/jenkins/tasks.py
+++ /dev/null
@@ -1,64 +0,0 @@
-##############################################################################
-# Copyright (c) 2016 Max Breitenfeldt and others.
-#
-# All rights reserved. This program and the accompanying materials
-# are made available under the terms of the Apache License, Version 2.0
-# which accompanies this distribution, and is available at
-# http://www.apache.org/licenses/LICENSE-2.0
-##############################################################################
-
-
-from celery import shared_task
-
-from dashboard.models import Resource
-from jenkins.models import JenkinsSlave, JenkinsStatistic
-from .adapter import *
-
-
-@shared_task
-def sync_jenkins():
- update_jenkins_slaves()
-
-
-def update_jenkins_slaves():
- JenkinsSlave.objects.all().update(active=False)
-
- jenkins_slaves = get_all_slaves()
- for slave in jenkins_slaves:
- jenkins_slave, created = JenkinsSlave.objects.get_or_create(name=slave['displayName'],
- url=get_slave_url(slave))
- jenkins_slave.active = True
- jenkins_slave.ci_slave = is_ci_slave(slave['displayName'])
- jenkins_slave.dev_pod = is_dev_pod(slave['displayName'])
- jenkins_slave.status = get_slave_status(slave)
-
- # if this is a new slave and a pod, check if there is a resource for it, create one if not
- if created and 'pod' in slave['displayName']:
- # parse resource name from slave name
- # naming example: orange-pod1, resource name: Orange POD 1
- tokens = slave['displayName'].split('-')
- name = tokens[0].capitalize() + ' POD '# company name
- name += tokens[1][3:] # remove 'pod'
- resource, created = Resource.objects.get_or_create(name=name)
- resource.slave = jenkins_slave
- resource.save()
-
- last_job = get_jenkins_job(jenkins_slave.name)
- if last_job is not None:
- last_job = parse_job(last_job)
- jenkins_slave.last_job_name = last_job['name']
- jenkins_slave.last_job_url = last_job['url']
- jenkins_slave.last_job_scenario = last_job['scenario']
- jenkins_slave.last_job_branch = last_job['branch']
- jenkins_slave.last_job_installer = last_job['installer']
- jenkins_slave.last_job_result = last_job['result']
- jenkins_slave.save()
-
- jenkins_statistic = JenkinsStatistic(slave=jenkins_slave)
- if jenkins_slave.status == 'online' or jenkins_slave.status == 'building':
- jenkins_statistic.online = True
- if jenkins_slave.status == 'offline':
- jenkins_statistic.offline = True
- if jenkins_slave.status == 'online / idle':
- jenkins_statistic.idle = True
- jenkins_statistic.save()
diff --git a/dashboard/src/jenkins/tests.py b/dashboard/src/jenkins/tests.py
deleted file mode 100644
index 3723cd3..0000000
--- a/dashboard/src/jenkins/tests.py
+++ /dev/null
@@ -1,129 +0,0 @@
-##############################################################################
-# Copyright (c) 2016 Max Breitenfeldt and others.
-#
-# All rights reserved. This program and the accompanying materials
-# are made available under the terms of the Apache License, Version 2.0
-# which accompanies this distribution, and is available at
-# http://www.apache.org/licenses/LICENSE-2.0
-##############################################################################
-
-
-from datetime import timedelta
-from unittest import TestCase
-
-import jenkins.adapter as jenkins
-from jenkins.models import *
-
-
-# Tests that the data we get with the jenkinsadapter contains all the
-# data we need. These test will fail if;
-# - there is no internet connection
-# - the opnfv jenkins url has changed
-# - the jenkins api has changed
-# - jenkins is not set up / there is no data
-class JenkinsAdapterTestCase(TestCase):
- def test_get_all_slaves(self):
- slaves = jenkins.get_all_slaves()
- self.assertTrue(len(slaves) > 0)
- for slave in slaves:
- self.assertTrue('displayName' in slave)
- self.assertTrue('idle' in slave)
- self.assertTrue('offline' in slave)
-
- def test_get_slave(self):
- slaves = jenkins.get_all_slaves()
- self.assertEqual(slaves[0], jenkins.get_slave(slaves[0]['displayName']))
- self.assertEqual({}, jenkins.get_slave('098f6bcd4621d373cade4e832627b4f6'))
-
- def test_get_ci_slaves(self):
- slaves = jenkins.get_ci_slaves()
- self.assertTrue(len(slaves) > 0)
- for slave in slaves:
- self.assertTrue('nodeName' in slave)
-
- def test_get_jenkins_job(self):
- slaves = jenkins.get_ci_slaves()
- job = None
- for slave in slaves:
- job = jenkins.get_jenkins_job(slave['nodeName'])
- if job is not None:
- break
- # We need to test at least one job
- self.assertNotEqual(job, None)
-
- def test_get_all_jobs(self):
- jobs = jenkins.get_all_jobs()
- lastBuild = False
- self.assertTrue(len(jobs) > 0)
- for job in jobs:
- self.assertTrue('displayName' in job)
- self.assertTrue('url' in job)
- self.assertTrue('lastBuild' in job)
- if job['lastBuild'] is not None:
- lastBuild = True
- self.assertTrue('building' in job['lastBuild'])
- self.assertTrue('fullDisplayName' in job['lastBuild'])
- self.assertTrue('result' in job['lastBuild'])
- self.assertTrue('timestamp' in job['lastBuild'])
- self.assertTrue('builtOn' in job['lastBuild'])
- self.assertTrue(lastBuild)
-
- def test_parse_job(self):
- job = {
- "displayName": "apex-deploy-baremetal-os-nosdn-fdio-noha-colorado",
- "url": "https://build.opnfv.org/ci/job/apex-deploy-baremetal-os-nosdn-fdio-noha-colorado/",
- "lastBuild": {
- "building": False,
- "fullDisplayName": "apex-deploy-baremetal-os-nosdn-fdio-noha-colorado #37",
- "result": "SUCCESS",
- "timestamp": 1476283629917,
- "builtOn": "lf-pod1"
- }
- }
-
- job = jenkins.parse_job(job)
- self.assertEqual(job['scenario'], 'os-nosdn-fdio-noha')
- self.assertEqual(job['installer'], 'apex')
- self.assertEqual(job['branch'], 'colorado')
- self.assertEqual(job['result'], 'SUCCESS')
- self.assertEqual(job['building'], False)
- self.assertEqual(job['url'],
- "https://build.opnfv.org/ci/job/apex-deploy-baremetal-os-nosdn-fdio-noha-colorado/")
- self.assertEqual(job['name'],
- 'apex-deploy-baremetal-os-nosdn-fdio-noha-colorado')
-
- def test_get_slave_status(self):
- slave = {
- 'offline': True,
- 'idle': False
- }
- self.assertEqual(jenkins.get_slave_status(slave), 'offline')
- slave = {
- 'offline': False,
- 'idle': False
- }
- self.assertEqual(jenkins.get_slave_status(slave), 'online')
- slave = {
- 'offline': False,
- 'idle': True
- }
- self.assertEqual(jenkins.get_slave_status(slave), 'online / idle')
-
-
-class JenkinsModelTestCase(TestCase):
- def test_get_utilization(self):
- jenkins_slave = JenkinsSlave.objects.create(name='test', status='offline', url='')
- utilization = jenkins_slave.get_utilization(timedelta(weeks=1))
- self.assertEqual(utilization['idle'], 0)
- self.assertEqual(utilization['offline'], 0)
- self.assertEqual(utilization['online'], 0)
-
- for i in range(10):
- JenkinsStatistic.objects.create(slave=jenkins_slave,
- offline=True, idle=True,
- online=True)
-
- utilization = jenkins_slave.get_utilization(timedelta(weeks=1))
- self.assertEqual(utilization['idle'], 10)
- self.assertEqual(utilization['offline'], 10)
- self.assertEqual(utilization['online'], 10)
diff --git a/dashboard/src/manage.py b/dashboard/src/manage.py
index 80c496f..80c496f 100644..100755
--- a/dashboard/src/manage.py
+++ b/dashboard/src/manage.py
diff --git a/dashboard/src/notifier/__init__.py b/dashboard/src/notifier/__init__.py
index e69de29..d65b13a 100644
--- a/dashboard/src/notifier/__init__.py
+++ b/dashboard/src/notifier/__init__.py
@@ -0,0 +1,8 @@
+##############################################################################
+# Copyright (c) 2018 Sawyer Bergeron and others.
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+##############################################################################
diff --git a/dashboard/src/notifier/admin.py b/dashboard/src/notifier/admin.py
index cfbe778..d3e8be5 100644
--- a/dashboard/src/notifier/admin.py
+++ b/dashboard/src/notifier/admin.py
@@ -1,5 +1,5 @@
##############################################################################
-# Copyright (c) 2016 Max Breitenfeldt and others.
+# Copyright (c) 2018 Sawyer Bergeron, Parker Berberian, and others.
#
# All rights reserved. This program and the accompanying materials
# are made available under the terms of the Apache License, Version 2.0
@@ -12,3 +12,5 @@ from django.contrib import admin
from notifier.models import *
admin.site.register(Notifier)
+admin.site.register(MetaBooking)
+admin.site.register(LabMessage)
diff --git a/dashboard/src/notifier/apps.py b/dashboard/src/notifier/apps.py
index da5d3b0..52902da 100644
--- a/dashboard/src/notifier/apps.py
+++ b/dashboard/src/notifier/apps.py
@@ -1,5 +1,5 @@
##############################################################################
-# Copyright (c) 2016 Max Breitenfeldt and others.
+# Copyright (c) 2018 Sawyer Bergeron, Parker Berberian, and others.
#
# All rights reserved. This program and the accompanying materials
# are made available under the terms of the Apache License, Version 2.0
@@ -7,6 +7,7 @@
# http://www.apache.org/licenses/LICENSE-2.0
##############################################################################
+
from django.apps import AppConfig
diff --git a/dashboard/src/notifier/dispatchers.py b/dashboard/src/notifier/dispatchers.py
index c35fe2b..1b66b37 100644
--- a/dashboard/src/notifier/dispatchers.py
+++ b/dashboard/src/notifier/dispatchers.py
@@ -1,5 +1,5 @@
##############################################################################
-# Copyright (c) 2016 Max Breitenfeldt and others.
+# Copyright (c) 2018 Sawyer Bergeron, Parker Berberian, and others.
#
# All rights reserved. This program and the accompanying materials
# are made available under the terms of the Apache License, Version 2.0
@@ -30,4 +30,4 @@ class DispatchHandler():
instance.sender,[instance.user.email_addr], fail_silently=False)
def webnotification(instance):
- instance.msg_sent='by web notification' \ No newline at end of file
+ instance.msg_sent='by web notification'
diff --git a/dashboard/src/notifier/manager.py b/dashboard/src/notifier/manager.py
new file mode 100644
index 0000000..a705d00
--- /dev/null
+++ b/dashboard/src/notifier/manager.py
@@ -0,0 +1,98 @@
+##############################################################################
+# Copyright (c) 2018 Sawyer Bergeron and others.
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+##############################################################################
+
+from booking.models import *
+from notifier.models import Notifier, MetaBooking, LabMessage
+from django.utils import timezone
+from datetime import timedelta
+from django.template import Template, Context
+from account.models import UserProfile
+
+from django.db import models
+
+class NotifyPeriodic(object):
+ def task():
+ bookings_new = Booking.objects.filter(metabooking__isnull=True)
+ bookings_old = Booking.objects.filter(end__lte=timezone.now() + timedelta(hours=24)).filter(metabooking__ended_notified=False)
+
+ for booking in bookings_old:
+ metabooking = booking.metabooking
+ if booking.end <= timezone.now() + timedelta(hours=24):
+ if not metabooking.ending_notified:
+ Notify().notify(Notify.TOCLEAN, booking)
+ metabooking.ending_notified = True
+ metabooking.save()
+ if booking.end <= timezone.now():
+ metabooking = booking.metabooking
+ if not metabooking.ended_notified:
+ Notify().notify(Notify.CLEANED, booking)
+ metabooking.ended_notified = True
+ metabooking.save()
+
+ for booking in bookings_new:
+ metabooking = MetaBooking()
+ metabooking.booking = booking
+ metabooking.created_notified = True
+ metabooking.save()
+
+ Notify().notify(Notify.CREATED, booking)
+
+
+class Notify(object):
+
+ CREATED = "created"
+ TOCLEAN = "toclean"
+ CLEANED = "cleaned"
+
+ TITLES = {}
+ TITLES["created"] = "Your booking has been confirmed"
+ TITLES["toclean"] = "Your booking is ending soon"
+ TITLES["cleaned"] = "Your booking has ended"
+
+ """
+ Lab message is provided with the following context elements:
+ * if is for owner or for collaborator (if owner)
+ * recipient username (<owner, collaborator>.username)
+ * recipient full name (<owner, collaborator>.userprofile.full_name)
+ * booking it pertains to (booking)
+ * status message should convey (currently "created", "toclean" and "cleaned" as strings)
+ It should be a django template that can be rendered with these context elements
+ and should generally use all of them in one way or another.
+ It should be applicable to email, the web based general view, and should be scalable for
+ all device formats across those mediums.
+ """
+ def notify(self, notifier_type, booking):
+ template = Template(LabMessage.objects.filter(lab=booking.lab).first().msg)
+
+ context = {}
+ context["owner"] = booking.owner
+ context["notify_type"] = notifier_type
+ context["booking"] = booking
+ message = template.render(Context(context))
+ notifier = Notifier()
+ notifier.title = self.TITLES[notifier_type]
+ notifier.content = message
+ notifier.user = booking.owner.userprofile
+ notifier.sender = str(booking.lab)
+ notifier.save()
+ notifier.send()
+
+
+ context["owner"] = False
+
+ for user in booking.collaborators.all():
+ context["collaborator"] = user
+ message = template.render(Context(context))
+ notifier = Notifier()
+ notifier.title = self.TITLES[notifier_type]
+ notifier.content = message
+ notifier.user = UserProfile.objects.get(user=user)
+ notifier.sender = str(booking.lab)
+ notifier.save()
+ notifier.send()
diff --git a/dashboard/src/notifier/migrations/0001_initial.py b/dashboard/src/notifier/migrations/0001_initial.py
index cac4d04..e5d0009 100644
--- a/dashboard/src/notifier/migrations/0001_initial.py
+++ b/dashboard/src/notifier/migrations/0001_initial.py
@@ -1,6 +1,4 @@
-# -*- coding: utf-8 -*-
-# Generated by Django 1.10 on 2017-12-14 21:41
-from __future__ import unicode_literals
+# Generated by Django 2.1 on 2018-09-14 14:48
from django.db import migrations, models
import django.db.models.deletion
@@ -12,11 +10,30 @@ class Migration(migrations.Migration):
initial = True
dependencies = [
- ('dashboard', '0002_auto_20170505_0815'),
+ ('account', '0001_initial'),
+ ('booking', '0001_initial'),
]
operations = [
migrations.CreateModel(
+ name='LabMessage',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('msg', models.TextField()),
+ ('lab', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='account.Lab')),
+ ],
+ ),
+ migrations.CreateModel(
+ name='MetaBooking',
+ fields=[
+ ('id', models.AutoField(primary_key=True, serialize=False)),
+ ('ending_notified', models.BooleanField(default=False)),
+ ('ended_notified', models.BooleanField(default=False)),
+ ('created_notified', models.BooleanField(default=False)),
+ ('booking', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='metabooking', to='booking.Booking')),
+ ],
+ ),
+ migrations.CreateModel(
name='Notifier',
fields=[
('id', models.AutoField(primary_key=True, serialize=False)),
diff --git a/dashboard/src/notifier/models.py b/dashboard/src/notifier/models.py
index 9ebc6fc..ed0edeb 100644
--- a/dashboard/src/notifier/models.py
+++ b/dashboard/src/notifier/models.py
@@ -1,5 +1,5 @@
##############################################################################
-# Copyright (c) 2016 Max Breitenfeldt and others.
+# Copyright (c) 2018 Sawyer Bergeron, Parker Berberian, and others.
#
# All rights reserved. This program and the accompanying materials
# are made available under the terms of the Apache License, Version 2.0
@@ -8,14 +8,24 @@
##############################################################################
from django.db import models
-from jira import JIRA, JIRAError
-from dashboard.models import Resource
from booking.models import Booking
-from django.contrib.auth.models import User
from account.models import UserProfile
-from django.contrib import messages
-from django.db.models.signals import pre_save
from fernet_fields import EncryptedTextField
+from account.models import Lab
+
+
+class MetaBooking(models.Model):
+ id = models.AutoField(primary_key=True)
+ booking = models.OneToOneField(Booking, on_delete=models.CASCADE, related_name="metabooking")
+ ending_notified = models.BooleanField(default=False)
+ ended_notified = models.BooleanField(default=False)
+ created_notified = models.BooleanField(default=False)
+
+
+class LabMessage(models.Model):
+ lab = models.ForeignKey(Lab, on_delete=models.CASCADE)
+ msg = models.TextField() # django template should be put here
+
class Notifier(models.Model):
id = models.AutoField(primary_key=True)
@@ -24,15 +34,18 @@ class Notifier(models.Model):
user = models.ForeignKey(UserProfile, on_delete=models.CASCADE, null=True, blank=True)
sender = models.CharField(max_length=240, default='unknown')
message_type = models.CharField(max_length=240, default='email', choices=(
- ('email','Email'),
+ ('email', 'Email'),
('webnotification', 'Web Notification')))
msg_sent = ''
- import notifier.dispatchers
-
def __str__(self):
return self.title
+ """
+ Implement for next PR: send Notifier by media agreed to by user
+ """
+ def send(self):
+ pass
+
def getEmail(self):
return self.user.email_addr
-
diff --git a/dashboard/src/jenkins/admin.py b/dashboard/src/notifier/tests/test_dispatcher.py
index c499670..07d8387 100644
--- a/dashboard/src/jenkins/admin.py
+++ b/dashboard/src/notifier/tests/test_dispatcher.py
@@ -1,5 +1,5 @@
##############################################################################
-# Copyright (c) 2016 Max Breitenfeldt and others.
+# Copyright (c) 2018 Sawyer Bergeron and others.
#
# All rights reserved. This program and the accompanying materials
# are made available under the terms of the Apache License, Version 2.0
@@ -8,10 +8,10 @@
##############################################################################
-from django.conf import settings
-from django.contrib import admin
+from django.test import TestCase
+from notifier.models import *
+from django.contrib.auth.models import User
-from jenkins.models import JenkinsSlave
-
-if settings.DEBUG:
- admin.site.register(JenkinsSlave) \ No newline at end of file
+class DispatchTestCase(TestCase):
+ # This is a stub, it will be filled out as this feature is remade with saner practices.
+ pass
diff --git a/dashboard/src/notifier/tests/test_models.py b/dashboard/src/notifier/tests/test_models.py
new file mode 100644
index 0000000..10aec3e
--- /dev/null
+++ b/dashboard/src/notifier/tests/test_models.py
@@ -0,0 +1,29 @@
+##############################################################################
+# Copyright (c) 2018 Sawyer Bergeron and others.
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+##############################################################################
+
+
+from django.test import TestCase
+from notifier.models import *
+from django.contrib.auth.models import User
+
+class NotifierTestCase(TestCase):
+
+ def test_valid_notifier_saves(self):
+
+ sender = User.objects.create()
+ recipient = User.objects.create()
+ self.assertTrue(
+ Notifier.objects.create(
+ title='notification title',
+ content='notification body',
+ user=recipient,
+ sender=sender,
+ message_type='email'
+ )
+ )
diff --git a/dashboard/src/notifier/urls.py b/dashboard/src/notifier/urls.py
new file mode 100644
index 0000000..9bbc3bf
--- /dev/null
+++ b/dashboard/src/notifier/urls.py
@@ -0,0 +1,21 @@
+##############################################################################
+# Copyright (c) 2018 Sawyer Bergeron, Parker Berberian, and others.
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+##############################################################################
+
+
+from django.conf.urls import url
+
+from notifier.views import *
+
+app_name = "notifier"
+urlpatterns = [
+
+
+ url(r'^$', InboxView, name='messages'),
+ url(r'^notification/(?P<notification_id>[0-9]+)/$', NotificationView, name='notifier_single')
+]
diff --git a/dashboard/src/notifier/views.py b/dashboard/src/notifier/views.py
new file mode 100644
index 0000000..026894a
--- /dev/null
+++ b/dashboard/src/notifier/views.py
@@ -0,0 +1,34 @@
+##############################################################################
+# Copyright (c) 2018 Sawyer Bergeron and others.
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+##############################################################################
+
+from notifier.models import *
+from django.shortcuts import render
+
+def InboxView(request):
+ if request.user.is_authenticated:
+ user = request.user
+ else:
+ return render(request, "dashboard/login.html", {'title': 'Authentication Required'})
+
+ return render(request, "notifier/inbox.html", {'notifier_messages': Notifier.objects.filter(user=user.userprofile)})
+
+
+def NotificationView(request, notification_id):
+ if notification_id == 0:
+ pass
+ if request.user.is_authenticated:
+ user = request.user
+ else:
+ return render(request, "dashboard/login.html", {'title': 'Authentication Required'})
+
+ notification = Notifier.objects.get(id=notification_id)
+ if not notification.user.user.username == user.username:
+ return render(request, "dashboard/login.html", {'title': 'Access Denied'})
+
+ return render(request, "notifier/notification.html", {'notification': notification})
diff --git a/dashboard/src/pharos_dashboard/settings.py b/dashboard/src/pharos_dashboard/settings.py
index 8155fd4..7fccb32 100644
--- a/dashboard/src/pharos_dashboard/settings.py
+++ b/dashboard/src/pharos_dashboard/settings.py
@@ -1,3 +1,11 @@
+##############################################################################
+# Copyright (c) 2018 Sawyer Bergeron and others.
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+##############################################################################
import os
from datetime import timedelta
@@ -8,15 +16,18 @@ BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# NOTE: os.environ only returns strings, so making a comparison to
# 'True' here will convert it to the correct Boolean value.
DEBUG = os.environ['DEBUG'] == 'True'
+TESTING = os.environ['TEST'] == 'True'
# Application definition
INSTALLED_APPS = [
'dashboard',
+ 'resource_inventory',
'booking',
'account',
- 'jenkins',
'notifier',
+ 'workflow',
+ 'api',
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
@@ -51,6 +62,7 @@ TEMPLATES = [
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
+ 'dashboard.context_processors.debug',
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
@@ -60,6 +72,10 @@ TEMPLATES = [
},
]
+TEMPLATE_CONTEXT_PROCESSORS = [
+ 'dashboard.context_processors.debug',
+]
+
WSGI_APPLICATION = 'pharos_dashboard.wsgi.application'
# Password validation
@@ -128,13 +144,12 @@ DATABASES = {
}
}
-
# Rest API Settings
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.DjangoModelPermissionsOrAnonReadOnly'
],
- 'DEFAULT_FILTER_BACKENDS': ('rest_framework.filters.DjangoFilterBackend',),
+ 'DEFAULT_FILTER_BACKENDS': ('django_filters.rest_framework.FilterSet',),
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.SessionAuthentication',
'rest_framework.authentication.TokenAuthentication',
@@ -170,32 +185,20 @@ RABBITMQ_DEFAULT_PASS = os.environ['RABBITMQ_DEFAULT_PASS']
BROKER_URL = 'amqp://' + RABBITMQ_DEFAULT_USER + ':' + RABBITMQ_DEFAULT_PASS + '@rabbitmq:5672//'
-BOOKING_EXP_TIME = os.environ['BOOKING_EXPIRE_TIME']
-BOOKING_MAX_NUM = os.environ['BOOKING_MAXIMUM_NUMBER']
-
CELERYBEAT_SCHEDULE = {
- 'sync-jenkins': {
- 'task': 'jenkins.tasks.sync_jenkins',
- 'schedule': timedelta(minutes=5)
- },
- 'send-booking-notifications': {
- 'task': 'notification.tasks.send_booking_notifications',
- 'schedule': timedelta(minutes=5)
+ 'booking_poll': {
+ 'task': 'dashboard.tasks.booking_poll',
+ 'schedule': timedelta(minutes=1)
},
- 'clean-database': {
- 'task': 'dashboard.tasks.database_cleanup',
- 'schedule': timedelta(hours=24)
+ 'free_hosts': {
+ 'task': 'dashboard.tasks.free_hosts',
+ 'schedule': timedelta(minutes=1)
},
- 'booking_cleanup': {
- 'task': 'dashboard.tasks.booking_cleanup',
- 'schedule': timedelta(hours=24)
+ 'conjure_notifiers': {
+ 'task': 'dashboard.tasks.conjure_aggregate_notifiers',
+ 'schedule': timedelta(seconds=30)
},
}
-# Jenkins Settings
-ALL_SLAVES_URL = os.environ['JENKINS_URL'] + '/computer/api/json?tree=computer[displayName,offline,idle]'
-CI_SLAVES_URL = os.environ['JENKINS_URL'] + '/label/ci-pod/api/json?tree=nodes[nodeName,offline,idle]'
-ALL_JOBS_URL = os.environ['JENKINS_URL'] + '/api/json?tree=jobs[displayName,url,lastBuild[fullDisplayName,building,builtOn,timestamp,result]'
-GET_SLAVE_URL = os.environ['JENKINS_URL'] + '/computer/'
# Notifier Settings
EMAIL_HOST = os.environ['EMAIL_HOST']
@@ -204,3 +207,4 @@ EMAIL_HOST_USER = os.environ['EMAIL_HOST_USER']
EMAIL_HOST_PASSWORD = os.environ['EMAIL_HOST_PASSWORD']
EMAIL_USE_TLS=True
DEFAULT_EMAIL_FROM = os.environ.get('DEFAULT_EMAIL_FROM', 'webmaster@localhost')
+SESSION_ENGINE="django.contrib.sessions.backends.signed_cookies"
diff --git a/dashboard/src/pharos_dashboard/urls.py b/dashboard/src/pharos_dashboard/urls.py
index adcb5b8..8535bed 100644
--- a/dashboard/src/pharos_dashboard/urls.py
+++ b/dashboard/src/pharos_dashboard/urls.py
@@ -1,5 +1,6 @@
##############################################################################
# Copyright (c) 2016 Max Breitenfeldt and others.
+# Copyright (c) 2018 Sawyer Bergeron, Parker Berberian, and others.
#
# All rights reserved. This program and the accompanying materials
# are made available under the terms of the Apache License, Version 2.0
@@ -30,15 +31,17 @@ from django.contrib import admin
urlpatterns = [
+
+ url(r'^wf/', include('workflow.urls', namespace='workflow')),
url(r'^', include('dashboard.urls', namespace='dashboard')),
url(r'^booking/', include('booking.urls', namespace='booking')),
url(r'^accounts/', include('account.urls', namespace='account')),
-
+ url(r'^resource/', include('resource_inventory.urls', namespace='resource')),
url(r'^admin/', admin.site.urls),
url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework')),
-
- url(r'^api/', include('api.urls'))
+ url(r'^api/', include('api.urls')),
+ url(r'^messages/', include('notifier.urls', namespace='notifier'))
]
if settings.DEBUG is True:
- urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) \ No newline at end of file
+ urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
diff --git a/dashboard/src/jenkins/migrations/__init__.py b/dashboard/src/resource_inventory/__init__.py
index b5914ce..f903394 100644
--- a/dashboard/src/jenkins/migrations/__init__.py
+++ b/dashboard/src/resource_inventory/__init__.py
@@ -1,10 +1,8 @@
##############################################################################
-# Copyright (c) 2016 Max Breitenfeldt and others.
+# Copyright (c) 2018 Sawyer Bergeron, Parker Berberian, and others.
#
# All rights reserved. This program and the accompanying materials
# are made available under the terms of the Apache License, Version 2.0
# which accompanies this distribution, and is available at
# http://www.apache.org/licenses/LICENSE-2.0
##############################################################################
-
-
diff --git a/dashboard/src/resource_inventory/admin.py b/dashboard/src/resource_inventory/admin.py
new file mode 100644
index 0000000..222877a
--- /dev/null
+++ b/dashboard/src/resource_inventory/admin.py
@@ -0,0 +1,29 @@
+##############################################################################
+# Copyright (c) 2016 Max Breitenfeldt and others.
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+##############################################################################
+
+
+from django.contrib import admin
+
+from resource_inventory.models import *
+
+profiles = [HostProfile, InterfaceProfile, DiskProfile, CpuProfile, RamProfile]
+
+admin.site.register(profiles)
+
+generics = [GenericResourceBundle, GenericResource, GenericHost, GenericPod, GenericInterface]
+
+admin.site.register(generics)
+
+physical = [Host, Interface, Network, Vlan, ResourceBundle]
+
+admin.site.register(physical)
+
+config = [Scenario, Installer, Opsys, ConfigBundle, OPNFVConfig, OPNFVRole, Image, HostConfiguration]
+
+admin.site.register(config)
diff --git a/dashboard/src/jenkins/apps.py b/dashboard/src/resource_inventory/apps.py
index 41faf60..79768a7 100644
--- a/dashboard/src/jenkins/apps.py
+++ b/dashboard/src/resource_inventory/apps.py
@@ -7,9 +7,8 @@
# http://www.apache.org/licenses/LICENSE-2.0
##############################################################################
-
from django.apps import AppConfig
-class JenkinsConfig(AppConfig):
- name = 'jenkins'
+class ResourcesConfig(AppConfig):
+ name = 'hwresource'
diff --git a/dashboard/src/resource_inventory/migrations/0001_initial.py b/dashboard/src/resource_inventory/migrations/0001_initial.py
new file mode 100644
index 0000000..d01e8e7
--- /dev/null
+++ b/dashboard/src/resource_inventory/migrations/0001_initial.py
@@ -0,0 +1,328 @@
+# Generated by Django 2.1 on 2018-09-14 14:48
+
+from django.conf import settings
+import django.core.validators
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+ initial = True
+
+ dependencies = [
+ migrations.swappable_dependency(settings.AUTH_USER_MODEL),
+ ('account', '0001_initial'),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='ConfigBundle',
+ fields=[
+ ('id', models.AutoField(primary_key=True, serialize=False)),
+ ('name', models.CharField(max_length=200, unique=True)),
+ ('description', models.CharField(default='', max_length=1000)),
+ ],
+ ),
+ migrations.CreateModel(
+ name='CpuProfile',
+ fields=[
+ ('id', models.AutoField(primary_key=True, serialize=False)),
+ ('cores', models.IntegerField()),
+ ('architecture', models.CharField(choices=[('x86_64', 'x86_64'), ('aarch64', 'aarch64')], max_length=50)),
+ ('cpus', models.IntegerField()),
+ ('cflags', models.TextField(null=True)),
+ ],
+ ),
+ migrations.CreateModel(
+ name='DiskProfile',
+ fields=[
+ ('id', models.AutoField(primary_key=True, serialize=False)),
+ ('size', models.IntegerField()),
+ ('media_type', models.CharField(choices=[('SSD', 'SSD'), ('HDD', 'HDD')], max_length=50)),
+ ('name', models.CharField(max_length=50)),
+ ('rotation', models.IntegerField(default=0)),
+ ('interface', models.CharField(choices=[('sata', 'sata'), ('sas', 'sas'), ('ssd', 'ssd'), ('nvme', 'nvme'), ('scsi', 'scsi'), ('iscsi', 'iscsi')], default='sata', max_length=50)),
+ ],
+ ),
+ migrations.CreateModel(
+ name='GenericHost',
+ fields=[
+ ('id', models.AutoField(primary_key=True, serialize=False)),
+ ],
+ ),
+ migrations.CreateModel(
+ name='GenericInterface',
+ fields=[
+ ('id', models.AutoField(primary_key=True, serialize=False)),
+ ('host', models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, related_name='generic_interfaces', to='resource_inventory.GenericHost')),
+ ],
+ ),
+ migrations.CreateModel(
+ name='GenericResource',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('name', models.CharField(max_length=200, validators=[django.core.validators.RegexValidator(message='Enter a valid hostname. Full domain name may be 1-253 characters, each hostname 1-63 characters (including suffixed dot), and valid characters for hostnames are A-Z, a-z, 0-9, hyphen (-), and underscore (_)', regex='(?=^.{1,253}$)(?=(^([A-Za-z0-9\\-\\_]{1,62}\\.)*[A-Za-z0-9\\-\\_]{1,63}$))')])),
+ ],
+ ),
+ migrations.CreateModel(
+ name='GenericResourceBundle',
+ fields=[
+ ('id', models.AutoField(primary_key=True, serialize=False)),
+ ('name', models.CharField(max_length=300, unique=True)),
+ ('xml', models.TextField()),
+ ('description', models.CharField(default='', max_length=1000)),
+ ('lab', models.ForeignKey(null=True, on_delete=django.db.models.deletion.DO_NOTHING, to='account.Lab')),
+ ('owner', models.ForeignKey(null=True, on_delete=django.db.models.deletion.DO_NOTHING, to=settings.AUTH_USER_MODEL)),
+ ],
+ ),
+ migrations.CreateModel(
+ name='Host',
+ fields=[
+ ('id', models.AutoField(primary_key=True, serialize=False)),
+ ('booked', models.BooleanField(default=False)),
+ ('name', models.CharField(max_length=200, unique=True)),
+ ('labid', models.CharField(default='default_id', max_length=200)),
+ ('working', models.BooleanField(default=True)),
+ ('vendor', models.CharField(default='unknown', max_length=100)),
+ ('model', models.CharField(default='unknown', max_length=150)),
+ ],
+ ),
+ migrations.CreateModel(
+ name='HostConfiguration',
+ fields=[
+ ('id', models.AutoField(primary_key=True, serialize=False)),
+ ('bundle', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='hostConfigurations', to='resource_inventory.ConfigBundle')),
+ ('host', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='configuration', to='resource_inventory.GenericHost')),
+ ],
+ ),
+ migrations.CreateModel(
+ name='HostProfile',
+ fields=[
+ ('id', models.AutoField(primary_key=True, serialize=False)),
+ ('host_type', models.PositiveSmallIntegerField()),
+ ('name', models.CharField(max_length=200, unique=True)),
+ ('description', models.TextField()),
+ ('labs', models.ManyToManyField(related_name='hostprofiles', to='account.Lab')),
+ ],
+ ),
+ migrations.CreateModel(
+ name='Image',
+ fields=[
+ ('id', models.AutoField(primary_key=True, serialize=False)),
+ ('lab_id', models.IntegerField()),
+ ('name', models.CharField(max_length=200)),
+ ('public', models.BooleanField(default=True)),
+ ('description', models.TextField()),
+ ('from_lab', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='account.Lab')),
+ ('host_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='resource_inventory.HostProfile')),
+ ('owner', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL)),
+ ],
+ ),
+ migrations.CreateModel(
+ name='Installer',
+ fields=[
+ ('id', models.AutoField(primary_key=True, serialize=False)),
+ ('name', models.CharField(max_length=200)),
+ ],
+ ),
+ migrations.CreateModel(
+ name='Interface',
+ fields=[
+ ('id', models.AutoField(primary_key=True, serialize=False)),
+ ('mac_address', models.CharField(max_length=17)),
+ ('bus_address', models.CharField(max_length=50)),
+ ('name', models.CharField(default='eth0', max_length=100)),
+ ],
+ ),
+ migrations.CreateModel(
+ name='InterfaceProfile',
+ fields=[
+ ('id', models.AutoField(primary_key=True, serialize=False)),
+ ('speed', models.IntegerField()),
+ ('name', models.CharField(max_length=100)),
+ ('nic_type', models.CharField(choices=[('onboard', 'onboard'), ('pcie', 'pcie')], default='onboard', max_length=50)),
+ ('host', models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, related_name='interfaceprofile', to='resource_inventory.HostProfile')),
+ ],
+ ),
+ migrations.CreateModel(
+ name='Network',
+ fields=[
+ ('id', models.AutoField(primary_key=True, serialize=False)),
+ ('vlan_id', models.IntegerField()),
+ ('name', models.CharField(max_length=100)),
+ ],
+ ),
+ migrations.CreateModel(
+ name='OPNFVConfig',
+ fields=[
+ ('id', models.AutoField(primary_key=True, serialize=False)),
+ ('bundle', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='opnfv_config', to='resource_inventory.ConfigBundle')),
+ ('installer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='resource_inventory.Installer')),
+ ],
+ ),
+ migrations.CreateModel(
+ name='OPNFVRole',
+ fields=[
+ ('id', models.AutoField(primary_key=True, serialize=False)),
+ ('name', models.CharField(max_length=200)),
+ ('description', models.TextField()),
+ ],
+ ),
+ migrations.CreateModel(
+ name='Opsys',
+ fields=[
+ ('id', models.AutoField(primary_key=True, serialize=False)),
+ ('name', models.CharField(max_length=100)),
+ ('sup_installers', models.ManyToManyField(blank=True, to='resource_inventory.Installer')),
+ ],
+ ),
+ migrations.CreateModel(
+ name='RamProfile',
+ fields=[
+ ('id', models.AutoField(primary_key=True, serialize=False)),
+ ('amount', models.IntegerField()),
+ ('channels', models.IntegerField()),
+ ('host', models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, related_name='ramprofile', to='resource_inventory.HostProfile')),
+ ],
+ ),
+ migrations.CreateModel(
+ name='ResourceBundle',
+ fields=[
+ ('id', models.AutoField(primary_key=True, serialize=False)),
+ ('template', models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, to='resource_inventory.GenericResourceBundle')),
+ ],
+ ),
+ migrations.CreateModel(
+ name='Scenario',
+ fields=[
+ ('id', models.AutoField(primary_key=True, serialize=False)),
+ ('name', models.CharField(max_length=300)),
+ ],
+ ),
+ migrations.CreateModel(
+ name='Vlan',
+ fields=[
+ ('id', models.AutoField(primary_key=True, serialize=False)),
+ ('vlan_id', models.IntegerField()),
+ ('tagged', models.BooleanField()),
+ ],
+ ),
+ migrations.CreateModel(
+ name='GenericPod',
+ fields=[
+ ('genericresource_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='resource_inventory.GenericResource')),
+ ],
+ bases=('resource_inventory.genericresource',),
+ ),
+ migrations.AddField(
+ model_name='opnfvconfig',
+ name='scenario',
+ field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='resource_inventory.Scenario'),
+ ),
+ migrations.AddField(
+ model_name='interface',
+ name='config',
+ field=models.ManyToManyField(to='resource_inventory.Vlan'),
+ ),
+ migrations.AddField(
+ model_name='interface',
+ name='host',
+ field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='interfaces', to='resource_inventory.Host'),
+ ),
+ migrations.AddField(
+ model_name='installer',
+ name='sup_scenarios',
+ field=models.ManyToManyField(blank=True, to='resource_inventory.Scenario'),
+ ),
+ migrations.AddField(
+ model_name='hostconfiguration',
+ name='image',
+ field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='resource_inventory.Image'),
+ ),
+ migrations.AddField(
+ model_name='hostconfiguration',
+ name='opnfvRole',
+ field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='resource_inventory.OPNFVRole'),
+ ),
+ migrations.AddField(
+ model_name='host',
+ name='bundle',
+ field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='hosts', to='resource_inventory.ResourceBundle'),
+ ),
+ migrations.AddField(
+ model_name='host',
+ name='config',
+ field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='configuration', to='resource_inventory.HostConfiguration'),
+ ),
+ migrations.AddField(
+ model_name='host',
+ name='lab',
+ field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='account.Lab'),
+ ),
+ migrations.AddField(
+ model_name='host',
+ name='profile',
+ field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='resource_inventory.HostProfile'),
+ ),
+ migrations.AddField(
+ model_name='host',
+ name='template',
+ field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='resource_inventory.GenericHost'),
+ ),
+ migrations.AddField(
+ model_name='genericresource',
+ name='bundle',
+ field=models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, related_name='generic_resources', to='resource_inventory.GenericResourceBundle'),
+ ),
+ migrations.AddField(
+ model_name='genericinterface',
+ name='profile',
+ field=models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, to='resource_inventory.InterfaceProfile'),
+ ),
+ migrations.AddField(
+ model_name='genericinterface',
+ name='vlans',
+ field=models.ManyToManyField(to='resource_inventory.Vlan'),
+ ),
+ migrations.AddField(
+ model_name='generichost',
+ name='profile',
+ field=models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, to='resource_inventory.HostProfile'),
+ ),
+ migrations.AddField(
+ model_name='generichost',
+ name='resource',
+ field=models.OneToOneField(on_delete=django.db.models.deletion.DO_NOTHING, related_name='generic_host', to='resource_inventory.GenericResource'),
+ ),
+ migrations.AddField(
+ model_name='diskprofile',
+ name='host',
+ field=models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, related_name='storageprofile', to='resource_inventory.HostProfile'),
+ ),
+ migrations.AddField(
+ model_name='cpuprofile',
+ name='host',
+ field=models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, related_name='cpuprofile', to='resource_inventory.HostProfile'),
+ ),
+ migrations.AddField(
+ model_name='configbundle',
+ name='bundle',
+ field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='resource_inventory.GenericResourceBundle'),
+ ),
+ migrations.AddField(
+ model_name='configbundle',
+ name='owner',
+ field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL),
+ ),
+ migrations.AddField(
+ model_name='genericpod',
+ name='hosts',
+ field=models.ManyToManyField(to='resource_inventory.GenericHost'),
+ ),
+ migrations.AddField(
+ model_name='genericpod',
+ name='networks',
+ field=models.ManyToManyField(to='resource_inventory.Network'),
+ ),
+ ]
diff --git a/dashboard/src/resource_inventory/migrations/0002_auto_20180919_1459.py b/dashboard/src/resource_inventory/migrations/0002_auto_20180919_1459.py
new file mode 100644
index 0000000..80c9e6f
--- /dev/null
+++ b/dashboard/src/resource_inventory/migrations/0002_auto_20180919_1459.py
@@ -0,0 +1,18 @@
+# Generated by Django 2.1 on 2018-09-19 14:59
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('resource_inventory', '0001_initial'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='hostprofile',
+ name='host_type',
+ field=models.PositiveSmallIntegerField(default=0),
+ ),
+ ]
diff --git a/dashboard/src/resource_inventory/migrations/0003_vlan_public.py b/dashboard/src/resource_inventory/migrations/0003_vlan_public.py
new file mode 100644
index 0000000..07dc647
--- /dev/null
+++ b/dashboard/src/resource_inventory/migrations/0003_vlan_public.py
@@ -0,0 +1,18 @@
+# Generated by Django 2.1 on 2018-09-26 14:41
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('resource_inventory', '0002_auto_20180919_1459'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='vlan',
+ name='public',
+ field=models.BooleanField(default=False),
+ ),
+ ]
diff --git a/dashboard/src/resource_inventory/migrations/__init__.py b/dashboard/src/resource_inventory/migrations/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/dashboard/src/resource_inventory/migrations/__init__.py
diff --git a/dashboard/src/resource_inventory/models.py b/dashboard/src/resource_inventory/models.py
new file mode 100644
index 0000000..b71748e
--- /dev/null
+++ b/dashboard/src/resource_inventory/models.py
@@ -0,0 +1,299 @@
+##############################################################################
+# Copyright (c) 2018 Sawyer Bergeron, Parker Berberian, and others.
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+##############################################################################
+
+from django.contrib.auth.models import User
+from django.db import models
+from django.core.validators import RegexValidator
+
+import re
+
+from account.models import Lab
+
+
+# profile of resources hosted by labs
+class HostProfile(models.Model):
+ id = models.AutoField(primary_key=True)
+ host_type = models.PositiveSmallIntegerField(default=0)
+ name = models.CharField(max_length=200, unique=True)
+ description = models.TextField()
+ labs = models.ManyToManyField(Lab, related_name="hostprofiles")
+
+ def validate(self):
+ validname = re.compile("^[A-Za-z0-9\-\_\.\/\, ]+$")
+ if not validname.match(self.name):
+ return "Invalid host profile name given. Name must only use A-Z, a-z, 0-9, hyphens, underscores, dots, commas, or spaces."
+ else:
+ return None
+
+ def __str__(self):
+ return self.name
+
+
+class InterfaceProfile(models.Model):
+ id = models.AutoField(primary_key=True)
+ speed = models.IntegerField()
+ name = models.CharField(max_length=100)
+ host = models.ForeignKey(HostProfile, on_delete=models.DO_NOTHING, related_name='interfaceprofile')
+ nic_type = models.CharField(max_length=50, choices=[
+ ("onboard", "onboard"),
+ ("pcie", "pcie")
+ ], default="onboard")
+
+ def __str__(self):
+ return self.name + " for " + str(self.host)
+
+
+class DiskProfile(models.Model):
+ id = models.AutoField(primary_key=True)
+ size = models.IntegerField()
+ media_type = models.CharField(max_length=50, choices=[
+ ("SSD", "SSD"),
+ ("HDD", "HDD")
+ ])
+ name = models.CharField(max_length=50)
+ host = models.ForeignKey(HostProfile, on_delete=models.DO_NOTHING, related_name='storageprofile')
+ rotation = models.IntegerField(default=0)
+ interface = models.CharField(max_length=50, choices=[
+ ("sata", "sata"),
+ ("sas", "sas"),
+ ("ssd", "ssd"),
+ ("nvme", "nvme"),
+ ("scsi", "scsi"),
+ ("iscsi", "iscsi"),
+ ], default="sata")
+
+ def __str__(self):
+ return self.name + " for " + str(self.host)
+
+
+class CpuProfile(models.Model):
+ id = models.AutoField(primary_key=True)
+ cores = models.IntegerField()
+ architecture = models.CharField(max_length=50, choices=[
+ ("x86_64", "x86_64"),
+ ("aarch64", "aarch64")
+ ])
+ cpus = models.IntegerField()
+ host = models.ForeignKey(HostProfile, on_delete=models.DO_NOTHING, related_name='cpuprofile')
+ cflags = models.TextField(null=True)
+
+ def __str__(self):
+ return str(self.architecture) + " " + str(self.cpus) + "S" + str(self.cores) + " C for " + str(self.host)
+
+
+class RamProfile(models.Model):
+ id = models.AutoField(primary_key=True)
+ amount = models.IntegerField()
+ channels = models.IntegerField()
+ host = models.ForeignKey(HostProfile, on_delete=models.DO_NOTHING, related_name='ramprofile')
+
+ def __str__(self):
+ return str(self.amount) + "G for " + str(self.host)
+
+
+##Networking -- located here due to import order requirements
+class Network(models.Model):
+ id = models.AutoField(primary_key=True)
+ vlan_id = models.IntegerField()
+ name = models.CharField(max_length=100)
+
+ def __str__(self):
+ return self.name
+
+class Vlan(models.Model):
+ id = models.AutoField(primary_key=True)
+ vlan_id = models.IntegerField()
+ tagged = models.BooleanField()
+ public = models.BooleanField(default=False)
+
+ def __str__(self):
+ return str(self.vlan_id) + ("_T" if self.tagged else "")
+
+
+# Generic resource templates
+class GenericResourceBundle(models.Model):
+ id = models.AutoField(primary_key=True)
+ name = models.CharField(max_length=300, unique=True)
+ xml = models.TextField()
+ owner = models.ForeignKey(User, null=True, on_delete=models.DO_NOTHING)
+ lab = models.ForeignKey(Lab, null=True, on_delete=models.DO_NOTHING)
+ description = models.CharField(max_length=1000, default="")
+
+ def getHosts(self):
+ return_hosts = []
+ for genericResource in self.generic_resources.all():
+ return_hosts.append(genericResource.getHost())
+
+ return return_hosts
+
+ def __str__(self):
+ return self.name
+
+
+class GenericResource(models.Model):
+ bundle = models.ForeignKey(GenericResourceBundle, related_name='generic_resources', on_delete=models.DO_NOTHING)
+ hostname_validchars = RegexValidator(regex='(?=^.{1,253}$)(?=(^([A-Za-z0-9\-\_]{1,62}\.)*[A-Za-z0-9\-\_]{1,63}$))', message="Enter a valid hostname. Full domain name may be 1-253 characters, each hostname 1-63 characters (including suffixed dot), and valid characters for hostnames are A-Z, a-z, 0-9, hyphen (-), and underscore (_)")
+ name = models.CharField(max_length=200, validators=[hostname_validchars])
+
+ def getHost(self):
+ return self.generic_host
+
+ def __str__(self):
+ return self.name
+
+ def validate(self):
+ validname = re.compile('(?=^.{1,253}$)(?=(^([A-Za-z0-9\-\_]{1,62}\.)*[A-Za-z0-9\-\_]{1,63}$))')
+ if not validname.match(self.name):
+ return "Enter a valid hostname. Full domain name may be 1-253 characters, each hostname 1-63 characters (including suffixed dot), and valid characters for hostnames are A-Z, a-z, 0-9, hyphen (-), and underscore (_)"
+ else:
+ return None
+
+
+# Host template
+class GenericHost(models.Model):
+ id = models.AutoField(primary_key=True)
+ profile = models.ForeignKey(HostProfile, on_delete=models.DO_NOTHING)
+ resource = models.OneToOneField(GenericResource, related_name='generic_host', on_delete=models.DO_NOTHING)
+
+ def __str__(self):
+ return self.resource.name
+
+
+# Physical, actual resources
+class ResourceBundle(models.Model):
+ id = models.AutoField(primary_key=True)
+ template = models.ForeignKey(GenericResourceBundle, on_delete=models.DO_NOTHING)
+
+ def __str__(self):
+ return "instance of " + str(self.template)
+
+
+# Networking
+
+
+class GenericInterface(models.Model):
+ id = models.AutoField(primary_key=True)
+ vlans = models.ManyToManyField(Vlan)
+ profile = models.ForeignKey(InterfaceProfile, on_delete=models.DO_NOTHING)
+ host = models.ForeignKey(GenericHost, on_delete=models.DO_NOTHING, related_name='generic_interfaces')
+
+ def __str__(self):
+ return "type " + str(self.profile) + " on host " + str(self.host)
+
+
+class Scenario(models.Model):
+ id = models.AutoField(primary_key=True)
+ name = models.CharField(max_length=300)
+
+ def __str__(self):
+ return self.name
+
+class Installer(models.Model):
+ id = models.AutoField(primary_key=True)
+ name = models.CharField(max_length=200)
+ sup_scenarios = models.ManyToManyField(Scenario, blank=True)
+
+ def __str__(self):
+ return self.name
+
+class Opsys(models.Model):
+ id = models.AutoField(primary_key=True)
+ name = models.CharField(max_length=100)
+ sup_installers = models.ManyToManyField(Installer, blank=True)
+
+ def __str__(self):
+ return self.name
+
+class ConfigBundle(models.Model):
+ id = models.AutoField(primary_key=True)
+ owner = models.ForeignKey(User, on_delete=models.CASCADE) #consider setting to root user?
+ name = models.CharField(max_length=200, unique=True)
+ description = models.CharField(max_length=1000, default="")
+ bundle = models.ForeignKey(GenericResourceBundle, null=True, on_delete=models.CASCADE)
+
+ def __str__(self):
+ return self.name
+
+class OPNFVConfig(models.Model):
+ id = models.AutoField(primary_key=True)
+ installer = models.ForeignKey(Installer, on_delete=models.CASCADE)
+ scenario = models.ForeignKey(Scenario, on_delete=models.CASCADE)
+ bundle = models.ForeignKey(ConfigBundle, related_name="opnfv_config", on_delete=models.CASCADE)
+
+ def __str__(self):
+ return "OPNFV job with " + str(self.installer) + " and " + str(self.scenario)
+
+class OPNFVRole(models.Model):
+ id = models.AutoField(primary_key=True)
+ name = models.CharField(max_length=200)
+ description = models.TextField()
+
+ def __str__(self):
+ return self.name
+
+class Image(models.Model):
+ """
+ model for representing OS images / snapshots of hosts
+ """
+ id = models.AutoField(primary_key=True)
+ lab_id = models.IntegerField() # ID the lab who holds this image knows
+ from_lab = models.ForeignKey(Lab, on_delete=models.CASCADE)
+ name = models.CharField(max_length=200)
+ owner = models.ForeignKey(User, null=True, on_delete=models.SET_NULL)
+ public = models.BooleanField(default=True)
+ host_type = models.ForeignKey(HostProfile, on_delete=models.CASCADE) #may need to change to models.SET() once images are transferrable between compatible host types
+ description = models.TextField()
+
+ def __str__(self):
+ return self.name
+
+class HostConfiguration(models.Model):
+ """
+ model to represent a complete configuration for a single
+ physical host
+ """
+ id = models.AutoField(primary_key=True)
+ host = models.ForeignKey(GenericHost, related_name="configuration", on_delete=models.CASCADE)
+ image = models.ForeignKey(Image, on_delete=models.PROTECT)
+ bundle = models.ForeignKey(ConfigBundle, related_name="hostConfigurations", null=True, on_delete=models.CASCADE)
+ opnfvRole = models.ForeignKey(OPNFVRole, on_delete=models.PROTECT) #need protocol for phasing out a role if we are going to allow that to happen
+
+ def __str__(self):
+ return "config with " + str(self.host) + " and image " + str(self.image)
+
+
+# Concrete host, actual machine in a lab
+class Host(models.Model):
+ id = models.AutoField(primary_key=True)
+ template = models.ForeignKey(GenericHost, on_delete=models.SET_NULL, null=True)
+ booked = models.BooleanField(default=False)
+ name = models.CharField(max_length=200, unique=True)
+ bundle = models.ForeignKey(ResourceBundle, related_name='hosts', on_delete=models.SET_NULL, null=True)
+ config = models.ForeignKey(HostConfiguration, null=True, related_name="configuration", on_delete=models.SET_NULL)
+ labid = models.CharField(max_length=200, default="default_id")
+ profile = models.ForeignKey(HostProfile, on_delete=models.CASCADE)
+ lab = models.ForeignKey(Lab, on_delete=models.CASCADE)
+ working = models.BooleanField(default=True)
+ vendor = models.CharField(max_length=100, default="unknown")
+ model = models.CharField(max_length=150, default="unknown")
+
+ def __str__(self):
+ return self.name
+
+
+class Interface(models.Model):
+ id = models.AutoField(primary_key=True)
+ mac_address = models.CharField(max_length=17)
+ bus_address = models.CharField(max_length=50)
+ name = models.CharField(max_length=100, default="eth0")
+ config = models.ManyToManyField(Vlan)
+ host = models.ForeignKey(Host, on_delete=models.CASCADE, related_name='interfaces')
+
+ def __str__(self):
+ return self.mac_address + " on host " + str(self.host)
diff --git a/dashboard/src/resource_inventory/resource_manager.py b/dashboard/src/resource_inventory/resource_manager.py
new file mode 100644
index 0000000..cd70867
--- /dev/null
+++ b/dashboard/src/resource_inventory/resource_manager.py
@@ -0,0 +1,197 @@
+##############################################################################
+# Copyright (c) 2018 Parker Berberian, Sawyer Bergeron, and others.
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+##############################################################################
+
+
+from django.core.exceptions import *
+from django.template.loader import render_to_string
+
+import booking
+from dashboard.exceptions import *
+from resource_inventory.models import *
+
+class ResourceManager:
+
+ instance = None
+
+ def __init__(self):
+ pass
+
+ @staticmethod
+ def getInstance():
+ if ResourceManager.instance is None:
+ ResourceManager.instance = ResourceManager()
+ return ResourceManager.instance
+
+ #public interface
+ def deleteResourceBundle(self, resourceBundle):
+ for host in Host.objects.filter(bundle=resourceBundle):
+ self.releaseHost(host)
+ resourceBundle.delete()
+
+ def convertResourceBundle(self, genericResourceBundle, lab=None, config=None):
+ """
+ Takes in a GenericResourceBundle and 'converts' it into a ResourceBundle
+ """
+ resource_bundle = ResourceBundle()
+ resource_bundle.template = genericResourceBundle
+ resource_bundle.save()
+
+ hosts = genericResourceBundle.getHosts()
+
+ #current supported case: user creating new booking
+ #currently unsupported: editing existing booking
+
+ physical_hosts = []
+
+ for host in hosts:
+ host_config=None
+ if config:
+ host_config = HostConfiguration.objects.get(bundle=config, host=host)
+ try:
+ physical_host = self.acquireHost(host, genericResourceBundle.lab.name)
+ except ResourceAvailabilityException:
+ self.fail_acquire(physical_hosts)
+ raise ResourceAvailabilityException("Could not provision hosts, not enough available")
+ try:
+ physical_host.bundle = resource_bundle
+ physical_host.template = host
+ physical_host.config = host_config
+ physical_hosts.append(physical_host)
+
+ self.configureNetworking(physical_host)
+ except:
+ self.fail_acquire(physical_hosts)
+ raise ResourceProvisioningException("Network configuration failed.")
+ try:
+ physical_host.save()
+ except:
+ self.fail_acquire(physical_hosts)
+ raise ModelValidationException("Saving hosts failed")
+
+ return resource_bundle
+
+ def configureNetworking(self, host):
+ generic_interfaces = list(host.template.generic_interfaces.all())
+ for int_num, physical_interface in enumerate(host.interfaces.all()):
+ generic_interface = generic_interfaces[int_num]
+ physical_interface.config.clear()
+ for vlan in generic_interface.vlans.all():
+ physical_interface.config.add(vlan)
+
+ #private interface
+ def acquireHost(self, genericHost, labName):
+ host_full_set = Host.objects.filter(lab__name__exact=labName, profile=genericHost.profile)
+ if not host_full_set.first():
+ raise ResourceExistenceException("No matching servers found")
+ host_set = host_full_set.filter(booked=False)
+ if not host_set.first():
+ raise ResourceAvailabilityException("No unbooked hosts match requested hosts")
+ host = host_set.first()
+ host.booked = True
+ host.template = genericHost
+ host.save()
+ return host
+
+ def releaseHost(self, host):
+ host.template = None
+ host.bundle = None
+ host.booked = False
+ host.save()
+
+ def fail_acquire(self, hosts):
+ for host in hosts:
+ self.releaseHost(host)
+
+ def makePDF(self, resource):
+ """
+ fills the pod descriptor file template with info about the resource
+ """
+ template = "dashboard/pdf.yaml"
+ info = {}
+ info['details'] = self.get_pdf_details(resource)
+ info['jumphost'] = self.get_pdf_jumphost(resource)
+ info['nodes'] = self.get_pdf_nodes(resource)
+
+ return render_to_string(template, context=info)
+
+ def get_pdf_details(self, resource):
+ details = {}
+ owner = "Anon"
+ email = "email@mail.com"
+ resource_lab = resource.template.lab
+ lab = resource_lab.name
+ location = resource_lab.location
+ pod_type = "development"
+ link = "https://wiki.opnfv.org/display/INF/Pharos+Laas"
+
+ try:
+ # try to get more specific info that may fail, we dont care if it does
+ booking_owner = booking.models.Booking.objects.get(resource=resource).owner
+ owner = booking_owner.username
+ email = booking_owner.userprofile.email_addr
+ except Exception as e:
+ pass
+
+ details['owner'] = owner
+ details['email'] = email
+ details['lab'] = lab
+ details['location'] = location
+ details['type'] = pod_type
+ details['link'] = link
+
+ return details
+
+ def get_pdf_jumphost(self, resource):
+ jumphost = Host.objects.get(bundle=resource, config__opnfvRole__name__iexact="jumphost")
+ return self.get_pdf_host(jumphost)
+
+ def get_pdf_nodes(self, resource):
+ pdf_nodes = []
+ nodes = Host.objects.filter(bundle=resource).exclude(config__opnfvRole__name__iexact="jumphost")
+ for node in nodes:
+ pdf_nodes.append(self.get_pdf_host(node))
+
+ return pdf_nodes
+
+
+ def get_pdf_host(self, host):
+ host_info = {}
+ host_info['name'] = host.template.resource.name
+ host_info['node'] = {}
+ host_info['node']['type'] = "baremetal"
+ host_info['node']['vendor'] = host.vendor
+ host_info['node']['model'] = host.model
+ host_info['node']['arch'] = host.profile.cpuprofile.first().architecture
+ host_info['node']['cpus'] = host.profile.cpuprofile.first().cpus
+ host_info['node']['cores'] = host.profile.cpuprofile.first().cores
+ cflags = host.profile.cpuprofile.first().cflags
+ if cflags and cflags.strip():
+ host_info['node']['cpu_cflags'] = cflags
+ host_info['node']['memory'] = str(host.profile.ramprofile.first().amount) + "G"
+ host_info['disks'] = []
+ for disk in host.profile.storageprofile.all():
+ disk_info = {}
+ disk_info['name'] = disk.name
+ disk_info['capacity'] = str(disk.size) + "G"
+ disk_info['type'] = disk.media_type
+ disk_info['interface'] = disk.interface
+ disk_info['rotation'] = disk.rotation
+ host_info['disks'].append(disk_info)
+
+ host_info['interfaces'] = []
+ for interface in host.interfaces.all():
+ iface_info = {}
+ iface_info['name'] = interface.name
+ iface_info['address'] = "unknown"
+ iface_info['mac_address'] = interface.mac_address
+ vlans = "|".join([str(vlan.vlan_id) for vlan in interface.config.all()])
+ iface_info['vlans'] = vlans
+ host_info['interfaces'].append(iface_info)
+
+ return host_info
diff --git a/dashboard/src/resource_inventory/tests/test_managers.py b/dashboard/src/resource_inventory/tests/test_managers.py
new file mode 100644
index 0000000..5a13b2e
--- /dev/null
+++ b/dashboard/src/resource_inventory/tests/test_managers.py
@@ -0,0 +1,236 @@
+##############################################################################
+# Copyright (c) 2018 Parker Berberian, Sawyer Bergeron, and others.
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+##############################################################################
+
+from django.test import TestCase
+from django.contrib.auth.models import User
+
+from resource.inventory_manager import InventoryManager
+from resource.resource_manager import ResourceManager
+from resource.models import *
+
+
+class InventoryManagerTestCase(TestCase):
+
+ def test_singleton(self):
+ instance = InventoryManager.getInstance()
+ self.assertTrue(isinstance(instance, InventoryManager))
+ self.assertTrue(instance is InventoryManager.getInstance())
+
+ def setUp(self):
+ # setup
+ # create lab and give it resources
+ user = User.objects.create(username="username")
+ self.lab = Lab.objects.create(
+ lab_user=user,
+ name='test lab',
+ contact_email='someone@email.com',
+ contact_phone='dont call me'
+ )
+
+ # create hostProfile
+ hostProfile = HostProfile.objects.create(
+ host_type=0,
+ name='Test profile',
+ description='a test profile'
+ )
+ interfaceProfile = InterfaceProfile.objects.create(
+ speed=1000,
+ name='eno3',
+ host=hostProfile
+ )
+ diskProfile = DiskProfile.objects.create(
+ size=1000,
+ media_type="SSD",
+ name='/dev/sda',
+ host=hostProfile
+ )
+ cpuProfile = CpuProfile.objects.create(
+ cores=96,
+ architecture="x86_64",
+ cpus=2,
+ host=hostProfile
+ )
+ ramProfile = RamProfile.objects.create(
+ amount=256,
+ channels=4,
+ host=hostProfile
+ )
+
+ #create GenericResourceBundle
+ genericBundle = GenericResourceBundle.objects.create()
+
+ self.gHost1 = GenericHost.objects.create(
+ bundle=genericBundle,
+ name='generic host 1',
+ profile=hostProfile
+ )
+ self.gHost2 = GenericHost.objects.create(
+ bundle=genericBundle,
+ name='generic host 2',
+ profile=hostProfile
+ )
+
+ #actual resource bundle
+ bundle = ResourceBundle.objects.create(template=genericBundle)
+
+ self.host1 = Host.objects.create(
+ template=self.gHost1,
+ booked=True,
+ name='host1',
+ bundle=bundle,
+ profile=hostProfile,
+ lab=self.lab
+ )
+
+ self.host2 = Host.objects.create(
+ template=self.gHost2,
+ booked=True,
+ name='host2',
+ bundle=bundle,
+ profile=hostProfile,
+ lab=self.lab
+ )
+
+ vlan1 = Vlan.objects.create(vlan_id=300, tagged=False)
+ vlan2 = Vlan.objects.create(vlan_id=300, tagged=False)
+
+ iface1 = Interface.objects.create(
+ mac_address='00:11:22:33:44:55',
+ bus_address='some bus address',
+ switch_name='switch1',
+ port_name='port10',
+ config=vlan1,
+ host=self.host1
+ )
+ iface2 = Interface.objects.create(
+ mac_address='00:11:22:33:44:56',
+ bus_address='some bus address',
+ switch_name='switch1',
+ port_name='port12',
+ config=vlan2,
+ host=self.host2
+ )
+
+ def test_acquire_host(self):
+ host = InventoryManager.getInstance().acquireHost(self.gHost1, self.lab.name)
+ self.assertNotEquals(host, None)
+ self.assertTrue(host.booked)
+ self.assertEqual(host.template, self.gHost1)
+
+ def test_release_host(self):
+ host = InventoryManager.getInstance().acquireHost(self.gHost1, self.lab.name)
+ self.assertTrue(host.booked)
+ InventoryManager.getInstance().releaseHost(host)
+ self.assertFalse(host.booked)
+
+
+class ResourceManagerTestCase(TestCase):
+ def test_singleton(self):
+ instance = ResourceManager.getInstance()
+ self.assertTrue(isinstance(instance, ResourceManager))
+ self.assertTrue(instance is ResourceManager.getInstance())
+
+ def setUp(self):
+ # setup
+ # create lab and give it resources
+ user = User.objects.create(username="username")
+ self.lab = Lab.objects.create(
+ lab_user=user,
+ name='test lab',
+ contact_email='someone@email.com',
+ contact_phone='dont call me'
+ )
+
+ # create hostProfile
+ hostProfile = HostProfile.objects.create(
+ host_type=0,
+ name='Test profile',
+ description='a test profile'
+ )
+ interfaceProfile = InterfaceProfile.objects.create(
+ speed=1000,
+ name='eno3',
+ host=hostProfile
+ )
+ diskProfile = DiskProfile.objects.create(
+ size=1000,
+ media_type="SSD",
+ name='/dev/sda',
+ host=hostProfile
+ )
+ cpuProfile = CpuProfile.objects.create(
+ cores=96,
+ architecture="x86_64",
+ cpus=2,
+ host=hostProfile
+ )
+ ramProfile = RamProfile.objects.create(
+ amount=256,
+ channels=4,
+ host=hostProfile
+ )
+
+ #create GenericResourceBundle
+ genericBundle = GenericResourceBundle.objects.create()
+
+ self.gHost1 = GenericHost.objects.create(
+ bundle=genericBundle,
+ name='generic host 1',
+ profile=hostProfile
+ )
+ self.gHost2 = GenericHost.objects.create(
+ bundle=genericBundle,
+ name='generic host 2',
+ profile=hostProfile
+ )
+
+ #actual resource bundle
+ bundle = ResourceBundle.objects.create(template=genericBundle)
+
+ self.host1 = Host.objects.create(
+ template=self.gHost1,
+ booked=True,
+ name='host1',
+ bundle=bundle,
+ profile=hostProfile,
+ lab=self.lab
+ )
+
+ self.host2 = Host.objects.create(
+ template=self.gHost2,
+ booked=True,
+ name='host2',
+ bundle=bundle,
+ profile=hostProfile,
+ lab=self.lab
+ )
+
+ vlan1 = Vlan.objects.create(vlan_id=300, tagged=False)
+ vlan2 = Vlan.objects.create(vlan_id=300, tagged=False)
+
+ iface1 = Interface.objects.create(
+ mac_address='00:11:22:33:44:55',
+ bus_address='some bus address',
+ switch_name='switch1',
+ port_name='port10',
+ config=vlan1,
+ host=self.host1
+ )
+ iface2 = Interface.objects.create(
+ mac_address='00:11:22:33:44:56',
+ bus_address='some bus address',
+ switch_name='switch1',
+ port_name='port12',
+ config=vlan2,
+ host=self.host2
+ )
+
+ def test_convert_bundle(self):
+ bundle = ResourceManager.getInstance().convertResoureBundle(self.genericBundle, self.lab.name)
+ # verify bundle configuration
diff --git a/dashboard/src/resource_inventory/tests/test_models.py b/dashboard/src/resource_inventory/tests/test_models.py
new file mode 100644
index 0000000..4ddedf2
--- /dev/null
+++ b/dashboard/src/resource_inventory/tests/test_models.py
@@ -0,0 +1,162 @@
+##############################################################################
+# Copyright (c) 2018 Parker Berberian, Sawyer Bergeron, and others.
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+##############################################################################
+from django.test import TestCase
+from django.contrib.auth.models import User
+from account.models import Lab
+from resource_inventory.models import *
+
+
+class ConfigUtil():
+ count=0
+
+ @staticmethod
+ def makeScenario():
+ return Scenario.objects.create(name="testScenario")
+
+ @staticmethod
+ def makeInstaller():
+ inst = Installer.objects.create(
+ name = "testInstaller"
+ )
+ inst.sup_scenarios = [ConfigUtil.makeScenario()]
+ return inst
+
+ @staticmethod
+ def makeOpsys():
+ os = Opsys.objects.create(
+ name = "test Operating System"
+ )
+ os.sup_installers = [ConfigUtil.makeInstaller()]
+ return os
+
+ @staticmethod
+ def makeConfigBundle():
+ user = User.objects.create(username="test_user" + str(ConfigUtil.count))
+ ConfigUtil.count += 1
+ return ConfigBundle.objects.create(
+ owner = user
+ )
+
+ @staticmethod
+ def makeOPNFVConfig():
+ installer = ConfigUtil.makeInstaller()
+ scenario = ConfigUtil.makeScenario()
+ bundle = ConfigUtil.makeConfigBundle()
+ return OPNFVConfig.objects.create(
+ installer=installer,
+ scenario=scenario,
+ bundle=bundle
+ )
+
+ @staticmethod
+ def makeOPNFVRole():
+ return OPNFVRole.objects.create(
+ name="Test role",
+ description="This is a test role"
+ )
+
+ @staticmethod
+ def makeImage():
+ owner = User.objects.create(username="another test user")
+ lab_user = User.objects.create(username="labUserForTests")
+ lab = Lab.objects.create(
+ lab_user=lab_user,
+ name="this is lab for testing",
+ contact_email="email@mail.com",
+ contact_phone="123-4567"
+ )
+
+ return Image.objects.create(
+ lab_id=0,
+ from_lab=lab,
+ name="an image for testing",
+ owner=owner
+ )
+
+
+ @staticmethod
+ def makeGenericHost():
+ profile = HostProfile.objects.create(
+ host_type=0,
+ name="test lab for config bundle",
+ description="this is a test profile"
+ )
+ user = User.objects.create(username="test sample user 12")
+ bundle = GenericResourceBundle.objects.create(
+ name="Generic bundle for config tests",
+ xml="",
+ owner=user,
+ description=""
+ )
+
+ resource = GenericResource.objects.create(
+ bundle=bundle,
+ name="a test generic resource"
+ )
+
+ return GenericHost.objects.create(
+ profile=profile,
+ resource=resource
+ )
+
+ @staticmethod
+ def makeHostConfiguration():
+ host = ConfigUtil.makeGenericHost()
+ image = ConfigUtil.makeImage()
+ bundle = ConfigUtil.makeConfigBundle()
+ opnfvRole = ConfigUtil.makeOPNFVRole()
+ return HostConfiguration.objects.create(
+ host=host,
+ image=image,
+ bundle=bundle,
+ opnfvRole=opnfvRole
+ )
+
+
+class ScenarioTestCase(TestCase):
+
+ def test_save(self):
+ self.assertTrue(ConfigUtil.makeScenario())
+
+class InstallerTestCase(TestCase):
+
+ def test_save(self):
+ self.assertTrue(ConfigUtil.makeInstaller())
+
+class OperatingSystemTestCase(TestCase):
+
+ def test_save(self):
+ self.assertTrue(ConfigUtil.makeOpsys())
+
+class ConfigBundleTestCase(TestCase):
+
+ def test_save(self):
+ self.assertTrue(ConfigUtil.makeConfigBundle())
+
+class OPNFVConfigTestCase(TestCase):
+
+ def test_save(self):
+ self.assertTrue(ConfigUtil.makeOPNFVConfig())
+
+class OPNFVRoleTestCase(TestCase):
+
+ def test_save(self):
+ self.assertTrue(ConfigUtil.makeOPNFVRole())
+
+
+class HostConfigurationTestCase(TestCase):
+
+ def test_save(self):
+ self.assertTrue(ConfigUtil.makeHostConfiguration())
+
+
+class ImageTestCase(TestCase):
+
+ def test_save(self):
+ self.assertTrue(ConfigUtil.makeImage())
diff --git a/dashboard/src/resource_inventory/urls.py b/dashboard/src/resource_inventory/urls.py
new file mode 100644
index 0000000..4e159ba
--- /dev/null
+++ b/dashboard/src/resource_inventory/urls.py
@@ -0,0 +1,34 @@
+##############################################################################
+# Copyright (c) 2016 Max Breitenfeldt and others.
+# Copyright (c) 2018 Parker Berberian, Sawyer Bergeron, and others.
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+##############################################################################
+
+
+"""pharos_dashboard URL Configuration
+
+The `urlpatterns` list routes URLs to views. For more information please see:
+ https://docs.djangoproject.com/en/1.10/topics/http/urls/
+Examples:
+Function views
+ 1. Add an import: from my_app import views
+ 2. Add a URL to urlpatterns: url(r'^$', views.home, name='home')
+Class-based views
+ 1. Add an import: from other_app.views import Home
+ 2. Add a URL to urlpatterns: url(r'^$', Home.as_view(), name='home')
+Including another URLconf
+ 1. Import the include() function: from django.conf.urls import url, include
+ 2. Add a URL to urlpatterns: url(r'^blog/', include('blog.urls'))
+"""
+from django.conf.urls import url
+from resource_inventory.views import HostView
+
+
+app_name = "resource"
+urlpatterns = [
+ url(r'^hosts$', HostView.as_view(), name='hosts')
+]
diff --git a/dashboard/src/resource_inventory/views.py b/dashboard/src/resource_inventory/views.py
new file mode 100644
index 0000000..7e73006
--- /dev/null
+++ b/dashboard/src/resource_inventory/views.py
@@ -0,0 +1,24 @@
+##############################################################################
+# Copyright (c) 2018 Sawyer Bergeron, Parker Berberian, and others.
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+##############################################################################
+
+
+from django.shortcuts import render
+from django.views import View
+from django.views.generic import TemplateView
+
+from resource_inventory.models import Host
+
+class HostView(TemplateView):
+ template_name = "resource/hosts.html"
+
+ def get_context_data(self, **kwargs):
+ context = super(HostView, self).get_context_data(**kwargs)
+ hosts = Host.objects.filter(working=True)
+ context.update({'hosts':hosts, 'title':"Hardware Resources"})
+ return context
diff --git a/dashboard/src/static/css/graph_common.css b/dashboard/src/static/css/graph_common.css
new file mode 100644
index 0000000..7f90a66
--- /dev/null
+++ b/dashboard/src/static/css/graph_common.css
@@ -0,0 +1,162 @@
+div.mxRubberband {
+ position: absolute;
+ overflow: hidden;
+ border-style: solid;
+ border-width: 1px;
+ border-color: #0000FF;
+ background: #0077FF;
+}
+.mxCellEditor {
+ background: url(data:image/gif;base64,R0lGODlhMAAwAIAAAP///wAAACH5BAEAAAAALAAAAAAwADAAAAIxhI+py+0Po5y02ouz3rz7D4biSJbmiabqyrbuC8fyTNf2jef6zvf+DwwKh8Si8egpAAA7);
+ _background: url('/static/img/mxgraph/transparent.gif');
+ border-color: transparent;
+ border-style: solid;
+ display: inline-block;
+ position: absolute;
+ overflow: visible;
+ word-wrap: normal;
+ border-width: 0;
+ min-width: 1px;
+ resize: none;
+ padding: 0px;
+ margin: 0px;
+}
+.mxPlainTextEditor * {
+ padding: 0px;
+ margin: 0px;
+}
+div.mxWindow {
+ -webkit-box-shadow: 3px 3px 12px #C0C0C0;
+ -moz-box-shadow: 3px 3px 12px #C0C0C0;
+ box-shadow: 3px 3px 12px #C0C0C0;
+ background: url('../img/mxgraph/window.gif');
+ border:1px solid #c3c3c3;
+ position: absolute;
+ overflow: hidden;
+ z-index: 3;
+}
+table.mxWindow {
+ border-collapse: collapse;
+ table-layout: fixed;
+ font-family: Arial;
+ font-size: 8pt;
+}
+td.mxWindowTitle {
+ background: url('/static/img/mxgraph/window-title.gif') repeat-x;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ text-align: center;
+ font-weight: bold;
+ overflow: hidden;
+ height: 13px;
+ padding: 2px;
+ padding-top: 4px;
+ padding-bottom: 6px;
+ color: black;
+}
+td.mxWindowPane {
+ vertical-align: top;
+ padding: 0px;
+}
+div.mxWindowPane {
+ overflow: hidden;
+ position: relative;
+}
+td.mxWindowPane td {
+ font-family: Arial;
+ font-size: 8pt;
+}
+td.mxWindowPane input, td.mxWindowPane select, td.mxWindowPane textarea, td.mxWindowPane radio {
+ border-color: #8C8C8C;
+ border-style: solid;
+ border-width: 1px;
+ font-family: Arial;
+ font-size: 8pt;
+ padding: 1px;
+}
+td.mxWindowPane button {
+ background: url('/static/img/mxgraph/button.gif') repeat-x;
+ font-family: Arial;
+ font-size: 8pt;
+ padding: 2px;
+ float: left;
+}
+img.mxToolbarItem {
+ margin-right: 6px;
+ margin-bottom: 6px;
+ border-width: 1px;
+}
+select.mxToolbarCombo {
+ vertical-align: top;
+ border-style: inset;
+ border-width: 2px;
+}
+div.mxToolbarComboContainer {
+ padding: 2px;
+}
+img.mxToolbarMode {
+ margin: 2px;
+ margin-right: 4px;
+ margin-bottom: 4px;
+ border-width: 0px;
+}
+img.mxToolbarModeSelected {
+ margin: 0px;
+ margin-right: 2px;
+ margin-bottom: 2px;
+ border-width: 2px;
+ border-style: inset;
+}
+div.mxTooltip {
+ -webkit-box-shadow: 3px 3px 12px #C0C0C0;
+ -moz-box-shadow: 3px 3px 12px #C0C0C0;
+ box-shadow: 3px 3px 12px #C0C0C0;
+ background: #FFFFCC;
+ border-style: solid;
+ border-width: 1px;
+ border-color: black;
+ font-family: Arial;
+ font-size: 8pt;
+ position: absolute;
+ cursor: default;
+ padding: 4px;
+ color: black;
+}
+div.mxPopupMenu {
+ -webkit-box-shadow: 3px 3px 12px #C0C0C0;
+ -moz-box-shadow: 3px 3px 12px #C0C0C0;
+ box-shadow: 3px 3px 12px #C0C0C0;
+ background: url('/static/img/mxgraph/window.gif');
+ position: absolute;
+ border-style: solid;
+ border-width: 1px;
+ border-color: black;
+}
+table.mxPopupMenu {
+ border-collapse: collapse;
+ margin-top: 1px;
+ margin-bottom: 1px;
+}
+tr.mxPopupMenuItem {
+ color: black;
+ cursor: pointer;
+}
+tr.mxPopupMenuItemHover {
+ background-color: #000066;
+ color: #FFFFFF;
+ cursor: pointer;
+}
+td.mxPopupMenuItem {
+ padding: 2px 30px 2px 10px;
+ white-space: nowrap;
+ font-family: Arial;
+ font-size: 8pt;
+}
+td.mxPopupMenuIcon {
+ background-color: #D0D0D0;
+ padding: 2px 4px 2px 4px;
+}
+.mxDisabled {
+ opacity: 0.2 !important;
+ cursor:default !important;
+}
diff --git a/dashboard/src/static/css/theme.css b/dashboard/src/static/css/theme.css
index bd15637..fb350e2 100644
--- a/dashboard/src/static/css/theme.css
+++ b/dashboard/src/static/css/theme.css
@@ -10,4 +10,10 @@
.modal p {
word-wrap: break-word;
+}
+
+.create_drop {
+ display: none;
+ width: 100%;
+
} \ No newline at end of file
diff --git a/dashboard/src/static/img/mxgraph/add.png b/dashboard/src/static/img/mxgraph/add.png
new file mode 100644
index 0000000..bf5f8ed
--- /dev/null
+++ b/dashboard/src/static/img/mxgraph/add.png
Binary files differ
diff --git a/dashboard/src/static/img/mxgraph/button.gif b/dashboard/src/static/img/mxgraph/button.gif
new file mode 100644
index 0000000..ad55cab
--- /dev/null
+++ b/dashboard/src/static/img/mxgraph/button.gif
Binary files differ
diff --git a/dashboard/src/static/img/mxgraph/camera.png b/dashboard/src/static/img/mxgraph/camera.png
new file mode 100644
index 0000000..aecc94d
--- /dev/null
+++ b/dashboard/src/static/img/mxgraph/camera.png
Binary files differ
diff --git a/dashboard/src/static/img/mxgraph/check.png b/dashboard/src/static/img/mxgraph/check.png
new file mode 100644
index 0000000..ce81bce
--- /dev/null
+++ b/dashboard/src/static/img/mxgraph/check.png
Binary files differ
diff --git a/dashboard/src/static/img/mxgraph/close.gif b/dashboard/src/static/img/mxgraph/close.gif
new file mode 100644
index 0000000..1069e94
--- /dev/null
+++ b/dashboard/src/static/img/mxgraph/close.gif
Binary files differ
diff --git a/dashboard/src/static/img/mxgraph/close.png b/dashboard/src/static/img/mxgraph/close.png
new file mode 100644
index 0000000..4de4396
--- /dev/null
+++ b/dashboard/src/static/img/mxgraph/close.png
Binary files differ
diff --git a/dashboard/src/static/img/mxgraph/collapsed.gif b/dashboard/src/static/img/mxgraph/collapsed.gif
new file mode 100644
index 0000000..0276444
--- /dev/null
+++ b/dashboard/src/static/img/mxgraph/collapsed.gif
Binary files differ
diff --git a/dashboard/src/static/img/mxgraph/connector.gif b/dashboard/src/static/img/mxgraph/connector.gif
new file mode 100644
index 0000000..326e061
--- /dev/null
+++ b/dashboard/src/static/img/mxgraph/connector.gif
Binary files differ
diff --git a/dashboard/src/static/img/mxgraph/copy.png b/dashboard/src/static/img/mxgraph/copy.png
new file mode 100644
index 0000000..a987d43
--- /dev/null
+++ b/dashboard/src/static/img/mxgraph/copy.png
Binary files differ
diff --git a/dashboard/src/static/img/mxgraph/cut.png b/dashboard/src/static/img/mxgraph/cut.png
new file mode 100644
index 0000000..52bf944
--- /dev/null
+++ b/dashboard/src/static/img/mxgraph/cut.png
Binary files differ
diff --git a/dashboard/src/static/img/mxgraph/delete2.png b/dashboard/src/static/img/mxgraph/delete2.png
new file mode 100644
index 0000000..be78c61
--- /dev/null
+++ b/dashboard/src/static/img/mxgraph/delete2.png
Binary files differ
diff --git a/dashboard/src/static/img/mxgraph/dot.gif b/dashboard/src/static/img/mxgraph/dot.gif
new file mode 100644
index 0000000..08b9947
--- /dev/null
+++ b/dashboard/src/static/img/mxgraph/dot.gif
Binary files differ
diff --git a/dashboard/src/static/img/mxgraph/error.gif b/dashboard/src/static/img/mxgraph/error.gif
new file mode 100644
index 0000000..14e1aee
--- /dev/null
+++ b/dashboard/src/static/img/mxgraph/error.gif
Binary files differ
diff --git a/dashboard/src/static/img/mxgraph/expanded.gif b/dashboard/src/static/img/mxgraph/expanded.gif
new file mode 100644
index 0000000..3767b0b
--- /dev/null
+++ b/dashboard/src/static/img/mxgraph/expanded.gif
Binary files differ
diff --git a/dashboard/src/static/img/mxgraph/export1.png b/dashboard/src/static/img/mxgraph/export1.png
new file mode 100644
index 0000000..b8a01b8
--- /dev/null
+++ b/dashboard/src/static/img/mxgraph/export1.png
Binary files differ
diff --git a/dashboard/src/static/img/mxgraph/fit_to_size.png b/dashboard/src/static/img/mxgraph/fit_to_size.png
new file mode 100644
index 0000000..4de46b0
--- /dev/null
+++ b/dashboard/src/static/img/mxgraph/fit_to_size.png
Binary files differ
diff --git a/dashboard/src/static/img/mxgraph/gradient_background.jpg b/dashboard/src/static/img/mxgraph/gradient_background.jpg
new file mode 100644
index 0000000..7dbf35b
--- /dev/null
+++ b/dashboard/src/static/img/mxgraph/gradient_background.jpg
Binary files differ
diff --git a/dashboard/src/static/img/mxgraph/green-dot.gif b/dashboard/src/static/img/mxgraph/green-dot.gif
new file mode 100644
index 0000000..acaf7b2
--- /dev/null
+++ b/dashboard/src/static/img/mxgraph/green-dot.gif
Binary files differ
diff --git a/dashboard/src/static/img/mxgraph/group.png b/dashboard/src/static/img/mxgraph/group.png
new file mode 100644
index 0000000..585ad79
--- /dev/null
+++ b/dashboard/src/static/img/mxgraph/group.png
Binary files differ
diff --git a/dashboard/src/static/img/mxgraph/handle-connect.png b/dashboard/src/static/img/mxgraph/handle-connect.png
new file mode 100644
index 0000000..513ab2b
--- /dev/null
+++ b/dashboard/src/static/img/mxgraph/handle-connect.png
Binary files differ
diff --git a/dashboard/src/static/img/mxgraph/handle-main.png b/dashboard/src/static/img/mxgraph/handle-main.png
new file mode 100644
index 0000000..ee067ff
--- /dev/null
+++ b/dashboard/src/static/img/mxgraph/handle-main.png
Binary files differ
diff --git a/dashboard/src/static/img/mxgraph/icons48/column.png b/dashboard/src/static/img/mxgraph/icons48/column.png
new file mode 100644
index 0000000..5ae2c24
--- /dev/null
+++ b/dashboard/src/static/img/mxgraph/icons48/column.png
Binary files differ
diff --git a/dashboard/src/static/img/mxgraph/icons48/earth.png b/dashboard/src/static/img/mxgraph/icons48/earth.png
new file mode 100644
index 0000000..4493880
--- /dev/null
+++ b/dashboard/src/static/img/mxgraph/icons48/earth.png
Binary files differ
diff --git a/dashboard/src/static/img/mxgraph/icons48/gear.png b/dashboard/src/static/img/mxgraph/icons48/gear.png
new file mode 100644
index 0000000..647d897
--- /dev/null
+++ b/dashboard/src/static/img/mxgraph/icons48/gear.png
Binary files differ
diff --git a/dashboard/src/static/img/mxgraph/icons48/keys.png b/dashboard/src/static/img/mxgraph/icons48/keys.png
new file mode 100644
index 0000000..41828e4
--- /dev/null
+++ b/dashboard/src/static/img/mxgraph/icons48/keys.png
Binary files differ
diff --git a/dashboard/src/static/img/mxgraph/icons48/mail_new.png b/dashboard/src/static/img/mxgraph/icons48/mail_new.png
new file mode 100644
index 0000000..16c6662
--- /dev/null
+++ b/dashboard/src/static/img/mxgraph/icons48/mail_new.png
Binary files differ
diff --git a/dashboard/src/static/img/mxgraph/icons48/server.png b/dashboard/src/static/img/mxgraph/icons48/server.png
new file mode 100644
index 0000000..9621c6e
--- /dev/null
+++ b/dashboard/src/static/img/mxgraph/icons48/server.png
Binary files differ
diff --git a/dashboard/src/static/img/mxgraph/icons48/table.png b/dashboard/src/static/img/mxgraph/icons48/table.png
new file mode 100644
index 0000000..d4df646
--- /dev/null
+++ b/dashboard/src/static/img/mxgraph/icons48/table.png
Binary files differ
diff --git a/dashboard/src/static/img/mxgraph/key.png b/dashboard/src/static/img/mxgraph/key.png
new file mode 100644
index 0000000..e66758a
--- /dev/null
+++ b/dashboard/src/static/img/mxgraph/key.png
Binary files differ
diff --git a/dashboard/src/static/img/mxgraph/loading.gif b/dashboard/src/static/img/mxgraph/loading.gif
new file mode 100644
index 0000000..118f4b0
--- /dev/null
+++ b/dashboard/src/static/img/mxgraph/loading.gif
Binary files differ
diff --git a/dashboard/src/static/img/mxgraph/maximize.gif b/dashboard/src/static/img/mxgraph/maximize.gif
new file mode 100644
index 0000000..e27cf3e
--- /dev/null
+++ b/dashboard/src/static/img/mxgraph/maximize.gif
Binary files differ
diff --git a/dashboard/src/static/img/mxgraph/minimize.gif b/dashboard/src/static/img/mxgraph/minimize.gif
new file mode 100644
index 0000000..1e95e7c
--- /dev/null
+++ b/dashboard/src/static/img/mxgraph/minimize.gif
Binary files differ
diff --git a/dashboard/src/static/img/mxgraph/navigate_minus.png b/dashboard/src/static/img/mxgraph/navigate_minus.png
new file mode 100644
index 0000000..71edaf9
--- /dev/null
+++ b/dashboard/src/static/img/mxgraph/navigate_minus.png
Binary files differ
diff --git a/dashboard/src/static/img/mxgraph/navigate_plus.png b/dashboard/src/static/img/mxgraph/navigate_plus.png
new file mode 100644
index 0000000..b5b7e87
--- /dev/null
+++ b/dashboard/src/static/img/mxgraph/navigate_plus.png
Binary files differ
diff --git a/dashboard/src/static/img/mxgraph/normalize.gif b/dashboard/src/static/img/mxgraph/normalize.gif
new file mode 100644
index 0000000..34a8d30
--- /dev/null
+++ b/dashboard/src/static/img/mxgraph/normalize.gif
Binary files differ
diff --git a/dashboard/src/static/img/mxgraph/paste.png b/dashboard/src/static/img/mxgraph/paste.png
new file mode 100644
index 0000000..fd628d9
--- /dev/null
+++ b/dashboard/src/static/img/mxgraph/paste.png
Binary files differ
diff --git a/dashboard/src/static/img/mxgraph/plus.png b/dashboard/src/static/img/mxgraph/plus.png
new file mode 100644
index 0000000..24a84bb
--- /dev/null
+++ b/dashboard/src/static/img/mxgraph/plus.png
Binary files differ
diff --git a/dashboard/src/static/img/mxgraph/point.gif b/dashboard/src/static/img/mxgraph/point.gif
new file mode 100644
index 0000000..9074c39
--- /dev/null
+++ b/dashboard/src/static/img/mxgraph/point.gif
Binary files differ
diff --git a/dashboard/src/static/img/mxgraph/press32.png b/dashboard/src/static/img/mxgraph/press32.png
new file mode 100644
index 0000000..f00e3f7
--- /dev/null
+++ b/dashboard/src/static/img/mxgraph/press32.png
Binary files differ
diff --git a/dashboard/src/static/img/mxgraph/print32.png b/dashboard/src/static/img/mxgraph/print32.png
new file mode 100644
index 0000000..0cca86c
--- /dev/null
+++ b/dashboard/src/static/img/mxgraph/print32.png
Binary files differ
diff --git a/dashboard/src/static/img/mxgraph/printer.png b/dashboard/src/static/img/mxgraph/printer.png
new file mode 100644
index 0000000..6004816
--- /dev/null
+++ b/dashboard/src/static/img/mxgraph/printer.png
Binary files differ
diff --git a/dashboard/src/static/img/mxgraph/redo.png b/dashboard/src/static/img/mxgraph/redo.png
new file mode 100644
index 0000000..3eae59c
--- /dev/null
+++ b/dashboard/src/static/img/mxgraph/redo.png
Binary files differ
diff --git a/dashboard/src/static/img/mxgraph/resize.gif b/dashboard/src/static/img/mxgraph/resize.gif
new file mode 100644
index 0000000..ff558db
--- /dev/null
+++ b/dashboard/src/static/img/mxgraph/resize.gif
Binary files differ
diff --git a/dashboard/src/static/img/mxgraph/separator.gif b/dashboard/src/static/img/mxgraph/separator.gif
new file mode 100644
index 0000000..5c1b895
--- /dev/null
+++ b/dashboard/src/static/img/mxgraph/separator.gif
Binary files differ
diff --git a/dashboard/src/static/img/mxgraph/sidebar_bg.gif b/dashboard/src/static/img/mxgraph/sidebar_bg.gif
new file mode 100644
index 0000000..67e8244
--- /dev/null
+++ b/dashboard/src/static/img/mxgraph/sidebar_bg.gif
Binary files differ
diff --git a/dashboard/src/static/img/mxgraph/spacer.gif b/dashboard/src/static/img/mxgraph/spacer.gif
new file mode 100644
index 0000000..35d42e8
--- /dev/null
+++ b/dashboard/src/static/img/mxgraph/spacer.gif
Binary files differ
diff --git a/dashboard/src/static/img/mxgraph/submenu.gif b/dashboard/src/static/img/mxgraph/submenu.gif
new file mode 100644
index 0000000..ffe7617
--- /dev/null
+++ b/dashboard/src/static/img/mxgraph/submenu.gif
Binary files differ
diff --git a/dashboard/src/static/img/mxgraph/toolbar_bg.gif b/dashboard/src/static/img/mxgraph/toolbar_bg.gif
new file mode 100644
index 0000000..87b9374
--- /dev/null
+++ b/dashboard/src/static/img/mxgraph/toolbar_bg.gif
Binary files differ
diff --git a/dashboard/src/static/img/mxgraph/transparent.gif b/dashboard/src/static/img/mxgraph/transparent.gif
new file mode 100644
index 0000000..76040f2
--- /dev/null
+++ b/dashboard/src/static/img/mxgraph/transparent.gif
Binary files differ
diff --git a/dashboard/src/static/img/mxgraph/undo.png b/dashboard/src/static/img/mxgraph/undo.png
new file mode 100644
index 0000000..4ba0ffb
--- /dev/null
+++ b/dashboard/src/static/img/mxgraph/undo.png
Binary files differ
diff --git a/dashboard/src/static/img/mxgraph/view_1_1.png b/dashboard/src/static/img/mxgraph/view_1_1.png
new file mode 100644
index 0000000..88657a1
--- /dev/null
+++ b/dashboard/src/static/img/mxgraph/view_1_1.png
Binary files differ
diff --git a/dashboard/src/static/img/mxgraph/view_1_132.png b/dashboard/src/static/img/mxgraph/view_1_132.png
new file mode 100644
index 0000000..e9a1b72
--- /dev/null
+++ b/dashboard/src/static/img/mxgraph/view_1_132.png
Binary files differ
diff --git a/dashboard/src/static/img/mxgraph/view_next.png b/dashboard/src/static/img/mxgraph/view_next.png
new file mode 100644
index 0000000..b4094f0
--- /dev/null
+++ b/dashboard/src/static/img/mxgraph/view_next.png
Binary files differ
diff --git a/dashboard/src/static/img/mxgraph/view_previous.png b/dashboard/src/static/img/mxgraph/view_previous.png
new file mode 100644
index 0000000..b385b44
--- /dev/null
+++ b/dashboard/src/static/img/mxgraph/view_previous.png
Binary files differ
diff --git a/dashboard/src/static/img/mxgraph/warning.gif b/dashboard/src/static/img/mxgraph/warning.gif
new file mode 100644
index 0000000..705235f
--- /dev/null
+++ b/dashboard/src/static/img/mxgraph/warning.gif
Binary files differ
diff --git a/dashboard/src/static/img/mxgraph/warning.png b/dashboard/src/static/img/mxgraph/warning.png
new file mode 100644
index 0000000..2f78789
--- /dev/null
+++ b/dashboard/src/static/img/mxgraph/warning.png
Binary files differ
diff --git a/dashboard/src/static/img/mxgraph/window-title.gif b/dashboard/src/static/img/mxgraph/window-title.gif
new file mode 100644
index 0000000..231def8
--- /dev/null
+++ b/dashboard/src/static/img/mxgraph/window-title.gif
Binary files differ
diff --git a/dashboard/src/static/img/mxgraph/window.gif b/dashboard/src/static/img/mxgraph/window.gif
new file mode 100644
index 0000000..6631c4f
--- /dev/null
+++ b/dashboard/src/static/img/mxgraph/window.gif
Binary files differ
diff --git a/dashboard/src/static/img/mxgraph/wires-grid.gif b/dashboard/src/static/img/mxgraph/wires-grid.gif
new file mode 100644
index 0000000..ad888a2
--- /dev/null
+++ b/dashboard/src/static/img/mxgraph/wires-grid.gif
Binary files differ
diff --git a/dashboard/src/static/img/mxgraph/zoom_in.png b/dashboard/src/static/img/mxgraph/zoom_in.png
new file mode 100644
index 0000000..ad6abb9
--- /dev/null
+++ b/dashboard/src/static/img/mxgraph/zoom_in.png
Binary files differ
diff --git a/dashboard/src/static/img/mxgraph/zoom_in32.png b/dashboard/src/static/img/mxgraph/zoom_in32.png
new file mode 100644
index 0000000..438ff0f
--- /dev/null
+++ b/dashboard/src/static/img/mxgraph/zoom_in32.png
Binary files differ
diff --git a/dashboard/src/static/img/mxgraph/zoom_out.png b/dashboard/src/static/img/mxgraph/zoom_out.png
new file mode 100644
index 0000000..0566f26
--- /dev/null
+++ b/dashboard/src/static/img/mxgraph/zoom_out.png
Binary files differ
diff --git a/dashboard/src/static/img/mxgraph/zoom_out32.png b/dashboard/src/static/img/mxgraph/zoom_out32.png
new file mode 100644
index 0000000..8edb765
--- /dev/null
+++ b/dashboard/src/static/img/mxgraph/zoom_out32.png
Binary files differ
diff --git a/dashboard/src/static/js/mxClient.min.js b/dashboard/src/static/js/mxClient.min.js
new file mode 100644
index 0000000..150a0ac
--- /dev/null
+++ b/dashboard/src/static/js/mxClient.min.js
@@ -0,0 +1,1808 @@
+var mxClient={VERSION:"3.9.6",IS_IE:0<=navigator.userAgent.indexOf("MSIE"),IS_IE6:0<=navigator.userAgent.indexOf("MSIE 6"),IS_IE11:!!navigator.userAgent.match(/Trident\/7\./),IS_EDGE:!!navigator.userAgent.match(/Edge\//),IS_QUIRKS:0<=navigator.userAgent.indexOf("MSIE")&&(null==document.documentMode||5==document.documentMode),IS_EM:"spellcheck"in document.createElement("textarea")&&8==document.documentMode,VML_PREFIX:"v",OFFICE_PREFIX:"o",IS_NS:0<=navigator.userAgent.indexOf("Mozilla/")&&0>navigator.userAgent.indexOf("MSIE")&&
+0>navigator.userAgent.indexOf("Edge/"),IS_OP:0<=navigator.userAgent.indexOf("Opera/")||0<=navigator.userAgent.indexOf("OPR/"),IS_OT:0<=navigator.userAgent.indexOf("Presto/")&&0>navigator.userAgent.indexOf("Presto/2.4.")&&0>navigator.userAgent.indexOf("Presto/2.3.")&&0>navigator.userAgent.indexOf("Presto/2.2.")&&0>navigator.userAgent.indexOf("Presto/2.1.")&&0>navigator.userAgent.indexOf("Presto/2.0.")&&0>navigator.userAgent.indexOf("Presto/1."),IS_SF:0<=navigator.userAgent.indexOf("AppleWebKit/")&&
+0>navigator.userAgent.indexOf("Chrome/")&&0>navigator.userAgent.indexOf("Edge/"),IS_IOS:navigator.userAgent.match(/(iPad|iPhone|iPod)/g)?!0:!1,IS_GC:0<=navigator.userAgent.indexOf("Chrome/")&&0>navigator.userAgent.indexOf("Edge/"),IS_CHROMEAPP:null!=window.chrome&&null!=chrome.app&&null!=chrome.app.runtime,IS_FF:0<=navigator.userAgent.indexOf("Firefox/"),IS_MT:0<=navigator.userAgent.indexOf("Firefox/")&&0>navigator.userAgent.indexOf("Firefox/1.")&&0>navigator.userAgent.indexOf("Firefox/2.")||0<=navigator.userAgent.indexOf("Iceweasel/")&&
+0>navigator.userAgent.indexOf("Iceweasel/1.")&&0>navigator.userAgent.indexOf("Iceweasel/2.")||0<=navigator.userAgent.indexOf("SeaMonkey/")&&0>navigator.userAgent.indexOf("SeaMonkey/1.")||0<=navigator.userAgent.indexOf("Iceape/")&&0>navigator.userAgent.indexOf("Iceape/1."),IS_SVG:0<=navigator.userAgent.indexOf("Firefox/")||0<=navigator.userAgent.indexOf("Iceweasel/")||0<=navigator.userAgent.indexOf("Seamonkey/")||0<=navigator.userAgent.indexOf("Iceape/")||0<=navigator.userAgent.indexOf("Galeon/")||
+0<=navigator.userAgent.indexOf("Epiphany/")||0<=navigator.userAgent.indexOf("AppleWebKit/")||0<=navigator.userAgent.indexOf("Gecko/")||0<=navigator.userAgent.indexOf("Opera/")||null!=document.documentMode&&9<=document.documentMode,NO_FO:!document.createElementNS||"[object SVGForeignObjectElement]"!=document.createElementNS("http://www.w3.org/2000/svg","foreignObject")||0<=navigator.userAgent.indexOf("Opera/"),IS_VML:"MICROSOFT INTERNET EXPLORER"==navigator.appName.toUpperCase(),IS_WIN:0<navigator.appVersion.indexOf("Win"),
+IS_MAC:0<navigator.appVersion.indexOf("Mac"),IS_TOUCH:"ontouchstart"in document.documentElement,IS_POINTER:null!=window.PointerEvent&&!(0<navigator.appVersion.indexOf("Mac")),IS_LOCAL:0>document.location.href.indexOf("http://")&&0>document.location.href.indexOf("https://"),defaultBundles:[],isBrowserSupported:function(){return mxClient.IS_VML||mxClient.IS_SVG},link:function(a,b,c){c=c||document;if(mxClient.IS_IE6)c.write('<link rel="'+a+'" href="'+b+'" charset="UTF-8" type="text/css"/>');else{var d=
+c.createElement("link");d.setAttribute("rel",a);d.setAttribute("href",b);d.setAttribute("charset","UTF-8");d.setAttribute("type","text/css");c.getElementsByTagName("head")[0].appendChild(d)}},loadResources:function(a,b){function c(){0==--d&&a()}for(var d=mxClient.defaultBundles.length,e=0;e<mxClient.defaultBundles.length;e++)mxResources.add(mxClient.defaultBundles[e],b,c)},include:function(a){document.write('<script src="'+a+'">\x3c/script>')},dispose:function(){for(var a=0;a<mxEvent.objects.length;a++)null!=
+mxEvent.objects[a].mxListenerList&&mxEvent.removeAllListeners(mxEvent.objects[a])}};"undefined"==typeof mxLoadResources&&(mxLoadResources=!0);"undefined"==typeof mxForceIncludes&&(mxForceIncludes=!1);"undefined"==typeof mxResourceExtension&&(mxResourceExtension=".txt");"undefined"==typeof mxLoadStylesheets&&(mxLoadStylesheets=!0);
+"undefined"!=typeof mxBasePath&&0<mxBasePath.length?("/"==mxBasePath.substring(mxBasePath.length-1)&&(mxBasePath=mxBasePath.substring(0,mxBasePath.length-1)),mxClient.basePath=mxBasePath):mxClient.basePath=".";"undefined"!=typeof mxImageBasePath&&0<mxImageBasePath.length?("/"==mxImageBasePath.substring(mxImageBasePath.length-1)&&(mxImageBasePath=mxImageBasePath.substring(0,mxImageBasePath.length-1)),mxClient.imageBasePath=mxImageBasePath):mxClient.imageBasePath=mxClient.basePath+"/images";
+mxClient.language="undefined"!=typeof mxLanguage&&null!=mxLanguage?mxLanguage:mxClient.IS_IE?navigator.userLanguage:navigator.language;mxClient.defaultLanguage="undefined"!=typeof mxDefaultLanguage&&null!=mxDefaultLanguage?mxDefaultLanguage:"en";mxLoadStylesheets&&mxClient.link("stylesheet",mxClient.basePath+"/css/common.css");"undefined"!=typeof mxLanguages&&null!=mxLanguages&&(mxClient.languages=mxLanguages);
+mxClient.IS_VML&&(mxClient.IS_SVG?mxClient.IS_VML=!1:(8==document.documentMode?(document.namespaces.add(mxClient.VML_PREFIX,"urn:schemas-microsoft-com:vml","#default#VML"),document.namespaces.add(mxClient.OFFICE_PREFIX,"urn:schemas-microsoft-com:office:office","#default#VML")):(document.namespaces.add(mxClient.VML_PREFIX,"urn:schemas-microsoft-com:vml"),document.namespaces.add(mxClient.OFFICE_PREFIX,"urn:schemas-microsoft-com:office:office")),mxClient.IS_QUIRKS&&30<=document.styleSheets.length?function(){var a=
+document.createElement("style");a.type="text/css";a.styleSheet.cssText=mxClient.VML_PREFIX+"\\:*{behavior:url(#default#VML)}"+mxClient.OFFICE_PREFIX+"\\:*{behavior:url(#default#VML)}";document.getElementsByTagName("head")[0].appendChild(a)}():document.createStyleSheet().cssText=mxClient.VML_PREFIX+"\\:*{behavior:url(#default#VML)}"+mxClient.OFFICE_PREFIX+"\\:*{behavior:url(#default#VML)}",mxLoadStylesheets&&mxClient.link("stylesheet",mxClient.basePath+"/css/explorer.css"),window.attachEvent("onunload",
+mxClient.dispose)));
+var mxLog={consoleName:"Console",TRACE:!1,DEBUG:!0,WARN:!0,buffer:"",init:function(){if(null==mxLog.window&&null!=document.body){var a=mxLog.consoleName+" - mxGraph "+mxClient.VERSION,b=document.createElement("table");b.setAttribute("width","100%");b.setAttribute("height","100%");var c=document.createElement("tbody"),d=document.createElement("tr"),e=document.createElement("td");e.style.verticalAlign="top";mxLog.textarea=document.createElement("textarea");mxLog.textarea.setAttribute("wrap","off");
+mxLog.textarea.setAttribute("readOnly","true");mxLog.textarea.style.height="100%";mxLog.textarea.style.resize="none";mxLog.textarea.value=mxLog.buffer;mxLog.textarea.style.width=mxClient.IS_NS&&"BackCompat"!=document.compatMode?"99%":"100%";e.appendChild(mxLog.textarea);d.appendChild(e);c.appendChild(d);d=document.createElement("tr");mxLog.td=document.createElement("td");mxLog.td.style.verticalAlign="top";mxLog.td.setAttribute("height","30px");d.appendChild(mxLog.td);c.appendChild(d);b.appendChild(c);
+mxLog.addButton("Info",function(a){mxLog.info()});mxLog.addButton("DOM",function(a){a=mxUtils.getInnerHtml(document.body);mxLog.debug(a)});mxLog.addButton("Trace",function(a){mxLog.TRACE=!mxLog.TRACE;mxLog.TRACE?mxLog.debug("Tracing enabled"):mxLog.debug("Tracing disabled")});mxLog.addButton("Copy",function(a){try{mxUtils.copy(mxLog.textarea.value)}catch(k){mxUtils.alert(k)}});mxLog.addButton("Show",function(a){try{mxUtils.popup(mxLog.textarea.value)}catch(k){mxUtils.alert(k)}});mxLog.addButton("Clear",
+function(a){mxLog.textarea.value=""});d=c=0;"number"===typeof window.innerWidth?(c=window.innerHeight,d=window.innerWidth):(c=document.documentElement.clientHeight||document.body.clientHeight,d=document.body.clientWidth);mxLog.window=new mxWindow(a,b,Math.max(0,d-320),Math.max(0,c-210),300,160);mxLog.window.setMaximizable(!0);mxLog.window.setScrollable(!1);mxLog.window.setResizable(!0);mxLog.window.setClosable(!0);mxLog.window.destroyOnClose=!1;if((mxClient.IS_NS||mxClient.IS_IE)&&!mxClient.IS_GC&&
+!mxClient.IS_SF&&"BackCompat"!=document.compatMode||11==document.documentMode){var f=mxLog.window.getElement(),a=function(a,b){mxLog.textarea.style.height=Math.max(0,f.offsetHeight-70)+"px"};mxLog.window.addListener(mxEvent.RESIZE_END,a);mxLog.window.addListener(mxEvent.MAXIMIZE,a);mxLog.window.addListener(mxEvent.NORMALIZE,a);mxLog.textarea.style.height="92px"}}},info:function(){mxLog.writeln(mxUtils.toString(navigator))},addButton:function(a,b){var c=document.createElement("button");mxUtils.write(c,
+a);mxEvent.addListener(c,"click",b);mxLog.td.appendChild(c)},isVisible:function(){return null!=mxLog.window?mxLog.window.isVisible():!1},show:function(){mxLog.setVisible(!0)},setVisible:function(a){null==mxLog.window&&mxLog.init();null!=mxLog.window&&mxLog.window.setVisible(a)},enter:function(a){if(mxLog.TRACE)return mxLog.writeln("Entering "+a),(new Date).getTime()},leave:function(a,b){if(mxLog.TRACE){var c=0!=b?" ("+((new Date).getTime()-b)+" ms)":"";mxLog.writeln("Leaving "+a+c)}},debug:function(){mxLog.DEBUG&&
+mxLog.writeln.apply(this,arguments)},warn:function(){mxLog.WARN&&mxLog.writeln.apply(this,arguments)},write:function(){for(var a="",b=0;b<arguments.length;b++)a+=arguments[b],b<arguments.length-1&&(a+=" ");null!=mxLog.textarea?(mxLog.textarea.value+=a,0<=navigator.userAgent.indexOf("Presto/2.5")&&(mxLog.textarea.style.visibility="hidden",mxLog.textarea.style.visibility="visible"),mxLog.textarea.scrollTop=mxLog.textarea.scrollHeight):mxLog.buffer+=a},writeln:function(){for(var a="",b=0;b<arguments.length;b++)a+=
+arguments[b],b<arguments.length-1&&(a+=" ");mxLog.write(a+"\n")}},mxObjectIdentity={FIELD_NAME:"mxObjectId",counter:0,get:function(a){if(null!=a){if(null==a[mxObjectIdentity.FIELD_NAME])if("object"===typeof a){var b=mxUtils.getFunctionName(a.constructor);a[mxObjectIdentity.FIELD_NAME]=b+"#"+mxObjectIdentity.counter++}else"function"===typeof a&&(a[mxObjectIdentity.FIELD_NAME]="Function#"+mxObjectIdentity.counter++);return a[mxObjectIdentity.FIELD_NAME]}return null},clear:function(a){"object"!==typeof a&&
+"function"!==typeof a||delete a[mxObjectIdentity.FIELD_NAME]}};function mxDictionary(){this.clear()}mxDictionary.prototype.map=null;mxDictionary.prototype.clear=function(){this.map={}};mxDictionary.prototype.get=function(a){a=mxObjectIdentity.get(a);return this.map[a]};mxDictionary.prototype.put=function(a,b){var c=mxObjectIdentity.get(a),d=this.map[c];this.map[c]=b;return d};mxDictionary.prototype.remove=function(a){a=mxObjectIdentity.get(a);var b=this.map[a];delete this.map[a];return b};
+mxDictionary.prototype.getKeys=function(){var a=[],b;for(b in this.map)a.push(b);return a};mxDictionary.prototype.getValues=function(){var a=[],b;for(b in this.map)a.push(this.map[b]);return a};mxDictionary.prototype.visit=function(a){for(var b in this.map)a(b,this.map[b])};
+var mxResources={resources:{},extension:mxResourceExtension,resourcesEncoded:!1,loadDefaultBundle:!0,loadSpecialBundle:!0,isLanguageSupported:function(a){return null!=mxClient.languages?0<=mxUtils.indexOf(mxClient.languages,a):!0},getDefaultBundle:function(a,b){return mxResources.loadDefaultBundle||!mxResources.isLanguageSupported(b)?a+mxResources.extension:null},getSpecialBundle:function(a,b){if(null==mxClient.languages||!this.isLanguageSupported(b)){var c=b.indexOf("-");0<c&&(b=b.substring(0,c))}return mxResources.loadSpecialBundle&&
+mxResources.isLanguageSupported(b)&&b!=mxClient.defaultLanguage?a+"_"+b+mxResources.extension:null},add:function(a,b,c){b=null!=b?b:null!=mxClient.language?mxClient.language.toLowerCase():mxConstants.NONE;if(b!=mxConstants.NONE){var d=mxResources.getDefaultBundle(a,b),e=mxResources.getSpecialBundle(a,b),f=function(){if(null!=e)if(c)mxUtils.get(e,function(a){mxResources.parse(a.getText());c()},function(){c()});else try{var a=mxUtils.load(e);a.isReady()&&mxResources.parse(a.getText())}catch(l){}else null!=
+c&&c()};if(null!=d)if(c)mxUtils.get(d,function(a){mxResources.parse(a.getText());f()},function(){f()});else try{var g=mxUtils.load(d);g.isReady()&&mxResources.parse(g.getText());f()}catch(k){}else f()}},parse:function(a){if(null!=a){a=a.split("\n");for(var b=0;b<a.length;b++)if("#"!=a[b].charAt(0)){var c=a[b].indexOf("=");if(0<c){var d=a[b].substring(0,c),e=a[b].length;13==a[b].charCodeAt(e-1)&&e--;c=a[b].substring(c+1,e);this.resourcesEncoded?(c=c.replace(/\\(?=u[a-fA-F\d]{4})/g,"%"),mxResources.resources[d]=
+unescape(c)):mxResources.resources[d]=c}}}},get:function(a,b,c){a=mxResources.resources[a];null==a&&(a=c);null!=a&&null!=b&&(a=mxResources.replacePlaceholders(a,b));return a},replacePlaceholders:function(a,b){for(var c=[],d=null,e=0;e<a.length;e++){var f=a.charAt(e);"{"==f?d="":null!=d&&"}"==f?(d=parseInt(d)-1,0<=d&&d<b.length&&c.push(b[d]),d=null):null!=d?d+=f:c.push(f)}return c.join("")},loadResources:function(a){mxResources.add(mxClient.basePath+"/resources/editor",null,function(){mxResources.add(mxClient.basePath+
+"/resources/graph",null,a)})}};function mxPoint(a,b){this.x=null!=a?a:0;this.y=null!=b?b:0}mxPoint.prototype.x=null;mxPoint.prototype.y=null;mxPoint.prototype.equals=function(a){return null!=a&&a.x==this.x&&a.y==this.y};mxPoint.prototype.clone=function(){return mxUtils.clone(this)};function mxRectangle(a,b,c,d){mxPoint.call(this,a,b);this.width=null!=c?c:0;this.height=null!=d?d:0}mxRectangle.prototype=new mxPoint;mxRectangle.prototype.constructor=mxRectangle;mxRectangle.prototype.width=null;
+mxRectangle.prototype.height=null;mxRectangle.prototype.setRect=function(a,b,c,d){this.x=a;this.y=b;this.width=c;this.height=d};mxRectangle.prototype.getCenterX=function(){return this.x+this.width/2};mxRectangle.prototype.getCenterY=function(){return this.y+this.height/2};
+mxRectangle.prototype.add=function(a){if(null!=a){var b=Math.min(this.x,a.x),c=Math.min(this.y,a.y),d=Math.max(this.x+this.width,a.x+a.width);a=Math.max(this.y+this.height,a.y+a.height);this.x=b;this.y=c;this.width=d-b;this.height=a-c}};mxRectangle.prototype.intersect=function(a){if(null!=a){var b=this.x+this.width,c=a.x+a.width,d=this.y+this.height,e=a.y+a.height;this.x=Math.max(this.x,a.x);this.y=Math.max(this.y,a.y);this.width=Math.min(b,c)-this.x;this.height=Math.min(d,e)-this.y}};
+mxRectangle.prototype.grow=function(a){this.x-=a;this.y-=a;this.width+=2*a;this.height+=2*a};mxRectangle.prototype.getPoint=function(){return new mxPoint(this.x,this.y)};mxRectangle.prototype.rotate90=function(){var a=(this.width-this.height)/2;this.x+=a;this.y-=a;a=this.width;this.width=this.height;this.height=a};mxRectangle.prototype.equals=function(a){return null!=a&&a.x==this.x&&a.y==this.y&&a.width==this.width&&a.height==this.height};
+mxRectangle.fromRectangle=function(a){return new mxRectangle(a.x,a.y,a.width,a.height)};
+var mxEffects={animateChanges:function(a,b,c){var d=0,e=function(){for(var g=!1,k=0;k<b.length;k++){var l=b[k];if(l instanceof mxGeometryChange||l instanceof mxTerminalChange||l instanceof mxValueChange||l instanceof mxChildChange||l instanceof mxStyleChange){var m=a.getView().getState(l.cell||l.child,!1);if(null!=m)if(g=!0,l.constructor!=mxGeometryChange||a.model.isEdge(l.cell))mxUtils.setOpacity(m.shape.node,100*d/10);else{var n=a.getView().scale,p=(l.geometry.x-l.previous.x)*n,q=(l.geometry.y-
+l.previous.y)*n,r=(l.geometry.width-l.previous.width)*n,n=(l.geometry.height-l.previous.height)*n;0==d?(m.x-=p,m.y-=q,m.width-=r,m.height-=n):(m.x+=p/10,m.y+=q/10,m.width+=r/10,m.height+=n/10);a.cellRenderer.redraw(m);mxEffects.cascadeOpacity(a,l.cell,100*d/10)}}}10>d&&g?(d++,window.setTimeout(e,f)):null!=c&&c()},f=30;e()},cascadeOpacity:function(a,b,c){for(var d=a.model.getChildCount(b),e=0;e<d;e++){var f=a.model.getChildAt(b,e),g=a.getView().getState(f);null!=g&&(mxUtils.setOpacity(g.shape.node,
+c),mxEffects.cascadeOpacity(a,f,c))}b=a.model.getEdges(b);if(null!=b)for(e=0;e<b.length;e++)d=a.getView().getState(b[e]),null!=d&&mxUtils.setOpacity(d.shape.node,c)},fadeOut:function(a,b,c,d,e,f){d=d||40;e=e||30;var g=b||100;mxUtils.setOpacity(a,g);if(f||null==f){var k=function(){g=Math.max(g-d,0);mxUtils.setOpacity(a,g);0<g?window.setTimeout(k,e):(a.style.visibility="hidden",c&&a.parentNode&&a.parentNode.removeChild(a))};window.setTimeout(k,e)}else a.style.visibility="hidden",c&&a.parentNode&&a.parentNode.removeChild(a)}},
+mxUtils={errorResource:"none"!=mxClient.language?"error":"",closeResource:"none"!=mxClient.language?"close":"",errorImage:mxClient.imageBasePath+"/error.gif",removeCursors:function(a){null!=a.style&&(a.style.cursor="");a=a.childNodes;if(null!=a)for(var b=a.length,c=0;c<b;c+=1)mxUtils.removeCursors(a[c])},getCurrentStyle:function(){return mxClient.IS_IE&&(null==document.documentMode||9>document.documentMode)?function(a){return null!=a?a.currentStyle:null}:function(a){return null!=a?window.getComputedStyle(a,
+""):null}}(),parseCssNumber:function(a){"thin"==a?a="2":"medium"==a?a="4":"thick"==a&&(a="6");a=parseFloat(a);isNaN(a)&&(a=0);return a},setPrefixedStyle:function(){var a=null;mxClient.IS_OT?a="O":mxClient.IS_SF||mxClient.IS_GC?a="Webkit":mxClient.IS_MT?a="Moz":mxClient.IS_IE&&9<=document.documentMode&&10>document.documentMode&&(a="ms");return function(b,c,d){b[c]=d;null!=a&&0<c.length&&(c=a+c.substring(0,1).toUpperCase()+c.substring(1),b[c]=d)}}(),hasScrollbars:function(a){a=mxUtils.getCurrentStyle(a);
+return null!=a&&("scroll"==a.overflow||"auto"==a.overflow)},bind:function(a,b){return function(){return b.apply(a,arguments)}},eval:function(a){var b=null;if(0<=a.indexOf("function"))try{eval("var _mxJavaScriptExpression="+a),b=_mxJavaScriptExpression,_mxJavaScriptExpression=null}catch(c){mxLog.warn(c.message+" while evaluating "+a)}else try{b=eval(a)}catch(c){mxLog.warn(c.message+" while evaluating "+a)}return b},findNode:function(a,b,c){if(a.nodeType==mxConstants.NODETYPE_ELEMENT){var d=a.getAttribute(b);
+if(null!=d&&d==c)return a}for(a=a.firstChild;null!=a;){d=mxUtils.findNode(a,b,c);if(null!=d)return d;a=a.nextSibling}return null},getFunctionName:function(a){var b=null;null!=a&&(null!=a.name?b=a.name:(b=mxUtils.trim(a.toString()),/^function\s/.test(b)&&(b=mxUtils.ltrim(b.substring(9)),a=b.indexOf("("),0<a&&(b=b.substring(0,a)))));return b},indexOf:function(a,b){if(null!=a&&null!=b)for(var c=0;c<a.length;c++)if(a[c]==b)return c;return-1},forEach:function(a,b){if(null!=a&&null!=b)for(var c=0;c<a.length;c++)b(a[c]);
+return a},remove:function(a,b){var c=null;if("object"==typeof b)for(var d=mxUtils.indexOf(b,a);0<=d;)b.splice(d,1),c=a,d=mxUtils.indexOf(b,a);for(var e in b)b[e]==a&&(delete b[e],c=a);return c},isNode:function(a,b,c,d){return null==a||isNaN(a.nodeType)||null!=b&&a.nodeName.toLowerCase()!=b.toLowerCase()?!1:null==c||a.getAttribute(c)==d},isAncestorNode:function(a,b){for(var c=b;null!=c;){if(c==a)return!0;c=c.parentNode}return!1},getChildNodes:function(a,b){b=b||mxConstants.NODETYPE_ELEMENT;for(var c=
+[],d=a.firstChild;null!=d;)d.nodeType==b&&c.push(d),d=d.nextSibling;return c},importNode:function(a,b,c){if(mxClient.IS_IE&&(null==document.documentMode||10>document.documentMode))switch(b.nodeType){case 1:var d=a.createElement(b.nodeName);if(b.attributes&&0<b.attributes.length){for(var e=0;e<b.attributes.length;e++)d.setAttribute(b.attributes[e].nodeName,b.getAttribute(b.attributes[e].nodeName));if(c&&b.childNodes&&0<b.childNodes.length)for(e=0;e<b.childNodes.length;e++)d.appendChild(mxUtils.importNode(a,
+b.childNodes[e],c))}return d;case 3:case 4:case 8:return a.createTextNode(b.value)}else return a.importNode(b,c)},createXmlDocument:function(){var a=null;document.implementation&&document.implementation.createDocument?a=document.implementation.createDocument("","",null):window.ActiveXObject&&(a=new ActiveXObject("Microsoft.XMLDOM"));return a},parseXml:function(){return window.DOMParser?function(a){return(new DOMParser).parseFromString(a,"text/xml")}:function(a){var b=mxUtils.createXmlDocument();b.async=
+!1;b.validateOnParse=!1;b.resolveExternals=!1;b.loadXML(a);return b}}(),clearSelection:function(){return document.selection?function(){document.selection.empty()}:window.getSelection?function(){window.getSelection().empty?window.getSelection().empty():window.getSelection().removeAllRanges&&window.getSelection().removeAllRanges()}:function(){}}(),getPrettyXml:function(a,b,c){var d=[];if(null!=a)if(b=b||" ",c=c||"",a.nodeType==mxConstants.NODETYPE_TEXT)a=mxUtils.trim(mxUtils.getTextContent(a)),0<a.length&&
+d.push(c+mxUtils.htmlEntities(a)+"\n");else{d.push(c+"<"+a.nodeName);var e=a.attributes;if(null!=e)for(var f=0;f<e.length;f++){var g=mxUtils.htmlEntities(e[f].value);d.push(" "+e[f].nodeName+'="'+g+'"')}e=a.firstChild;if(null!=e){for(d.push(">\n");null!=e;)d.push(mxUtils.getPrettyXml(e,b,c+b)),e=e.nextSibling;d.push(c+"</"+a.nodeName+">\n")}else d.push("/>\n")}return d.join("")},removeWhitespace:function(a,b){for(var c=b?a.previousSibling:a.nextSibling;null!=c&&c.nodeType==mxConstants.NODETYPE_TEXT;){var d=
+b?c.previousSibling:c.nextSibling,e=mxUtils.getTextContent(c);0==mxUtils.trim(e).length&&c.parentNode.removeChild(c);c=d}},htmlEntities:function(a,b){a=String(a||"");a=a.replace(/&/g,"&amp;");a=a.replace(/"/g,"&quot;");a=a.replace(/\'/g,"&#39;");a=a.replace(/</g,"&lt;");a=a.replace(/>/g,"&gt;");if(null==b||b)a=a.replace(/\n/g,"&#xa;");return a},isVml:function(a){return null!=a&&"urn:schemas-microsoft-com:vml"==a.tagUrn},getXml:function(a,b){var c="";null!=window.XMLSerializer?c=(new XMLSerializer).serializeToString(a):
+null!=a.xml&&(c=a.xml.replace(/\r\n\t[\t]*/g,"").replace(/>\r\n/g,">").replace(/\r\n/g,"\n"));return c=c.replace(/\n/g,b||"&#xa;")},extractTextWithWhitespace:function(a){function b(a){if(1!=a.length||"BR"!=a[0].nodeName&&"\n"!=a[0].innerHTML)for(var e=0;e<a.length;e++){var g=a[e];"BR"==g.nodeName||"\n"==g.innerHTML||(1==a.length||0==e)&&"DIV"==g.nodeName&&"<br>"==g.innerHTML.toLowerCase()?d.push("\n"):(3===g.nodeType||4===g.nodeType?0<g.nodeValue.length&&d.push(g.nodeValue):8!==g.nodeType&&0<g.childNodes.length&&
+b(g.childNodes),e<a.length-1&&0<=mxUtils.indexOf(c,a[e+1].nodeName)&&d.push("\n"))}}var c="BLOCKQUOTE DIV H1 H2 H3 H4 H5 H6 OL P PRE TABLE UL".split(" "),d=[];b(a);return d.join("")},replaceTrailingNewlines:function(a,b){for(var c="";0<a.length&&"\n"==a.charAt(a.length-1);)a=a.substring(0,a.length-1),c+=b;return a+c},getTextContent:function(a){return mxClient.IS_IE&&void 0!==a.innerText?a.innerText:null!=a?a[void 0===a.textContent?"text":"textContent"]:""},setTextContent:function(a,b){void 0!==a.innerText?
+a.innerText=b:a[void 0===a.textContent?"text":"textContent"]=b},getInnerHtml:function(){return mxClient.IS_IE?function(a){return null!=a?a.innerHTML:""}:function(a){return null!=a?(new XMLSerializer).serializeToString(a):""}}(),getOuterHtml:function(){return mxClient.IS_IE?function(a){if(null!=a){if(null!=a.outerHTML)return a.outerHTML;var b=[];b.push("<"+a.nodeName);var c=a.attributes;if(null!=c)for(var d=0;d<c.length;d++){var e=c[d].value;null!=e&&0<e.length&&(b.push(" "),b.push(c[d].nodeName),
+b.push('="'),b.push(e),b.push('"'))}0==a.innerHTML.length?b.push("/>"):(b.push(">"),b.push(a.innerHTML),b.push("</"+a.nodeName+">"));return b.join("")}return""}:function(a){return null!=a?(new XMLSerializer).serializeToString(a):""}}(),write:function(a,b){var c=a.ownerDocument.createTextNode(b);null!=a&&a.appendChild(c);return c},writeln:function(a,b){var c=a.ownerDocument.createTextNode(b);null!=a&&(a.appendChild(c),a.appendChild(document.createElement("br")));return c},br:function(a,b){b=b||1;for(var c=
+null,d=0;d<b;d++)null!=a&&(c=a.ownerDocument.createElement("br"),a.appendChild(c));return c},button:function(a,b,c){c=null!=c?c:document;c=c.createElement("button");mxUtils.write(c,a);mxEvent.addListener(c,"click",function(a){b(a)});return c},para:function(a,b){var c=document.createElement("p");mxUtils.write(c,b);null!=a&&a.appendChild(c);return c},addTransparentBackgroundFilter:function(a){a.style.filter+="progid:DXImageTransform.Microsoft.AlphaImageLoader(src='"+mxClient.imageBasePath+"/transparent.gif', sizingMethod='scale')"},
+linkAction:function(a,b,c,d,e){return mxUtils.link(a,b,function(){c.execute(d)},e)},linkInvoke:function(a,b,c,d,e,f){return mxUtils.link(a,b,function(){c[d](e)},f)},link:function(a,b,c,d){var e=document.createElement("span");e.style.color="blue";e.style.textDecoration="underline";e.style.cursor="pointer";null!=d&&(e.style.paddingLeft=d+"px");mxEvent.addListener(e,"click",c);mxUtils.write(e,b);null!=a&&a.appendChild(e);return e},fit:function(a){var b=parseInt(a.offsetLeft),c=parseInt(a.offsetWidth),
+d=mxUtils.getDocumentScrollOrigin(a.ownerDocument),e=d.x,d=d.y,f=document.body,g=document.documentElement,k=e+(f.clientWidth||g.clientWidth);b+c>k&&(a.style.left=Math.max(e,k-c)+"px");b=parseInt(a.offsetTop);c=parseInt(a.offsetHeight);f=d+Math.max(f.clientHeight||0,g.clientHeight);b+c>f&&(a.style.top=Math.max(d,f-c)+"px")},load:function(a){a=new mxXmlRequest(a,null,"GET",!1);a.send();return a},get:function(a,b,c,d,e,f){a=new mxXmlRequest(a,null,"GET");null!=d&&a.setBinary(d);a.send(b,c,e,f);return a},
+getAll:function(a,b,c){for(var d=a.length,e=[],f=0,g=function(){0==f&&null!=c&&c();f++},k=0;k<a.length;k++)(function(a,c){mxUtils.get(a,function(a){var f=a.getStatus();200>f||299<f?g():(e[c]=a,d--,0==d&&b(e))},g)})(a[k],k);0==d&&b(e)},post:function(a,b,c,d){return(new mxXmlRequest(a,b)).send(c,d)},submit:function(a,b,c,d){return(new mxXmlRequest(a,b)).simulate(c,d)},loadInto:function(a,b,c){mxClient.IS_IE?b.onreadystatechange=function(){4==b.readyState&&c()}:b.addEventListener("load",c,!1);b.load(a)},
+getValue:function(a,b,c){a=null!=a?a[b]:null;null==a&&(a=c);return a},getNumber:function(a,b,c){a=null!=a?a[b]:null;null==a&&(a=c||0);return Number(a)},getColor:function(a,b,c){a=null!=a?a[b]:null;null==a?a=c:a==mxConstants.NONE&&(a=null);return a},clone:function(a,b,c){c=null!=c?c:!1;var d=null;if(null!=a&&"function"==typeof a.constructor){var d=new a.constructor,e;for(e in a)e!=mxObjectIdentity.FIELD_NAME&&(null==b||0>mxUtils.indexOf(b,e))&&(d[e]=c||"object"!=typeof a[e]?a[e]:mxUtils.clone(a[e]))}return d},
+equalPoints:function(a,b){if(null==a&&null!=b||null!=a&&null==b||null!=a&&null!=b&&a.length!=b.length)return!1;if(null!=a&&null!=b)for(var c=0;c<a.length;c++)if(a[c]==b[c]||null!=a[c]&&!a[c].equals(b[c]))return!1;return!0},equalEntries:function(a,b){if(null==a&&null!=b||null!=a&&null==b||null!=a&&null!=b&&a.length!=b.length)return!1;if(null!=a&&null!=b){var c=0,d;for(d in b)c++;for(d in a)if(c--,!(mxUtils.isNaN(a[d])&&mxUtils.isNaN(b[d])||a[d]==b[d]))return!1}return 0==c},removeDuplicates:function(a){for(var b=
+new mxDictionary,c=[],d=0;d<a.length;d++)b.get(a[d])||(c.push(a[d]),b.put(a[d],!0));return c},isNaN:function(a){return"number"==typeof a&&isNaN(a)},extend:function(a,b){var c=function(){};c.prototype=b.prototype;a.prototype=new c;a.prototype.constructor=a},toString:function(a){var b="",c;for(c in a)try{if(null==a[c])b+=c+" = [null]\n";else if("function"==typeof a[c])b+=c+" => [Function]\n";else if("object"==typeof a[c])var d=mxUtils.getFunctionName(a[c].constructor),b=b+(c+" => ["+d+"]\n");else b+=
+c+" = "+a[c]+"\n"}catch(e){b+=c+"="+e.message}return b},toRadians:function(a){return Math.PI*a/180},toDegree:function(a){return 180*a/Math.PI},arcToCurves:function(a,b,c,d,e,f,g,k,l){k-=a;l-=b;if(0===c||0===d)return q;c=Math.abs(c);d=Math.abs(d);var m=-k/2,n=-l/2,p=Math.cos(e*Math.PI/180),q=Math.sin(e*Math.PI/180);e=p*m+q*n;var m=-1*q*m+p*n,n=e*e,r=m*m,t=c*c,u=d*d,x=n/t+r/u;1<x?(c*=Math.sqrt(x),d*=Math.sqrt(x),f=0):(x=1,f===g&&(x=-1),f=x*Math.sqrt((t*u-t*r-u*n)/(t*r+u*n)));n=f*c*m/d;r=-1*f*d*e/c;
+k=p*n-q*r+k/2;l=q*n+p*r+l/2;t=Math.atan2((m-r)/d,(e-n)/c)-Math.atan2(0,1);f=0<=t?t:2*Math.PI+t;t=Math.atan2((-m-r)/d,(-e-n)/c)-Math.atan2((m-r)/d,(e-n)/c);e=0<=t?t:2*Math.PI+t;0==g&&0<e?e-=2*Math.PI:0!=g&&0>e&&(e+=2*Math.PI);g=2*e/Math.PI;g=Math.ceil(0>g?-1*g:g);e/=g;m=8/3*Math.sin(e/4)*Math.sin(e/4)/Math.sin(e/2);n=p*c;p*=d;c*=q;d*=q;for(var y=Math.cos(f),A=Math.sin(f),r=-m*(n*A+d*y),t=-m*(c*A-p*y),q=[],z=0;z<g;++z){f+=e;var y=Math.cos(f),A=Math.sin(f),u=n*y-d*A+k,x=c*y+p*A+l,v=-m*(n*A+d*y),y=-m*
+(c*A-p*y),A=6*z;q[A]=Number(r+a);q[A+1]=Number(t+b);q[A+2]=Number(u-v+a);q[A+3]=Number(x-y+b);q[A+4]=Number(u+a);q[A+5]=Number(x+b);r=u+v;t=x+y}return q},getBoundingBox:function(a,b,c){var d=null;if(null!=a&&null!=b&&0!=b){b=mxUtils.toRadians(b);var d=Math.cos(b),e=Math.sin(b);c=null!=c?c:new mxPoint(a.x+a.width/2,a.y+a.height/2);var f=new mxPoint(a.x,a.y);b=new mxPoint(a.x+a.width,a.y);var g=new mxPoint(b.x,a.y+a.height);a=new mxPoint(a.x,g.y);f=mxUtils.getRotatedPoint(f,d,e,c);b=mxUtils.getRotatedPoint(b,
+d,e,c);g=mxUtils.getRotatedPoint(g,d,e,c);a=mxUtils.getRotatedPoint(a,d,e,c);d=new mxRectangle(f.x,f.y,0,0);d.add(new mxRectangle(b.x,b.y,0,0));d.add(new mxRectangle(g.x,g.y,0,0));d.add(new mxRectangle(a.x,a.y,0,0))}return d},getRotatedPoint:function(a,b,c,d){d=null!=d?d:new mxPoint;var e=a.x-d.x;a=a.y-d.y;return new mxPoint(e*b-a*c+d.x,a*b+e*c+d.y)},getPortConstraints:function(a,b,c,d){b=mxUtils.getValue(a.style,mxConstants.STYLE_PORT_CONSTRAINT,mxUtils.getValue(b.style,c?mxConstants.STYLE_SOURCE_PORT_CONSTRAINT:
+mxConstants.STYLE_TARGET_PORT_CONSTRAINT,null));if(null==b)return d;d=b.toString();b=mxConstants.DIRECTION_MASK_NONE;c=0;1==mxUtils.getValue(a.style,mxConstants.STYLE_PORT_CONSTRAINT_ROTATION,0)&&(c=mxUtils.getValue(a.style,mxConstants.STYLE_ROTATION,0));a=0;45<c?(a=1,135<=c&&(a=2)):-45>c&&(a=3,-135>=c&&(a=2));if(0<=d.indexOf(mxConstants.DIRECTION_NORTH))switch(a){case 0:b|=mxConstants.DIRECTION_MASK_NORTH;break;case 1:b|=mxConstants.DIRECTION_MASK_EAST;break;case 2:b|=mxConstants.DIRECTION_MASK_SOUTH;
+break;case 3:b|=mxConstants.DIRECTION_MASK_WEST}if(0<=d.indexOf(mxConstants.DIRECTION_WEST))switch(a){case 0:b|=mxConstants.DIRECTION_MASK_WEST;break;case 1:b|=mxConstants.DIRECTION_MASK_NORTH;break;case 2:b|=mxConstants.DIRECTION_MASK_EAST;break;case 3:b|=mxConstants.DIRECTION_MASK_SOUTH}if(0<=d.indexOf(mxConstants.DIRECTION_SOUTH))switch(a){case 0:b|=mxConstants.DIRECTION_MASK_SOUTH;break;case 1:b|=mxConstants.DIRECTION_MASK_WEST;break;case 2:b|=mxConstants.DIRECTION_MASK_NORTH;break;case 3:b|=
+mxConstants.DIRECTION_MASK_EAST}if(0<=d.indexOf(mxConstants.DIRECTION_EAST))switch(a){case 0:b|=mxConstants.DIRECTION_MASK_EAST;break;case 1:b|=mxConstants.DIRECTION_MASK_SOUTH;break;case 2:b|=mxConstants.DIRECTION_MASK_WEST;break;case 3:b|=mxConstants.DIRECTION_MASK_NORTH}return b},reversePortConstraints:function(a){var b;b=(a&mxConstants.DIRECTION_MASK_WEST)<<3;b|=(a&mxConstants.DIRECTION_MASK_NORTH)<<1;b|=(a&mxConstants.DIRECTION_MASK_SOUTH)>>1;return b|=(a&mxConstants.DIRECTION_MASK_EAST)>>3},
+findNearestSegment:function(a,b,c){var d=-1;if(0<a.absolutePoints.length)for(var e=a.absolutePoints[0],f=null,g=1;g<a.absolutePoints.length;g++){var k=a.absolutePoints[g],e=mxUtils.ptSegDistSq(e.x,e.y,k.x,k.y,b,c);if(null==f||e<f)f=e,d=g-1;e=k}return d},getDirectedBounds:function(a,b,c,d,e){var f=mxUtils.getValue(c,mxConstants.STYLE_DIRECTION,mxConstants.DIRECTION_EAST);d=null!=d?d:mxUtils.getValue(c,mxConstants.STYLE_FLIPH,!1);e=null!=e?e:mxUtils.getValue(c,mxConstants.STYLE_FLIPV,!1);b.x=Math.round(Math.max(0,
+Math.min(a.width,b.x)));b.y=Math.round(Math.max(0,Math.min(a.height,b.y)));b.width=Math.round(Math.max(0,Math.min(a.width,b.width)));b.height=Math.round(Math.max(0,Math.min(a.height,b.height)));if(e&&(f==mxConstants.DIRECTION_SOUTH||f==mxConstants.DIRECTION_NORTH)||d&&(f==mxConstants.DIRECTION_EAST||f==mxConstants.DIRECTION_WEST))c=b.x,b.x=b.width,b.width=c;if(d&&(f==mxConstants.DIRECTION_SOUTH||f==mxConstants.DIRECTION_NORTH)||e&&(f==mxConstants.DIRECTION_EAST||f==mxConstants.DIRECTION_WEST))c=b.y,
+b.y=b.height,b.height=c;d=mxRectangle.fromRectangle(b);f==mxConstants.DIRECTION_SOUTH?(d.y=b.x,d.x=b.height,d.width=b.y,d.height=b.width):f==mxConstants.DIRECTION_WEST?(d.y=b.height,d.x=b.width,d.width=b.x,d.height=b.y):f==mxConstants.DIRECTION_NORTH&&(d.y=b.width,d.x=b.y,d.width=b.height,d.height=b.x);return new mxRectangle(a.x+d.x,a.y+d.y,a.width-d.width-d.x,a.height-d.height-d.y)},getPerimeterPoint:function(a,b,c){for(var d=null,e=0;e<a.length-1;e++){var f=mxUtils.intersection(a[e].x,a[e].y,a[e+
+1].x,a[e+1].y,b.x,b.y,c.x,c.y);if(null!=f){var g=c.x-f.x,k=c.y-f.y,f={p:f,distSq:k*k+g*g};null!=f&&(null==d||d.distSq>f.distSq)&&(d=f)}}return null!=d?d.p:null},rectangleIntersectsSegment:function(a,b,c){var d=a.y,e=a.x,f=d+a.height,g=e+a.width;a=b.x;var k=c.x;b.x>c.x&&(a=c.x,k=b.x);k>g&&(k=g);a<e&&(a=e);if(a>k)return!1;var e=b.y,g=c.y,l=c.x-b.x;1E-7<Math.abs(l)&&(c=(c.y-b.y)/l,b=b.y-c*b.x,e=c*a+b,g=c*k+b);e>g&&(b=g,g=e,e=b);g>f&&(g=f);e<d&&(e=d);return e>g?!1:!0},contains:function(a,b,c){return a.x<=
+b&&a.x+a.width>=b&&a.y<=c&&a.y+a.height>=c},intersects:function(a,b){var c=a.width,d=a.height,e=b.width,f=b.height;if(0>=e||0>=f||0>=c||0>=d)return!1;var g=a.x,k=a.y,l=b.x,m=b.y,e=e+l,f=f+m,c=c+g,d=d+k;return(e<l||e>g)&&(f<m||f>k)&&(c<g||c>l)&&(d<k||d>m)},intersectsHotspot:function(a,b,c,d,e,f){d=null!=d?d:1;e=null!=e?e:0;f=null!=f?f:0;if(0<d){var g=a.getCenterX(),k=a.getCenterY(),l=a.width,m=a.height,n=mxUtils.getValue(a.style,mxConstants.STYLE_STARTSIZE)*a.view.scale;0<n&&(mxUtils.getValue(a.style,
+mxConstants.STYLE_HORIZONTAL,!0)?(k=a.y+n/2,m=n):(g=a.x+n/2,l=n));l=Math.max(e,l*d);m=Math.max(e,m*d);0<f&&(l=Math.min(l,f),m=Math.min(m,f));d=new mxRectangle(g-l/2,k-m/2,l,m);g=mxUtils.toRadians(mxUtils.getValue(a.style,mxConstants.STYLE_ROTATION)||0);0!=g&&(e=Math.cos(-g),f=Math.sin(-g),g=new mxPoint(a.getCenterX(),a.getCenterY()),a=mxUtils.getRotatedPoint(new mxPoint(b,c),e,f,g),b=a.x,c=a.y);return mxUtils.contains(d,b,c)}return!0},getOffset:function(a,b){for(var c=0,d=0,e=!1,f=a,g=document.body,
+k=document.documentElement;null!=f&&f!=g&&f!=k&&!e;){var l=mxUtils.getCurrentStyle(f);null!=l&&(e=e||"fixed"==l.position);f=f.parentNode}b||e||(e=mxUtils.getDocumentScrollOrigin(a.ownerDocument),c+=e.x,d+=e.y);e=a.getBoundingClientRect();null!=e&&(c+=e.left,d+=e.top);return new mxPoint(c,d)},getDocumentScrollOrigin:function(a){if(mxClient.IS_QUIRKS)return new mxPoint(a.body.scrollLeft,a.body.scrollTop);a=a.defaultView||a.parentWindow;return new mxPoint(null!=a&&void 0!==window.pageXOffset?window.pageXOffset:
+(document.documentElement||document.body.parentNode||document.body).scrollLeft,null!=a&&void 0!==window.pageYOffset?window.pageYOffset:(document.documentElement||document.body.parentNode||document.body).scrollTop)},getScrollOrigin:function(a,b,c){b=null!=b?b:!1;c=null!=c?c:!0;for(var d=null!=a?a.ownerDocument:document,e=d.body,f=d.documentElement,g=new mxPoint,k=!1;null!=a&&a!=e&&a!=f;){isNaN(a.scrollLeft)||isNaN(a.scrollTop)||(g.x+=a.scrollLeft,g.y+=a.scrollTop);var l=mxUtils.getCurrentStyle(a);
+null!=l&&(k=k||"fixed"==l.position);a=b?a.parentNode:null}!k&&c&&(a=mxUtils.getDocumentScrollOrigin(d),g.x+=a.x,g.y+=a.y);return g},convertPoint:function(a,b,c){var d=mxUtils.getScrollOrigin(a,!1);a=mxUtils.getOffset(a);a.x-=d.x;a.y-=d.y;return new mxPoint(b-a.x,c-a.y)},ltrim:function(a,b){return null!=a?a.replace(new RegExp("^["+(b||"\\s")+"]+","g"),""):null},rtrim:function(a,b){return null!=a?a.replace(new RegExp("["+(b||"\\s")+"]+$","g"),""):null},trim:function(a,b){return mxUtils.ltrim(mxUtils.rtrim(a,
+b),b)},isNumeric:function(a){return!isNaN(parseFloat(a))&&isFinite(a)&&("string"!=typeof a||0>a.toLowerCase().indexOf("0x"))},isInteger:function(a){return String(parseInt(a))===String(a)},mod:function(a,b){return(a%b+b)%b},intersection:function(a,b,c,d,e,f,g,k){var l=(k-f)*(c-a)-(g-e)*(d-b);g=((g-e)*(b-f)-(k-f)*(a-e))/l;e=((c-a)*(b-f)-(d-b)*(a-e))/l;return 0<=g&&1>=g&&0<=e&&1>=e?new mxPoint(a+g*(c-a),b+g*(d-b)):null},ptSegDistSq:function(a,b,c,d,e,f){c-=a;d-=b;e-=a;f-=b;0>=e*c+f*d?c=0:(e=c-e,f=d-
+f,a=e*c+f*d,c=0>=a?0:a*a/(c*c+d*d));e=e*e+f*f-c;0>e&&(e=0);return e},ptLineDist:function(a,b,c,d,e,f){return Math.abs((d-b)*e-(c-a)*f+c*b-d*a)/Math.sqrt((d-b)*(d-b)+(c-a)*(c-a))},relativeCcw:function(a,b,c,d,e,f){c-=a;d-=b;e-=a;f-=b;a=e*d-f*c;0==a&&(a=e*c+f*d,0<a&&(a=(e-c)*c+(f-d)*d,0>a&&(a=0)));return 0>a?-1:0<a?1:0},animateChanges:function(a,b){mxEffects.animateChanges.apply(this,arguments)},cascadeOpacity:function(a,b,c){mxEffects.cascadeOpacity.apply(this,arguments)},fadeOut:function(a,b,c,d,
+e,f){mxEffects.fadeOut.apply(this,arguments)},setOpacity:function(a,b){mxUtils.isVml(a)?a.style.filter=100<=b?"":"alpha(opacity="+b/5+")":mxClient.IS_IE&&("undefined"===typeof document.documentMode||9>document.documentMode)?a.style.filter=100<=b?"":"alpha(opacity="+b+")":a.style.opacity=b/100},createImage:function(a){var b;mxClient.IS_IE6&&"CSS1Compat"!=document.compatMode?(b=document.createElement(mxClient.VML_PREFIX+":image"),b.setAttribute("src",a),b.style.borderStyle="none"):(b=document.createElement("img"),
+b.setAttribute("src",a),b.setAttribute("border","0"));return b},sortCells:function(a,b){b=null!=b?b:!0;var c=new mxDictionary;a.sort(function(a,e){var d=c.get(a);null==d&&(d=mxCellPath.create(a).split(mxCellPath.PATH_SEPARATOR),c.put(a,d));var g=c.get(e);null==g&&(g=mxCellPath.create(e).split(mxCellPath.PATH_SEPARATOR),c.put(e,g));d=mxCellPath.compare(d,g);return 0==d?0:0<d==b?1:-1});return a},getStylename:function(a){return null!=a&&(a=a.split(";")[0],0>a.indexOf("="))?a:""},getStylenames:function(a){var b=
+[];if(null!=a){a=a.split(";");for(var c=0;c<a.length;c++)0>a[c].indexOf("=")&&b.push(a[c])}return b},indexOfStylename:function(a,b){if(null!=a&&null!=b)for(var c=a.split(";"),d=0,e=0;e<c.length;e++){if(c[e]==b)return d;d+=c[e].length+1}return-1},addStylename:function(a,b){0>mxUtils.indexOfStylename(a,b)&&(null==a?a="":0<a.length&&";"!=a.charAt(a.length-1)&&(a+=";"),a+=b);return a},removeStylename:function(a,b){var c=[];if(null!=a)for(var d=a.split(";"),e=0;e<d.length;e++)d[e]!=b&&c.push(d[e]);return c.join(";")},
+removeAllStylenames:function(a){var b=[];if(null!=a){a=a.split(";");for(var c=0;c<a.length;c++)0<=a[c].indexOf("=")&&b.push(a[c])}return b.join(";")},setCellStyles:function(a,b,c,d){if(null!=b&&0<b.length){a.beginUpdate();try{for(var e=0;e<b.length;e++)if(null!=b[e]){var f=mxUtils.setStyle(a.getStyle(b[e]),c,d);a.setStyle(b[e],f)}}finally{a.endUpdate()}}},setStyle:function(a,b,c){var d=null!=c&&("undefined"==typeof c.length||0<c.length);if(null==a||0==a.length)d&&(a=b+"="+c+";");else if(a.substring(0,
+b.length+1)==b+"="){var e=a.indexOf(";");a=d?b+"="+c+(0>e?";":a.substring(e)):0>e||e==a.length-1?"":a.substring(e+1)}else{var f=a.indexOf(";"+b+"=");0>f?d&&(d=";"==a.charAt(a.length-1)?"":";",a=a+d+b+"="+c+";"):(e=a.indexOf(";",f+1),a=d?a.substring(0,f+1)+b+"="+c+(0>e?";":a.substring(e)):a.substring(0,f)+(0>e?";":a.substring(e)))}return a},setCellStyleFlags:function(a,b,c,d,e){if(null!=b&&0<b.length){a.beginUpdate();try{for(var f=0;f<b.length;f++)if(null!=b[f]){var g=mxUtils.setStyleFlag(a.getStyle(b[f]),
+c,d,e);a.setStyle(b[f],g)}}finally{a.endUpdate()}}},setStyleFlag:function(a,b,c,d){if(null==a||0==a.length)a=d||null==d?b+"="+c:b+"=0";else{var e=a.indexOf(b+"=");if(0>e)e=";"==a.charAt(a.length-1)?"":";",a=d||null==d?a+e+b+"="+c:a+e+b+"=0";else{var f=a.indexOf(";",e),g;g=0>f?a.substring(e+b.length+1):a.substring(e+b.length+1,f);g=null==d?parseInt(g)^c:d?parseInt(g)|c:parseInt(g)&~c;a=a.substring(0,e)+b+"="+g+(0<=f?a.substring(f):"")}}return a},getAlignmentAsPoint:function(a,b){var c=0,d=0;a==mxConstants.ALIGN_CENTER?
+c=-.5:a==mxConstants.ALIGN_RIGHT&&(c=-1);b==mxConstants.ALIGN_MIDDLE?d=-.5:b==mxConstants.ALIGN_BOTTOM&&(d=-1);return new mxPoint(c,d)},getSizeForString:function(a,b,c,d){b=null!=b?b:mxConstants.DEFAULT_FONTSIZE;c=null!=c?c:mxConstants.DEFAULT_FONTFAMILY;var e=document.createElement("div");e.style.fontFamily=c;e.style.fontSize=Math.round(b)+"px";e.style.lineHeight=Math.round(b*mxConstants.LINE_HEIGHT)+"px";e.style.position="absolute";e.style.visibility="hidden";e.style.display=mxClient.IS_QUIRKS?
+"inline":"inline-block";e.style.zoom="1";null!=d?(e.style.width=d+"px",e.style.whiteSpace="normal"):e.style.whiteSpace="nowrap";e.innerHTML=a;document.body.appendChild(e);a=new mxRectangle(0,0,e.offsetWidth,e.offsetHeight);document.body.removeChild(e);return a},getViewXml:function(a,b,c,d,e){d=null!=d?d:0;e=null!=e?e:0;b=null!=b?b:1;null==c&&(c=[a.getModel().getRoot()]);var f=a.getView(),g=null,k=f.isEventsEnabled();f.setEventsEnabled(!1);var l=f.drawPane,m=f.overlayPane;a.dialect==mxConstants.DIALECT_SVG?
+(f.drawPane=document.createElementNS(mxConstants.NS_SVG,"g"),f.canvas.appendChild(f.drawPane),f.overlayPane=document.createElementNS(mxConstants.NS_SVG,"g")):(f.drawPane=f.drawPane.cloneNode(!1),f.canvas.appendChild(f.drawPane),f.overlayPane=f.overlayPane.cloneNode(!1));f.canvas.appendChild(f.overlayPane);var n=f.getTranslate();f.translate=new mxPoint(d,e);b=new mxTemporaryCellStates(a.getView(),b,c);try{g=(new mxCodec).encode(a.getView())}finally{b.destroy(),f.translate=n,f.canvas.removeChild(f.drawPane),
+f.canvas.removeChild(f.overlayPane),f.drawPane=l,f.overlayPane=m,f.setEventsEnabled(k)}return g},getScaleForPageCount:function(a,b,c,d){if(1>a)return 1;c=null!=c?c:mxConstants.PAGE_FORMAT_A4_PORTRAIT;d=null!=d?d:0;var e=c.width-2*d;c=c.height-2*d;d=b.getGraphBounds().clone();b=b.getView().getScale();d.width/=b;d.height/=b;b=d.width;var f=Math.sqrt(a);d=Math.sqrt(b/d.height/(e/c));c=f*d;d=f/d;if(1>c&&d>a){var g=d/a;d=a;c/=g}1>d&&c>a&&(g=c/a,c=a,d/=g);g=Math.ceil(c)*Math.ceil(d);for(f=0;g>a;){var g=
+Math.floor(c)/c,k=Math.floor(d)/d;1==g&&(g=Math.floor(c-1)/c);1==k&&(k=Math.floor(d-1)/d);g=g>k?g:k;c*=g;d*=g;g=Math.ceil(c)*Math.ceil(d);f++;if(10<f)break}return e*c/b*.99999},show:function(a,b,c,d,e,f){c=null!=c?c:0;d=null!=d?d:0;null==b?b=window.open().document:b.open();9==document.documentMode&&b.writeln('\x3c!--[if IE]><meta http-equiv="X-UA-Compatible" content="IE=9"><![endif]--\x3e');var g=a.getGraphBounds(),k=Math.ceil(c-g.x),l=Math.ceil(d-g.y);null==e&&(e=Math.ceil(g.width+c)+Math.ceil(Math.ceil(g.x)-
+g.x));null==f&&(f=Math.ceil(g.height+d)+Math.ceil(Math.ceil(g.y)-g.y));if(mxClient.IS_IE||11==document.documentMode){d="<html><head>";g=document.getElementsByTagName("base");for(c=0;c<g.length;c++)d+=g[c].outerHTML;d+="<style>";for(c=0;c<document.styleSheets.length;c++)try{d+=document.styleSheets[c].cssText}catch(m){}d=d+'</style></head><body style="margin:0px;">'+('<div style="position:absolute;overflow:hidden;width:'+e+"px;height:"+f+'px;"><div style="position:relative;left:'+k+"px;top:"+l+'px;">')+
+a.container.innerHTML;b.writeln(d+"</div></div></body><html>");b.close()}else{b.writeln("<html><head>");g=document.getElementsByTagName("base");for(c=0;c<g.length;c++)b.writeln(mxUtils.getOuterHtml(g[c]));d=document.getElementsByTagName("link");for(c=0;c<d.length;c++)b.writeln(mxUtils.getOuterHtml(d[c]));d=document.getElementsByTagName("style");for(c=0;c<d.length;c++)b.writeln(mxUtils.getOuterHtml(d[c]));b.writeln('</head><body style="margin:0px;"></body></html>');b.close();c=b.createElement("div");
+c.position="absolute";c.overflow="hidden";c.style.width=e+"px";c.style.height=f+"px";e=b.createElement("div");e.style.position="absolute";e.style.left=k+"px";e.style.top=l+"px";f=a.container.firstChild;for(d=null;null!=f;)g=f.cloneNode(!0),f==a.view.drawPane.ownerSVGElement?(c.appendChild(g),d=g):e.appendChild(g),f=f.nextSibling;b.body.appendChild(c);null!=e.firstChild&&b.body.appendChild(e);null!=d&&(d.style.minWidth="",d.style.minHeight="",d.firstChild.setAttribute("transform","translate("+k+","+
+l+")"))}mxUtils.removeCursors(b.body);return b},printScreen:function(a){var b=window.open();a.getGraphBounds();mxUtils.show(a,b.document);a=function(){b.focus();b.print();b.close()};mxClient.IS_GC?b.setTimeout(a,500):a()},popup:function(a,b){if(b){var c=document.createElement("div");c.style.overflow="scroll";c.style.width="636px";c.style.height="460px";var d=document.createElement("pre");d.innerHTML=mxUtils.htmlEntities(a,!1).replace(/\n/g,"<br>").replace(/ /g,"&nbsp;");c.appendChild(d);c=new mxWindow("Popup Window",
+c,document.body.clientWidth/2-320,Math.max(document.body.clientHeight||0,document.documentElement.clientHeight)/2-240,640,480,!1,!0);c.setClosable(!0);c.setVisible(!0)}else mxClient.IS_NS?(c=window.open(),c.document.writeln("<pre>"+mxUtils.htmlEntities(a)+"</pre"),c.document.close()):(c=window.open(),d=c.document.createElement("pre"),d.innerHTML=mxUtils.htmlEntities(a,!1).replace(/\n/g,"<br>").replace(/ /g,"&nbsp;"),c.document.body.appendChild(d))},alert:function(a){alert(a)},prompt:function(a,b){return prompt(a,
+null!=b?b:"")},confirm:function(a){return confirm(a)},error:function(a,b,c,d){var e=document.createElement("div");e.style.padding="20px";var f=document.createElement("img");f.setAttribute("src",d||mxUtils.errorImage);f.setAttribute("valign","bottom");f.style.verticalAlign="middle";e.appendChild(f);e.appendChild(document.createTextNode(" "));e.appendChild(document.createTextNode(" "));e.appendChild(document.createTextNode(" "));mxUtils.write(e,a);a=document.body.clientWidth;d=document.body.clientHeight||
+document.documentElement.clientHeight;var g=new mxWindow(mxResources.get(mxUtils.errorResource)||mxUtils.errorResource,e,(a-b)/2,d/4,b,null,!1,!0);c&&(mxUtils.br(e),b=document.createElement("p"),c=document.createElement("button"),mxClient.IS_IE?c.style.cssText="float:right":c.setAttribute("style","float:right"),mxEvent.addListener(c,"click",function(a){g.destroy()}),mxUtils.write(c,mxResources.get(mxUtils.closeResource)||mxUtils.closeResource),b.appendChild(c),e.appendChild(b),mxUtils.br(e),g.setClosable(!0));
+g.setVisible(!0);return g},makeDraggable:function(a,b,c,d,e,f,g,k,l,m){a=new mxDragSource(a,c);a.dragOffset=new mxPoint(null!=e?e:0,null!=f?f:mxConstants.TOOLTIP_VERTICAL_OFFSET);a.autoscroll=g;a.setGuidesEnabled(!1);null!=l&&(a.highlightDropTargets=l);null!=m&&(a.getDropTarget=m);a.getGraphForEvent=function(a){return"function"==typeof b?b(a):b};null!=d&&(a.createDragElement=function(){return d.cloneNode(!0)},k&&(a.createPreviewElement=function(a){var b=d.cloneNode(!0),c=parseInt(b.style.width),e=
+parseInt(b.style.height);b.style.width=Math.round(c*a.view.scale)+"px";b.style.height=Math.round(e*a.view.scale)+"px";return b}));return a}},mxConstants={DEFAULT_HOTSPOT:.3,MIN_HOTSPOT_SIZE:8,MAX_HOTSPOT_SIZE:0,RENDERING_HINT_EXACT:"exact",RENDERING_HINT_FASTER:"faster",RENDERING_HINT_FASTEST:"fastest",DIALECT_SVG:"svg",DIALECT_VML:"vml",DIALECT_MIXEDHTML:"mixedHtml",DIALECT_PREFERHTML:"preferHtml",DIALECT_STRICTHTML:"strictHtml",NS_SVG:"http://www.w3.org/2000/svg",NS_XHTML:"http://www.w3.org/1999/xhtml",
+NS_XLINK:"http://www.w3.org/1999/xlink",SHADOWCOLOR:"gray",VML_SHADOWCOLOR:"gray",SHADOW_OFFSET_X:2,SHADOW_OFFSET_Y:3,SHADOW_OPACITY:1,NODETYPE_ELEMENT:1,NODETYPE_ATTRIBUTE:2,NODETYPE_TEXT:3,NODETYPE_CDATA:4,NODETYPE_ENTITY_REFERENCE:5,NODETYPE_ENTITY:6,NODETYPE_PROCESSING_INSTRUCTION:7,NODETYPE_COMMENT:8,NODETYPE_DOCUMENT:9,NODETYPE_DOCUMENTTYPE:10,NODETYPE_DOCUMENT_FRAGMENT:11,NODETYPE_NOTATION:12,TOOLTIP_VERTICAL_OFFSET:16,DEFAULT_VALID_COLOR:"#00FF00",DEFAULT_INVALID_COLOR:"#FF0000",OUTLINE_HIGHLIGHT_COLOR:"#00FF00",
+OUTLINE_HIGHLIGHT_STROKEWIDTH:5,HIGHLIGHT_STROKEWIDTH:3,HIGHLIGHT_SIZE:2,HIGHLIGHT_OPACITY:100,CURSOR_MOVABLE_VERTEX:"move",CURSOR_MOVABLE_EDGE:"move",CURSOR_LABEL_HANDLE:"default",CURSOR_TERMINAL_HANDLE:"pointer",CURSOR_BEND_HANDLE:"crosshair",CURSOR_VIRTUAL_BEND_HANDLE:"crosshair",CURSOR_CONNECT:"pointer",HIGHLIGHT_COLOR:"#00FF00",CONNECT_TARGET_COLOR:"#0000FF",INVALID_CONNECT_TARGET_COLOR:"#FF0000",DROP_TARGET_COLOR:"#0000FF",VALID_COLOR:"#00FF00",INVALID_COLOR:"#FF0000",EDGE_SELECTION_COLOR:"#00FF00",
+VERTEX_SELECTION_COLOR:"#00FF00",VERTEX_SELECTION_STROKEWIDTH:1,EDGE_SELECTION_STROKEWIDTH:1,VERTEX_SELECTION_DASHED:!0,EDGE_SELECTION_DASHED:!0,GUIDE_COLOR:"#FF0000",GUIDE_STROKEWIDTH:1,OUTLINE_COLOR:"#0099FF",OUTLINE_STROKEWIDTH:mxClient.IS_IE?2:3,HANDLE_SIZE:6,LABEL_HANDLE_SIZE:4,HANDLE_FILLCOLOR:"#00FF00",HANDLE_STROKECOLOR:"black",LABEL_HANDLE_FILLCOLOR:"yellow",CONNECT_HANDLE_FILLCOLOR:"#0000FF",LOCKED_HANDLE_FILLCOLOR:"#FF0000",OUTLINE_HANDLE_FILLCOLOR:"#00FFFF",OUTLINE_HANDLE_STROKECOLOR:"#0033FF",
+DEFAULT_FONTFAMILY:"Arial,Helvetica",DEFAULT_FONTSIZE:11,DEFAULT_TEXT_DIRECTION:"",LINE_HEIGHT:1.2,WORD_WRAP:"normal",ABSOLUTE_LINE_HEIGHT:!1,DEFAULT_FONTSTYLE:0,DEFAULT_STARTSIZE:40,DEFAULT_MARKERSIZE:6,DEFAULT_IMAGESIZE:24,ENTITY_SEGMENT:30,RECTANGLE_ROUNDING_FACTOR:.15,LINE_ARCSIZE:20,ARROW_SPACING:0,ARROW_WIDTH:30,ARROW_SIZE:30,PAGE_FORMAT_A4_PORTRAIT:new mxRectangle(0,0,827,1169),PAGE_FORMAT_A4_LANDSCAPE:new mxRectangle(0,0,1169,827),PAGE_FORMAT_LETTER_PORTRAIT:new mxRectangle(0,0,850,1100),
+PAGE_FORMAT_LETTER_LANDSCAPE:new mxRectangle(0,0,1100,850),NONE:"none",STYLE_PERIMETER:"perimeter",STYLE_SOURCE_PORT:"sourcePort",STYLE_TARGET_PORT:"targetPort",STYLE_PORT_CONSTRAINT:"portConstraint",STYLE_PORT_CONSTRAINT_ROTATION:"portConstraintRotation",STYLE_SOURCE_PORT_CONSTRAINT:"sourcePortConstraint",STYLE_TARGET_PORT_CONSTRAINT:"targetPortConstraint",STYLE_OPACITY:"opacity",STYLE_FILL_OPACITY:"fillOpacity",STYLE_STROKE_OPACITY:"strokeOpacity",STYLE_TEXT_OPACITY:"textOpacity",STYLE_TEXT_DIRECTION:"textDirection",
+STYLE_OVERFLOW:"overflow",STYLE_ORTHOGONAL:"orthogonal",STYLE_EXIT_X:"exitX",STYLE_EXIT_Y:"exitY",STYLE_EXIT_PERIMETER:"exitPerimeter",STYLE_ENTRY_X:"entryX",STYLE_ENTRY_Y:"entryY",STYLE_ENTRY_PERIMETER:"entryPerimeter",STYLE_WHITE_SPACE:"whiteSpace",STYLE_ROTATION:"rotation",STYLE_FILLCOLOR:"fillColor",STYLE_POINTER_EVENTS:"pointerEvents",STYLE_SWIMLANE_FILLCOLOR:"swimlaneFillColor",STYLE_MARGIN:"margin",STYLE_GRADIENTCOLOR:"gradientColor",STYLE_GRADIENT_DIRECTION:"gradientDirection",STYLE_STROKECOLOR:"strokeColor",
+STYLE_SEPARATORCOLOR:"separatorColor",STYLE_STROKEWIDTH:"strokeWidth",STYLE_ALIGN:"align",STYLE_VERTICAL_ALIGN:"verticalAlign",STYLE_LABEL_WIDTH:"labelWidth",STYLE_LABEL_POSITION:"labelPosition",STYLE_VERTICAL_LABEL_POSITION:"verticalLabelPosition",STYLE_IMAGE_ASPECT:"imageAspect",STYLE_IMAGE_ALIGN:"imageAlign",STYLE_IMAGE_VERTICAL_ALIGN:"imageVerticalAlign",STYLE_GLASS:"glass",STYLE_IMAGE:"image",STYLE_IMAGE_WIDTH:"imageWidth",STYLE_IMAGE_HEIGHT:"imageHeight",STYLE_IMAGE_BACKGROUND:"imageBackground",
+STYLE_IMAGE_BORDER:"imageBorder",STYLE_FLIPH:"flipH",STYLE_FLIPV:"flipV",STYLE_NOLABEL:"noLabel",STYLE_NOEDGESTYLE:"noEdgeStyle",STYLE_LABEL_BACKGROUNDCOLOR:"labelBackgroundColor",STYLE_LABEL_BORDERCOLOR:"labelBorderColor",STYLE_LABEL_PADDING:"labelPadding",STYLE_INDICATOR_SHAPE:"indicatorShape",STYLE_INDICATOR_IMAGE:"indicatorImage",STYLE_INDICATOR_COLOR:"indicatorColor",STYLE_INDICATOR_STROKECOLOR:"indicatorStrokeColor",STYLE_INDICATOR_GRADIENTCOLOR:"indicatorGradientColor",STYLE_INDICATOR_SPACING:"indicatorSpacing",
+STYLE_INDICATOR_WIDTH:"indicatorWidth",STYLE_INDICATOR_HEIGHT:"indicatorHeight",STYLE_INDICATOR_DIRECTION:"indicatorDirection",STYLE_SHADOW:"shadow",STYLE_SEGMENT:"segment",STYLE_ENDARROW:"endArrow",STYLE_STARTARROW:"startArrow",STYLE_ENDSIZE:"endSize",STYLE_STARTSIZE:"startSize",STYLE_SWIMLANE_LINE:"swimlaneLine",STYLE_ENDFILL:"endFill",STYLE_STARTFILL:"startFill",STYLE_DASHED:"dashed",STYLE_DASH_PATTERN:"dashPattern",STYLE_FIX_DASH:"fixDash",STYLE_ROUNDED:"rounded",STYLE_CURVED:"curved",STYLE_ARCSIZE:"arcSize",
+STYLE_ABSOLUTE_ARCSIZE:"absoluteArcSize",STYLE_SOURCE_PERIMETER_SPACING:"sourcePerimeterSpacing",STYLE_TARGET_PERIMETER_SPACING:"targetPerimeterSpacing",STYLE_PERIMETER_SPACING:"perimeterSpacing",STYLE_SPACING:"spacing",STYLE_SPACING_TOP:"spacingTop",STYLE_SPACING_LEFT:"spacingLeft",STYLE_SPACING_BOTTOM:"spacingBottom",STYLE_SPACING_RIGHT:"spacingRight",STYLE_HORIZONTAL:"horizontal",STYLE_DIRECTION:"direction",STYLE_ELBOW:"elbow",STYLE_FONTCOLOR:"fontColor",STYLE_FONTFAMILY:"fontFamily",STYLE_FONTSIZE:"fontSize",
+STYLE_FONTSTYLE:"fontStyle",STYLE_ASPECT:"aspect",STYLE_AUTOSIZE:"autosize",STYLE_FOLDABLE:"foldable",STYLE_EDITABLE:"editable",STYLE_BENDABLE:"bendable",STYLE_MOVABLE:"movable",STYLE_RESIZABLE:"resizable",STYLE_RESIZE_WIDTH:"resizeWidth",STYLE_RESIZE_HEIGHT:"resizeHeight",STYLE_ROTATABLE:"rotatable",STYLE_CLONEABLE:"cloneable",STYLE_DELETABLE:"deletable",STYLE_SHAPE:"shape",STYLE_EDGE:"edgeStyle",STYLE_JETTY_SIZE:"jettySize",STYLE_SOURCE_JETTY_SIZE:"sourceJettySize",STYLE_TARGET_JETTY_SIZE:"targetJettySize",
+STYLE_LOOP:"loopStyle",STYLE_ORTHOGONAL_LOOP:"orthogonalLoop",STYLE_ROUTING_CENTER_X:"routingCenterX",STYLE_ROUTING_CENTER_Y:"routingCenterY",FONT_BOLD:1,FONT_ITALIC:2,FONT_UNDERLINE:4,SHAPE_RECTANGLE:"rectangle",SHAPE_ELLIPSE:"ellipse",SHAPE_DOUBLE_ELLIPSE:"doubleEllipse",SHAPE_RHOMBUS:"rhombus",SHAPE_LINE:"line",SHAPE_IMAGE:"image",SHAPE_ARROW:"arrow",SHAPE_ARROW_CONNECTOR:"arrowConnector",SHAPE_LABEL:"label",SHAPE_CYLINDER:"cylinder",SHAPE_SWIMLANE:"swimlane",SHAPE_CONNECTOR:"connector",SHAPE_ACTOR:"actor",
+SHAPE_CLOUD:"cloud",SHAPE_TRIANGLE:"triangle",SHAPE_HEXAGON:"hexagon",ARROW_CLASSIC:"classic",ARROW_CLASSIC_THIN:"classicThin",ARROW_BLOCK:"block",ARROW_BLOCK_THIN:"blockThin",ARROW_OPEN:"open",ARROW_OPEN_THIN:"openThin",ARROW_OVAL:"oval",ARROW_DIAMOND:"diamond",ARROW_DIAMOND_THIN:"diamondThin",ALIGN_LEFT:"left",ALIGN_CENTER:"center",ALIGN_RIGHT:"right",ALIGN_TOP:"top",ALIGN_MIDDLE:"middle",ALIGN_BOTTOM:"bottom",DIRECTION_NORTH:"north",DIRECTION_SOUTH:"south",DIRECTION_EAST:"east",DIRECTION_WEST:"west",
+TEXT_DIRECTION_DEFAULT:"",TEXT_DIRECTION_AUTO:"auto",TEXT_DIRECTION_LTR:"ltr",TEXT_DIRECTION_RTL:"rtl",DIRECTION_MASK_NONE:0,DIRECTION_MASK_WEST:1,DIRECTION_MASK_NORTH:2,DIRECTION_MASK_SOUTH:4,DIRECTION_MASK_EAST:8,DIRECTION_MASK_ALL:15,ELBOW_VERTICAL:"vertical",ELBOW_HORIZONTAL:"horizontal",EDGESTYLE_ELBOW:"elbowEdgeStyle",EDGESTYLE_ENTITY_RELATION:"entityRelationEdgeStyle",EDGESTYLE_LOOP:"loopEdgeStyle",EDGESTYLE_SIDETOSIDE:"sideToSideEdgeStyle",EDGESTYLE_TOPTOBOTTOM:"topToBottomEdgeStyle",EDGESTYLE_ORTHOGONAL:"orthogonalEdgeStyle",
+EDGESTYLE_SEGMENT:"segmentEdgeStyle",PERIMETER_ELLIPSE:"ellipsePerimeter",PERIMETER_RECTANGLE:"rectanglePerimeter",PERIMETER_RHOMBUS:"rhombusPerimeter",PERIMETER_HEXAGON:"hexagonPerimeter",PERIMETER_TRIANGLE:"trianglePerimeter"};function mxEventObject(a){this.name=a;this.properties=[];for(var b=1;b<arguments.length;b+=2)null!=arguments[b+1]&&(this.properties[arguments[b]]=arguments[b+1])}mxEventObject.prototype.name=null;mxEventObject.prototype.properties=null;mxEventObject.prototype.consumed=!1;
+mxEventObject.prototype.getName=function(){return this.name};mxEventObject.prototype.getProperties=function(){return this.properties};mxEventObject.prototype.getProperty=function(a){return this.properties[a]};mxEventObject.prototype.isConsumed=function(){return this.consumed};mxEventObject.prototype.consume=function(){this.consumed=!0};function mxMouseEvent(a,b){this.evt=a;this.sourceState=this.state=b}mxMouseEvent.prototype.consumed=!1;mxMouseEvent.prototype.evt=null;
+mxMouseEvent.prototype.graphX=null;mxMouseEvent.prototype.graphY=null;mxMouseEvent.prototype.state=null;mxMouseEvent.prototype.sourceState=null;mxMouseEvent.prototype.getEvent=function(){return this.evt};mxMouseEvent.prototype.getSource=function(){return mxEvent.getSource(this.evt)};mxMouseEvent.prototype.isSource=function(a){return null!=a?mxUtils.isAncestorNode(a.node,this.getSource()):!1};mxMouseEvent.prototype.getX=function(){return mxEvent.getClientX(this.getEvent())};
+mxMouseEvent.prototype.getY=function(){return mxEvent.getClientY(this.getEvent())};mxMouseEvent.prototype.getGraphX=function(){return this.graphX};mxMouseEvent.prototype.getGraphY=function(){return this.graphY};mxMouseEvent.prototype.getState=function(){return this.state};mxMouseEvent.prototype.getCell=function(){var a=this.getState();return null!=a?a.cell:null};mxMouseEvent.prototype.isPopupTrigger=function(){return mxEvent.isPopupTrigger(this.getEvent())};mxMouseEvent.prototype.isConsumed=function(){return this.consumed};
+mxMouseEvent.prototype.consume=function(a){(null!=a?a:1)&&this.evt.preventDefault&&this.evt.preventDefault();mxClient.IS_IE&&(this.evt.returnValue=!0);this.consumed=!0};function mxEventSource(a){this.setEventSource(a)}mxEventSource.prototype.eventListeners=null;mxEventSource.prototype.eventsEnabled=!0;mxEventSource.prototype.eventSource=null;mxEventSource.prototype.isEventsEnabled=function(){return this.eventsEnabled};mxEventSource.prototype.setEventsEnabled=function(a){this.eventsEnabled=a};
+mxEventSource.prototype.getEventSource=function(){return this.eventSource};mxEventSource.prototype.setEventSource=function(a){this.eventSource=a};mxEventSource.prototype.addListener=function(a,b){null==this.eventListeners&&(this.eventListeners=[]);this.eventListeners.push(a);this.eventListeners.push(b)};mxEventSource.prototype.removeListener=function(a){if(null!=this.eventListeners)for(var b=0;b<this.eventListeners.length;)this.eventListeners[b+1]==a?this.eventListeners.splice(b,2):b+=2};
+mxEventSource.prototype.fireEvent=function(a,b){if(null!=this.eventListeners&&this.isEventsEnabled()){null==a&&(a=new mxEventObject);null==b&&(b=this.getEventSource());null==b&&(b=this);for(var c=[b,a],d=0;d<this.eventListeners.length;d+=2){var e=this.eventListeners[d];null!=e&&e!=a.getName()||this.eventListeners[d+1].apply(this,c)}}};
+var mxEvent={objects:[],addListener:function(){var a=function(a,c,d){null==a.mxListenerList&&(a.mxListenerList=[],mxEvent.objects.push(a));a.mxListenerList.push({name:c,f:d})};return window.addEventListener?function(b,c,d){b.addEventListener(c,d,!1);a(b,c,d)}:function(b,c,d){b.attachEvent("on"+c,d);a(b,c,d)}}(),removeListener:function(){var a=function(a,c,d){if(null!=a.mxListenerList){c=a.mxListenerList.length;for(var b=0;b<c;b++)if(a.mxListenerList[b].f==d){a.mxListenerList.splice(b,1);break}0==
+a.mxListenerList.length&&(a.mxListenerList=null,a=mxUtils.indexOf(mxEvent.objects,a),0<=a&&mxEvent.objects.splice(a,1))}};return window.removeEventListener?function(b,c,d){b.removeEventListener(c,d,!1);a(b,c,d)}:function(b,c,d){b.detachEvent("on"+c,d);a(b,c,d)}}(),removeAllListeners:function(a){var b=a.mxListenerList;if(null!=b)for(;0<b.length;){var c=b[0];mxEvent.removeListener(a,c.name,c.f)}},addGestureListeners:function(a,b,c,d){null!=b&&mxEvent.addListener(a,mxClient.IS_POINTER?"pointerdown":
+"mousedown",b);null!=c&&mxEvent.addListener(a,mxClient.IS_POINTER?"pointermove":"mousemove",c);null!=d&&mxEvent.addListener(a,mxClient.IS_POINTER?"pointerup":"mouseup",d);!mxClient.IS_POINTER&&mxClient.IS_TOUCH&&(null!=b&&mxEvent.addListener(a,"touchstart",b),null!=c&&mxEvent.addListener(a,"touchmove",c),null!=d&&mxEvent.addListener(a,"touchend",d))},removeGestureListeners:function(a,b,c,d){null!=b&&mxEvent.removeListener(a,mxClient.IS_POINTER?"pointerdown":"mousedown",b);null!=c&&mxEvent.removeListener(a,
+mxClient.IS_POINTER?"pointermove":"mousemove",c);null!=d&&mxEvent.removeListener(a,mxClient.IS_POINTER?"pointerup":"mouseup",d);!mxClient.IS_POINTER&&mxClient.IS_TOUCH&&(null!=b&&mxEvent.removeListener(a,"touchstart",b),null!=c&&mxEvent.removeListener(a,"touchmove",c),null!=d&&mxEvent.removeListener(a,"touchend",d))},redirectMouseEvents:function(a,b,c,d,e,f,g){var k=function(a){return"function"==typeof c?c(a):c};mxEvent.addGestureListeners(a,function(a){null!=d?d(a):mxEvent.isConsumed(a)||b.fireMouseEvent(mxEvent.MOUSE_DOWN,
+new mxMouseEvent(a,k(a)))},function(a){null!=e?e(a):mxEvent.isConsumed(a)||b.fireMouseEvent(mxEvent.MOUSE_MOVE,new mxMouseEvent(a,k(a)))},function(a){null!=f?f(a):mxEvent.isConsumed(a)||b.fireMouseEvent(mxEvent.MOUSE_UP,new mxMouseEvent(a,k(a)))});mxEvent.addListener(a,"dblclick",function(a){if(null!=g)g(a);else if(!mxEvent.isConsumed(a)){var c=k(a);b.dblClick(a,null!=c?c.cell:null)}})},release:function(a){if(null!=a&&(mxEvent.removeAllListeners(a),a=a.childNodes,null!=a))for(var b=a.length,c=0;c<
+b;c+=1)mxEvent.release(a[c])},addMouseWheelListener:function(a){if(null!=a){var b=function(b){null==b&&(b=window.event);var c;c=mxClient.IS_FF?-b.detail/2:b.wheelDelta/120;0!=c&&a(b,0<c)};mxClient.IS_NS&&null==document.documentMode?mxEvent.addListener(window,mxClient.IS_SF||mxClient.IS_GC?"mousewheel":"DOMMouseScroll",b):mxEvent.addListener(document,"mousewheel",b)}},disableContextMenu:function(a){mxEvent.addListener(a,"contextmenu",function(a){a.preventDefault&&a.preventDefault();return!1})},getSource:function(a){return null!=
+a.srcElement?a.srcElement:a.target},isConsumed:function(a){return null!=a.isConsumed&&a.isConsumed},isTouchEvent:function(a){return null!=a.pointerType?"touch"==a.pointerType||a.pointerType===a.MSPOINTER_TYPE_TOUCH:null!=a.mozInputSource?5==a.mozInputSource:0==a.type.indexOf("touch")},isPenEvent:function(a){return null!=a.pointerType?"pen"==a.pointerType||a.pointerType===a.MSPOINTER_TYPE_PEN:null!=a.mozInputSource?2==a.mozInputSource:0==a.type.indexOf("pen")},isMultiTouchEvent:function(a){return null!=
+a.type&&0==a.type.indexOf("touch")&&null!=a.touches&&1<a.touches.length},isMouseEvent:function(a){return null!=a.pointerType?"mouse"==a.pointerType||a.pointerType===a.MSPOINTER_TYPE_MOUSE:null!=a.mozInputSource?1==a.mozInputSource:0==a.type.indexOf("mouse")},isLeftMouseButton:function(a){return"buttons"in a&&("mousedown"==a.type||"mousemove"==a.type)?1==a.buttons:"which"in a?1===a.which:1===a.button},isMiddleMouseButton:function(a){return"which"in a?2===a.which:4===a.button},isRightMouseButton:function(a){return"which"in
+a?3===a.which:2===a.button},isPopupTrigger:function(a){return mxEvent.isRightMouseButton(a)||mxClient.IS_MAC&&mxEvent.isControlDown(a)&&!mxEvent.isShiftDown(a)&&!mxEvent.isMetaDown(a)&&!mxEvent.isAltDown(a)},isShiftDown:function(a){return null!=a?a.shiftKey:!1},isAltDown:function(a){return null!=a?a.altKey:!1},isControlDown:function(a){return null!=a?a.ctrlKey:!1},isMetaDown:function(a){return null!=a?a.metaKey:!1},getMainEvent:function(a){"touchstart"!=a.type&&"touchmove"!=a.type||null==a.touches||
+null==a.touches[0]?"touchend"==a.type&&null!=a.changedTouches&&null!=a.changedTouches[0]&&(a=a.changedTouches[0]):a=a.touches[0];return a},getClientX:function(a){return mxEvent.getMainEvent(a).clientX},getClientY:function(a){return mxEvent.getMainEvent(a).clientY},consume:function(a,b,c){c=null!=c?c:!0;if(null!=b?b:1)a.preventDefault?(c&&a.stopPropagation(),a.preventDefault()):c&&(a.cancelBubble=!0);a.isConsumed=!0;a.preventDefault||(a.returnValue=!1)},LABEL_HANDLE:-1,ROTATION_HANDLE:-2,CUSTOM_HANDLE:-100,
+VIRTUAL_HANDLE:-1E5,MOUSE_DOWN:"mouseDown",MOUSE_MOVE:"mouseMove",MOUSE_UP:"mouseUp",ACTIVATE:"activate",RESIZE_START:"resizeStart",RESIZE:"resize",RESIZE_END:"resizeEnd",MOVE_START:"moveStart",MOVE:"move",MOVE_END:"moveEnd",PAN_START:"panStart",PAN:"pan",PAN_END:"panEnd",MINIMIZE:"minimize",NORMALIZE:"normalize",MAXIMIZE:"maximize",HIDE:"hide",SHOW:"show",CLOSE:"close",DESTROY:"destroy",REFRESH:"refresh",SIZE:"size",SELECT:"select",FIRED:"fired",FIRE_MOUSE_EVENT:"fireMouseEvent",GESTURE:"gesture",
+TAP_AND_HOLD:"tapAndHold",GET:"get",RECEIVE:"receive",CONNECT:"connect",DISCONNECT:"disconnect",SUSPEND:"suspend",RESUME:"resume",MARK:"mark",ROOT:"root",POST:"post",OPEN:"open",SAVE:"save",BEFORE_ADD_VERTEX:"beforeAddVertex",ADD_VERTEX:"addVertex",AFTER_ADD_VERTEX:"afterAddVertex",DONE:"done",EXECUTE:"execute",EXECUTED:"executed",BEGIN_UPDATE:"beginUpdate",START_EDIT:"startEdit",END_UPDATE:"endUpdate",END_EDIT:"endEdit",BEFORE_UNDO:"beforeUndo",UNDO:"undo",REDO:"redo",CHANGE:"change",NOTIFY:"notify",
+LAYOUT_CELLS:"layoutCells",CLICK:"click",SCALE:"scale",TRANSLATE:"translate",SCALE_AND_TRANSLATE:"scaleAndTranslate",UP:"up",DOWN:"down",ADD:"add",REMOVE:"remove",CLEAR:"clear",ADD_CELLS:"addCells",CELLS_ADDED:"cellsAdded",MOVE_CELLS:"moveCells",CELLS_MOVED:"cellsMoved",RESIZE_CELLS:"resizeCells",CELLS_RESIZED:"cellsResized",TOGGLE_CELLS:"toggleCells",CELLS_TOGGLED:"cellsToggled",ORDER_CELLS:"orderCells",CELLS_ORDERED:"cellsOrdered",REMOVE_CELLS:"removeCells",CELLS_REMOVED:"cellsRemoved",GROUP_CELLS:"groupCells",
+UNGROUP_CELLS:"ungroupCells",REMOVE_CELLS_FROM_PARENT:"removeCellsFromParent",FOLD_CELLS:"foldCells",CELLS_FOLDED:"cellsFolded",ALIGN_CELLS:"alignCells",LABEL_CHANGED:"labelChanged",CONNECT_CELL:"connectCell",CELL_CONNECTED:"cellConnected",SPLIT_EDGE:"splitEdge",FLIP_EDGE:"flipEdge",START_EDITING:"startEditing",EDITING_STARTED:"editingStarted",EDITING_STOPPED:"editingStopped",ADD_OVERLAY:"addOverlay",REMOVE_OVERLAY:"removeOverlay",UPDATE_CELL_SIZE:"updateCellSize",ESCAPE:"escape",DOUBLE_CLICK:"doubleClick",
+START:"start",RESET:"reset"};function mxXmlRequest(a,b,c,d,e,f){this.url=a;this.params=b;this.method=c||"POST";this.async=null!=d?d:!0;this.username=e;this.password=f}mxXmlRequest.prototype.url=null;mxXmlRequest.prototype.params=null;mxXmlRequest.prototype.method=null;mxXmlRequest.prototype.async=null;mxXmlRequest.prototype.binary=!1;mxXmlRequest.prototype.withCredentials=!1;mxXmlRequest.prototype.username=null;mxXmlRequest.prototype.password=null;mxXmlRequest.prototype.request=null;
+mxXmlRequest.prototype.decodeSimulateValues=!1;mxXmlRequest.prototype.isBinary=function(){return this.binary};mxXmlRequest.prototype.setBinary=function(a){this.binary=a};mxXmlRequest.prototype.getText=function(){return this.request.responseText};mxXmlRequest.prototype.isReady=function(){return 4==this.request.readyState};mxXmlRequest.prototype.getDocumentElement=function(){var a=this.getXml();return null!=a?a.documentElement:null};
+mxXmlRequest.prototype.getXml=function(){var a=this.request.responseXML;if(9<=document.documentMode||null==a||null==a.documentElement)a=mxUtils.parseXml(this.request.responseText);return a};mxXmlRequest.prototype.getText=function(){return this.request.responseText};mxXmlRequest.prototype.getStatus=function(){return this.request.status};
+mxXmlRequest.prototype.create=function(){if(window.XMLHttpRequest)return function(){var a=new XMLHttpRequest;this.isBinary()&&a.overrideMimeType&&a.overrideMimeType("text/plain; charset=x-user-defined");return a};if("undefined"!=typeof ActiveXObject)return function(){return new ActiveXObject("Microsoft.XMLHTTP")}}();
+mxXmlRequest.prototype.send=function(a,b,c,d){this.request=this.create();null!=this.request&&(null!=a&&(this.request.onreadystatechange=mxUtils.bind(this,function(){this.isReady()&&(a(this),this.onreadystatechaange=null)})),this.request.open(this.method,this.url,this.async,this.username,this.password),this.setRequestHeaders(this.request,this.params),window.XMLHttpRequest&&this.withCredentials&&(this.request.withCredentials="true"),!mxClient.IS_QUIRKS&&(null==document.documentMode||9<document.documentMode)&&
+window.XMLHttpRequest&&null!=c&&null!=d&&(this.request.timeout=c,this.request.ontimeout=d),this.request.send(this.params))};mxXmlRequest.prototype.setRequestHeaders=function(a,b){null!=b&&a.setRequestHeader("Content-Type","application/x-www-form-urlencoded")};
+mxXmlRequest.prototype.simulate=function(a,b){a=a||document;var c=null;a==document&&(c=window.onbeforeunload,window.onbeforeunload=null);var d=a.createElement("form");d.setAttribute("method",this.method);d.setAttribute("action",this.url);null!=b&&d.setAttribute("target",b);d.style.display="none";d.style.visibility="hidden";for(var e=0<this.params.indexOf("&")?this.params.split("&"):this.params.split(),f=0;f<e.length;f++){var g=e[f].indexOf("=");if(0<g){var k=e[f].substring(0,g),g=e[f].substring(g+
+1);this.decodeSimulateValues&&(g=decodeURIComponent(g));var l=a.createElement("textarea");l.setAttribute("wrap","off");l.setAttribute("name",k);mxUtils.write(l,g);d.appendChild(l)}}a.body.appendChild(d);d.submit();null!=d.parentNode&&d.parentNode.removeChild(d);null!=c&&(window.onbeforeunload=c)};
+var mxClipboard={STEPSIZE:10,insertCount:1,cells:null,setCells:function(a){mxClipboard.cells=a},getCells:function(){return mxClipboard.cells},isEmpty:function(){return null==mxClipboard.getCells()},cut:function(a,b){b=mxClipboard.copy(a,b);mxClipboard.insertCount=0;mxClipboard.removeCells(a,b);return b},removeCells:function(a,b){a.removeCells(b)},copy:function(a,b){b=b||a.getSelectionCells();var c=a.getExportableCells(a.model.getTopmostCells(b));mxClipboard.insertCount=1;mxClipboard.setCells(a.cloneCells(c));
+return c},paste:function(a){var b=null;if(!mxClipboard.isEmpty()){var b=a.getImportableCells(mxClipboard.getCells()),c=mxClipboard.insertCount*mxClipboard.STEPSIZE,d=a.getDefaultParent(),b=a.importCells(b,c,c,d);mxClipboard.insertCount++;a.setSelectionCells(b)}return b}};
+function mxWindow(a,b,c,d,e,f,g,k,l,m){null!=b&&(g=null!=g?g:!0,this.content=b,this.init(c,d,e,f,m),this.installMaximizeHandler(),this.installMinimizeHandler(),this.installCloseHandler(),this.setMinimizable(g),this.setTitle(a),(null==k||k)&&this.installMoveHandler(),null!=l&&null!=l.parentNode?l.parentNode.replaceChild(this.div,l):document.body.appendChild(this.div))}mxWindow.prototype=new mxEventSource;mxWindow.prototype.constructor=mxWindow;mxWindow.prototype.closeImage=mxClient.imageBasePath+"/close.gif";
+mxWindow.prototype.minimizeImage=mxClient.imageBasePath+"/minimize.gif";mxWindow.prototype.normalizeImage=mxClient.imageBasePath+"/normalize.gif";mxWindow.prototype.maximizeImage=mxClient.imageBasePath+"/maximize.gif";mxWindow.prototype.resizeImage=mxClient.imageBasePath+"/resize.gif";mxWindow.prototype.visible=!1;mxWindow.prototype.minimumSize=new mxRectangle(0,0,50,40);mxWindow.prototype.destroyOnClose=!0;
+mxWindow.prototype.contentHeightCorrection=8==document.documentMode||7==document.documentMode?6:2;mxWindow.prototype.title=null;mxWindow.prototype.content=null;
+mxWindow.prototype.init=function(a,b,c,d,e){e=null!=e?e:"mxWindow";this.div=document.createElement("div");this.div.className=e;this.div.style.left=a+"px";this.div.style.top=b+"px";this.table=document.createElement("table");this.table.className=e;mxClient.IS_POINTER&&(this.div.style.touchAction="none");null!=c&&(mxClient.IS_QUIRKS||(this.div.style.width=c+"px"),this.table.style.width=c+"px");null!=d&&(mxClient.IS_QUIRKS||(this.div.style.height=d+"px"),this.table.style.height=d+"px");a=document.createElement("tbody");
+b=document.createElement("tr");this.title=document.createElement("td");this.title.className=e+"Title";this.buttons=document.createElement("div");this.buttons.style.position="absolute";this.buttons.style.display="inline-block";this.buttons.style.right="4px";this.buttons.style.top="5px";this.title.appendChild(this.buttons);b.appendChild(this.title);a.appendChild(b);b=document.createElement("tr");this.td=document.createElement("td");this.td.className=e+"Pane";7==document.documentMode&&(this.td.style.height=
+"100%");this.contentWrapper=document.createElement("div");this.contentWrapper.className=e+"Pane";this.contentWrapper.style.width="100%";this.contentWrapper.appendChild(this.content);if(mxClient.IS_QUIRKS||"DIV"!=this.content.nodeName.toUpperCase())this.contentWrapper.style.height="100%";this.td.appendChild(this.contentWrapper);b.appendChild(this.td);a.appendChild(b);this.table.appendChild(a);this.div.appendChild(this.table);e=mxUtils.bind(this,function(a){this.activate()});mxEvent.addGestureListeners(this.title,
+e);mxEvent.addGestureListeners(this.table,e);this.hide()};mxWindow.prototype.setTitle=function(a){for(var b=this.title.firstChild;null!=b;){var c=b.nextSibling;b.nodeType==mxConstants.NODETYPE_TEXT&&b.parentNode.removeChild(b);b=c}mxUtils.write(this.title,a||"");this.title.appendChild(this.buttons)};mxWindow.prototype.setScrollable=function(a){0>navigator.userAgent.indexOf("Presto/2.5")&&(this.contentWrapper.style.overflow=a?"auto":"hidden")};
+mxWindow.prototype.activate=function(){if(mxWindow.activeWindow!=this){var a=mxUtils.getCurrentStyle(this.getElement()),a=null!=a?a.zIndex:3;if(mxWindow.activeWindow){var b=mxWindow.activeWindow.getElement();null!=b&&null!=b.style&&(b.style.zIndex=a)}b=mxWindow.activeWindow;this.getElement().style.zIndex=parseInt(a)+1;mxWindow.activeWindow=this;this.fireEvent(new mxEventObject(mxEvent.ACTIVATE,"previousWindow",b))}};mxWindow.prototype.getElement=function(){return this.div};
+mxWindow.prototype.fit=function(){mxUtils.fit(this.div)};mxWindow.prototype.isResizable=function(){return null!=this.resize?"none"!=this.resize.style.display:!1};
+mxWindow.prototype.setResizable=function(a){if(a)if(null==this.resize){this.resize=document.createElement("img");this.resize.style.position="absolute";this.resize.style.bottom="2px";this.resize.style.right="2px";this.resize.setAttribute("src",this.resizeImage);this.resize.style.cursor="nw-resize";var b=null,c=null,d=null,e=null;a=mxUtils.bind(this,function(a){this.activate();b=mxEvent.getClientX(a);c=mxEvent.getClientY(a);d=this.div.offsetWidth;e=this.div.offsetHeight;mxEvent.addGestureListeners(document,
+null,f,g);this.fireEvent(new mxEventObject(mxEvent.RESIZE_START,"event",a));mxEvent.consume(a)});var f=mxUtils.bind(this,function(a){if(null!=b&&null!=c){var f=mxEvent.getClientX(a)-b,g=mxEvent.getClientY(a)-c;this.setSize(d+f,e+g);this.fireEvent(new mxEventObject(mxEvent.RESIZE,"event",a));mxEvent.consume(a)}}),g=mxUtils.bind(this,function(a){null!=b&&null!=c&&(c=b=null,mxEvent.removeGestureListeners(document,null,f,g),this.fireEvent(new mxEventObject(mxEvent.RESIZE_END,"event",a)),mxEvent.consume(a))});
+mxEvent.addGestureListeners(this.resize,a,f,g);this.div.appendChild(this.resize)}else this.resize.style.display="inline";else null!=this.resize&&(this.resize.style.display="none")};
+mxWindow.prototype.setSize=function(a,b){a=Math.max(this.minimumSize.width,a);b=Math.max(this.minimumSize.height,b);mxClient.IS_QUIRKS||(this.div.style.width=a+"px",this.div.style.height=b+"px");this.table.style.width=a+"px";this.table.style.height=b+"px";mxClient.IS_QUIRKS||(this.contentWrapper.style.height=this.div.offsetHeight-this.title.offsetHeight-this.contentHeightCorrection+"px")};mxWindow.prototype.setMinimizable=function(a){this.minimize.style.display=a?"":"none"};
+mxWindow.prototype.getMinimumSize=function(){return new mxRectangle(0,0,0,this.title.offsetHeight)};
+mxWindow.prototype.installMinimizeHandler=function(){this.minimize=document.createElement("img");this.minimize.setAttribute("src",this.minimizeImage);this.minimize.setAttribute("title","Minimize");this.minimize.style.cursor="pointer";this.minimize.style.marginLeft="2px";this.minimize.style.display="none";this.buttons.appendChild(this.minimize);var a=!1,b=null,c=null,d=mxUtils.bind(this,function(d){this.activate();if(a)a=!1,this.minimize.setAttribute("src",this.minimizeImage),this.minimize.setAttribute("title",
+"Minimize"),this.contentWrapper.style.display="",this.maximize.style.display=b,mxClient.IS_QUIRKS||(this.div.style.height=c),this.table.style.height=c,null!=this.resize&&(this.resize.style.visibility=""),this.fireEvent(new mxEventObject(mxEvent.NORMALIZE,"event",d));else{a=!0;this.minimize.setAttribute("src",this.normalizeImage);this.minimize.setAttribute("title","Normalize");this.contentWrapper.style.display="none";b=this.maximize.style.display;this.maximize.style.display="none";c=this.table.style.height;
+var e=this.getMinimumSize();0<e.height&&(mxClient.IS_QUIRKS||(this.div.style.height=e.height+"px"),this.table.style.height=e.height+"px");0<e.width&&(mxClient.IS_QUIRKS||(this.div.style.width=e.width+"px"),this.table.style.width=e.width+"px");null!=this.resize&&(this.resize.style.visibility="hidden");this.fireEvent(new mxEventObject(mxEvent.MINIMIZE,"event",d))}mxEvent.consume(d)});mxEvent.addGestureListeners(this.minimize,d)};
+mxWindow.prototype.setMaximizable=function(a){this.maximize.style.display=a?"":"none"};
+mxWindow.prototype.installMaximizeHandler=function(){this.maximize=document.createElement("img");this.maximize.setAttribute("src",this.maximizeImage);this.maximize.setAttribute("title","Maximize");this.maximize.style.cursor="default";this.maximize.style.marginLeft="2px";this.maximize.style.cursor="pointer";this.maximize.style.display="none";this.buttons.appendChild(this.maximize);var a=!1,b=null,c=null,d=null,e=null,f=null,g=mxUtils.bind(this,function(g){this.activate();if("none"!=this.maximize.style.display){if(a)a=
+!1,this.maximize.setAttribute("src",this.maximizeImage),this.maximize.setAttribute("title","Maximize"),this.contentWrapper.style.display="",this.minimize.style.display=f,this.div.style.left=b+"px",this.div.style.top=c+"px",mxClient.IS_QUIRKS||(this.div.style.height=d,this.div.style.width=e,k=mxUtils.getCurrentStyle(this.contentWrapper),"auto"!=k.overflow&&null==this.resize)||(this.contentWrapper.style.height=this.div.offsetHeight-this.title.offsetHeight-this.contentHeightCorrection+"px"),this.table.style.height=
+d,this.table.style.width=e,null!=this.resize&&(this.resize.style.visibility=""),this.fireEvent(new mxEventObject(mxEvent.NORMALIZE,"event",g));else{a=!0;this.maximize.setAttribute("src",this.normalizeImage);this.maximize.setAttribute("title","Normalize");this.contentWrapper.style.display="";f=this.minimize.style.display;this.minimize.style.display="none";b=parseInt(this.div.style.left);c=parseInt(this.div.style.top);d=this.table.style.height;e=this.table.style.width;this.div.style.left="0px";this.div.style.top=
+"0px";k=Math.max(document.body.clientHeight||0,document.documentElement.clientHeight||0);mxClient.IS_QUIRKS||(this.div.style.width=document.body.clientWidth-2+"px",this.div.style.height=k-2+"px");this.table.style.width=document.body.clientWidth-2+"px";this.table.style.height=k-2+"px";null!=this.resize&&(this.resize.style.visibility="hidden");if(!mxClient.IS_QUIRKS){var k=mxUtils.getCurrentStyle(this.contentWrapper);if("auto"==k.overflow||null!=this.resize)this.contentWrapper.style.height=this.div.offsetHeight-
+this.title.offsetHeight-this.contentHeightCorrection+"px"}this.fireEvent(new mxEventObject(mxEvent.MAXIMIZE,"event",g))}mxEvent.consume(g)}});mxEvent.addGestureListeners(this.maximize,g);mxEvent.addListener(this.title,"dblclick",g)};
+mxWindow.prototype.installMoveHandler=function(){this.title.style.cursor="move";mxEvent.addGestureListeners(this.title,mxUtils.bind(this,function(a){var b=mxEvent.getClientX(a),c=mxEvent.getClientY(a),d=this.getX(),e=this.getY(),f=mxUtils.bind(this,function(a){var f=mxEvent.getClientX(a)-b,g=mxEvent.getClientY(a)-c;this.setLocation(d+f,e+g);this.fireEvent(new mxEventObject(mxEvent.MOVE,"event",a));mxEvent.consume(a)}),g=mxUtils.bind(this,function(a){mxEvent.removeGestureListeners(document,null,f,
+g);this.fireEvent(new mxEventObject(mxEvent.MOVE_END,"event",a));mxEvent.consume(a)});mxEvent.addGestureListeners(document,null,f,g);this.fireEvent(new mxEventObject(mxEvent.MOVE_START,"event",a));mxEvent.consume(a)}));mxClient.IS_POINTER&&(this.title.style.touchAction="none")};mxWindow.prototype.setLocation=function(a,b){this.div.style.left=a+"px";this.div.style.top=b+"px"};mxWindow.prototype.getX=function(){return parseInt(this.div.style.left)};mxWindow.prototype.getY=function(){return parseInt(this.div.style.top)};
+mxWindow.prototype.installCloseHandler=function(){this.closeImg=document.createElement("img");this.closeImg.setAttribute("src",this.closeImage);this.closeImg.setAttribute("title","Close");this.closeImg.style.marginLeft="2px";this.closeImg.style.cursor="pointer";this.closeImg.style.display="none";this.buttons.appendChild(this.closeImg);mxEvent.addGestureListeners(this.closeImg,mxUtils.bind(this,function(a){this.fireEvent(new mxEventObject(mxEvent.CLOSE,"event",a));this.destroyOnClose?this.destroy():
+this.setVisible(!1);mxEvent.consume(a)}))};mxWindow.prototype.setImage=function(a){this.image=document.createElement("img");this.image.setAttribute("src",a);this.image.setAttribute("align","left");this.image.style.marginRight="4px";this.image.style.marginLeft="0px";this.image.style.marginTop="-2px";this.title.insertBefore(this.image,this.title.firstChild)};mxWindow.prototype.setClosable=function(a){this.closeImg.style.display=a?"":"none"};
+mxWindow.prototype.isVisible=function(){return null!=this.div?"none"!=this.div.style.display:!1};mxWindow.prototype.setVisible=function(a){null!=this.div&&this.isVisible()!=a&&(a?this.show():this.hide())};
+mxWindow.prototype.show=function(){this.div.style.display="";this.activate();var a=mxUtils.getCurrentStyle(this.contentWrapper);mxClient.IS_QUIRKS||"auto"!=a.overflow&&null==this.resize||"none"==this.contentWrapper.style.display||(this.contentWrapper.style.height=this.div.offsetHeight-this.title.offsetHeight-this.contentHeightCorrection+"px");this.fireEvent(new mxEventObject(mxEvent.SHOW))};mxWindow.prototype.hide=function(){this.div.style.display="none";this.fireEvent(new mxEventObject(mxEvent.HIDE))};
+mxWindow.prototype.destroy=function(){this.fireEvent(new mxEventObject(mxEvent.DESTROY));null!=this.div&&(mxEvent.release(this.div),this.div.parentNode.removeChild(this.div),this.div=null);this.contentWrapper=this.content=this.title=null};function mxForm(a){this.table=document.createElement("table");this.table.className=a;this.body=document.createElement("tbody");this.table.appendChild(this.body)}mxForm.prototype.table=null;mxForm.prototype.body=!1;mxForm.prototype.getTable=function(){return this.table};
+mxForm.prototype.addButtons=function(a,b){var c=document.createElement("tr"),d=document.createElement("td");c.appendChild(d);var d=document.createElement("td"),e=document.createElement("button");mxUtils.write(e,mxResources.get("ok")||"OK");d.appendChild(e);mxEvent.addListener(e,"click",function(){a()});e=document.createElement("button");mxUtils.write(e,mxResources.get("cancel")||"Cancel");d.appendChild(e);mxEvent.addListener(e,"click",function(){b()});c.appendChild(d);this.body.appendChild(c)};
+mxForm.prototype.addText=function(a,b,c){var d=document.createElement("input");d.setAttribute("type",c||"text");d.value=b;return this.addField(a,d)};mxForm.prototype.addCheckbox=function(a,b){var c=document.createElement("input");c.setAttribute("type","checkbox");this.addField(a,c);b&&(c.checked=!0);return c};mxForm.prototype.addTextarea=function(a,b,c){var d=document.createElement("textarea");mxClient.IS_NS&&c--;d.setAttribute("rows",c||2);d.value=b;return this.addField(a,d)};
+mxForm.prototype.addCombo=function(a,b,c){var d=document.createElement("select");null!=c&&d.setAttribute("size",c);b&&d.setAttribute("multiple","true");return this.addField(a,d)};mxForm.prototype.addOption=function(a,b,c,d){var e=document.createElement("option");mxUtils.writeln(e,b);e.setAttribute("value",c);d&&e.setAttribute("selected",d);a.appendChild(e)};
+mxForm.prototype.addField=function(a,b){var c=document.createElement("tr"),d=document.createElement("td");mxUtils.write(d,a);c.appendChild(d);d=document.createElement("td");d.appendChild(b);c.appendChild(d);this.body.appendChild(c);return b};function mxImage(a,b,c){this.src=a;this.width=b;this.height=c}mxImage.prototype.src=null;mxImage.prototype.width=null;mxImage.prototype.height=null;
+function mxDivResizer(a,b){if("div"==a.nodeName.toLowerCase()){null==b&&(b=window);this.div=a;var c=mxUtils.getCurrentStyle(a);null!=c&&(this.resizeWidth="auto"==c.width,this.resizeHeight="auto"==c.height);mxEvent.addListener(b,"resize",mxUtils.bind(this,function(a){this.handlingResize||(this.handlingResize=!0,this.resize(),this.handlingResize=!1)}));this.resize()}}mxDivResizer.prototype.resizeWidth=!0;mxDivResizer.prototype.resizeHeight=!0;mxDivResizer.prototype.handlingResize=!1;
+mxDivResizer.prototype.resize=function(){var a=this.getDocumentWidth(),b=this.getDocumentHeight(),c=parseInt(this.div.style.left),d=parseInt(this.div.style.right),e=parseInt(this.div.style.top),f=parseInt(this.div.style.bottom);this.resizeWidth&&!isNaN(c)&&!isNaN(d)&&0<=c&&0<=d&&0<a-d-c&&(this.div.style.width=a-d-c+"px");this.resizeHeight&&!isNaN(e)&&!isNaN(f)&&0<=e&&0<=f&&0<b-e-f&&(this.div.style.height=b-e-f+"px")};mxDivResizer.prototype.getDocumentWidth=function(){return document.body.clientWidth};
+mxDivResizer.prototype.getDocumentHeight=function(){return document.body.clientHeight};function mxDragSource(a,b){this.element=a;this.dropHandler=b;mxEvent.addGestureListeners(a,mxUtils.bind(this,function(a){this.mouseDown(a)}));mxEvent.addListener(a,"dragstart",function(a){mxEvent.consume(a)});this.eventConsumer=function(a,b){var c=b.getProperty("eventName"),d=b.getProperty("event");c!=mxEvent.MOUSE_DOWN&&d.consume()}}mxDragSource.prototype.element=null;mxDragSource.prototype.dropHandler=null;
+mxDragSource.prototype.dragOffset=null;mxDragSource.prototype.dragElement=null;mxDragSource.prototype.previewElement=null;mxDragSource.prototype.enabled=!0;mxDragSource.prototype.currentGraph=null;mxDragSource.prototype.currentDropTarget=null;mxDragSource.prototype.currentPoint=null;mxDragSource.prototype.currentGuide=null;mxDragSource.prototype.currentHighlight=null;mxDragSource.prototype.autoscroll=!0;mxDragSource.prototype.guidesEnabled=!0;mxDragSource.prototype.gridEnabled=!0;
+mxDragSource.prototype.highlightDropTargets=!0;mxDragSource.prototype.dragElementZIndex=100;mxDragSource.prototype.dragElementOpacity=70;mxDragSource.prototype.checkEventSource=!0;mxDragSource.prototype.isEnabled=function(){return this.enabled};mxDragSource.prototype.setEnabled=function(a){this.enabled=a};mxDragSource.prototype.isGuidesEnabled=function(){return this.guidesEnabled};mxDragSource.prototype.setGuidesEnabled=function(a){this.guidesEnabled=a};mxDragSource.prototype.isGridEnabled=function(){return this.gridEnabled};
+mxDragSource.prototype.setGridEnabled=function(a){this.gridEnabled=a};mxDragSource.prototype.getGraphForEvent=function(a){return null};mxDragSource.prototype.getDropTarget=function(a,b,c,d){return a.getCellAt(b,c)};mxDragSource.prototype.createDragElement=function(a){return this.element.cloneNode(!0)};mxDragSource.prototype.createPreviewElement=function(a){return null};mxDragSource.prototype.isActive=function(){return null!=this.mouseMoveHandler};
+mxDragSource.prototype.reset=function(){null!=this.currentGraph&&(this.dragExit(this.currentGraph),this.currentGraph=null);this.removeDragElement();this.removeListeners();this.stopDrag()};
+mxDragSource.prototype.mouseDown=function(a){this.enabled&&!mxEvent.isConsumed(a)&&null==this.mouseMoveHandler&&(this.startDrag(a),this.mouseMoveHandler=mxUtils.bind(this,this.mouseMove),this.mouseUpHandler=mxUtils.bind(this,this.mouseUp),mxEvent.addGestureListeners(document,null,this.mouseMoveHandler,this.mouseUpHandler),mxClient.IS_TOUCH&&!mxEvent.isMouseEvent(a)&&(this.eventSource=mxEvent.getSource(a),mxEvent.addGestureListeners(this.eventSource,null,this.mouseMoveHandler,this.mouseUpHandler)))};
+mxDragSource.prototype.startDrag=function(a){this.dragElement=this.createDragElement(a);this.dragElement.style.position="absolute";this.dragElement.style.zIndex=this.dragElementZIndex;mxUtils.setOpacity(this.dragElement,this.dragElementOpacity);this.checkEventSource&&mxClient.IS_SVG&&(this.dragElement.style.pointerEvents="none")};mxDragSource.prototype.stopDrag=function(){this.removeDragElement()};
+mxDragSource.prototype.removeDragElement=function(){null!=this.dragElement&&(null!=this.dragElement.parentNode&&this.dragElement.parentNode.removeChild(this.dragElement),this.dragElement=null)};mxDragSource.prototype.getElementForEvent=function(a){return mxEvent.isTouchEvent(a)||mxEvent.isPenEvent(a)?document.elementFromPoint(mxEvent.getClientX(a),mxEvent.getClientY(a)):mxEvent.getSource(a)};
+mxDragSource.prototype.graphContainsEvent=function(a,b){var c=mxEvent.getClientX(b),d=mxEvent.getClientY(b),e=mxUtils.getOffset(a.container),f=mxUtils.getScrollOrigin(),g=this.getElementForEvent(b);if(this.checkEventSource)for(;null!=g&&g!=a.container;)g=g.parentNode;return null!=g&&c>=e.x-f.x&&d>=e.y-f.y&&c<=e.x-f.x+a.container.offsetWidth&&d<=e.y-f.y+a.container.offsetHeight};
+mxDragSource.prototype.mouseMove=function(a){var b=this.getGraphForEvent(a);null==b||this.graphContainsEvent(b,a)||(b=null);b!=this.currentGraph&&(null!=this.currentGraph&&this.dragExit(this.currentGraph,a),this.currentGraph=b,null!=this.currentGraph&&this.dragEnter(this.currentGraph,a));null!=this.currentGraph&&this.dragOver(this.currentGraph,a);if(null==this.dragElement||null!=this.previewElement&&"visible"==this.previewElement.style.visibility)null!=this.dragElement&&(this.dragElement.style.visibility=
+"hidden");else{var b=mxEvent.getClientX(a),c=mxEvent.getClientY(a);null==this.dragElement.parentNode&&document.body.appendChild(this.dragElement);this.dragElement.style.visibility="visible";null!=this.dragOffset&&(b+=this.dragOffset.x,c+=this.dragOffset.y);var d=mxUtils.getDocumentScrollOrigin(document);this.dragElement.style.left=b+d.x+"px";this.dragElement.style.top=c+d.y+"px"}mxEvent.consume(a)};
+mxDragSource.prototype.mouseUp=function(a){if(null!=this.currentGraph){if(null!=this.currentPoint&&(null==this.previewElement||"hidden"!=this.previewElement.style.visibility)){var b=this.currentGraph.view.scale,c=this.currentGraph.view.translate;this.drop(this.currentGraph,a,this.currentDropTarget,this.currentPoint.x/b-c.x,this.currentPoint.y/b-c.y)}this.dragExit(this.currentGraph);this.currentGraph=null}this.stopDrag();this.removeListeners();mxEvent.consume(a)};
+mxDragSource.prototype.removeListeners=function(){null!=this.eventSource&&(mxEvent.removeGestureListeners(this.eventSource,null,this.mouseMoveHandler,this.mouseUpHandler),this.eventSource=null);mxEvent.removeGestureListeners(document,null,this.mouseMoveHandler,this.mouseUpHandler);this.mouseUpHandler=this.mouseMoveHandler=null};
+mxDragSource.prototype.dragEnter=function(a,b){a.isMouseDown=!0;a.isMouseTrigger=mxEvent.isMouseEvent(b);this.previewElement=this.createPreviewElement(a);null!=this.previewElement&&this.checkEventSource&&mxClient.IS_SVG&&(this.previewElement.style.pointerEvents="none");this.isGuidesEnabled()&&null!=this.previewElement&&(this.currentGuide=new mxGuide(a,a.graphHandler.getGuideStates()));this.highlightDropTargets&&(this.currentHighlight=new mxCellHighlight(a,mxConstants.DROP_TARGET_COLOR));a.addListener(mxEvent.FIRE_MOUSE_EVENT,
+this.eventConsumer)};mxDragSource.prototype.dragExit=function(a,b){this.currentPoint=this.currentDropTarget=null;a.isMouseDown=!1;a.removeListener(this.eventConsumer);null!=this.previewElement&&(null!=this.previewElement.parentNode&&this.previewElement.parentNode.removeChild(this.previewElement),this.previewElement=null);null!=this.currentGuide&&(this.currentGuide.destroy(),this.currentGuide=null);null!=this.currentHighlight&&(this.currentHighlight.destroy(),this.currentHighlight=null)};
+mxDragSource.prototype.dragOver=function(a,b){var c=mxUtils.getOffset(a.container),d=mxUtils.getScrollOrigin(a.container),e=mxEvent.getClientX(b)-c.x+d.x-a.panDx,c=mxEvent.getClientY(b)-c.y+d.y-a.panDy;a.autoScroll&&(null==this.autoscroll||this.autoscroll)&&a.scrollPointToVisible(e,c,a.autoExtend);null!=this.currentHighlight&&a.isDropEnabled()&&(this.currentDropTarget=this.getDropTarget(a,e,c,b),d=a.getView().getState(this.currentDropTarget),this.currentHighlight.highlight(d));if(null!=this.previewElement){null==
+this.previewElement.parentNode&&(a.container.appendChild(this.previewElement),this.previewElement.style.zIndex="3",this.previewElement.style.position="absolute");var d=this.isGridEnabled()&&a.isGridEnabledEvent(b),f=!0;if(null!=this.currentGuide&&this.currentGuide.isEnabledForEvent(b))var f=parseInt(this.previewElement.style.width),g=parseInt(this.previewElement.style.height),f=new mxRectangle(0,0,f,g),c=new mxPoint(e,c),c=this.currentGuide.move(f,c,d),f=!1,e=c.x,c=c.y;else if(d)var d=a.view.scale,
+g=a.view.translate,k=a.gridSize/2,e=(a.snap(e/d-g.x-k)+g.x)*d,c=(a.snap(c/d-g.y-k)+g.y)*d;null!=this.currentGuide&&f&&this.currentGuide.hide();null!=this.previewOffset&&(e+=this.previewOffset.x,c+=this.previewOffset.y);this.previewElement.style.left=Math.round(e)+"px";this.previewElement.style.top=Math.round(c)+"px";this.previewElement.style.visibility="visible"}this.currentPoint=new mxPoint(e,c)};
+mxDragSource.prototype.drop=function(a,b,c,d,e){this.dropHandler.apply(this,arguments);"hidden"!=a.container.style.visibility&&a.container.focus()};function mxToolbar(a){this.container=a}mxToolbar.prototype=new mxEventSource;mxToolbar.prototype.constructor=mxToolbar;mxToolbar.prototype.container=null;mxToolbar.prototype.enabled=!0;mxToolbar.prototype.noReset=!1;mxToolbar.prototype.updateDefaultMode=!0;
+mxToolbar.prototype.addItem=function(a,b,c,d,e,f){var g=document.createElement(null!=b?"img":"button"),k=e||(null!=f?"mxToolbarMode":"mxToolbarItem");g.className=k;g.setAttribute("src",b);null!=a&&(null!=b?g.setAttribute("title",a):mxUtils.write(g,a));this.container.appendChild(g);null!=c&&(mxEvent.addListener(g,"click",c),mxClient.IS_TOUCH&&mxEvent.addListener(g,"touchend",c));a=mxUtils.bind(this,function(a){null!=d?g.setAttribute("src",b):g.style.backgroundColor=""});mxEvent.addGestureListeners(g,
+mxUtils.bind(this,function(a){null!=d?g.setAttribute("src",d):g.style.backgroundColor="gray";if(null!=f){null==this.menu&&(this.menu=new mxPopupMenu,this.menu.init());var b=this.currentImg;this.menu.isMenuShowing()&&this.menu.hideMenu();b!=g&&(this.currentImg=g,this.menu.factoryMethod=f,b=new mxPoint(g.offsetLeft,g.offsetTop+g.offsetHeight),this.menu.popup(b.x,b.y,null,a),this.menu.isMenuShowing()&&(g.className=k+"Selected",this.menu.hideMenu=function(){mxPopupMenu.prototype.hideMenu.apply(this);
+g.className=k;this.currentImg=null}))}}),null,a);mxEvent.addListener(g,"mouseout",a);return g};mxToolbar.prototype.addCombo=function(a){var b=document.createElement("div");b.style.display="inline";b.className="mxToolbarComboContainer";var c=document.createElement("select");c.className=a||"mxToolbarCombo";b.appendChild(c);this.container.appendChild(b);return c};
+mxToolbar.prototype.addActionCombo=function(a,b){var c=document.createElement("select");c.className=b||"mxToolbarCombo";this.addOption(c,a,null);mxEvent.addListener(c,"change",function(a){var b=c.options[c.selectedIndex];c.selectedIndex=0;null!=b.funct&&b.funct(a)});this.container.appendChild(c);return c};mxToolbar.prototype.addOption=function(a,b,c){var d=document.createElement("option");mxUtils.writeln(d,b);"function"==typeof c?d.funct=c:d.setAttribute("value",c);a.appendChild(d);return d};
+mxToolbar.prototype.addSwitchMode=function(a,b,c,d,e){var f=document.createElement("img");f.initialClassName=e||"mxToolbarMode";f.className=f.initialClassName;f.setAttribute("src",b);f.altIcon=d;null!=a&&f.setAttribute("title",a);mxEvent.addListener(f,"click",mxUtils.bind(this,function(a){a=this.selectedMode.altIcon;null!=a?(this.selectedMode.altIcon=this.selectedMode.getAttribute("src"),this.selectedMode.setAttribute("src",a)):this.selectedMode.className=this.selectedMode.initialClassName;this.updateDefaultMode&&
+(this.defaultMode=f);this.selectedMode=f;a=f.altIcon;null!=a?(f.altIcon=f.getAttribute("src"),f.setAttribute("src",a)):f.className=f.initialClassName+"Selected";this.fireEvent(new mxEventObject(mxEvent.SELECT));c()}));this.container.appendChild(f);null==this.defaultMode&&(this.defaultMode=f,this.selectMode(f),c());return f};
+mxToolbar.prototype.addMode=function(a,b,c,d,e,f){f=null!=f?f:!0;var g=document.createElement(null!=b?"img":"button");g.initialClassName=e||"mxToolbarMode";g.className=g.initialClassName;g.setAttribute("src",b);g.altIcon=d;null!=a&&g.setAttribute("title",a);this.enabled&&f&&(mxEvent.addListener(g,"click",mxUtils.bind(this,function(a){this.selectMode(g,c);this.noReset=!1})),mxEvent.addListener(g,"dblclick",mxUtils.bind(this,function(a){this.selectMode(g,c);this.noReset=!0})),null==this.defaultMode&&
+(this.defaultMode=g,this.defaultFunction=c,this.selectMode(g,c)));this.container.appendChild(g);return g};
+mxToolbar.prototype.selectMode=function(a,b){if(this.selectedMode!=a){if(null!=this.selectedMode){var c=this.selectedMode.altIcon;null!=c?(this.selectedMode.altIcon=this.selectedMode.getAttribute("src"),this.selectedMode.setAttribute("src",c)):this.selectedMode.className=this.selectedMode.initialClassName}this.selectedMode=a;c=this.selectedMode.altIcon;null!=c?(this.selectedMode.altIcon=this.selectedMode.getAttribute("src"),this.selectedMode.setAttribute("src",c)):this.selectedMode.className=this.selectedMode.initialClassName+
+"Selected";this.fireEvent(new mxEventObject(mxEvent.SELECT,"function",b))}};mxToolbar.prototype.resetMode=function(a){!a&&this.noReset||this.selectedMode==this.defaultMode||this.selectMode(this.defaultMode,this.defaultFunction)};mxToolbar.prototype.addSeparator=function(a){return this.addItem(null,a,null)};mxToolbar.prototype.addBreak=function(){mxUtils.br(this.container)};
+mxToolbar.prototype.addLine=function(){var a=document.createElement("hr");a.style.marginRight="6px";a.setAttribute("size","1");this.container.appendChild(a)};mxToolbar.prototype.destroy=function(){mxEvent.release(this.container);this.selectedMode=this.defaultFunction=this.defaultMode=this.container=null;null!=this.menu&&this.menu.destroy()};function mxUndoableEdit(a,b){this.source=a;this.changes=[];this.significant=null!=b?b:!0}mxUndoableEdit.prototype.source=null;
+mxUndoableEdit.prototype.changes=null;mxUndoableEdit.prototype.significant=null;mxUndoableEdit.prototype.undone=!1;mxUndoableEdit.prototype.redone=!1;mxUndoableEdit.prototype.isEmpty=function(){return 0==this.changes.length};mxUndoableEdit.prototype.isSignificant=function(){return this.significant};mxUndoableEdit.prototype.add=function(a){this.changes.push(a)};mxUndoableEdit.prototype.notify=function(){};mxUndoableEdit.prototype.die=function(){};
+mxUndoableEdit.prototype.undo=function(){if(!this.undone){this.source.fireEvent(new mxEventObject(mxEvent.START_EDIT));for(var a=this.changes.length-1;0<=a;a--){var b=this.changes[a];null!=b.execute?b.execute():null!=b.undo&&b.undo();this.source.fireEvent(new mxEventObject(mxEvent.EXECUTED,"change",b))}this.undone=!0;this.redone=!1;this.source.fireEvent(new mxEventObject(mxEvent.END_EDIT))}this.notify()};
+mxUndoableEdit.prototype.redo=function(){if(!this.redone){this.source.fireEvent(new mxEventObject(mxEvent.START_EDIT));for(var a=this.changes.length,b=0;b<a;b++){var c=this.changes[b];null!=c.execute?c.execute():null!=c.redo&&c.redo();this.source.fireEvent(new mxEventObject(mxEvent.EXECUTED,"change",c))}this.undone=!1;this.redone=!0;this.source.fireEvent(new mxEventObject(mxEvent.END_EDIT))}this.notify()};function mxUndoManager(a){this.size=null!=a?a:100;this.clear()}mxUndoManager.prototype=new mxEventSource;
+mxUndoManager.prototype.constructor=mxUndoManager;mxUndoManager.prototype.size=null;mxUndoManager.prototype.history=null;mxUndoManager.prototype.indexOfNextAdd=0;mxUndoManager.prototype.isEmpty=function(){return 0==this.history.length};mxUndoManager.prototype.clear=function(){this.history=[];this.indexOfNextAdd=0;this.fireEvent(new mxEventObject(mxEvent.CLEAR))};mxUndoManager.prototype.canUndo=function(){return 0<this.indexOfNextAdd};
+mxUndoManager.prototype.undo=function(){for(;0<this.indexOfNextAdd;){var a=this.history[--this.indexOfNextAdd];a.undo();if(a.isSignificant()){this.fireEvent(new mxEventObject(mxEvent.UNDO,"edit",a));break}}};mxUndoManager.prototype.canRedo=function(){return this.indexOfNextAdd<this.history.length};
+mxUndoManager.prototype.redo=function(){for(var a=this.history.length;this.indexOfNextAdd<a;){var b=this.history[this.indexOfNextAdd++];b.redo();if(b.isSignificant()){this.fireEvent(new mxEventObject(mxEvent.REDO,"edit",b));break}}};mxUndoManager.prototype.undoableEditHappened=function(a){this.trim();0<this.size&&this.size==this.history.length&&this.history.shift();this.history.push(a);this.indexOfNextAdd=this.history.length;this.fireEvent(new mxEventObject(mxEvent.ADD,"edit",a))};
+mxUndoManager.prototype.trim=function(){if(this.history.length>this.indexOfNextAdd)for(var a=this.history.splice(this.indexOfNextAdd,this.history.length-this.indexOfNextAdd),b=0;b<a.length;b++)a[b].die()};var mxUrlConverter=function(){};mxUrlConverter.prototype.enabled=!0;mxUrlConverter.prototype.baseUrl=null;mxUrlConverter.prototype.baseDomain=null;
+mxUrlConverter.prototype.updateBaseUrl=function(){this.baseDomain=location.protocol+"//"+location.host;this.baseUrl=this.baseDomain+location.pathname;var a=this.baseUrl.lastIndexOf("/");0<a&&(this.baseUrl=this.baseUrl.substring(0,a+1))};mxUrlConverter.prototype.isEnabled=function(){return this.enabled};mxUrlConverter.prototype.setEnabled=function(a){this.enabled=a};mxUrlConverter.prototype.getBaseUrl=function(){return this.baseUrl};mxUrlConverter.prototype.setBaseUrl=function(a){this.baseUrl=a};
+mxUrlConverter.prototype.getBaseDomain=function(){return this.baseDomain};mxUrlConverter.prototype.setBaseDomain=function(a){this.baseDomain=a};mxUrlConverter.prototype.isRelativeUrl=function(a){return"//"!=a.substring(0,2)&&"http://"!=a.substring(0,7)&&"https://"!=a.substring(0,8)&&"data:image"!=a.substring(0,10)&&"file://"!=a.substring(0,7)};
+mxUrlConverter.prototype.convert=function(a){this.isEnabled()&&this.isRelativeUrl(a)&&(null==this.getBaseUrl()&&this.updateBaseUrl(),a="/"==a.charAt(0)?this.getBaseDomain()+a:this.getBaseUrl()+a);return a};
+function mxPanningManager(a){this.thread=null;this.active=!1;this.dy=this.dx=this.t0y=this.t0x=this.tdy=this.tdx=0;this.scrollbars=!1;this.scrollTop=this.scrollLeft=0;this.mouseListener={mouseDown:function(a,b){},mouseMove:function(a,b){},mouseUp:mxUtils.bind(this,function(a,b){this.active&&this.stop()})};a.addMouseListener(this.mouseListener);this.mouseUpListener=mxUtils.bind(this,function(){this.active&&this.stop()});mxEvent.addListener(document,"mouseup",this.mouseUpListener);var b=mxUtils.bind(this,
+function(){this.scrollbars=mxUtils.hasScrollbars(a.container);this.scrollLeft=a.container.scrollLeft;this.scrollTop=a.container.scrollTop;return window.setInterval(mxUtils.bind(this,function(){this.tdx-=this.dx;this.tdy-=this.dy;this.scrollbars?(a.panGraph(-a.container.scrollLeft-Math.ceil(this.dx),-a.container.scrollTop-Math.ceil(this.dy)),a.panDx=this.scrollLeft-a.container.scrollLeft,a.panDy=this.scrollTop-a.container.scrollTop,a.fireEvent(new mxEventObject(mxEvent.PAN))):a.panGraph(this.getDx(),
+this.getDy())}),this.delay)});this.isActive=function(){return active};this.getDx=function(){return Math.round(this.tdx)};this.getDy=function(){return Math.round(this.tdy)};this.start=function(){this.t0x=a.view.translate.x;this.t0y=a.view.translate.y;this.active=!0};this.panTo=function(c,d,e,f){this.active||this.start();this.scrollLeft=a.container.scrollLeft;this.scrollTop=a.container.scrollTop;var g=a.container;this.dx=c+(null!=e?e:0)-g.scrollLeft-g.clientWidth;this.dx=0>this.dx&&Math.abs(this.dx)<
+this.border?this.border+this.dx:this.handleMouseOut?Math.max(this.dx,0):0;0==this.dx&&(this.dx=c-g.scrollLeft,this.dx=0<this.dx&&this.dx<this.border?this.dx-this.border:this.handleMouseOut?Math.min(0,this.dx):0);this.dy=d+(null!=f?f:0)-g.scrollTop-g.clientHeight;this.dy=0>this.dy&&Math.abs(this.dy)<this.border?this.border+this.dy:this.handleMouseOut?Math.max(this.dy,0):0;0==this.dy&&(this.dy=d-g.scrollTop,this.dy=0<this.dy&&this.dy<this.border?this.dy-this.border:this.handleMouseOut?Math.min(0,this.dy):
+0);0!=this.dx||0!=this.dy?(this.dx*=this.damper,this.dy*=this.damper,null==this.thread&&(this.thread=b())):null!=this.thread&&(window.clearInterval(this.thread),this.thread=null)};this.stop=function(){if(this.active)if(this.active=!1,null!=this.thread&&(window.clearInterval(this.thread),this.thread=null),this.tdy=this.tdx=0,this.scrollbars)a.panDx=0,a.panDy=0,a.fireEvent(new mxEventObject(mxEvent.PAN));else{var b=a.panDx,d=a.panDy;if(0!=b||0!=d)a.panGraph(0,0),a.view.setTranslate(this.t0x+b/a.view.scale,
+this.t0y+d/a.view.scale)}};this.destroy=function(){a.removeMouseListener(this.mouseListener);mxEvent.removeListener(document,"mouseup",this.mouseUpListener)}}mxPanningManager.prototype.damper=1/6;mxPanningManager.prototype.delay=10;mxPanningManager.prototype.handleMouseOut=!0;mxPanningManager.prototype.border=0;function mxPopupMenu(a){this.factoryMethod=a;null!=a&&this.init()}mxPopupMenu.prototype=new mxEventSource;mxPopupMenu.prototype.constructor=mxPopupMenu;
+mxPopupMenu.prototype.submenuImage=mxClient.imageBasePath+"/submenu.gif";mxPopupMenu.prototype.zIndex=10006;mxPopupMenu.prototype.factoryMethod=null;mxPopupMenu.prototype.useLeftButtonForPopup=!1;mxPopupMenu.prototype.enabled=!0;mxPopupMenu.prototype.itemCount=0;mxPopupMenu.prototype.autoExpand=!1;mxPopupMenu.prototype.smartSeparators=!1;mxPopupMenu.prototype.labels=!0;
+mxPopupMenu.prototype.init=function(){this.table=document.createElement("table");this.table.className="mxPopupMenu";this.tbody=document.createElement("tbody");this.table.appendChild(this.tbody);this.div=document.createElement("div");this.div.className="mxPopupMenu";this.div.style.display="inline";this.div.style.zIndex=this.zIndex;this.div.appendChild(this.table);mxEvent.disableContextMenu(this.div)};mxPopupMenu.prototype.isEnabled=function(){return this.enabled};
+mxPopupMenu.prototype.setEnabled=function(a){this.enabled=a};mxPopupMenu.prototype.isPopupTrigger=function(a){return a.isPopupTrigger()||this.useLeftButtonForPopup&&mxEvent.isLeftMouseButton(a.getEvent())};
+mxPopupMenu.prototype.addItem=function(a,b,c,d,e,f,g){d=d||this;this.itemCount++;d.willAddSeparator&&(d.containsItems&&this.addSeparator(d,!0),d.willAddSeparator=!1);d.containsItems=!0;var k=document.createElement("tr");k.className="mxPopupMenuItem";var l=document.createElement("td");l.className="mxPopupMenuIcon";null!=b?(e=document.createElement("img"),e.src=b,l.appendChild(e)):null!=e&&(b=document.createElement("div"),b.className=e,l.appendChild(b));k.appendChild(l);this.labels&&(l=document.createElement("td"),
+l.className="mxPopupMenuItem"+(null==f||f?"":" mxDisabled"),mxUtils.write(l,a),l.align="left",k.appendChild(l),a=document.createElement("td"),a.className="mxPopupMenuItem"+(null==f||f?"":" mxDisabled"),a.style.paddingRight="6px",a.style.textAlign="right",k.appendChild(a),null==d.div&&this.createSubmenu(d));d.tbody.appendChild(k);if(0!=g&&0!=f){var m=null;mxEvent.addGestureListeners(k,mxUtils.bind(this,function(a){this.eventReceiver=k;d.activeRow!=k&&d.activeRow!=d&&(null!=d.activeRow&&null!=d.activeRow.div.parentNode&&
+this.hideSubmenu(d),null!=k.div&&(this.showSubmenu(d,k),d.activeRow=k));if(mxClient.IS_QUIRKS||8==document.documentMode)m=document.selection.createRange();mxEvent.consume(a)}),mxUtils.bind(this,function(a){d.activeRow!=k&&d.activeRow!=d&&(null!=d.activeRow&&null!=d.activeRow.div.parentNode&&this.hideSubmenu(d),this.autoExpand&&null!=k.div&&(this.showSubmenu(d,k),d.activeRow=k));k.className="mxPopupMenuItemHover"}),mxUtils.bind(this,function(a){if(this.eventReceiver==k){d.activeRow!=k&&this.hideMenu();
+if(null!=m){try{m.select()}catch(p){}m=null}null!=c&&c(a)}this.eventReceiver=null;mxEvent.consume(a)}));mxEvent.addListener(k,"mouseout",mxUtils.bind(this,function(a){k.className="mxPopupMenuItem"}))}return k};mxPopupMenu.prototype.addCheckmark=function(a,b){var c=a.firstChild.nextSibling;c.style.backgroundImage="url('"+b+"')";c.style.backgroundRepeat="no-repeat";c.style.backgroundPosition="2px 50%"};
+mxPopupMenu.prototype.createSubmenu=function(a){a.table=document.createElement("table");a.table.className="mxPopupMenu";a.tbody=document.createElement("tbody");a.table.appendChild(a.tbody);a.div=document.createElement("div");a.div.className="mxPopupMenu";a.div.style.position="absolute";a.div.style.display="inline";a.div.style.zIndex=this.zIndex;a.div.appendChild(a.table);var b=document.createElement("img");b.setAttribute("src",this.submenuImage);td=a.firstChild.nextSibling.nextSibling;td.appendChild(b)};
+mxPopupMenu.prototype.showSubmenu=function(a,b){if(null!=b.div){b.div.style.left=a.div.offsetLeft+b.offsetLeft+b.offsetWidth-1+"px";b.div.style.top=a.div.offsetTop+b.offsetTop+"px";document.body.appendChild(b.div);var c=parseInt(b.div.offsetLeft),d=parseInt(b.div.offsetWidth),e=mxUtils.getDocumentScrollOrigin(document),f=document.documentElement;c+d>e.x+(document.body.clientWidth||f.clientWidth)&&(b.div.style.left=Math.max(0,a.div.offsetLeft-d+(mxClient.IS_IE?6:-6))+"px");mxUtils.fit(b.div)}};
+mxPopupMenu.prototype.addSeparator=function(a,b){a=a||this;if(this.smartSeparators&&!b)a.willAddSeparator=!0;else if(null!=a.tbody){a.willAddSeparator=!1;var c=document.createElement("tr"),d=document.createElement("td");d.className="mxPopupMenuIcon";d.style.padding="0 0 0 0px";c.appendChild(d);d=document.createElement("td");d.style.padding="0 0 0 0px";d.setAttribute("colSpan","2");var e=document.createElement("hr");e.setAttribute("size","1");d.appendChild(e);c.appendChild(d);a.tbody.appendChild(c)}};
+mxPopupMenu.prototype.popup=function(a,b,c,d){if(null!=this.div&&null!=this.tbody&&null!=this.factoryMethod){this.div.style.left=a+"px";for(this.div.style.top=b+"px";null!=this.tbody.firstChild;)mxEvent.release(this.tbody.firstChild),this.tbody.removeChild(this.tbody.firstChild);this.itemCount=0;this.factoryMethod(this,c,d);0<this.itemCount&&(this.showMenu(),this.fireEvent(new mxEventObject(mxEvent.SHOW)))}};
+mxPopupMenu.prototype.isMenuShowing=function(){return null!=this.div&&this.div.parentNode==document.body};mxPopupMenu.prototype.showMenu=function(){9<=document.documentMode&&(this.div.style.filter="none");document.body.appendChild(this.div);mxUtils.fit(this.div)};mxPopupMenu.prototype.hideMenu=function(){null!=this.div&&(null!=this.div.parentNode&&this.div.parentNode.removeChild(this.div),this.hideSubmenu(this),this.containsItems=!1,this.fireEvent(new mxEventObject(mxEvent.HIDE)))};
+mxPopupMenu.prototype.hideSubmenu=function(a){null!=a.activeRow&&(this.hideSubmenu(a.activeRow),null!=a.activeRow.div.parentNode&&a.activeRow.div.parentNode.removeChild(a.activeRow.div),a.activeRow=null)};mxPopupMenu.prototype.destroy=function(){null!=this.div&&(mxEvent.release(this.div),null!=this.div.parentNode&&this.div.parentNode.removeChild(this.div),this.div=null)};
+function mxAutoSaveManager(a){this.changeHandler=mxUtils.bind(this,function(a,c){this.isEnabled()&&this.graphModelChanged(c.getProperty("edit").changes)});this.setGraph(a)}mxAutoSaveManager.prototype=new mxEventSource;mxAutoSaveManager.prototype.constructor=mxAutoSaveManager;mxAutoSaveManager.prototype.graph=null;mxAutoSaveManager.prototype.autoSaveDelay=10;mxAutoSaveManager.prototype.autoSaveThrottle=2;mxAutoSaveManager.prototype.autoSaveThreshold=5;mxAutoSaveManager.prototype.ignoredChanges=0;
+mxAutoSaveManager.prototype.lastSnapshot=0;mxAutoSaveManager.prototype.enabled=!0;mxAutoSaveManager.prototype.changeHandler=null;mxAutoSaveManager.prototype.isEnabled=function(){return this.enabled};mxAutoSaveManager.prototype.setEnabled=function(a){this.enabled=a};mxAutoSaveManager.prototype.setGraph=function(a){null!=this.graph&&this.graph.getModel().removeListener(this.changeHandler);this.graph=a;null!=this.graph&&this.graph.getModel().addListener(mxEvent.CHANGE,this.changeHandler)};
+mxAutoSaveManager.prototype.save=function(){};mxAutoSaveManager.prototype.graphModelChanged=function(a){a=((new Date).getTime()-this.lastSnapshot)/1E3;a>this.autoSaveDelay||this.ignoredChanges>=this.autoSaveThreshold&&a>this.autoSaveThrottle?(this.save(),this.reset()):this.ignoredChanges++};mxAutoSaveManager.prototype.reset=function(){this.lastSnapshot=(new Date).getTime();this.ignoredChanges=0};mxAutoSaveManager.prototype.destroy=function(){this.setGraph(null)};
+function mxAnimation(a){this.delay=null!=a?a:20}mxAnimation.prototype=new mxEventSource;mxAnimation.prototype.constructor=mxAnimation;mxAnimation.prototype.delay=null;mxAnimation.prototype.thread=null;mxAnimation.prototype.isRunning=function(){return null!=this.thread};mxAnimation.prototype.startAnimation=function(){null==this.thread&&(this.thread=window.setInterval(mxUtils.bind(this,this.updateAnimation),this.delay))};mxAnimation.prototype.updateAnimation=function(){this.fireEvent(new mxEventObject(mxEvent.EXECUTE))};
+mxAnimation.prototype.stopAnimation=function(){null!=this.thread&&(window.clearInterval(this.thread),this.thread=null,this.fireEvent(new mxEventObject(mxEvent.DONE)))};function mxMorphing(a,b,c,d){mxAnimation.call(this,d);this.graph=a;this.steps=null!=b?b:6;this.ease=null!=c?c:1.5}mxMorphing.prototype=new mxAnimation;mxMorphing.prototype.constructor=mxMorphing;mxMorphing.prototype.graph=null;mxMorphing.prototype.steps=null;mxMorphing.prototype.step=0;mxMorphing.prototype.ease=null;
+mxMorphing.prototype.cells=null;mxMorphing.prototype.updateAnimation=function(){mxAnimation.prototype.updateAnimation.apply(this,arguments);var a=new mxCellStatePreview(this.graph);if(null!=this.cells)for(var b=0;b<this.cells.length;b++)this.animateCell(this.cells[b],a,!1);else this.animateCell(this.graph.getModel().getRoot(),a,!0);this.show(a);(a.isEmpty()||this.step++>=this.steps)&&this.stopAnimation()};mxMorphing.prototype.show=function(a){a.show()};
+mxMorphing.prototype.animateCell=function(a,b,c){var d=this.graph.getView().getState(a),e=null;if(null!=d&&(e=this.getDelta(d),this.graph.getModel().isVertex(a)&&(0!=e.x||0!=e.y))){var f=this.graph.view.getTranslate(),g=this.graph.view.getScale();e.x+=f.x*g;e.y+=f.y*g;b.moveState(d,-e.x/this.ease,-e.y/this.ease)}if(c&&!this.stopRecursion(d,e))for(d=this.graph.getModel().getChildCount(a),e=0;e<d;e++)this.animateCell(this.graph.getModel().getChildAt(a,e),b,c)};
+mxMorphing.prototype.stopRecursion=function(a,b){return null!=b&&(0!=b.x||0!=b.y)};mxMorphing.prototype.getDelta=function(a){var b=this.getOriginForCell(a.cell),c=this.graph.getView().getTranslate(),d=this.graph.getView().getScale();return new mxPoint((b.x-(a.x/d-c.x))*d,(b.y-(a.y/d-c.y))*d)};
+mxMorphing.prototype.getOriginForCell=function(a){var b=null;if(null!=a){var c=this.graph.getModel().getParent(a);a=this.graph.getCellGeometry(a);b=this.getOriginForCell(c);null!=a&&(a.relative?(c=this.graph.getCellGeometry(c),null!=c&&(b.x+=a.x*c.width,b.y+=a.y*c.height)):(b.x+=a.x,b.y+=a.y))}null==b&&(b=this.graph.view.getTranslate(),b=new mxPoint(-b.x,-b.y));return b};function mxImageBundle(a){this.images=[];this.alt=null!=a?a:!1}mxImageBundle.prototype.images=null;
+mxImageBundle.prototype.images=null;mxImageBundle.prototype.putImage=function(a,b,c){this.images[a]={value:b,fallback:c}};mxImageBundle.prototype.getImage=function(a){var b=null;null!=a&&(a=this.images[a],null!=a&&(b=this.alt?a.fallback:a.value));return b};function mxImageExport(){}mxImageExport.prototype.includeOverlays=!1;
+mxImageExport.prototype.drawState=function(a,b){null!=a&&(this.visitStatesRecursive(a,b,mxUtils.bind(this,function(){this.drawCellState.apply(this,arguments)})),this.includeOverlays&&this.visitStatesRecursive(a,b,mxUtils.bind(this,function(){this.drawOverlays.apply(this,arguments)})))};
+mxImageExport.prototype.visitStatesRecursive=function(a,b,c){if(null!=a){c(a,b);for(var d=a.view.graph,e=d.model.getChildCount(a.cell),f=0;f<e;f++){var g=d.view.getState(d.model.getChildAt(a.cell,f));this.visitStatesRecursive(g,b,c)}}};mxImageExport.prototype.getLinkForCellState=function(a,b){return null};mxImageExport.prototype.drawCellState=function(a,b){var c=this.getLinkForCellState(a,b);null!=c&&b.setLink(c);this.drawShape(a,b);this.drawText(a,b);null!=c&&b.setLink(null)};
+mxImageExport.prototype.drawShape=function(a,b){a.shape instanceof mxShape&&a.shape.checkBounds()&&(b.save(),a.shape.paint(b),b.restore())};mxImageExport.prototype.drawText=function(a,b){null!=a.text&&a.text.checkBounds()&&(b.save(),a.text.paint(b),b.restore())};mxImageExport.prototype.drawOverlays=function(a,b){null!=a.overlays&&a.overlays.visit(function(a,d){d instanceof mxShape&&d.paint(b)})};function mxAbstractCanvas2D(){this.converter=this.createUrlConverter();this.reset()}
+mxAbstractCanvas2D.prototype.state=null;mxAbstractCanvas2D.prototype.states=null;mxAbstractCanvas2D.prototype.path=null;mxAbstractCanvas2D.prototype.rotateHtml=!0;mxAbstractCanvas2D.prototype.lastX=0;mxAbstractCanvas2D.prototype.lastY=0;mxAbstractCanvas2D.prototype.moveOp="M";mxAbstractCanvas2D.prototype.lineOp="L";mxAbstractCanvas2D.prototype.quadOp="Q";mxAbstractCanvas2D.prototype.curveOp="C";mxAbstractCanvas2D.prototype.closeOp="Z";mxAbstractCanvas2D.prototype.pointerEvents=!1;
+mxAbstractCanvas2D.prototype.createUrlConverter=function(){return new mxUrlConverter};mxAbstractCanvas2D.prototype.reset=function(){this.state=this.createState();this.states=[]};
+mxAbstractCanvas2D.prototype.createState=function(){return{dx:0,dy:0,scale:1,alpha:1,fillAlpha:1,strokeAlpha:1,fillColor:null,gradientFillAlpha:1,gradientColor:null,gradientAlpha:1,gradientDirection:null,strokeColor:null,strokeWidth:1,dashed:!1,dashPattern:"3 3",fixDash:!1,lineCap:"flat",lineJoin:"miter",miterLimit:10,fontColor:"#000000",fontBackgroundColor:null,fontBorderColor:null,fontSize:mxConstants.DEFAULT_FONTSIZE,fontFamily:mxConstants.DEFAULT_FONTFAMILY,fontStyle:0,shadow:!1,shadowColor:mxConstants.SHADOWCOLOR,
+shadowAlpha:mxConstants.SHADOW_OPACITY,shadowDx:mxConstants.SHADOW_OFFSET_X,shadowDy:mxConstants.SHADOW_OFFSET_Y,rotation:0,rotationCx:0,rotationCy:0}};mxAbstractCanvas2D.prototype.format=function(a){return Math.round(parseFloat(a))};
+mxAbstractCanvas2D.prototype.addOp=function(){if(null!=this.path&&(this.path.push(arguments[0]),2<arguments.length))for(var a=this.state,b=2;b<arguments.length;b+=2)this.lastX=arguments[b-1],this.lastY=arguments[b],this.path.push(this.format((this.lastX+a.dx)*a.scale)),this.path.push(this.format((this.lastY+a.dy)*a.scale))};mxAbstractCanvas2D.prototype.rotatePoint=function(a,b,c,d,e){c*=Math.PI/180;return mxUtils.getRotatedPoint(new mxPoint(a,b),Math.cos(c),Math.sin(c),new mxPoint(d,e))};
+mxAbstractCanvas2D.prototype.save=function(){this.states.push(this.state);this.state=mxUtils.clone(this.state)};mxAbstractCanvas2D.prototype.restore=function(){0<this.states.length&&(this.state=this.states.pop())};mxAbstractCanvas2D.prototype.setLink=function(a){};mxAbstractCanvas2D.prototype.scale=function(a){this.state.scale*=a;this.state.strokeWidth*=a};mxAbstractCanvas2D.prototype.translate=function(a,b){this.state.dx+=a;this.state.dy+=b};mxAbstractCanvas2D.prototype.rotate=function(a,b,c,d,e){};
+mxAbstractCanvas2D.prototype.setAlpha=function(a){this.state.alpha=a};mxAbstractCanvas2D.prototype.setFillAlpha=function(a){this.state.fillAlpha=a};mxAbstractCanvas2D.prototype.setStrokeAlpha=function(a){this.state.strokeAlpha=a};mxAbstractCanvas2D.prototype.setFillColor=function(a){a==mxConstants.NONE&&(a=null);this.state.fillColor=a;this.state.gradientColor=null};
+mxAbstractCanvas2D.prototype.setGradient=function(a,b,c,d,e,f,g,k,l){c=this.state;c.fillColor=a;c.gradientFillAlpha=null!=k?k:1;c.gradientColor=b;c.gradientAlpha=null!=l?l:1;c.gradientDirection=g};mxAbstractCanvas2D.prototype.setStrokeColor=function(a){a==mxConstants.NONE&&(a=null);this.state.strokeColor=a};mxAbstractCanvas2D.prototype.setStrokeWidth=function(a){this.state.strokeWidth=a};mxAbstractCanvas2D.prototype.setDashed=function(a,b){this.state.dashed=a;this.state.fixDash=b};
+mxAbstractCanvas2D.prototype.setDashPattern=function(a){this.state.dashPattern=a};mxAbstractCanvas2D.prototype.setLineCap=function(a){this.state.lineCap=a};mxAbstractCanvas2D.prototype.setLineJoin=function(a){this.state.lineJoin=a};mxAbstractCanvas2D.prototype.setMiterLimit=function(a){this.state.miterLimit=a};mxAbstractCanvas2D.prototype.setFontColor=function(a){a==mxConstants.NONE&&(a=null);this.state.fontColor=a};
+mxAbstractCanvas2D.prototype.setFontBackgroundColor=function(a){a==mxConstants.NONE&&(a=null);this.state.fontBackgroundColor=a};mxAbstractCanvas2D.prototype.setFontBorderColor=function(a){a==mxConstants.NONE&&(a=null);this.state.fontBorderColor=a};mxAbstractCanvas2D.prototype.setFontSize=function(a){this.state.fontSize=parseFloat(a)};mxAbstractCanvas2D.prototype.setFontFamily=function(a){this.state.fontFamily=a};
+mxAbstractCanvas2D.prototype.setFontStyle=function(a){null==a&&(a=0);this.state.fontStyle=a};mxAbstractCanvas2D.prototype.setShadow=function(a){this.state.shadow=a};mxAbstractCanvas2D.prototype.setShadowColor=function(a){a==mxConstants.NONE&&(a=null);this.state.shadowColor=a};mxAbstractCanvas2D.prototype.setShadowAlpha=function(a){this.state.shadowAlpha=a};mxAbstractCanvas2D.prototype.setShadowOffset=function(a,b){this.state.shadowDx=a;this.state.shadowDy=b};
+mxAbstractCanvas2D.prototype.begin=function(){this.lastY=this.lastX=0;this.path=[]};mxAbstractCanvas2D.prototype.moveTo=function(a,b){this.addOp(this.moveOp,a,b)};mxAbstractCanvas2D.prototype.lineTo=function(a,b){this.addOp(this.lineOp,a,b)};mxAbstractCanvas2D.prototype.quadTo=function(a,b,c,d){this.addOp(this.quadOp,a,b,c,d)};mxAbstractCanvas2D.prototype.curveTo=function(a,b,c,d,e,f){this.addOp(this.curveOp,a,b,c,d,e,f)};
+mxAbstractCanvas2D.prototype.arcTo=function(a,b,c,d,e,f,g){a=mxUtils.arcToCurves(this.lastX,this.lastY,a,b,c,d,e,f,g);if(null!=a)for(b=0;b<a.length;b+=6)this.curveTo(a[b],a[b+1],a[b+2],a[b+3],a[b+4],a[b+5])};mxAbstractCanvas2D.prototype.close=function(a,b,c,d,e,f){this.addOp(this.closeOp)};mxAbstractCanvas2D.prototype.end=function(){};function mxXmlCanvas2D(a){mxAbstractCanvas2D.call(this);this.root=a;this.writeDefaults()}mxUtils.extend(mxXmlCanvas2D,mxAbstractCanvas2D);
+mxXmlCanvas2D.prototype.textEnabled=!0;mxXmlCanvas2D.prototype.compressed=!0;
+mxXmlCanvas2D.prototype.writeDefaults=function(){var a;a=this.createElement("fontfamily");a.setAttribute("family",mxConstants.DEFAULT_FONTFAMILY);this.root.appendChild(a);a=this.createElement("fontsize");a.setAttribute("size",mxConstants.DEFAULT_FONTSIZE);this.root.appendChild(a);a=this.createElement("shadowcolor");a.setAttribute("color",mxConstants.SHADOWCOLOR);this.root.appendChild(a);a=this.createElement("shadowalpha");a.setAttribute("alpha",mxConstants.SHADOW_OPACITY);this.root.appendChild(a);
+a=this.createElement("shadowoffset");a.setAttribute("dx",mxConstants.SHADOW_OFFSET_X);a.setAttribute("dy",mxConstants.SHADOW_OFFSET_Y);this.root.appendChild(a)};mxXmlCanvas2D.prototype.format=function(a){return parseFloat(parseFloat(a).toFixed(2))};mxXmlCanvas2D.prototype.createElement=function(a){return this.root.ownerDocument.createElement(a)};mxXmlCanvas2D.prototype.save=function(){this.compressed&&mxAbstractCanvas2D.prototype.save.apply(this,arguments);this.root.appendChild(this.createElement("save"))};
+mxXmlCanvas2D.prototype.restore=function(){this.compressed&&mxAbstractCanvas2D.prototype.restore.apply(this,arguments);this.root.appendChild(this.createElement("restore"))};mxXmlCanvas2D.prototype.scale=function(a){var b=this.createElement("scale");b.setAttribute("scale",a);this.root.appendChild(b)};mxXmlCanvas2D.prototype.translate=function(a,b){var c=this.createElement("translate");c.setAttribute("dx",this.format(a));c.setAttribute("dy",this.format(b));this.root.appendChild(c)};
+mxXmlCanvas2D.prototype.rotate=function(a,b,c,d,e){var f=this.createElement("rotate");if(0!=a||b||c)f.setAttribute("theta",this.format(a)),f.setAttribute("flipH",b?"1":"0"),f.setAttribute("flipV",c?"1":"0"),f.setAttribute("cx",this.format(d)),f.setAttribute("cy",this.format(e)),this.root.appendChild(f)};
+mxXmlCanvas2D.prototype.setAlpha=function(a){if(this.compressed){if(this.state.alpha==a)return;mxAbstractCanvas2D.prototype.setAlpha.apply(this,arguments)}var b=this.createElement("alpha");b.setAttribute("alpha",this.format(a));this.root.appendChild(b)};mxXmlCanvas2D.prototype.setFillAlpha=function(a){if(this.compressed){if(this.state.fillAlpha==a)return;mxAbstractCanvas2D.prototype.setFillAlpha.apply(this,arguments)}var b=this.createElement("fillalpha");b.setAttribute("alpha",this.format(a));this.root.appendChild(b)};
+mxXmlCanvas2D.prototype.setStrokeAlpha=function(a){if(this.compressed){if(this.state.strokeAlpha==a)return;mxAbstractCanvas2D.prototype.setStrokeAlpha.apply(this,arguments)}var b=this.createElement("strokealpha");b.setAttribute("alpha",this.format(a));this.root.appendChild(b)};
+mxXmlCanvas2D.prototype.setFillColor=function(a){a==mxConstants.NONE&&(a=null);if(this.compressed){if(this.state.fillColor==a)return;mxAbstractCanvas2D.prototype.setFillColor.apply(this,arguments)}var b=this.createElement("fillcolor");b.setAttribute("color",null!=a?a:mxConstants.NONE);this.root.appendChild(b)};
+mxXmlCanvas2D.prototype.setGradient=function(a,b,c,d,e,f,g,k,l){if(null!=a&&null!=b){mxAbstractCanvas2D.prototype.setGradient.apply(this,arguments);var m=this.createElement("gradient");m.setAttribute("c1",a);m.setAttribute("c2",b);m.setAttribute("x",this.format(c));m.setAttribute("y",this.format(d));m.setAttribute("w",this.format(e));m.setAttribute("h",this.format(f));null!=g&&m.setAttribute("direction",g);null!=k&&m.setAttribute("alpha1",k);null!=l&&m.setAttribute("alpha2",l);this.root.appendChild(m)}};
+mxXmlCanvas2D.prototype.setStrokeColor=function(a){a==mxConstants.NONE&&(a=null);if(this.compressed){if(this.state.strokeColor==a)return;mxAbstractCanvas2D.prototype.setStrokeColor.apply(this,arguments)}var b=this.createElement("strokecolor");b.setAttribute("color",null!=a?a:mxConstants.NONE);this.root.appendChild(b)};
+mxXmlCanvas2D.prototype.setStrokeWidth=function(a){if(this.compressed){if(this.state.strokeWidth==a)return;mxAbstractCanvas2D.prototype.setStrokeWidth.apply(this,arguments)}var b=this.createElement("strokewidth");b.setAttribute("width",this.format(a));this.root.appendChild(b)};
+mxXmlCanvas2D.prototype.setDashed=function(a,b){if(this.compressed){if(this.state.dashed==a)return;mxAbstractCanvas2D.prototype.setDashed.apply(this,arguments)}var c=this.createElement("dashed");c.setAttribute("dashed",a?"1":"0");null!=b&&c.setAttribute("fixDash",b?"1":"0");this.root.appendChild(c)};
+mxXmlCanvas2D.prototype.setDashPattern=function(a){if(this.compressed){if(this.state.dashPattern==a)return;mxAbstractCanvas2D.prototype.setDashPattern.apply(this,arguments)}var b=this.createElement("dashpattern");b.setAttribute("pattern",a);this.root.appendChild(b)};mxXmlCanvas2D.prototype.setLineCap=function(a){if(this.compressed){if(this.state.lineCap==a)return;mxAbstractCanvas2D.prototype.setLineCap.apply(this,arguments)}var b=this.createElement("linecap");b.setAttribute("cap",a);this.root.appendChild(b)};
+mxXmlCanvas2D.prototype.setLineJoin=function(a){if(this.compressed){if(this.state.lineJoin==a)return;mxAbstractCanvas2D.prototype.setLineJoin.apply(this,arguments)}var b=this.createElement("linejoin");b.setAttribute("join",a);this.root.appendChild(b)};mxXmlCanvas2D.prototype.setMiterLimit=function(a){if(this.compressed){if(this.state.miterLimit==a)return;mxAbstractCanvas2D.prototype.setMiterLimit.apply(this,arguments)}var b=this.createElement("miterlimit");b.setAttribute("limit",a);this.root.appendChild(b)};
+mxXmlCanvas2D.prototype.setFontColor=function(a){if(this.textEnabled){a==mxConstants.NONE&&(a=null);if(this.compressed){if(this.state.fontColor==a)return;mxAbstractCanvas2D.prototype.setFontColor.apply(this,arguments)}var b=this.createElement("fontcolor");b.setAttribute("color",null!=a?a:mxConstants.NONE);this.root.appendChild(b)}};
+mxXmlCanvas2D.prototype.setFontBackgroundColor=function(a){if(this.textEnabled){a==mxConstants.NONE&&(a=null);if(this.compressed){if(this.state.fontBackgroundColor==a)return;mxAbstractCanvas2D.prototype.setFontBackgroundColor.apply(this,arguments)}var b=this.createElement("fontbackgroundcolor");b.setAttribute("color",null!=a?a:mxConstants.NONE);this.root.appendChild(b)}};
+mxXmlCanvas2D.prototype.setFontBorderColor=function(a){if(this.textEnabled){a==mxConstants.NONE&&(a=null);if(this.compressed){if(this.state.fontBorderColor==a)return;mxAbstractCanvas2D.prototype.setFontBorderColor.apply(this,arguments)}var b=this.createElement("fontbordercolor");b.setAttribute("color",null!=a?a:mxConstants.NONE);this.root.appendChild(b)}};
+mxXmlCanvas2D.prototype.setFontSize=function(a){if(this.textEnabled){if(this.compressed){if(this.state.fontSize==a)return;mxAbstractCanvas2D.prototype.setFontSize.apply(this,arguments)}var b=this.createElement("fontsize");b.setAttribute("size",a);this.root.appendChild(b)}};
+mxXmlCanvas2D.prototype.setFontFamily=function(a){if(this.textEnabled){if(this.compressed){if(this.state.fontFamily==a)return;mxAbstractCanvas2D.prototype.setFontFamily.apply(this,arguments)}var b=this.createElement("fontfamily");b.setAttribute("family",a);this.root.appendChild(b)}};
+mxXmlCanvas2D.prototype.setFontStyle=function(a){if(this.textEnabled){null==a&&(a=0);if(this.compressed){if(this.state.fontStyle==a)return;mxAbstractCanvas2D.prototype.setFontStyle.apply(this,arguments)}var b=this.createElement("fontstyle");b.setAttribute("style",a);this.root.appendChild(b)}};
+mxXmlCanvas2D.prototype.setShadow=function(a){if(this.compressed){if(this.state.shadow==a)return;mxAbstractCanvas2D.prototype.setShadow.apply(this,arguments)}var b=this.createElement("shadow");b.setAttribute("enabled",a?"1":"0");this.root.appendChild(b)};
+mxXmlCanvas2D.prototype.setShadowColor=function(a){if(this.compressed){a==mxConstants.NONE&&(a=null);if(this.state.shadowColor==a)return;mxAbstractCanvas2D.prototype.setShadowColor.apply(this,arguments)}var b=this.createElement("shadowcolor");b.setAttribute("color",null!=a?a:mxConstants.NONE);this.root.appendChild(b)};
+mxXmlCanvas2D.prototype.setShadowAlpha=function(a){if(this.compressed){if(this.state.shadowAlpha==a)return;mxAbstractCanvas2D.prototype.setShadowAlpha.apply(this,arguments)}var b=this.createElement("shadowalpha");b.setAttribute("alpha",a);this.root.appendChild(b)};
+mxXmlCanvas2D.prototype.setShadowOffset=function(a,b){if(this.compressed){if(this.state.shadowDx==a&&this.state.shadowDy==b)return;mxAbstractCanvas2D.prototype.setShadowOffset.apply(this,arguments)}var c=this.createElement("shadowoffset");c.setAttribute("dx",a);c.setAttribute("dy",b);this.root.appendChild(c)};
+mxXmlCanvas2D.prototype.rect=function(a,b,c,d){var e=this.createElement("rect");e.setAttribute("x",this.format(a));e.setAttribute("y",this.format(b));e.setAttribute("w",this.format(c));e.setAttribute("h",this.format(d));this.root.appendChild(e)};
+mxXmlCanvas2D.prototype.roundrect=function(a,b,c,d,e,f){var g=this.createElement("roundrect");g.setAttribute("x",this.format(a));g.setAttribute("y",this.format(b));g.setAttribute("w",this.format(c));g.setAttribute("h",this.format(d));g.setAttribute("dx",this.format(e));g.setAttribute("dy",this.format(f));this.root.appendChild(g)};
+mxXmlCanvas2D.prototype.ellipse=function(a,b,c,d){var e=this.createElement("ellipse");e.setAttribute("x",this.format(a));e.setAttribute("y",this.format(b));e.setAttribute("w",this.format(c));e.setAttribute("h",this.format(d));this.root.appendChild(e)};
+mxXmlCanvas2D.prototype.image=function(a,b,c,d,e,f,g,k){e=this.converter.convert(e);var l=this.createElement("image");l.setAttribute("x",this.format(a));l.setAttribute("y",this.format(b));l.setAttribute("w",this.format(c));l.setAttribute("h",this.format(d));l.setAttribute("src",e);l.setAttribute("aspect",f?"1":"0");l.setAttribute("flipH",g?"1":"0");l.setAttribute("flipV",k?"1":"0");this.root.appendChild(l)};
+mxXmlCanvas2D.prototype.begin=function(){this.root.appendChild(this.createElement("begin"));this.lastY=this.lastX=0};mxXmlCanvas2D.prototype.moveTo=function(a,b){var c=this.createElement("move");c.setAttribute("x",this.format(a));c.setAttribute("y",this.format(b));this.root.appendChild(c);this.lastX=a;this.lastY=b};
+mxXmlCanvas2D.prototype.lineTo=function(a,b){var c=this.createElement("line");c.setAttribute("x",this.format(a));c.setAttribute("y",this.format(b));this.root.appendChild(c);this.lastX=a;this.lastY=b};mxXmlCanvas2D.prototype.quadTo=function(a,b,c,d){var e=this.createElement("quad");e.setAttribute("x1",this.format(a));e.setAttribute("y1",this.format(b));e.setAttribute("x2",this.format(c));e.setAttribute("y2",this.format(d));this.root.appendChild(e);this.lastX=c;this.lastY=d};
+mxXmlCanvas2D.prototype.curveTo=function(a,b,c,d,e,f){var g=this.createElement("curve");g.setAttribute("x1",this.format(a));g.setAttribute("y1",this.format(b));g.setAttribute("x2",this.format(c));g.setAttribute("y2",this.format(d));g.setAttribute("x3",this.format(e));g.setAttribute("y3",this.format(f));this.root.appendChild(g);this.lastX=e;this.lastY=f};mxXmlCanvas2D.prototype.close=function(){this.root.appendChild(this.createElement("close"))};
+mxXmlCanvas2D.prototype.text=function(a,b,c,d,e,f,g,k,l,m,n,p,q){if(this.textEnabled&&null!=e){mxUtils.isNode(e)&&(e=mxUtils.getOuterHtml(e));var r=this.createElement("text");r.setAttribute("x",this.format(a));r.setAttribute("y",this.format(b));r.setAttribute("w",this.format(c));r.setAttribute("h",this.format(d));r.setAttribute("str",e);null!=f&&r.setAttribute("align",f);null!=g&&r.setAttribute("valign",g);r.setAttribute("wrap",k?"1":"0");null==l&&(l="");r.setAttribute("format",l);null!=m&&r.setAttribute("overflow",
+m);null!=n&&r.setAttribute("clip",n?"1":"0");null!=p&&r.setAttribute("rotation",p);null!=q&&r.setAttribute("dir",q);this.root.appendChild(r)}};mxXmlCanvas2D.prototype.stroke=function(){this.root.appendChild(this.createElement("stroke"))};mxXmlCanvas2D.prototype.fill=function(){this.root.appendChild(this.createElement("fill"))};mxXmlCanvas2D.prototype.fillAndStroke=function(){this.root.appendChild(this.createElement("fillstroke"))};
+function mxSvgCanvas2D(a,b){mxAbstractCanvas2D.call(this);this.root=a;this.gradients=[];this.defs=null;this.styleEnabled=null!=b?b:!1;var c=null;if(a.ownerDocument!=document)for(c=a;null!=c&&"svg"!=c.nodeName;)c=c.parentNode;null!=c&&(0<c.getElementsByTagName("defs").length&&(this.defs=c.getElementsByTagName("defs")[0]),null==this.defs&&(this.defs=this.createElement("defs"),null!=c.firstChild?c.insertBefore(this.defs,c.firstChild):c.appendChild(this.defs)),this.styleEnabled&&this.defs.appendChild(this.createStyle()))}
+mxUtils.extend(mxSvgCanvas2D,mxAbstractCanvas2D);(function(){mxSvgCanvas2D.prototype.useDomParser=!mxClient.IS_IE&&"function"===typeof DOMParser&&"function"===typeof XMLSerializer;if(mxSvgCanvas2D.prototype.useDomParser)try{var a=(new DOMParser).parseFromString("test text","text/html");mxSvgCanvas2D.prototype.useDomParser=null!=a}catch(b){mxSvgCanvas2D.prototype.useDomParser=!1}})();mxSvgCanvas2D.prototype.node=null;mxSvgCanvas2D.prototype.matchHtmlAlignment=!0;
+mxSvgCanvas2D.prototype.textEnabled=!0;mxSvgCanvas2D.prototype.foEnabled=!0;mxSvgCanvas2D.prototype.foAltText="[Object]";mxSvgCanvas2D.prototype.foOffset=0;mxSvgCanvas2D.prototype.textOffset=0;mxSvgCanvas2D.prototype.imageOffset=0;mxSvgCanvas2D.prototype.strokeTolerance=0;mxSvgCanvas2D.prototype.minStrokeWidth=1;mxSvgCanvas2D.prototype.refCount=0;mxSvgCanvas2D.prototype.blockImagePointerEvents=!1;mxSvgCanvas2D.prototype.lineHeightCorrection=1;mxSvgCanvas2D.prototype.pointerEventsValue="all";
+mxSvgCanvas2D.prototype.fontMetricsPadding=10;mxSvgCanvas2D.prototype.cacheOffsetSize=!0;mxSvgCanvas2D.prototype.format=function(a){return parseFloat(parseFloat(a).toFixed(2))};mxSvgCanvas2D.prototype.getBaseUrl=function(){var a=window.location.href,b=a.lastIndexOf("#");0<b&&(a=a.substring(0,b));return a};mxSvgCanvas2D.prototype.reset=function(){mxAbstractCanvas2D.prototype.reset.apply(this,arguments);this.gradients=[]};
+mxSvgCanvas2D.prototype.createStyle=function(a){a=this.createElement("style");a.setAttribute("type","text/css");mxUtils.write(a,"svg{font-family:"+mxConstants.DEFAULT_FONTFAMILY+";font-size:"+mxConstants.DEFAULT_FONTSIZE+";fill:none;stroke-miterlimit:10}");return a};
+mxSvgCanvas2D.prototype.createElement=function(a,b){if(null!=this.root.ownerDocument.createElementNS)return this.root.ownerDocument.createElementNS(b||mxConstants.NS_SVG,a);var c=this.root.ownerDocument.createElement(a);null!=b&&c.setAttribute("xmlns",b);return c};
+mxSvgCanvas2D.prototype.createAlternateContent=function(a,b,c,d,e,f,g,k,l,m,n,p,q){return null!=this.foAltText?(a=this.state,b=this.createElement("text"),b.setAttribute("x",Math.round(d/2)),b.setAttribute("y",Math.round((e+a.fontSize)/2)),b.setAttribute("fill",a.fontColor||"black"),b.setAttribute("text-anchor","middle"),b.setAttribute("font-size",a.fontSize+"px"),b.setAttribute("font-family",a.fontFamily),(a.fontStyle&mxConstants.FONT_BOLD)==mxConstants.FONT_BOLD&&b.setAttribute("font-weight","bold"),
+(a.fontStyle&mxConstants.FONT_ITALIC)==mxConstants.FONT_ITALIC&&b.setAttribute("font-style","italic"),(a.fontStyle&mxConstants.FONT_UNDERLINE)==mxConstants.FONT_UNDERLINE&&b.setAttribute("text-decoration","underline"),mxUtils.write(b,this.foAltText),b):null};
+mxSvgCanvas2D.prototype.createGradientId=function(a,b,c,d,e){"#"==a.charAt(0)&&(a=a.substring(1));"#"==b.charAt(0)&&(b=b.substring(1));a=a.toLowerCase()+"-"+c;b=b.toLowerCase()+"-"+d;c=null;null==e||e==mxConstants.DIRECTION_SOUTH?c="s":e==mxConstants.DIRECTION_EAST?c="e":(d=a,a=b,b=d,e==mxConstants.DIRECTION_NORTH?c="s":e==mxConstants.DIRECTION_WEST&&(c="e"));return"mx-gradient-"+a+"-"+b+"-"+c};
+mxSvgCanvas2D.prototype.getSvgGradient=function(a,b,c,d,e){var f=this.createGradientId(a,b,c,d,e),g=this.gradients[f];if(null==g){var k=this.root.ownerSVGElement,l=0,m=f+"-"+l;if(null!=k)for(g=k.ownerDocument.getElementById(m);null!=g&&g.ownerSVGElement!=k;)m=f+"-"+l++,g=k.ownerDocument.getElementById(m);else m="id"+ ++this.refCount;null==g&&(g=this.createSvgGradient(a,b,c,d,e),g.setAttribute("id",m),null!=this.defs?this.defs.appendChild(g):k.appendChild(g));this.gradients[f]=g}return g.getAttribute("id")};
+mxSvgCanvas2D.prototype.createSvgGradient=function(a,b,c,d,e){var f=this.createElement("linearGradient");f.setAttribute("x1","0%");f.setAttribute("y1","0%");f.setAttribute("x2","0%");f.setAttribute("y2","0%");null==e||e==mxConstants.DIRECTION_SOUTH?f.setAttribute("y2","100%"):e==mxConstants.DIRECTION_EAST?f.setAttribute("x2","100%"):e==mxConstants.DIRECTION_NORTH?f.setAttribute("y1","100%"):e==mxConstants.DIRECTION_WEST&&f.setAttribute("x1","100%");c=1>c?";stop-opacity:"+c:"";e=this.createElement("stop");
+e.setAttribute("offset","0%");e.setAttribute("style","stop-color:"+a+c);f.appendChild(e);c=1>d?";stop-opacity:"+d:"";e=this.createElement("stop");e.setAttribute("offset","100%");e.setAttribute("style","stop-color:"+b+c);f.appendChild(e);return f};
+mxSvgCanvas2D.prototype.addNode=function(a,b){var c=this.node,d=this.state;if(null!=c){if("path"==c.nodeName)if(null!=this.path&&0<this.path.length)c.setAttribute("d",this.path.join(" "));else return;a&&null!=d.fillColor?this.updateFill():this.styleEnabled||("ellipse"==c.nodeName&&mxClient.IS_FF?c.setAttribute("fill","transparent"):c.setAttribute("fill","none"),a=!1);b&&null!=d.strokeColor?this.updateStroke():this.styleEnabled||c.setAttribute("stroke","none");null!=d.transform&&0<d.transform.length&&
+c.setAttribute("transform",d.transform);d.shadow&&this.root.appendChild(this.createShadow(c));0<this.strokeTolerance&&!a&&this.root.appendChild(this.createTolerance(c));!this.pointerEvents||"path"==c.nodeName&&this.path[this.path.length-1]!=this.closeOp?this.pointerEvents||null!=this.originalRoot||c.setAttribute("pointer-events","none"):c.setAttribute("pointer-events",this.pointerEventsValue);("rect"!=c.nodeName&&"path"!=c.nodeName&&"ellipse"!=c.nodeName||"none"!=c.getAttribute("fill")&&"transparent"!=
+c.getAttribute("fill")||"none"!=c.getAttribute("stroke")||"none"!=c.getAttribute("pointer-events"))&&this.root.appendChild(c);this.node=null}};
+mxSvgCanvas2D.prototype.updateFill=function(){var a=this.state;(1>a.alpha||1>a.fillAlpha)&&this.node.setAttribute("fill-opacity",a.alpha*a.fillAlpha);if(null!=a.fillColor)if(null!=a.gradientColor)if(a=this.getSvgGradient(a.fillColor,a.gradientColor,a.gradientFillAlpha,a.gradientAlpha,a.gradientDirection),mxClient.IS_CHROME_APP||mxClient.IS_IE||mxClient.IS_IE11||mxClient.IS_EDGE||this.root.ownerDocument!=document)this.node.setAttribute("fill","url(#"+a+")");else{var b=this.getBaseUrl().replace(/([\(\)])/g,
+"\\$1");this.node.setAttribute("fill","url("+b+"#"+a+")")}else this.node.setAttribute("fill",a.fillColor.toLowerCase())};mxSvgCanvas2D.prototype.getCurrentStrokeWidth=function(){return Math.max(this.minStrokeWidth,Math.max(.01,this.format(this.state.strokeWidth*this.state.scale)))};
+mxSvgCanvas2D.prototype.updateStroke=function(){var a=this.state;this.node.setAttribute("stroke",a.strokeColor.toLowerCase());(1>a.alpha||1>a.strokeAlpha)&&this.node.setAttribute("stroke-opacity",a.alpha*a.strokeAlpha);var b=this.getCurrentStrokeWidth();1!=b&&this.node.setAttribute("stroke-width",b);"path"==this.node.nodeName&&this.updateStrokeAttributes();a.dashed&&this.node.setAttribute("stroke-dasharray",this.createDashPattern((a.fixDash?1:a.strokeWidth)*a.scale))};
+mxSvgCanvas2D.prototype.updateStrokeAttributes=function(){var a=this.state;null!=a.lineJoin&&"miter"!=a.lineJoin&&this.node.setAttribute("stroke-linejoin",a.lineJoin);if(null!=a.lineCap){var b=a.lineCap;"flat"==b&&(b="butt");"butt"!=b&&this.node.setAttribute("stroke-linecap",b)}null==a.miterLimit||this.styleEnabled&&10==a.miterLimit||this.node.setAttribute("stroke-miterlimit",a.miterLimit)};
+mxSvgCanvas2D.prototype.createDashPattern=function(a){var b=[];if("string"===typeof this.state.dashPattern){var c=this.state.dashPattern.split(" ");if(0<c.length)for(var d=0;d<c.length;d++)b[d]=Number(c[d])*a}return b.join(" ")};
+mxSvgCanvas2D.prototype.createTolerance=function(a){a=a.cloneNode(!0);var b=parseFloat(a.getAttribute("stroke-width")||1)+this.strokeTolerance;a.setAttribute("pointer-events","stroke");a.setAttribute("visibility","hidden");a.removeAttribute("stroke-dasharray");a.setAttribute("stroke-width",b);a.setAttribute("fill","none");a.setAttribute("stroke",mxClient.IS_OT?"none":"white");return a};
+mxSvgCanvas2D.prototype.createShadow=function(a){a=a.cloneNode(!0);var b=this.state;"none"==a.getAttribute("fill")||mxClient.IS_FF&&"transparent"==a.getAttribute("fill")||a.setAttribute("fill",b.shadowColor);"none"!=a.getAttribute("stroke")&&a.setAttribute("stroke",b.shadowColor);a.setAttribute("transform","translate("+this.format(b.shadowDx*b.scale)+","+this.format(b.shadowDy*b.scale)+")"+(b.transform||""));a.setAttribute("opacity",b.shadowAlpha);return a};
+mxSvgCanvas2D.prototype.setLink=function(a){if(null==a)this.root=this.originalRoot;else{this.originalRoot=this.root;var b=this.createElement("a");null==b.setAttributeNS||this.root.ownerDocument!=document&&null==document.documentMode?b.setAttribute("xlink:href",a):b.setAttributeNS(mxConstants.NS_XLINK,"xlink:href",a);this.root.appendChild(b);this.root=b}};
+mxSvgCanvas2D.prototype.rotate=function(a,b,c,d,e){if(0!=a||b||c){var f=this.state;d+=f.dx;e+=f.dy;d*=f.scale;e*=f.scale;f.transform=f.transform||"";if(b&&c)a+=180;else if(b!=c){var g=b?d:0,k=b?-1:1,l=c?e:0,m=c?-1:1;f.transform+="translate("+this.format(g)+","+this.format(l)+")scale("+this.format(k)+","+this.format(m)+")translate("+this.format(-g)+","+this.format(-l)+")"}if(b?!c:c)a*=-1;0!=a&&(f.transform+="rotate("+this.format(a)+","+this.format(d)+","+this.format(e)+")");f.rotation+=a;f.rotationCx=
+d;f.rotationCy=e}};mxSvgCanvas2D.prototype.begin=function(){mxAbstractCanvas2D.prototype.begin.apply(this,arguments);this.node=this.createElement("path")};mxSvgCanvas2D.prototype.rect=function(a,b,c,d){var e=this.state,f=this.createElement("rect");f.setAttribute("x",this.format((a+e.dx)*e.scale));f.setAttribute("y",this.format((b+e.dy)*e.scale));f.setAttribute("width",this.format(c*e.scale));f.setAttribute("height",this.format(d*e.scale));this.node=f};
+mxSvgCanvas2D.prototype.roundrect=function(a,b,c,d,e,f){this.rect(a,b,c,d);0<e&&this.node.setAttribute("rx",this.format(e*this.state.scale));0<f&&this.node.setAttribute("ry",this.format(f*this.state.scale))};mxSvgCanvas2D.prototype.ellipse=function(a,b,c,d){var e=this.state,f=this.createElement("ellipse");f.setAttribute("cx",Math.round((a+c/2+e.dx)*e.scale));f.setAttribute("cy",Math.round((b+d/2+e.dy)*e.scale));f.setAttribute("rx",c/2*e.scale);f.setAttribute("ry",d/2*e.scale);this.node=f};
+mxSvgCanvas2D.prototype.image=function(a,b,c,d,e,f,g,k){e=this.converter.convert(e);f=null!=f?f:!0;g=null!=g?g:!1;k=null!=k?k:!1;var l=this.state;a+=l.dx;b+=l.dy;var m=this.createElement("image");m.setAttribute("x",this.format(a*l.scale)+this.imageOffset);m.setAttribute("y",this.format(b*l.scale)+this.imageOffset);m.setAttribute("width",this.format(c*l.scale));m.setAttribute("height",this.format(d*l.scale));null==m.setAttributeNS?m.setAttribute("xlink:href",e):m.setAttributeNS(mxConstants.NS_XLINK,
+"xlink:href",e);f||m.setAttribute("preserveAspectRatio","none");(1>l.alpha||1>l.fillAlpha)&&m.setAttribute("opacity",l.alpha*l.fillAlpha);e=this.state.transform||"";if(g||k){var n=f=1,p=0,q=0;g&&(f=-1,p=-c-2*a);k&&(n=-1,q=-d-2*b);e+="scale("+f+","+n+")translate("+p*l.scale+","+q*l.scale+")"}0<e.length&&m.setAttribute("transform",e);this.pointerEvents||m.setAttribute("pointer-events","none");this.root.appendChild(m);this.blockImagePointerEvents&&(m.setAttribute("style","pointer-events:none"),m=this.createElement("rect"),
+m.setAttribute("visibility","hidden"),m.setAttribute("pointer-events","fill"),m.setAttribute("x",this.format(a*l.scale)),m.setAttribute("y",this.format(b*l.scale)),m.setAttribute("width",this.format(c*l.scale)),m.setAttribute("height",this.format(d*l.scale)),this.root.appendChild(m))};
+mxSvgCanvas2D.prototype.convertHtml=function(a){if(this.useDomParser){var b=(new DOMParser).parseFromString(a,"text/html");null!=b&&(a=(new XMLSerializer).serializeToString(b.body),"<body"==a.substring(0,5)&&(a=a.substring(a.indexOf(">",5)+1)),"</body>"==a.substring(a.length-7,a.length)&&(a=a.substring(0,a.length-7)))}else{if(null!=document.implementation&&null!=document.implementation.createDocument){var b=document.implementation.createDocument("http://www.w3.org/1999/xhtml","html",null),c=b.createElement("body");
+b.documentElement.appendChild(c);var d=document.createElement("div");d.innerHTML=a;for(a=d.firstChild;null!=a;)d=a.nextSibling,c.appendChild(b.adoptNode(a)),a=d;return c.innerHTML}b=document.createElement("textarea");b.innerHTML=a.replace(/&amp;/g,"&amp;amp;").replace(/&#60;/g,"&amp;lt;").replace(/&#62;/g,"&amp;gt;").replace(/&lt;/g,"&amp;lt;").replace(/&gt;/g,"&amp;gt;").replace(/</g,"&lt;").replace(/>/g,"&gt;");a=b.value.replace(/&/g,"&amp;").replace(/&amp;lt;/g,"&lt;").replace(/&amp;gt;/g,"&gt;").replace(/&amp;amp;/g,
+"&amp;").replace(/<br>/g,"<br />").replace(/<hr>/g,"<hr />").replace(/(<img[^>]+)>/gm,"$1 />")}return a};
+mxSvgCanvas2D.prototype.createDiv=function(a,b,c,d,e){c=this.state;d="display:inline-block;font-size:"+c.fontSize+"px;font-family:"+c.fontFamily+";color:"+c.fontColor+";line-height:"+(mxConstants.ABSOLUTE_LINE_HEIGHT?c.fontSize*mxConstants.LINE_HEIGHT+"px":mxConstants.LINE_HEIGHT*this.lineHeightCorrection)+";"+d;(c.fontStyle&mxConstants.FONT_BOLD)==mxConstants.FONT_BOLD&&(d+="font-weight:bold;");(c.fontStyle&mxConstants.FONT_ITALIC)==mxConstants.FONT_ITALIC&&(d+="font-style:italic;");(c.fontStyle&
+mxConstants.FONT_UNDERLINE)==mxConstants.FONT_UNDERLINE&&(d+="text-decoration:underline;");b==mxConstants.ALIGN_CENTER?d+="text-align:center;":b==mxConstants.ALIGN_RIGHT&&(d+="text-align:right;");b="";null!=c.fontBackgroundColor&&(b+="background-color:"+c.fontBackgroundColor+";");null!=c.fontBorderColor&&(b+="border:1px solid "+c.fontBorderColor+";");mxUtils.isNode(a)||(a=this.convertHtml(a),"fill"!=e&&"width"!=e?a='<div xmlns="http://www.w3.org/1999/xhtml" style="display:inline-block;text-align:inherit;text-decoration:inherit;'+
+b+'">'+a+"</div>":d+=b);if(!mxClient.IS_IE&&document.createElementNS)return e=document.createElementNS("http://www.w3.org/1999/xhtml","div"),e.setAttribute("style",d),mxUtils.isNode(a)?this.root.ownerDocument!=document?e.appendChild(a.cloneNode(!0)):e.appendChild(a):e.innerHTML=a,e;mxUtils.isNode(a)&&this.root.ownerDocument!=document&&(a=a.outerHTML);return mxUtils.parseXml('<div xmlns="http://www.w3.org/1999/xhtml" style="'+d+'">'+a+"</div>").documentElement};
+mxSvgCanvas2D.prototype.invalidateCachedOffsetSize=function(a){delete a.firstChild.mxCachedOffsetWidth;delete a.firstChild.mxCachedFinalOffsetWidth;delete a.firstChild.mxCachedFinalOffsetHeight};
+mxSvgCanvas2D.prototype.updateText=function(a,b,c,d,e,f,g,k,l,m,n){if(null!=n&&null!=n.firstChild&&null!=n.firstChild.firstChild&&null!=n.firstChild.firstChild.firstChild){n=n.firstChild;var p=n.firstChild,q=p.firstChild;m=null!=m?m:0;var r=this.state;a+=r.dx;b+=r.dy;l?(q.style.maxHeight=Math.round(d)+"px",q.style.maxWidth=Math.round(c)+"px"):"fill"==k?(q.style.width=Math.round(c+1)+"px",q.style.height=Math.round(d+1)+"px"):"width"==k&&(q.style.width=Math.round(c+1)+"px",0<d&&(q.style.maxHeight=Math.round(d)+
+"px"));g&&0<c&&(q.style.width=Math.round(c+1)+"px");var t,u=q;null!=u.firstChild&&"DIV"==u.firstChild.nodeName&&(u=u.firstChild);var x=null!=n.mxCachedOffsetWidth?n.mxCachedOffsetWidth:u.offsetWidth;t=x+0;g&&"fill"!=k&&(l&&(t=Math.min(t,c)),q.style.width=Math.round(t+1)+"px");t=null!=n.mxCachedFinalOffsetWidth?n.mxCachedFinalOffsetWidth:u.offsetWidth;g=null!=n.mxCachedFinalOffsetHeight?n.mxCachedFinalOffsetHeight:u.offsetHeight;this.cacheOffsetSize&&(n.mxCachedOffsetWidth=x,n.mxCachedFinalOffsetWidth=
+t,n.mxCachedFinalOffsetHeight=g);t+=0;g-=2;l&&(g=Math.min(g,d),t=Math.min(t,c));"width"==k?d=g:"fill"!=k&&(c=t,d=g);g=l=0;e==mxConstants.ALIGN_CENTER?l-=c/2:e==mxConstants.ALIGN_RIGHT&&(l-=c);a+=l;f==mxConstants.ALIGN_MIDDLE?g-=d/2:f==mxConstants.ALIGN_BOTTOM&&(g-=d);"fill"!=k&&mxClient.IS_FF&&mxClient.IS_WIN&&(g-=2);b+=g;e=1!=r.scale?"scale("+r.scale+")":"";0!=r.rotation&&this.rotateHtml?(e+="rotate("+r.rotation+","+c/2+","+d/2+")",b=this.rotatePoint((a+c/2)*r.scale,(b+d/2)*r.scale,r.rotation,r.rotationCx,
+r.rotationCy),a=b.x-c*r.scale/2,b=b.y-d*r.scale/2):(a*=r.scale,b*=r.scale);0!=m&&(e+="rotate("+m+","+-l+","+-g+")");n.setAttribute("transform","translate("+Math.round(a)+","+Math.round(b)+")"+e);p.setAttribute("width",Math.round(Math.max(1,c)));p.setAttribute("height",Math.round(Math.max(1,d)))}};
+mxSvgCanvas2D.prototype.text=function(a,b,c,d,e,f,g,k,l,m,n,p,q){if(this.textEnabled&&null!=e){p=null!=p?p:0;var r=this.state;a+=r.dx;b+=r.dy;if(this.foEnabled&&"html"==l){var t="vertical-align:top;";n?t+="overflow:hidden;max-height:"+Math.round(d)+"px;max-width:"+Math.round(c)+"px;":"fill"==m?t+="width:"+Math.round(c+1)+"px;height:"+Math.round(d+1)+"px;overflow:hidden;":"width"==m&&(t+="width:"+Math.round(c+1)+"px;",0<d&&(t+="max-height:"+Math.round(d)+"px;overflow:hidden;"));var t=k&&0<c?t+("width:"+
+Math.round(c+1)+"px;white-space:normal;word-wrap:"+mxConstants.WORD_WRAP+";"):t+"white-space:nowrap;",u=this.createElement("g");1>r.alpha&&u.setAttribute("opacity",r.alpha);var x=this.createElement("foreignObject");x.setAttribute("style","overflow:visible;");x.setAttribute("pointer-events","all");t=this.createDiv(e,f,g,t,m);if(null!=t){null!=q&&t.setAttribute("dir",q);u.appendChild(x);this.root.appendChild(u);var y,A;q=y=2;if(!mxClient.IS_IE||9!=document.documentMode&&mxClient.IS_SVG){this.root.ownerDocument!=
+document?(t.style.visibility="hidden",document.body.appendChild(t)):x.appendChild(t);var z=t;null!=z.firstChild&&"DIV"==z.firstChild.nodeName&&(z=z.firstChild,k&&"break-word"==t.style.wordWrap&&(z.style.width="100%"));v=z.offsetWidth;0==v&&t.parentNode==x&&(t.style.visibility="hidden",document.body.appendChild(t),v=z.offsetWidth);this.cacheOffsetSize&&(u.mxCachedOffsetWidth=v);!n&&k&&0<c&&this.root.ownerDocument!=document&&"fill"!=m&&"width"!=m&&(B=t.style.whiteSpace,t.style.whiteSpace="nowrap",v<
+z.offsetWidth&&(t.style.whiteSpace=B));y=v+y-1;k&&"fill"!=m&&"width"!=m&&(n&&(y=Math.min(y,c)),t.style.width=y+"px");y=z.offsetWidth;A=z.offsetHeight;this.cacheOffsetSize&&(u.mxCachedFinalOffsetWidth=y,u.mxCachedFinalOffsetHeight=A);A-=q;t.parentNode!=x&&(x.appendChild(t),t.style.visibility="")}else{z=document.createElement("div");z.style.cssText=t.getAttribute("style");z.style.display=mxClient.IS_QUIRKS?"inline":"inline-block";z.style.position="absolute";z.style.visibility="hidden";A=document.createElement("div");
+A.style.display=mxClient.IS_QUIRKS?"inline":"inline-block";A.style.wordWrap=mxConstants.WORD_WRAP;A.innerHTML=mxUtils.isNode(e)?e.outerHTML:e;z.appendChild(A);document.body.appendChild(z);8!=document.documentMode&&9!=document.documentMode&&null!=r.fontBorderColor&&(y+=2,q+=2);if(k&&0<c){var v=A.offsetWidth;padDx=0;if(!n&&k&&0<c&&this.root.ownerDocument!=document&&"fill"!=m){var B=z.style.whiteSpace;A.style.whiteSpace="nowrap";v<A.offsetWidth&&(z.style.whiteSpace=B)}n&&(v=Math.min(v,c));z.style.width=
+v+"px";y=A.offsetWidth+y+padDx;A=A.offsetHeight+q;z.style.display="inline-block";z.style.position="";z.style.visibility="";z.style.width=y+"px";t.setAttribute("style",z.style.cssText)}else y=A.offsetWidth+y,A=A.offsetHeight+q;z.parentNode.removeChild(z);x.appendChild(t)}n&&(A=Math.min(A,d),y=Math.min(y,c));"width"==m?d=A:"fill"!=m&&(c=y,d=A);1>r.alpha&&u.setAttribute("opacity",r.alpha);q=t=0;f==mxConstants.ALIGN_CENTER?t-=c/2:f==mxConstants.ALIGN_RIGHT&&(t-=c);a+=t;g==mxConstants.ALIGN_MIDDLE?q-=
+d/2:g==mxConstants.ALIGN_BOTTOM&&(q-=d);"fill"!=m&&mxClient.IS_FF&&mxClient.IS_WIN&&(q-=2);b+=q;z=1!=r.scale?"scale("+r.scale+")":"";0!=r.rotation&&this.rotateHtml?(z+="rotate("+r.rotation+","+c/2+","+d/2+")",b=this.rotatePoint((a+c/2)*r.scale,(b+d/2)*r.scale,r.rotation,r.rotationCx,r.rotationCy),a=b.x-c*r.scale/2,b=b.y-d*r.scale/2):(a*=r.scale,b*=r.scale);0!=p&&(z+="rotate("+p+","+-t+","+-q+")");u.setAttribute("transform","translate("+(Math.round(a)+this.foOffset)+","+(Math.round(b)+this.foOffset)+
+")"+z);x.setAttribute("width",Math.round(Math.max(1,c)));x.setAttribute("height",Math.round(Math.max(1,d)));this.root.ownerDocument!=document&&(a=this.createAlternateContent(x,a,b,c,d,e,f,g,k,l,m,n,p),null!=a&&(x.setAttribute("requiredFeatures","http://www.w3.org/TR/SVG11/feature#Extensibility"),c=this.createElement("switch"),c.appendChild(x),c.appendChild(a),u.appendChild(c)))}}else this.plainText(a,b,c,d,e,f,g,k,m,n,p,q)}};
+mxSvgCanvas2D.prototype.createClip=function(a,b,c,d){a=Math.round(a);b=Math.round(b);c=Math.round(c);d=Math.round(d);for(var e="mx-clip-"+a+"-"+b+"-"+c+"-"+d,f=0,g=e+"-"+f;null!=document.getElementById(g);)g=e+"-"+ ++f;clip=this.createElement("clipPath");clip.setAttribute("id",g);e=this.createElement("rect");e.setAttribute("x",a);e.setAttribute("y",b);e.setAttribute("width",c);e.setAttribute("height",d);clip.appendChild(e);return clip};
+mxSvgCanvas2D.prototype.plainText=function(a,b,c,d,e,f,g,k,l,m,n,p){n=null!=n?n:0;k=this.state;var q=k.fontSize,r=this.createElement("g"),t=k.transform||"";this.updateFont(r);0!=n&&(t+="rotate("+n+","+this.format(a*k.scale)+","+this.format(b*k.scale)+")");null!=p&&r.setAttribute("direction",p);m&&0<c&&0<d&&(p=a,n=b,f==mxConstants.ALIGN_CENTER?p-=c/2:f==mxConstants.ALIGN_RIGHT&&(p-=c),"fill"!=l&&(g==mxConstants.ALIGN_MIDDLE?n-=d/2:g==mxConstants.ALIGN_BOTTOM&&(n-=d)),n=this.createClip(p*k.scale-2,
+n*k.scale-2,c*k.scale+4,d*k.scale+4),null!=this.defs?this.defs.appendChild(n):this.root.appendChild(n),mxClient.IS_CHROME_APP||mxClient.IS_IE||mxClient.IS_IE11||mxClient.IS_EDGE||this.root.ownerDocument!=document?r.setAttribute("clip-path","url(#"+n.getAttribute("id")+")"):(p=this.getBaseUrl().replace(/([\(\)])/g,"\\$1"),r.setAttribute("clip-path","url("+p+"#"+n.getAttribute("id")+")")));n=f==mxConstants.ALIGN_RIGHT?"end":f==mxConstants.ALIGN_CENTER?"middle":"start";"start"!=n&&r.setAttribute("text-anchor",
+n);this.styleEnabled&&q==mxConstants.DEFAULT_FONTSIZE||r.setAttribute("font-size",q*k.scale+"px");0<t.length&&r.setAttribute("transform",t);1>k.alpha&&r.setAttribute("opacity",k.alpha);t=e.split("\n");p=Math.round(q*mxConstants.LINE_HEIGHT);var u=q+(t.length-1)*p;n=b+q-1;g==mxConstants.ALIGN_MIDDLE?"fill"==l?n-=d/2:(m=(this.matchHtmlAlignment&&m&&0<d?Math.min(u,d):u)/2,n-=m+1):g==mxConstants.ALIGN_BOTTOM&&("fill"==l?n-=d:(m=this.matchHtmlAlignment&&m&&0<d?Math.min(u,d):u,n-=m+2));for(m=0;m<t.length;m++)0<
+t[m].length&&0<mxUtils.trim(t[m]).length&&(q=this.createElement("text"),q.setAttribute("x",this.format(a*k.scale)+this.textOffset),q.setAttribute("y",this.format(n*k.scale)+this.textOffset),mxUtils.write(q,t[m]),r.appendChild(q)),n+=p;this.root.appendChild(r);this.addTextBackground(r,e,a,b,c,"fill"==l?d:u,f,g,l)};
+mxSvgCanvas2D.prototype.updateFont=function(a){var b=this.state;a.setAttribute("fill",b.fontColor);this.styleEnabled&&b.fontFamily==mxConstants.DEFAULT_FONTFAMILY||a.setAttribute("font-family",b.fontFamily);(b.fontStyle&mxConstants.FONT_BOLD)==mxConstants.FONT_BOLD&&a.setAttribute("font-weight","bold");(b.fontStyle&mxConstants.FONT_ITALIC)==mxConstants.FONT_ITALIC&&a.setAttribute("font-style","italic");(b.fontStyle&mxConstants.FONT_UNDERLINE)==mxConstants.FONT_UNDERLINE&&a.setAttribute("text-decoration",
+"underline")};
+mxSvgCanvas2D.prototype.addTextBackground=function(a,b,c,d,e,f,g,k,l){var m=this.state;if(null!=m.fontBackgroundColor||null!=m.fontBorderColor){var n=null;if("fill"==l||"width"==l)g==mxConstants.ALIGN_CENTER?c-=e/2:g==mxConstants.ALIGN_RIGHT&&(c-=e),k==mxConstants.ALIGN_MIDDLE?d-=f/2:k==mxConstants.ALIGN_BOTTOM&&(d-=f),n=new mxRectangle((c+1)*m.scale,d*m.scale,(e-2)*m.scale,(f+2)*m.scale);else if(null!=a.getBBox&&this.root.ownerDocument==document)try{var n=a.getBBox(),p=mxClient.IS_IE&&mxClient.IS_SVG,
+n=new mxRectangle(n.x,n.y+(p?0:1),n.width,n.height+(p?1:0))}catch(q){}else n=document.createElement("div"),n.style.lineHeight=mxConstants.ABSOLUTE_LINE_HEIGHT?m.fontSize*mxConstants.LINE_HEIGHT+"px":mxConstants.LINE_HEIGHT,n.style.fontSize=m.fontSize+"px",n.style.fontFamily=m.fontFamily,n.style.whiteSpace="nowrap",n.style.position="absolute",n.style.visibility="hidden",n.style.display=mxClient.IS_QUIRKS?"inline":"inline-block",n.style.zoom="1",(m.fontStyle&mxConstants.FONT_BOLD)==mxConstants.FONT_BOLD&&
+(n.style.fontWeight="bold"),(m.fontStyle&mxConstants.FONT_ITALIC)==mxConstants.FONT_ITALIC&&(n.style.fontStyle="italic"),b=mxUtils.htmlEntities(b,!1),n.innerHTML=b.replace(/\n/g,"<br/>"),document.body.appendChild(n),e=n.offsetWidth,f=n.offsetHeight,n.parentNode.removeChild(n),g==mxConstants.ALIGN_CENTER?c-=e/2:g==mxConstants.ALIGN_RIGHT&&(c-=e),k==mxConstants.ALIGN_MIDDLE?d-=f/2:k==mxConstants.ALIGN_BOTTOM&&(d-=f),n=new mxRectangle((c+1)*m.scale,(d+2)*m.scale,e*m.scale,(f+1)*m.scale);null!=n&&(b=
+this.createElement("rect"),b.setAttribute("fill",m.fontBackgroundColor||"none"),b.setAttribute("stroke",m.fontBorderColor||"none"),b.setAttribute("x",Math.floor(n.x-1)),b.setAttribute("y",Math.floor(n.y-1)),b.setAttribute("width",Math.ceil(n.width+2)),b.setAttribute("height",Math.ceil(n.height)),m=null!=m.fontBorderColor?Math.max(1,this.format(m.scale)):0,b.setAttribute("stroke-width",m),this.root.ownerDocument==document&&1==mxUtils.mod(m,2)&&b.setAttribute("transform","translate(0.5, 0.5)"),a.insertBefore(b,
+a.firstChild))}};mxSvgCanvas2D.prototype.stroke=function(){this.addNode(!1,!0)};mxSvgCanvas2D.prototype.fill=function(){this.addNode(!0,!1)};mxSvgCanvas2D.prototype.fillAndStroke=function(){this.addNode(!0,!0)};var mxVmlCanvas2D=function(a){mxAbstractCanvas2D.call(this);this.root=a};mxUtils.extend(mxVmlCanvas2D,mxAbstractCanvas2D);mxVmlCanvas2D.prototype.node=null;mxVmlCanvas2D.prototype.textEnabled=!0;mxVmlCanvas2D.prototype.moveOp="m";mxVmlCanvas2D.prototype.lineOp="l";
+mxVmlCanvas2D.prototype.curveOp="c";mxVmlCanvas2D.prototype.closeOp="x";mxVmlCanvas2D.prototype.rotatedHtmlBackground="";mxVmlCanvas2D.prototype.vmlScale=1;mxVmlCanvas2D.prototype.createElement=function(a){return document.createElement(a)};mxVmlCanvas2D.prototype.createVmlElement=function(a){return this.createElement(mxClient.VML_PREFIX+":"+a)};
+mxVmlCanvas2D.prototype.addNode=function(a,b){var c=this.node,d=this.state;if(null!=c){if("shape"==c.nodeName)if(null!=this.path&&0<this.path.length)c.path=this.path.join(" ")+" e",c.style.width=this.root.style.width,c.style.height=this.root.style.height,c.coordsize=parseInt(c.style.width)+" "+parseInt(c.style.height);else return;c.strokeweight=this.format(Math.max(1,d.strokeWidth*d.scale/this.vmlScale))+"px";d.shadow&&this.root.appendChild(this.createShadow(c,a&&null!=d.fillColor,b&&null!=d.strokeColor));
+b&&null!=d.strokeColor?(c.stroked="true",c.strokecolor=d.strokeColor):c.stroked="false";c.appendChild(this.createStroke());a&&null!=d.fillColor?c.appendChild(this.createFill()):!this.pointerEvents||"shape"==c.nodeName&&this.path[this.path.length-1]!=this.closeOp?c.filled="false":c.appendChild(this.createTransparentFill());this.root.appendChild(c)}};
+mxVmlCanvas2D.prototype.createTransparentFill=function(){var a=this.createVmlElement("fill");a.src=mxClient.imageBasePath+"/transparent.gif";a.type="tile";return a};
+mxVmlCanvas2D.prototype.createFill=function(){var a=this.state,b=this.createVmlElement("fill");b.color=a.fillColor;if(null!=a.gradientColor){b.type="gradient";b.method="none";b.color2=a.gradientColor;var c=180-a.rotation,c=a.gradientDirection==mxConstants.DIRECTION_WEST?c-(90+("x"==this.root.style.flip?180:0)):a.gradientDirection==mxConstants.DIRECTION_EAST?c+(90+("x"==this.root.style.flip?180:0)):a.gradientDirection==mxConstants.DIRECTION_NORTH?c-(180+("y"==this.root.style.flip?-180:0)):c+("y"==
+this.root.style.flip?-180:0);if("x"==this.root.style.flip||"y"==this.root.style.flip)c*=-1;b.angle=mxUtils.mod(c,360);b.opacity=a.alpha*a.gradientFillAlpha*100+"%";b.setAttribute(mxClient.OFFICE_PREFIX+":opacity2",a.alpha*a.gradientAlpha*100+"%")}else if(1>a.alpha||1>a.fillAlpha)b.opacity=a.alpha*a.fillAlpha*100+"%";return b};
+mxVmlCanvas2D.prototype.createStroke=function(){var a=this.state,b=this.createVmlElement("stroke");b.endcap=a.lineCap||"flat";b.joinstyle=a.lineJoin||"miter";b.miterlimit=a.miterLimit||"10";if(1>a.alpha||1>a.strokeAlpha)b.opacity=a.alpha*a.strokeAlpha*100+"%";a.dashed&&(b.dashstyle=this.getVmlDashStyle());return b};mxVmlCanvas2D.prototype.getVmlDashStyle=function(){var a="dash";if("string"===typeof this.state.dashPattern){var b=this.state.dashPattern.split(" ");0<b.length&&1==b[0]&&(a="0 2")}return a};
+mxVmlCanvas2D.prototype.createShadow=function(a,b,c){var d=this.state,e=Math.PI/180*-d.rotation,f=Math.cos(e),e=Math.sin(e),g=d.shadowDx*d.scale,k=d.shadowDy*d.scale;"x"==this.root.style.flip?g*=-1:"y"==this.root.style.flip&&(k*=-1);var l=a.cloneNode(!0);l.style.marginLeft=Math.round(g*f-k*e)+"px";l.style.marginTop=Math.round(g*e+k*f)+"px";8==document.documentMode&&(l.strokeweight=a.strokeweight,"shape"==a.nodeName&&(l.path=this.path.join(" ")+" e",l.style.width=this.root.style.width,l.style.height=
+this.root.style.height,l.coordsize=parseInt(a.style.width)+" "+parseInt(a.style.height)));c?(l.strokecolor=d.shadowColor,l.appendChild(this.createShadowStroke())):l.stroked="false";b?l.appendChild(this.createShadowFill()):l.filled="false";return l};mxVmlCanvas2D.prototype.createShadowFill=function(){var a=this.createVmlElement("fill");a.color=this.state.shadowColor;a.opacity=this.state.alpha*this.state.shadowAlpha*100+"%";return a};
+mxVmlCanvas2D.prototype.createShadowStroke=function(){var a=this.createStroke();a.opacity=this.state.alpha*this.state.shadowAlpha*100+"%";return a};mxVmlCanvas2D.prototype.rotate=function(a,b,c,d,e){b&&c?a+=180:b?this.root.style.flip="x":c&&(this.root.style.flip="y");if(b?!c:c)a*=-1;this.root.style.rotation=a;this.state.rotation+=a;this.state.rotationCx=d;this.state.rotationCy=e};
+mxVmlCanvas2D.prototype.begin=function(){mxAbstractCanvas2D.prototype.begin.apply(this,arguments);this.node=this.createVmlElement("shape");this.node.style.position="absolute"};
+mxVmlCanvas2D.prototype.quadTo=function(a,b,c,d){var e=this.state,f=(this.lastX+e.dx)*e.scale,g=(this.lastY+e.dy)*e.scale;a=(a+e.dx)*e.scale;b=(b+e.dy)*e.scale;c=(c+e.dx)*e.scale;d=(d+e.dy)*e.scale;var g=g+2/3*(b-g),k=c+2/3*(a-c);b=d+2/3*(b-d);this.path.push("c "+this.format(f+2/3*(a-f))+" "+this.format(g)+" "+this.format(k)+" "+this.format(b)+" "+this.format(c)+" "+this.format(d));this.lastX=c/e.scale-e.dx;this.lastY=d/e.scale-e.dy};
+mxVmlCanvas2D.prototype.createRect=function(a,b,c,d,e){var f=this.state;a=this.createVmlElement(a);a.style.position="absolute";a.style.left=this.format((b+f.dx)*f.scale)+"px";a.style.top=this.format((c+f.dy)*f.scale)+"px";a.style.width=this.format(d*f.scale)+"px";a.style.height=this.format(e*f.scale)+"px";return a};mxVmlCanvas2D.prototype.rect=function(a,b,c,d){this.node=this.createRect("rect",a,b,c,d)};
+mxVmlCanvas2D.prototype.roundrect=function(a,b,c,d,e,f){this.node=this.createRect("roundrect",a,b,c,d);this.node.setAttribute("arcsize",Math.max(100*e/c,100*f/d)+"%")};mxVmlCanvas2D.prototype.ellipse=function(a,b,c,d){this.node=this.createRect("oval",a,b,c,d)};
+mxVmlCanvas2D.prototype.image=function(a,b,c,d,e,f,g,k){f?(a=this.createRect("rect",a,b,c,d),a.stroked="false",b=this.createVmlElement("fill"),b.aspect=f?"atmost":"ignore",b.rotate="true",b.type="frame",b.src=e,a.appendChild(b)):(a=this.createRect("image",a,b,c,d),a.src=e);g&&k?a.style.rotation="180":g?a.style.flip="x":k&&(a.style.flip="y");if(1>this.state.alpha||1>this.state.fillAlpha)a.style.filter+="alpha(opacity="+this.state.alpha*this.state.fillAlpha*100+")";this.root.appendChild(a)};
+mxVmlCanvas2D.prototype.createDiv=function(a,b,c,d){c=this.createElement("div");var e=this.state,f="";null!=e.fontBackgroundColor&&(f+="background-color:"+e.fontBackgroundColor+";");null!=e.fontBorderColor&&(f+="border:1px solid "+e.fontBorderColor+";");mxUtils.isNode(a)?c.appendChild(a):"fill"!=d&&"width"!=d?(d=this.createElement("div"),d.style.cssText=f,d.style.display=mxClient.IS_QUIRKS?"inline":"inline-block",d.style.zoom="1",d.style.textDecoration="inherit",d.innerHTML=a,c.appendChild(d)):(c.style.cssText=
+f,c.innerHTML=a);a=c.style;a.fontSize=e.fontSize/this.vmlScale+"px";a.fontFamily=e.fontFamily;a.color=e.fontColor;a.verticalAlign="top";a.textAlign=b||"left";a.lineHeight=mxConstants.ABSOLUTE_LINE_HEIGHT?e.fontSize*mxConstants.LINE_HEIGHT/this.vmlScale+"px":mxConstants.LINE_HEIGHT;(e.fontStyle&mxConstants.FONT_BOLD)==mxConstants.FONT_BOLD&&(a.fontWeight="bold");(e.fontStyle&mxConstants.FONT_ITALIC)==mxConstants.FONT_ITALIC&&(a.fontStyle="italic");(e.fontStyle&mxConstants.FONT_UNDERLINE)==mxConstants.FONT_UNDERLINE&&
+(a.textDecoration="underline");return c};
+mxVmlCanvas2D.prototype.text=function(a,b,c,d,e,f,g,k,l,m,n,p,q){if(this.textEnabled&&null!=e){var r=this.state;if("html"==l){null!=r.rotation&&(b=this.rotatePoint(a,b,r.rotation,r.rotationCx,r.rotationCy),a=b.x,b=b.y);8!=document.documentMode||mxClient.IS_EM?(a*=r.scale,b*=r.scale):(a+=r.dx,b+=r.dy,"fill"!=m&&g==mxConstants.ALIGN_TOP&&--b);l=8!=document.documentMode||mxClient.IS_EM?this.createElement("div"):this.createVmlElement("group");l.style.position="absolute";l.style.display="inline";l.style.left=
+this.format(a)+"px";l.style.top=this.format(b)+"px";l.style.zoom=r.scale;var t=this.createElement("div");t.style.position="relative";t.style.display="inline";var u=mxUtils.getAlignmentAsPoint(f,g),x=u.x,u=u.y;e=this.createDiv(e,f,g,m);f=this.createElement("div");null!=q&&e.setAttribute("dir",q);if(k&&0<c){if(n||(e.style.width=Math.round(c)+"px"),e.style.wordWrap=mxConstants.WORD_WRAP,e.style.whiteSpace="normal","break-word"==e.style.wordWrap){var y=e;null!=y.firstChild&&"DIV"==y.firstChild.nodeName&&
+(y.firstChild.style.width="100%")}}else e.style.whiteSpace="nowrap";p=r.rotation+(p||0);this.rotateHtml&&0!=p?(f.style.display="inline",f.style.zoom="1",f.appendChild(e),8!=document.documentMode||mxClient.IS_EM||"DIV"==this.root.nodeName?l.appendChild(f):(t.appendChild(f),l.appendChild(t))):8!=document.documentMode||mxClient.IS_EM?(e.style.display="inline",l.appendChild(e)):(t.appendChild(e),l.appendChild(t));"DIV"!=this.root.nodeName?(q=this.createVmlElement("rect"),q.stroked="false",q.filled="false",
+q.appendChild(l),this.root.appendChild(q)):this.root.appendChild(l);n?(e.style.overflow="hidden",e.style.width=Math.round(c)+"px",mxClient.IS_QUIRKS||(e.style.maxHeight=Math.round(d)+"px")):"fill"==m?(e.style.overflow="hidden",e.style.width=Math.max(0,c)+1+"px",e.style.height=Math.max(0,d)+1+"px"):"width"==m&&(e.style.overflow="hidden",e.style.width=Math.max(0,c)+1+"px",e.style.maxHeight=Math.max(0,d)+1+"px");if(this.rotateHtml&&0!=p){y=Math.PI/180*p;p=parseFloat(parseFloat(Math.cos(y)).toFixed(8));
+q=parseFloat(parseFloat(Math.sin(-y)).toFixed(8));y%=2*Math.PI;0>y&&(y+=2*Math.PI);y%=Math.PI;y>Math.PI/2&&(y=Math.PI-y);g=Math.cos(y);var A=Math.sin(y);8!=document.documentMode||mxClient.IS_EM||(e.style.display="inline-block",f.style.display="inline-block",t.style.display="inline-block");e.style.visibility="hidden";e.style.position="absolute";document.body.appendChild(e);t=e;null!=t.firstChild&&"DIV"==t.firstChild.nodeName&&(t=t.firstChild);y=t.offsetWidth+3;t=t.offsetHeight;n?(c=Math.min(c,y),t=
+Math.min(t,d)):c=y;k&&(e.style.width=c+"px");mxClient.IS_QUIRKS&&(n||"width"==m)&&t>d&&(t=d,e.style.height=t+"px");d=t;n=(d-d*g+c*-A)/2-q*c*(x+.5)+p*d*(u+.5);k=(c-c*g+d*-A)/2+p*c*(x+.5)+q*d*(u+.5);"group"==l.nodeName&&"DIV"==this.root.nodeName?(m=this.createElement("div"),m.style.display="inline-block",m.style.position="absolute",m.style.left=this.format(a+(k-c/2)*r.scale)+"px",m.style.top=this.format(b+(n-d/2)*r.scale)+"px",l.parentNode.appendChild(m),m.appendChild(l)):(r=8!=document.documentMode||
+mxClient.IS_EM?r.scale:1,l.style.left=this.format(a+(k-c/2)*r)+"px",l.style.top=this.format(b+(n-d/2)*r)+"px");f.style.filter="progid:DXImageTransform.Microsoft.Matrix(M11="+p+", M12="+q+", M21="+-q+", M22="+p+", sizingMethod='auto expand')";f.style.backgroundColor=this.rotatedHtmlBackground;1>this.state.alpha&&(f.style.filter+="alpha(opacity="+100*this.state.alpha+")");f.appendChild(e);e.style.position="";e.style.visibility=""}else 8!=document.documentMode||mxClient.IS_EM?(e.style.verticalAlign=
+"top",1>this.state.alpha&&(l.style.filter="alpha(opacity="+100*this.state.alpha+")"),r=e.parentNode,e.style.visibility="hidden",document.body.appendChild(e),c=e.offsetWidth,t=e.offsetHeight,mxClient.IS_QUIRKS&&n&&t>d&&(t=d,e.style.height=t+"px"),d=t,e.style.visibility="",r.appendChild(e),l.style.left=this.format(a+c*x*this.state.scale)+"px",l.style.top=this.format(b+d*u*this.state.scale)+"px"):(1>this.state.alpha&&(e.style.filter="alpha(opacity="+100*this.state.alpha+")"),t.style.left=100*x+"%",t.style.top=
+100*u+"%")}else this.plainText(a,b,c,d,mxUtils.htmlEntities(e,!1),f,g,k,l,m,n,p,q)}};
+mxVmlCanvas2D.prototype.plainText=function(a,b,c,d,e,f,g,k,l,m,n,p,q){k=this.state;a=(a+k.dx)*k.scale;b=(b+k.dy)*k.scale;c=this.createVmlElement("shape");c.style.width="1px";c.style.height="1px";c.stroked="false";d=this.createVmlElement("fill");d.color=k.fontColor;d.opacity=100*k.alpha+"%";c.appendChild(d);d=this.createVmlElement("path");d.textpathok="true";d.v="m "+this.format(0)+" "+this.format(0)+" l "+this.format(1)+" "+this.format(0);c.appendChild(d);d=this.createVmlElement("textpath");d.style.cssText=
+"v-text-align:"+f;d.style.align=f;d.style.fontFamily=k.fontFamily;d.string=e;d.on="true";f=k.fontSize*k.scale/this.vmlScale;d.style.fontSize=f+"px";(k.fontStyle&mxConstants.FONT_BOLD)==mxConstants.FONT_BOLD&&(d.style.fontWeight="bold");(k.fontStyle&mxConstants.FONT_ITALIC)==mxConstants.FONT_ITALIC&&(d.style.fontStyle="italic");(k.fontStyle&mxConstants.FONT_UNDERLINE)==mxConstants.FONT_UNDERLINE&&(d.style.textDecoration="underline");e=e.split("\n");k=f+(e.length-1)*f*mxConstants.LINE_HEIGHT;f=e=0;
+g==mxConstants.ALIGN_BOTTOM?f=-k/2:g!=mxConstants.ALIGN_MIDDLE&&(f=k/2);null!=p&&(c.style.rotation=p,g=Math.PI/180*p,e=Math.sin(g)*f,f*=Math.cos(g));c.appendChild(d);c.style.left=this.format(a-e)+"px";c.style.top=this.format(b+f)+"px";this.root.appendChild(c)};mxVmlCanvas2D.prototype.stroke=function(){this.addNode(!1,!0)};mxVmlCanvas2D.prototype.fill=function(){this.addNode(!0,!1)};mxVmlCanvas2D.prototype.fillAndStroke=function(){this.addNode(!0,!0)};
+function mxGuide(a,b){this.graph=a;this.setStates(b)}mxGuide.prototype.graph=null;mxGuide.prototype.states=null;mxGuide.prototype.horizontal=!0;mxGuide.prototype.vertical=!0;mxGuide.prototype.guideX=null;mxGuide.prototype.guideY=null;mxGuide.prototype.setStates=function(a){this.states=a};mxGuide.prototype.isEnabledForEvent=function(a){return!0};mxGuide.prototype.getGuideTolerance=function(){return this.graph.gridSize/2};
+mxGuide.prototype.createGuideShape=function(a){a=new mxPolyline([],mxConstants.GUIDE_COLOR,mxConstants.GUIDE_STROKEWIDTH);a.isDashed=!0;return a};
+mxGuide.prototype.move=function(a,b,c){if(null!=this.states&&(this.horizontal||this.vertical)&&null!=a&&null!=b){var d=function(b){b+=this.graph.panDy;var c=!1;Math.abs(b-F)<y?(l=b-a.getCenterY(),y=Math.abs(b-F),c=!0):Math.abs(b-B)<y?(l=b-a.y,y=Math.abs(b-B),c=!0):Math.abs(b-C)<y&&(l=b-a.y-a.height,y=Math.abs(b-C),c=!0);c&&(r=D,t=Math.round(b-this.graph.panDy),null==this.guideY&&(this.guideY=this.createGuideShape(!1),this.guideY.dialect=this.graph.dialect!=mxConstants.DIALECT_SVG?mxConstants.DIALECT_VML:
+mxConstants.DIALECT_SVG,this.guideY.pointerEvents=!1,this.guideY.init(this.graph.getView().getOverlayPane())));q=q||c},e=function(b,c){b+=this.graph.panDx;var d=!1;Math.abs(b-v)<x?(k=b-a.getCenterX(),x=Math.abs(b-v),d=!0):Math.abs(b-A)<x?(k=b-a.x,x=Math.abs(b-A),d=!0):Math.abs(b-z)<x&&(k=b-a.x-a.width,x=Math.abs(b-z),d=!0);d&&(n=c,p=Math.round(b-this.graph.panDx),null==this.guideX&&(this.guideX=this.createGuideShape(!0),this.guideX.dialect=this.graph.dialect!=mxConstants.DIALECT_SVG?mxConstants.DIALECT_VML:
+mxConstants.DIALECT_SVG,this.guideX.pointerEvents=!1,this.guideX.init(this.graph.getView().getOverlayPane())));m=m||d},f=this.graph.getView().translate,g=this.graph.getView().scale,k=b.x,l=b.y,m=!1,n=null,p=null,q=!1,r=null,t=null,u=this.getGuideTolerance(),x=u,y=u,u=a.clone();u.x+=b.x;u.y+=b.y;var A=u.x,z=u.x+u.width,v=u.getCenterX(),B=u.y,C=u.y+u.height,F=u.getCenterY();for(b=0;b<this.states.length;b++){var D=this.states[b];null!=D&&(this.horizontal&&(e.call(this,D.getCenterX(),D),e.call(this,D.x,
+D),e.call(this,D.x+D.width,D)),this.vertical&&(d.call(this,D.getCenterY(),D),d.call(this,D.y,D),d.call(this,D.y+D.height,D)))}c&&(m||(c=a.x-(this.graph.snap(a.x/g-f.x)+f.x)*g,k=this.graph.snap(k/g)*g-c),q||(f=a.y-(this.graph.snap(a.y/g-f.y)+f.y)*g,l=this.graph.snap(l/g)*g-f));g=this.graph.container;m||null==this.guideX?null!=this.guideX&&(null!=n&&null!=a&&(minY=Math.min(a.y+l-this.graph.panDy,n.y),maxY=Math.max(a.y+a.height+l-this.graph.panDy,n.y+n.height)),this.guideX.points=null!=minY&&null!=maxY?
+[new mxPoint(p,minY),new mxPoint(p,maxY)]:[new mxPoint(p,-this.graph.panDy),new mxPoint(p,g.scrollHeight-3-this.graph.panDy)],this.guideX.stroke=this.getGuideColor(n,!0),this.guideX.node.style.visibility="visible",this.guideX.redraw()):this.guideX.node.style.visibility="hidden";q||null==this.guideY?null!=this.guideY&&(null!=r&&null!=a&&(minX=Math.min(a.x+k-this.graph.panDx,r.x),maxX=Math.max(a.x+a.width+k-this.graph.panDx,r.x+r.width)),this.guideY.points=null!=minX&&null!=maxX?[new mxPoint(minX,t),
+new mxPoint(maxX,t)]:[new mxPoint(-this.graph.panDx,t),new mxPoint(g.scrollWidth-3-this.graph.panDx,t)],this.guideY.stroke=this.getGuideColor(r,!1),this.guideY.node.style.visibility="visible",this.guideY.redraw()):this.guideY.node.style.visibility="hidden";b=new mxPoint(k,l)}return b};mxGuide.prototype.getGuideColor=function(a,b){return mxConstants.GUIDE_COLOR};mxGuide.prototype.hide=function(){this.setVisible(!1)};
+mxGuide.prototype.setVisible=function(a){null!=this.guideX&&(this.guideX.node.style.visibility=a?"visible":"hidden");null!=this.guideY&&(this.guideY.node.style.visibility=a?"visible":"hidden")};mxGuide.prototype.destroy=function(){null!=this.guideX&&(this.guideX.destroy(),this.guideX=null);null!=this.guideY&&(this.guideY.destroy(),this.guideY=null)};function mxStencil(a){this.desc=a;this.parseDescription();this.parseConstraints()}mxStencil.defaultLocalized=!1;mxStencil.allowEval=!1;
+mxStencil.prototype.desc=null;mxStencil.prototype.constraints=null;mxStencil.prototype.aspect=null;mxStencil.prototype.w0=null;mxStencil.prototype.h0=null;mxStencil.prototype.bgNode=null;mxStencil.prototype.fgNode=null;mxStencil.prototype.strokewidth=null;
+mxStencil.prototype.parseDescription=function(){this.fgNode=this.desc.getElementsByTagName("foreground")[0];this.bgNode=this.desc.getElementsByTagName("background")[0];this.w0=Number(this.desc.getAttribute("w")||100);this.h0=Number(this.desc.getAttribute("h")||100);var a=this.desc.getAttribute("aspect");this.aspect=null!=a?a:"variable";a=this.desc.getAttribute("strokewidth");this.strokewidth=null!=a?a:"1"};
+mxStencil.prototype.parseConstraints=function(){var a=this.desc.getElementsByTagName("connections")[0];if(null!=a&&(a=mxUtils.getChildNodes(a),null!=a&&0<a.length)){this.constraints=[];for(var b=0;b<a.length;b++)this.constraints.push(this.parseConstraint(a[b]))}};mxStencil.prototype.parseConstraint=function(a){var b=Number(a.getAttribute("x")),c=Number(a.getAttribute("y")),d="1"==a.getAttribute("perimeter");a=a.getAttribute("name");return new mxConnectionConstraint(new mxPoint(b,c),d,a)};
+mxStencil.prototype.evaluateTextAttribute=function(a,b,c){b=this.evaluateAttribute(a,b,c);a=a.getAttribute("localized");if(mxStencil.defaultLocalized&&null==a||"1"==a)b=mxResources.get(b);return b};mxStencil.prototype.evaluateAttribute=function(a,b,c){b=a.getAttribute(b);null==b&&(a=mxUtils.getTextContent(a),null!=a&&mxStencil.allowEval&&(a=mxUtils.eval(a),"function"==typeof a&&(b=a(c))));return b};
+mxStencil.prototype.drawShape=function(a,b,c,d,e,f){var g=mxUtils.getValue(b.style,mxConstants.STYLE_DIRECTION,null),g=this.computeAspect(b.style,c,d,e,f,g),k=Math.min(g.width,g.height),k="inherit"==this.strokewidth?Number(mxUtils.getNumber(b.style,mxConstants.STYLE_STROKEWIDTH,1)):Number(this.strokewidth)*k;a.setStrokeWidth(k);this.drawChildren(a,b,c,d,e,f,this.bgNode,g,!1);this.drawChildren(a,b,c,d,e,f,this.fgNode,g,!0)};
+mxStencil.prototype.drawChildren=function(a,b,c,d,e,f,g,k,l){if(null!=g&&0<e&&0<f)for(c=g.firstChild;null!=c;)c.nodeType==mxConstants.NODETYPE_ELEMENT&&this.drawNode(a,b,c,k,l),c=c.nextSibling};
+mxStencil.prototype.computeAspect=function(a,b,c,d,e,f){a=b;b=d/this.w0;var g=e/this.h0;if(f=f==mxConstants.DIRECTION_NORTH||f==mxConstants.DIRECTION_SOUTH){g=d/this.h0;b=e/this.w0;var k=(d-e)/2;a+=k;c-=k}"fixed"==this.aspect&&(b=g=Math.min(b,g),f?(a+=(e-this.w0*b)/2,c+=(d-this.h0*g)/2):(a+=(d-this.w0*b)/2,c+=(e-this.h0*g)/2));return new mxRectangle(a,c,b,g)};
+mxStencil.prototype.drawNode=function(a,b,c,d,e){var f=c.nodeName,g=d.x,k=d.y,l=d.width,m=d.height,n=Math.min(l,m);if("save"==f)a.save();else if("restore"==f)a.restore();else if("path"==f)for(a.begin(),c=c.firstChild;null!=c;)c.nodeType==mxConstants.NODETYPE_ELEMENT&&this.drawNode(a,b,c,d,e),c=c.nextSibling;else if("close"==f)a.close();else if("move"==f)a.moveTo(g+Number(c.getAttribute("x"))*l,k+Number(c.getAttribute("y"))*m);else if("line"==f)a.lineTo(g+Number(c.getAttribute("x"))*l,k+Number(c.getAttribute("y"))*
+m);else if("quad"==f)a.quadTo(g+Number(c.getAttribute("x1"))*l,k+Number(c.getAttribute("y1"))*m,g+Number(c.getAttribute("x2"))*l,k+Number(c.getAttribute("y2"))*m);else if("curve"==f)a.curveTo(g+Number(c.getAttribute("x1"))*l,k+Number(c.getAttribute("y1"))*m,g+Number(c.getAttribute("x2"))*l,k+Number(c.getAttribute("y2"))*m,g+Number(c.getAttribute("x3"))*l,k+Number(c.getAttribute("y3"))*m);else if("arc"==f)a.arcTo(Number(c.getAttribute("rx"))*l,Number(c.getAttribute("ry"))*m,Number(c.getAttribute("x-axis-rotation")),
+Number(c.getAttribute("large-arc-flag")),Number(c.getAttribute("sweep-flag")),g+Number(c.getAttribute("x"))*l,k+Number(c.getAttribute("y"))*m);else if("rect"==f)a.rect(g+Number(c.getAttribute("x"))*l,k+Number(c.getAttribute("y"))*m,Number(c.getAttribute("w"))*l,Number(c.getAttribute("h"))*m);else if("roundrect"==f)b=Number(c.getAttribute("arcsize")),0==b&&(b=100*mxConstants.RECTANGLE_ROUNDING_FACTOR),n=Number(c.getAttribute("w"))*l,d=Number(c.getAttribute("h"))*m,b=Number(b)/100,b=Math.min(n*b,d*
+b),a.roundrect(g+Number(c.getAttribute("x"))*l,k+Number(c.getAttribute("y"))*m,n,d,b,b);else if("ellipse"==f)a.ellipse(g+Number(c.getAttribute("x"))*l,k+Number(c.getAttribute("y"))*m,Number(c.getAttribute("w"))*l,Number(c.getAttribute("h"))*m);else if("image"==f)b.outline||(b=this.evaluateAttribute(c,"src",b),a.image(g+Number(c.getAttribute("x"))*l,k+Number(c.getAttribute("y"))*m,Number(c.getAttribute("w"))*l,Number(c.getAttribute("h"))*m,b,!1,"1"==c.getAttribute("flipH"),"1"==c.getAttribute("flipV")));
+else if("text"==f){if(!b.outline){n=this.evaluateTextAttribute(c,"str",b);d="1"==c.getAttribute("vertical")?-90:0;if("0"==c.getAttribute("align-shape")){var p=b.rotation,q=1==mxUtils.getValue(b.style,mxConstants.STYLE_FLIPH,0);b=1==mxUtils.getValue(b.style,mxConstants.STYLE_FLIPV,0);d=q&&b?d-p:q||b?d+p:d-p}d-=c.getAttribute("rotation");a.text(g+Number(c.getAttribute("x"))*l,k+Number(c.getAttribute("y"))*m,0,0,n,c.getAttribute("align")||"left",c.getAttribute("valign")||"top",!1,"",null,!1,d)}}else if("include-shape"==
+f)p=mxStencilRegistry.getStencil(c.getAttribute("name")),null!=p&&(g+=Number(c.getAttribute("x"))*l,k+=Number(c.getAttribute("y"))*m,n=Number(c.getAttribute("w"))*l,d=Number(c.getAttribute("h"))*m,p.drawShape(a,b,g,k,n,d));else if("fillstroke"==f)a.fillAndStroke();else if("fill"==f)a.fill();else if("stroke"==f)a.stroke();else if("strokewidth"==f)l="1"==c.getAttribute("fixed")?1:n,a.setStrokeWidth(Number(c.getAttribute("width"))*l);else if("dashed"==f)a.setDashed("1"==c.getAttribute("dashed"));else if("dashpattern"==
+f){if(c=c.getAttribute("pattern"),null!=c){c=c.split(" ");l=[];for(m=0;m<c.length;m++)0<c[m].length&&l.push(Number(c[m])*n);c=l.join(" ");a.setDashPattern(c)}}else"strokecolor"==f?a.setStrokeColor(c.getAttribute("color")):"linecap"==f?a.setLineCap(c.getAttribute("cap")):"linejoin"==f?a.setLineJoin(c.getAttribute("join")):"miterlimit"==f?a.setMiterLimit(Number(c.getAttribute("limit"))):"fillcolor"==f?a.setFillColor(c.getAttribute("color")):"alpha"==f?a.setAlpha(c.getAttribute("alpha")):"fontcolor"==
+f?a.setFontColor(c.getAttribute("color")):"fontstyle"==f?a.setFontStyle(c.getAttribute("style")):"fontfamily"==f?a.setFontFamily(c.getAttribute("family")):"fontsize"==f&&a.setFontSize(Number(c.getAttribute("size"))*n);!e||"fillstroke"!=f&&"fill"!=f&&"stroke"!=f||a.setShadow(!1)};function mxShape(a){this.stencil=a;this.initStyles()}mxShape.prototype.dialect=null;mxShape.prototype.scale=1;mxShape.prototype.antiAlias=!0;mxShape.prototype.minSvgStrokeWidth=1;mxShape.prototype.bounds=null;
+mxShape.prototype.points=null;mxShape.prototype.node=null;mxShape.prototype.state=null;mxShape.prototype.style=null;mxShape.prototype.boundingBox=null;mxShape.prototype.stencil=null;mxShape.prototype.svgStrokeTolerance=8;mxShape.prototype.pointerEvents=!0;mxShape.prototype.svgPointerEvents="all";mxShape.prototype.shapePointerEvents=!1;mxShape.prototype.stencilPointerEvents=!1;mxShape.prototype.vmlScale=1;mxShape.prototype.outline=!1;mxShape.prototype.visible=!0;
+mxShape.prototype.useSvgBoundingBox=!1;mxShape.prototype.init=function(a){null==this.node&&(this.node=this.create(a),null!=a&&a.appendChild(this.node))};mxShape.prototype.initStyles=function(a){this.strokewidth=1;this.rotation=0;this.strokeOpacity=this.fillOpacity=this.opacity=100;this.flipV=this.flipH=!1};mxShape.prototype.isParseVml=function(){return!0};mxShape.prototype.isHtmlAllowed=function(){return!1};
+mxShape.prototype.getSvgScreenOffset=function(){return 1==mxUtils.mod(Math.max(1,Math.round((this.stencil&&"inherit"!=this.stencil.strokewidth?Number(this.stencil.strokewidth):this.strokewidth)*this.scale)),2)?.5:0};mxShape.prototype.create=function(a){return null!=a&&null!=a.ownerSVGElement?this.createSvg(a):8==document.documentMode||!mxClient.IS_VML||this.dialect!=mxConstants.DIALECT_VML&&this.isHtmlAllowed()?this.createHtml(a):this.createVml(a)};
+mxShape.prototype.createSvg=function(){return document.createElementNS(mxConstants.NS_SVG,"g")};mxShape.prototype.createVml=function(){var a=document.createElement(mxClient.VML_PREFIX+":group");a.style.position="absolute";return a};mxShape.prototype.createHtml=function(){var a=document.createElement("div");a.style.position="absolute";return a};mxShape.prototype.reconfigure=function(){this.redraw()};
+mxShape.prototype.redraw=function(){this.updateBoundsFromPoints();this.visible&&this.checkBounds()?(this.node.style.visibility="visible",this.clear(),"DIV"!=this.node.nodeName||!this.isHtmlAllowed()&&mxClient.IS_VML?this.redrawShape():this.redrawHtmlShape(),this.updateBoundingBox()):(this.node.style.visibility="hidden",this.boundingBox=null)};
+mxShape.prototype.clear=function(){if(null!=this.node.ownerSVGElement)for(;null!=this.node.lastChild;)this.node.removeChild(this.node.lastChild);else this.node.style.cssText="position:absolute;"+(null!=this.cursor?"cursor:"+this.cursor+";":""),this.node.innerHTML=""};
+mxShape.prototype.updateBoundsFromPoints=function(){var a=this.points;if(null!=a&&0<a.length&&null!=a[0]){this.bounds=new mxRectangle(Number(a[0].x),Number(a[0].y),1,1);for(var b=1;b<this.points.length;b++)null!=a[b]&&this.bounds.add(new mxRectangle(Number(a[b].x),Number(a[b].y),1,1))}};
+mxShape.prototype.getLabelBounds=function(a){var b=mxUtils.getValue(this.style,mxConstants.STYLE_DIRECTION,mxConstants.DIRECTION_EAST),c=a;b!=mxConstants.DIRECTION_SOUTH&&b!=mxConstants.DIRECTION_NORTH&&null!=this.state&&null!=this.state.text&&this.state.text.isPaintBoundsInverted()&&(c=c.clone(),b=c.width,c.width=c.height,c.height=b);c=this.getLabelMargins(c);if(null!=c){var d="1"==mxUtils.getValue(this.style,mxConstants.STYLE_FLIPH,!1),e="1"==mxUtils.getValue(this.style,mxConstants.STYLE_FLIPV,
+!1);null!=this.state&&null!=this.state.text&&this.state.text.isPaintBoundsInverted()&&(b=c.x,c.x=c.height,c.height=c.width,c.width=c.y,c.y=b,b=d,d=e,e=b);return mxUtils.getDirectedBounds(a,c,this.style,d,e)}return a};mxShape.prototype.getLabelMargins=function(a){return null};
+mxShape.prototype.checkBounds=function(){return!isNaN(this.scale)&&isFinite(this.scale)&&0<this.scale&&null!=this.bounds&&!isNaN(this.bounds.x)&&!isNaN(this.bounds.y)&&!isNaN(this.bounds.width)&&!isNaN(this.bounds.height)&&0<this.bounds.width&&0<this.bounds.height};mxShape.prototype.createVmlGroup=function(){var a=document.createElement(mxClient.VML_PREFIX+":group");a.style.position="absolute";a.style.width=this.node.style.width;a.style.height=this.node.style.height;return a};
+mxShape.prototype.redrawShape=function(){var a=this.createCanvas();null!=a&&(a.pointerEvents=this.pointerEvents,this.paint(a),this.node!=a.root&&this.node.insertAdjacentHTML("beforeend",a.root.outerHTML),"DIV"==this.node.nodeName&&8==document.documentMode&&(this.node.style.filter="",mxUtils.addTransparentBackgroundFilter(this.node)),this.destroyCanvas(a))};
+mxShape.prototype.createCanvas=function(){var a=null;null!=this.node.ownerSVGElement?a=this.createSvgCanvas():mxClient.IS_VML&&(this.updateVmlContainer(),a=this.createVmlCanvas());null!=a&&this.outline&&(a.setStrokeWidth(this.strokewidth),a.setStrokeColor(this.stroke),null!=this.isDashed&&a.setDashed(this.isDashed),a.setStrokeWidth=function(){},a.setStrokeColor=function(){},a.setFillColor=function(){},a.setGradient=function(){},a.setDashed=function(){});return a};
+mxShape.prototype.createSvgCanvas=function(){var a=new mxSvgCanvas2D(this.node,!1);a.strokeTolerance=this.pointerEvents?this.svgStrokeTolerance:0;a.pointerEventsValue=this.svgPointerEvents;a.blockImagePointerEvents=mxClient.IS_FF;var b=this.getSvgScreenOffset();0!=b?this.node.setAttribute("transform","translate("+b+","+b+")"):this.node.removeAttribute("transform");a.minStrokeWidth=this.minSvgStrokeWidth;this.antiAlias||(a.format=function(a){return Math.round(parseFloat(a))});return a};
+mxShape.prototype.createVmlCanvas=function(){var a=8==document.documentMode&&this.isParseVml()?this.createVmlGroup():this.node,b=new mxVmlCanvas2D(a,!1);""!=a.tagUrn&&(a.coordsize=Math.max(1,Math.round(this.bounds.width))*this.vmlScale+","+Math.max(1,Math.round(this.bounds.height))*this.vmlScale,b.scale(this.vmlScale),b.vmlScale=this.vmlScale);a=this.scale;b.translate(-Math.round(this.bounds.x/a),-Math.round(this.bounds.y/a));return b};
+mxShape.prototype.updateVmlContainer=function(){this.node.style.left=Math.round(this.bounds.x)+"px";this.node.style.top=Math.round(this.bounds.y)+"px";var a=Math.max(1,Math.round(this.bounds.height));this.node.style.width=Math.max(1,Math.round(this.bounds.width))+"px";this.node.style.height=a+"px";this.node.style.overflow="visible"};mxShape.prototype.redrawHtmlShape=function(){this.updateHtmlBounds(this.node);this.updateHtmlFilters(this.node);this.updateHtmlColors(this.node)};
+mxShape.prototype.updateHtmlFilters=function(a){var b="";100>this.opacity&&(b+="alpha(opacity="+this.opacity+")");this.isShadow&&(b+="progid:DXImageTransform.Microsoft.dropShadow (OffX='"+Math.round(mxConstants.SHADOW_OFFSET_X*this.scale)+"', OffY='"+Math.round(mxConstants.SHADOW_OFFSET_Y*this.scale)+"', Color='"+mxConstants.VML_SHADOWCOLOR+"')");if(null!=this.fill&&this.fill!=mxConstants.NONE&&this.gradient&&this.gradient!=mxConstants.NONE){var c=this.fill,d=this.gradient,e="0",f={east:0,south:1,
+west:2,north:3},g=null!=this.direction?f[this.direction]:0;null!=this.gradientDirection&&(g=mxUtils.mod(g+f[this.gradientDirection]-1,4));1==g?(e="1",f=c,c=d,d=f):2==g?(f=c,c=d,d=f):3==g&&(e="1");b+="progid:DXImageTransform.Microsoft.gradient(startColorStr='"+c+"', endColorStr='"+d+"', gradientType='"+e+"')"}a.style.filter=b};
+mxShape.prototype.updateHtmlColors=function(a){var b=this.stroke;null!=b&&b!=mxConstants.NONE?(a.style.borderColor=b,this.isDashed?a.style.borderStyle="dashed":0<this.strokewidth&&(a.style.borderStyle="solid"),a.style.borderWidth=Math.max(1,Math.ceil(this.strokewidth*this.scale))+"px"):a.style.borderWidth="0px";b=this.outline?null:this.fill;null!=b&&b!=mxConstants.NONE?(a.style.backgroundColor=b,a.style.backgroundImage="none"):this.pointerEvents?a.style.backgroundColor="transparent":8==document.documentMode?
+mxUtils.addTransparentBackgroundFilter(a):this.setTransparentBackgroundImage(a)};
+mxShape.prototype.updateHtmlBounds=function(a){var b=9<=document.documentMode?0:Math.ceil(this.strokewidth*this.scale);a.style.borderWidth=Math.max(1,b)+"px";a.style.overflow="hidden";a.style.left=Math.round(this.bounds.x-b/2)+"px";a.style.top=Math.round(this.bounds.y-b/2)+"px";"CSS1Compat"==document.compatMode&&(b=-b);a.style.width=Math.round(Math.max(0,this.bounds.width+b))+"px";a.style.height=Math.round(Math.max(0,this.bounds.height+b))+"px"};
+mxShape.prototype.destroyCanvas=function(a){if(a instanceof mxSvgCanvas2D){for(var b in a.gradients){var c=a.gradients[b];null!=c&&(c.mxRefCount=(c.mxRefCount||0)+1)}this.releaseSvgGradients(this.oldGradients);this.oldGradients=a.gradients}};
+mxShape.prototype.paint=function(a){var b=!1;if(null!=a&&this.outline){var c=a.stroke;a.stroke=function(){b=!0;c.apply(this,arguments)};var d=a.fillAndStroke;a.fillAndStroke=function(){b=!0;d.apply(this,arguments)}}var e=this.scale,f=this.bounds.x/e,g=this.bounds.y/e,k=this.bounds.width/e,l=this.bounds.height/e;if(this.isPaintBoundsInverted())var m=(k-l)/2,f=f+m,g=g-m,m=k,k=l,l=m;this.updateTransform(a,f,g,k,l);this.configureCanvas(a,f,g,k,l);m=null;if(null==this.stencil&&null==this.points&&this.shapePointerEvents||
+null!=this.stencil&&this.stencilPointerEvents){var n=this.createBoundingBox();this.dialect==mxConstants.DIALECT_SVG?(m=this.createTransparentSvgRectangle(n.x,n.y,n.width,n.height),this.node.appendChild(m)):(n=a.createRect("rect",n.x/e,n.y/e,n.width/e,n.height/e),n.appendChild(a.createTransparentFill()),n.stroked="false",a.root.appendChild(n))}if(null!=this.stencil)this.stencil.drawShape(a,this,f,g,k,l);else if(a.setStrokeWidth(this.strokewidth),null!=this.points){for(var n=[],p=0;p<this.points.length;p++)null!=
+this.points[p]&&n.push(new mxPoint(this.points[p].x/e,this.points[p].y/e));this.paintEdgeShape(a,n)}else this.paintVertexShape(a,f,g,k,l);null!=m&&null!=a.state&&null!=a.state.transform&&m.setAttribute("transform",a.state.transform);null!=a&&this.outline&&!b&&(a.rect(f,g,k,l),a.stroke())};
+mxShape.prototype.configureCanvas=function(a,b,c,d,e){var f=null;null!=this.style&&(f=this.style.dashPattern);a.setAlpha(this.opacity/100);a.setFillAlpha(this.fillOpacity/100);a.setStrokeAlpha(this.strokeOpacity/100);null!=this.isShadow&&a.setShadow(this.isShadow);null!=this.isDashed&&a.setDashed(this.isDashed,null!=this.style?1==mxUtils.getValue(this.style,mxConstants.STYLE_FIX_DASH,!1):!1);null!=f&&a.setDashPattern(f);null!=this.fill&&this.fill!=mxConstants.NONE&&this.gradient&&this.gradient!=mxConstants.NONE?
+(b=this.getGradientBounds(a,b,c,d,e),a.setGradient(this.fill,this.gradient,b.x,b.y,b.width,b.height,this.gradientDirection)):a.setFillColor(this.fill);a.setStrokeColor(this.stroke)};mxShape.prototype.getGradientBounds=function(a,b,c,d,e){return new mxRectangle(b,c,d,e)};mxShape.prototype.updateTransform=function(a,b,c,d,e){a.scale(this.scale);a.rotate(this.getShapeRotation(),this.flipH,this.flipV,b+d/2,c+e/2)};
+mxShape.prototype.paintVertexShape=function(a,b,c,d,e){this.paintBackground(a,b,c,d,e);a.setShadow(!1);this.paintForeground(a,b,c,d,e)};mxShape.prototype.paintBackground=function(a,b,c,d,e){};mxShape.prototype.paintForeground=function(a,b,c,d,e){};mxShape.prototype.paintEdgeShape=function(a,b){};
+mxShape.prototype.getArcSize=function(a,b){var c;"1"==mxUtils.getValue(this.style,mxConstants.STYLE_ABSOLUTE_ARCSIZE,0)?c=Math.min(a/2,Math.min(b/2,mxUtils.getValue(this.style,mxConstants.STYLE_ARCSIZE,mxConstants.LINE_ARCSIZE)/2)):(c=mxUtils.getValue(this.style,mxConstants.STYLE_ARCSIZE,100*mxConstants.RECTANGLE_ROUNDING_FACTOR)/100,c=Math.min(a*c,b*c));return c};
+mxShape.prototype.paintGlassEffect=function(a,b,c,d,e,f){var g=Math.ceil(this.strokewidth/2);a.setGradient("#ffffff","#ffffff",b,c,d,.6*e,"south",.9,.1);a.begin();f+=2*g;this.isRounded?(a.moveTo(b-g+f,c-g),a.quadTo(b-g,c-g,b-g,c-g+f),a.lineTo(b-g,c+.4*e),a.quadTo(b+.5*d,c+.7*e,b+d+g,c+.4*e),a.lineTo(b+d+g,c-g+f),a.quadTo(b+d+g,c-g,b+d+g-f,c-g)):(a.moveTo(b-g,c-g),a.lineTo(b-g,c+.4*e),a.quadTo(b+.5*d,c+.7*e,b+d+g,c+.4*e),a.lineTo(b+d+g,c-g));a.close();a.fill()};
+mxShape.prototype.addPoints=function(a,b,c,d,e,f,g){if(null!=b&&0<b.length){g=null!=g?g:!0;var k=b[b.length-1];if(e&&c){b=b.slice();var l=b[0],l=new mxPoint(k.x+(l.x-k.x)/2,k.y+(l.y-k.y)/2);b.splice(0,0,l)}var m=b[0],l=1;for(g?a.moveTo(m.x,m.y):a.lineTo(m.x,m.y);l<(e?b.length:b.length-1);){g=b[mxUtils.mod(l,b.length)];var n=m.x-g.x,m=m.y-g.y;if(c&&(0!=n||0!=m)&&(null==f||0>mxUtils.indexOf(f,l-1))){var p=Math.sqrt(n*n+m*m);a.lineTo(g.x+n*Math.min(d,p/2)/p,g.y+m*Math.min(d,p/2)/p);for(m=b[mxUtils.mod(l+
+1,b.length)];l<b.length-2&&0==Math.round(m.x-g.x)&&0==Math.round(m.y-g.y);)m=b[mxUtils.mod(l+2,b.length)],l++;n=m.x-g.x;m=m.y-g.y;p=Math.max(1,Math.sqrt(n*n+m*m));n=g.x+n*Math.min(d,p/2)/p;m=g.y+m*Math.min(d,p/2)/p;a.quadTo(g.x,g.y,n,m);g=new mxPoint(n,m)}else a.lineTo(g.x,g.y);m=g;l++}e?a.close():a.lineTo(k.x,k.y)}};
+mxShape.prototype.resetStyles=function(){this.initStyles();this.spacing=0;delete this.fill;delete this.gradient;delete this.gradientDirection;delete this.stroke;delete this.startSize;delete this.endSize;delete this.startArrow;delete this.endArrow;delete this.direction;delete this.isShadow;delete this.isDashed;delete this.isRounded;delete this.glass};
+mxShape.prototype.apply=function(a){this.state=a;this.style=a.style;if(null!=this.style){this.fill=mxUtils.getValue(this.style,mxConstants.STYLE_FILLCOLOR,this.fill);this.gradient=mxUtils.getValue(this.style,mxConstants.STYLE_GRADIENTCOLOR,this.gradient);this.gradientDirection=mxUtils.getValue(this.style,mxConstants.STYLE_GRADIENT_DIRECTION,this.gradientDirection);this.opacity=mxUtils.getValue(this.style,mxConstants.STYLE_OPACITY,this.opacity);this.fillOpacity=mxUtils.getValue(this.style,mxConstants.STYLE_FILL_OPACITY,
+this.fillOpacity);this.strokeOpacity=mxUtils.getValue(this.style,mxConstants.STYLE_STROKE_OPACITY,this.strokeOpacity);this.stroke=mxUtils.getValue(this.style,mxConstants.STYLE_STROKECOLOR,this.stroke);this.strokewidth=mxUtils.getNumber(this.style,mxConstants.STYLE_STROKEWIDTH,this.strokewidth);this.spacing=mxUtils.getValue(this.style,mxConstants.STYLE_SPACING,this.spacing);this.startSize=mxUtils.getNumber(this.style,mxConstants.STYLE_STARTSIZE,this.startSize);this.endSize=mxUtils.getNumber(this.style,
+mxConstants.STYLE_ENDSIZE,this.endSize);this.startArrow=mxUtils.getValue(this.style,mxConstants.STYLE_STARTARROW,this.startArrow);this.endArrow=mxUtils.getValue(this.style,mxConstants.STYLE_ENDARROW,this.endArrow);this.rotation=mxUtils.getValue(this.style,mxConstants.STYLE_ROTATION,this.rotation);this.direction=mxUtils.getValue(this.style,mxConstants.STYLE_DIRECTION,this.direction);this.flipH=1==mxUtils.getValue(this.style,mxConstants.STYLE_FLIPH,0);this.flipV=1==mxUtils.getValue(this.style,mxConstants.STYLE_FLIPV,
+0);null!=this.stencil&&(this.flipH=1==mxUtils.getValue(this.style,"stencilFlipH",0)||this.flipH,this.flipV=1==mxUtils.getValue(this.style,"stencilFlipV",0)||this.flipV);if(this.direction==mxConstants.DIRECTION_NORTH||this.direction==mxConstants.DIRECTION_SOUTH)a=this.flipH,this.flipH=this.flipV,this.flipV=a;this.isShadow=1==mxUtils.getValue(this.style,mxConstants.STYLE_SHADOW,this.isShadow);this.isDashed=1==mxUtils.getValue(this.style,mxConstants.STYLE_DASHED,this.isDashed);this.isRounded=1==mxUtils.getValue(this.style,
+mxConstants.STYLE_ROUNDED,this.isRounded);this.glass=1==mxUtils.getValue(this.style,mxConstants.STYLE_GLASS,this.glass);this.fill==mxConstants.NONE&&(this.fill=null);this.gradient==mxConstants.NONE&&(this.gradient=null);this.stroke==mxConstants.NONE&&(this.stroke=null)}};mxShape.prototype.setCursor=function(a){null==a&&(a="");this.cursor=a;null!=this.node&&(this.node.style.cursor=a)};mxShape.prototype.getCursor=function(){return this.cursor};
+mxShape.prototype.updateBoundingBox=function(){if(this.useSvgBoundingBox&&null!=this.node&&null!=this.node.ownerSVGElement)try{var a=this.node.getBBox();if(0<a.width&&0<a.height){this.boundingBox=new mxRectangle(a.x,a.y,a.width,a.height);this.boundingBox.grow(this.strokewidth*this.scale/2);return}}catch(c){}if(null!=this.bounds){a=this.createBoundingBox();if(null!=a){this.augmentBoundingBox(a);var b=this.getShapeRotation();0!=b&&(a=mxUtils.getBoundingBox(a,b))}this.boundingBox=a}};
+mxShape.prototype.createBoundingBox=function(){var a=this.bounds.clone();(null!=this.stencil&&(this.direction==mxConstants.DIRECTION_NORTH||this.direction==mxConstants.DIRECTION_SOUTH)||this.isPaintBoundsInverted())&&a.rotate90();return a};mxShape.prototype.augmentBoundingBox=function(a){this.isShadow&&(a.width+=Math.ceil(mxConstants.SHADOW_OFFSET_X*this.scale),a.height+=Math.ceil(mxConstants.SHADOW_OFFSET_Y*this.scale));a.grow(this.strokewidth*this.scale/2)};
+mxShape.prototype.isPaintBoundsInverted=function(){return null==this.stencil&&(this.direction==mxConstants.DIRECTION_NORTH||this.direction==mxConstants.DIRECTION_SOUTH)};mxShape.prototype.getRotation=function(){return null!=this.rotation?this.rotation:0};mxShape.prototype.getTextRotation=function(){var a=this.getRotation();1!=mxUtils.getValue(this.style,mxConstants.STYLE_HORIZONTAL,1)&&(a+=mxText.prototype.verticalTextRotation);return a};
+mxShape.prototype.getShapeRotation=function(){var a=this.getRotation();null!=this.direction&&(this.direction==mxConstants.DIRECTION_NORTH?a+=270:this.direction==mxConstants.DIRECTION_WEST?a+=180:this.direction==mxConstants.DIRECTION_SOUTH&&(a+=90));return a};
+mxShape.prototype.createTransparentSvgRectangle=function(a,b,c,d){var e=document.createElementNS(mxConstants.NS_SVG,"rect");e.setAttribute("x",a);e.setAttribute("y",b);e.setAttribute("width",c);e.setAttribute("height",d);e.setAttribute("fill","none");e.setAttribute("stroke","none");e.setAttribute("pointer-events","all");return e};mxShape.prototype.setTransparentBackgroundImage=function(a){a.style.backgroundImage="url('"+mxClient.imageBasePath+"/transparent.gif')"};
+mxShape.prototype.releaseSvgGradients=function(a){if(null!=a)for(var b in a){var c=a[b];null!=c&&(c.mxRefCount=(c.mxRefCount||0)-1,0==c.mxRefCount&&null!=c.parentNode&&c.parentNode.removeChild(c))}};mxShape.prototype.destroy=function(){null!=this.node&&(mxEvent.release(this.node),null!=this.node.parentNode&&this.node.parentNode.removeChild(this.node),this.node=null);this.releaseSvgGradients(this.oldGradients);this.oldGradients=null};
+var mxStencilRegistry={stencils:{},addStencil:function(a,b){mxStencilRegistry.stencils[a]=b},getStencil:function(a){return mxStencilRegistry.stencils[a]}},mxMarker={markers:[],addMarker:function(a,b){mxMarker.markers[a]=b},createMarker:function(a,b,c,d,e,f,g,k,l,m){var n=mxMarker.markers[c];return null!=n?n(a,b,c,d,e,f,g,k,l,m):null}};
+(function(){function a(a){a=null!=a?a:2;return function(b,c,d,k,l,m,n,p,q,r){c=l*q*1.118;p=m*q*1.118;l*=n+q;m*=n+q;var e=k.clone();e.x-=c;e.y-=p;n=d!=mxConstants.ARROW_CLASSIC&&d!=mxConstants.ARROW_CLASSIC_THIN?1:.75;k.x+=-l*n-c;k.y+=-m*n-p;return function(){b.begin();b.moveTo(e.x,e.y);b.lineTo(e.x-l-m/a,e.y-m+l/a);d!=mxConstants.ARROW_CLASSIC&&d!=mxConstants.ARROW_CLASSIC_THIN||b.lineTo(e.x-3*l/4,e.y-3*m/4);b.lineTo(e.x+m/a-l,e.y-m-l/a);b.close();r?b.fillAndStroke():b.stroke()}}}function b(a){a=
+null!=a?a:2;return function(b,c,d,k,l,m,n,p,q,r){c=l*q*1.118;d=m*q*1.118;l*=n+q;m*=n+q;var e=k.clone();e.x-=c;e.y-=d;k.x+=2*-c;k.y+=2*-d;return function(){b.begin();b.moveTo(e.x-l-m/a,e.y-m+l/a);b.lineTo(e.x,e.y);b.lineTo(e.x+m/a-l,e.y-m-l/a);b.stroke()}}}function c(a,b,c,g,k,l,m,n,p,q){n=c==mxConstants.ARROW_DIAMOND?.7071:.9862;b=k*p*n;n*=l*p;k*=m+p;l*=m+p;var d=g.clone();d.x-=b;d.y-=n;g.x+=-k-b;g.y+=-l-n;var e=c==mxConstants.ARROW_DIAMOND?2:3.4;return function(){a.begin();a.moveTo(d.x,d.y);a.lineTo(d.x-
+k/2-l/e,d.y+k/e-l/2);a.lineTo(d.x-k,d.y-l);a.lineTo(d.x-k/2+l/e,d.y-l/2-k/e);a.close();q?a.fillAndStroke():a.stroke()}}mxMarker.addMarker("classic",a(2));mxMarker.addMarker("classicThin",a(3));mxMarker.addMarker("block",a(2));mxMarker.addMarker("blockThin",a(3));mxMarker.addMarker("open",b(2));mxMarker.addMarker("openThin",b(3));mxMarker.addMarker("oval",function(a,b,c,g,k,l,m,n,p,q){var d=m/2,e=g.clone();g.x-=k*d;g.y-=l*d;return function(){a.ellipse(e.x-d,e.y-d,m,m);q?a.fillAndStroke():a.stroke()}});
+mxMarker.addMarker("diamond",c);mxMarker.addMarker("diamondThin",c)})();function mxActor(a,b,c,d){mxShape.call(this);this.bounds=a;this.fill=b;this.stroke=c;this.strokewidth=null!=d?d:1}mxUtils.extend(mxActor,mxShape);mxActor.prototype.paintVertexShape=function(a,b,c,d,e){a.translate(b,c);a.begin();this.redrawPath(a,b,c,d,e);a.fillAndStroke()};
+mxActor.prototype.redrawPath=function(a,b,c,d,e){b=d/3;a.moveTo(0,e);a.curveTo(0,3*e/5,0,2*e/5,d/2,2*e/5);a.curveTo(d/2-b,2*e/5,d/2-b,0,d/2,0);a.curveTo(d/2+b,0,d/2+b,2*e/5,d/2,2*e/5);a.curveTo(d,2*e/5,d,3*e/5,d,e);a.close()};function mxCloud(a,b,c,d){mxActor.call(this);this.bounds=a;this.fill=b;this.stroke=c;this.strokewidth=null!=d?d:1}mxUtils.extend(mxCloud,mxActor);
+mxCloud.prototype.redrawPath=function(a,b,c,d,e){a.moveTo(.25*d,.25*e);a.curveTo(.05*d,.25*e,0,.5*e,.16*d,.55*e);a.curveTo(0,.66*e,.18*d,.9*e,.31*d,.8*e);a.curveTo(.4*d,e,.7*d,e,.8*d,.8*e);a.curveTo(d,.8*e,d,.6*e,.875*d,.5*e);a.curveTo(d,.3*e,.8*d,.1*e,.625*d,.2*e);a.curveTo(.5*d,.05*e,.3*d,.05*e,.25*d,.25*e);a.close()};function mxRectangleShape(a,b,c,d){mxShape.call(this);this.bounds=a;this.fill=b;this.stroke=c;this.strokewidth=null!=d?d:1}mxUtils.extend(mxRectangleShape,mxShape);
+mxRectangleShape.prototype.isHtmlAllowed=function(){var a=!0;null!=this.style&&(a="1"==mxUtils.getValue(this.style,mxConstants.STYLE_POINTER_EVENTS,"1"));return!this.isRounded&&!this.glass&&0==this.rotation&&(a||null!=this.fill&&this.fill!=mxConstants.NONE)};
+mxRectangleShape.prototype.paintBackground=function(a,b,c,d,e){var f=!0;null!=this.style&&(f="1"==mxUtils.getValue(this.style,mxConstants.STYLE_POINTER_EVENTS,"1"));if(f||null!=this.fill&&this.fill!=mxConstants.NONE||null!=this.stroke&&this.stroke!=mxConstants.NONE)f||null!=this.fill&&this.fill!=mxConstants.NONE||(a.pointerEvents=!1),this.isRounded?("1"==mxUtils.getValue(this.style,mxConstants.STYLE_ABSOLUTE_ARCSIZE,0)?f=Math.min(d/2,Math.min(e/2,mxUtils.getValue(this.style,mxConstants.STYLE_ARCSIZE,
+mxConstants.LINE_ARCSIZE)/2)):(f=mxUtils.getValue(this.style,mxConstants.STYLE_ARCSIZE,100*mxConstants.RECTANGLE_ROUNDING_FACTOR)/100,f=Math.min(d*f,e*f)),a.roundrect(b,c,d,e,f,f)):a.rect(b,c,d,e),a.fillAndStroke()};mxRectangleShape.prototype.paintForeground=function(a,b,c,d,e){this.glass&&!this.outline&&null!=this.fill&&this.fill!=mxConstants.NONE&&this.paintGlassEffect(a,b,c,d,e,this.getArcSize(d+this.strokewidth,e+this.strokewidth))};
+function mxEllipse(a,b,c,d){mxShape.call(this);this.bounds=a;this.fill=b;this.stroke=c;this.strokewidth=null!=d?d:1}mxUtils.extend(mxEllipse,mxShape);mxEllipse.prototype.paintVertexShape=function(a,b,c,d,e){a.ellipse(b,c,d,e);a.fillAndStroke()};function mxDoubleEllipse(a,b,c,d){mxShape.call(this);this.bounds=a;this.fill=b;this.stroke=c;this.strokewidth=null!=d?d:1}mxUtils.extend(mxDoubleEllipse,mxShape);mxDoubleEllipse.prototype.vmlScale=10;
+mxDoubleEllipse.prototype.paintBackground=function(a,b,c,d,e){a.ellipse(b,c,d,e);a.fillAndStroke()};mxDoubleEllipse.prototype.paintForeground=function(a,b,c,d,e){if(!this.outline){var f=mxUtils.getValue(this.style,mxConstants.STYLE_MARGIN,Math.min(3+this.strokewidth,Math.min(d/5,e/5)));d-=2*f;e-=2*f;0<d&&0<e&&a.ellipse(b+f,c+f,d,e);a.stroke()}};
+mxDoubleEllipse.prototype.getLabelBounds=function(a){var b=mxUtils.getValue(this.style,mxConstants.STYLE_MARGIN,Math.min(3+this.strokewidth,Math.min(a.width/5/this.scale,a.height/5/this.scale)))*this.scale;return new mxRectangle(a.x+b,a.y+b,a.width-2*b,a.height-2*b)};function mxRhombus(a,b,c,d){mxShape.call(this);this.bounds=a;this.fill=b;this.stroke=c;this.strokewidth=null!=d?d:1}mxUtils.extend(mxRhombus,mxShape);
+mxRhombus.prototype.paintVertexShape=function(a,b,c,d,e){var f=d/2,g=e/2,k=mxUtils.getValue(this.style,mxConstants.STYLE_ARCSIZE,mxConstants.LINE_ARCSIZE)/2;a.begin();this.addPoints(a,[new mxPoint(b+f,c),new mxPoint(b+d,c+g),new mxPoint(b+f,c+e),new mxPoint(b,c+g)],this.isRounded,k,!0);a.fillAndStroke()};function mxPolyline(a,b,c){mxShape.call(this);this.points=a;this.stroke=b;this.strokewidth=null!=c?c:1}mxUtils.extend(mxPolyline,mxShape);mxPolyline.prototype.getRotation=function(){return 0};
+mxPolyline.prototype.getShapeRotation=function(){return 0};mxPolyline.prototype.isPaintBoundsInverted=function(){return!1};mxPolyline.prototype.paintEdgeShape=function(a,b){null==this.style||1!=this.style[mxConstants.STYLE_CURVED]?this.paintLine(a,b,this.isRounded):this.paintCurvedLine(a,b)};mxPolyline.prototype.paintLine=function(a,b,c){var d=mxUtils.getValue(this.style,mxConstants.STYLE_ARCSIZE,mxConstants.LINE_ARCSIZE)/2;a.begin();this.addPoints(a,b,c,d,!1);a.stroke()};
+mxPolyline.prototype.paintCurvedLine=function(a,b){a.begin();var c=b[0],d=b.length;a.moveTo(c.x,c.y);for(c=1;c<d-2;c++){var e=b[c],f=b[c+1];a.quadTo(e.x,e.y,(e.x+f.x)/2,(e.y+f.y)/2)}e=b[d-2];f=b[d-1];a.quadTo(e.x,e.y,f.x,f.y);a.stroke()};
+function mxArrow(a,b,c,d,e,f,g){mxShape.call(this);this.points=a;this.fill=b;this.stroke=c;this.strokewidth=null!=d?d:1;this.arrowWidth=null!=e?e:mxConstants.ARROW_WIDTH;this.spacing=null!=f?f:mxConstants.ARROW_SPACING;this.endSize=null!=g?g:mxConstants.ARROW_SIZE}mxUtils.extend(mxArrow,mxShape);mxArrow.prototype.augmentBoundingBox=function(a){mxShape.prototype.augmentBoundingBox.apply(this,arguments);a.grow((Math.max(this.arrowWidth,this.endSize)/2+this.strokewidth)*this.scale)};
+mxArrow.prototype.paintEdgeShape=function(a,b){var c=mxConstants.ARROW_SPACING,d=mxConstants.ARROW_WIDTH,e=b[0],f=b[b.length-1],g=f.x-e.x,k=f.y-e.y,l=Math.sqrt(g*g+k*k),m=l-2*c-mxConstants.ARROW_SIZE,g=g/l,k=k/l,l=d*k/3,d=-d*g/3,n=e.x-l/2+c*g,e=e.y-d/2+c*k,p=n+l,q=e+d,r=p+m*g,m=q+m*k,t=r+l,u=m+d,x=t-3*l,y=u-3*d;a.begin();a.moveTo(n,e);a.lineTo(p,q);a.lineTo(r,m);a.lineTo(t,u);a.lineTo(f.x-c*g,f.y-c*k);a.lineTo(x,y);a.lineTo(x+l,y+d);a.close();a.fillAndStroke()};
+function mxArrowConnector(a,b,c,d,e,f,g){mxShape.call(this);this.points=a;this.fill=b;this.stroke=c;this.strokewidth=null!=d?d:1;this.arrowWidth=null!=e?e:mxConstants.ARROW_WIDTH;this.arrowSpacing=null!=f?f:mxConstants.ARROW_SPACING;this.startSize=mxConstants.ARROW_SIZE/5;this.endSize=mxConstants.ARROW_SIZE/5}mxUtils.extend(mxArrowConnector,mxShape);mxArrowConnector.prototype.useSvgBoundingBox=!0;
+mxArrowConnector.prototype.resetStyles=function(){mxShape.prototype.resetStyles.apply(this,arguments);this.arrowSpacing=mxConstants.ARROW_SPACING};mxArrowConnector.prototype.apply=function(a){mxShape.prototype.apply.apply(this,arguments);null!=this.style&&(this.startSize=3*mxUtils.getNumber(this.style,mxConstants.STYLE_STARTSIZE,mxConstants.ARROW_SIZE/5),this.endSize=3*mxUtils.getNumber(this.style,mxConstants.STYLE_ENDSIZE,mxConstants.ARROW_SIZE/5))};
+mxArrowConnector.prototype.augmentBoundingBox=function(a){mxShape.prototype.augmentBoundingBox.apply(this,arguments);var b=this.getEdgeWidth();this.isMarkerStart()&&(b=Math.max(b,this.getStartArrowWidth()));this.isMarkerEnd()&&(b=Math.max(b,this.getEndArrowWidth()));a.grow((b/2+this.strokewidth)*this.scale)};
+mxArrowConnector.prototype.paintEdgeShape=function(a,b){var c=this.strokewidth;this.outline&&(c=Math.max(1,mxUtils.getNumber(this.style,mxConstants.STYLE_STROKEWIDTH,this.strokewidth)));for(var d=this.getStartArrowWidth()+c,e=this.getEndArrowWidth()+c,f=this.outline?this.getEdgeWidth()+c:this.getEdgeWidth(),g=this.isOpenEnded(),k=this.isMarkerStart(),l=this.isMarkerEnd(),m=g?0:this.arrowSpacing+c/2,n=this.startSize+c,c=this.endSize+c,p=this.isArrowRounded(),q=b[b.length-1],r=1;r<b.length-1&&b[r].x==
+b[0].x&&b[r].y==b[0].y;)r++;var t=b[r].x-b[0].x,r=b[r].y-b[0].y,u=Math.sqrt(t*t+r*r);if(0!=u){var x=t/u,y,A=x,z=r/u,v,B=z,u=f*z,C=-f*x,F=[];p?a.setLineJoin("round"):2<b.length&&a.setMiterLimit(1.42);a.begin();t=x;r=z;if(k&&!g)this.paintMarker(a,b[0].x,b[0].y,x,z,n,d,f,m,!0);else{y=b[0].x+u/2+m*x;v=b[0].y+C/2+m*z;var D=b[0].x-u/2+m*x,I=b[0].y-C/2+m*z;g?(a.moveTo(y,v),F.push(function(){a.lineTo(D,I)})):(a.moveTo(D,I),a.lineTo(y,v))}for(var E=v=y=0,u=0;u<b.length-2;u++)if(C=mxUtils.relativeCcw(b[u].x,
+b[u].y,b[u+1].x,b[u+1].y,b[u+2].x,b[u+2].y),y=b[u+2].x-b[u+1].x,v=b[u+2].y-b[u+1].y,E=Math.sqrt(y*y+v*v),0!=E&&(A=y/E,B=v/E,tmp=Math.max(Math.sqrt((x*A+z*B+1)/2),.04),y=x+A,v=z+B,E=Math.sqrt(y*y+v*v),0!=E)){y/=E;v/=E;var E=Math.max(tmp,Math.min(this.strokewidth/200+.04,.35)),E=0!=C&&p?Math.max(.1,E):Math.max(tmp,.06),G=b[u+1].x+v*f/2/E,H=b[u+1].y-y*f/2/E;v=b[u+1].x-v*f/2/E;y=b[u+1].y+y*f/2/E;0!=C&&p?-1==C?(C=v+B*f,E=y-A*f,a.lineTo(v+z*f,y-x*f),a.quadTo(G,H,C,E),function(b,c){F.push(function(){a.lineTo(b,
+c)})}(v,y)):(a.lineTo(G,H),function(b,c){var d=G-z*f,e=H+x*f,g=G-B*f,k=H+A*f;F.push(function(){a.quadTo(b,c,d,e)});F.push(function(){a.lineTo(g,k)})}(v,y)):(a.lineTo(G,H),function(b,c){F.push(function(){a.lineTo(b,c)})}(v,y));x=A;z=B}u=f*B;C=-f*A;if(l&&!g)this.paintMarker(a,q.x,q.y,-x,-z,c,e,f,m,!1);else{a.lineTo(q.x-m*A+u/2,q.y-m*B+C/2);var J=q.x-m*A-u/2,K=q.y-m*B-C/2;g?(a.moveTo(J,K),F.splice(0,0,function(){a.moveTo(J,K)})):a.lineTo(J,K)}for(u=F.length-1;0<=u;u--)F[u]();g?(a.end(),a.stroke()):(a.close(),
+a.fillAndStroke());a.setShadow(!1);a.setMiterLimit(4);p&&a.setLineJoin("flat");2<b.length&&(a.setMiterLimit(4),k&&!g&&(a.begin(),this.paintMarker(a,b[0].x,b[0].y,t,r,n,d,f,m,!0),a.stroke(),a.end()),l&&!g&&(a.begin(),this.paintMarker(a,q.x,q.y,-x,-z,c,e,f,m,!0),a.stroke(),a.end()))}};
+mxArrowConnector.prototype.paintMarker=function(a,b,c,d,e,f,g,k,l,m){g=k/g;var n=k*e/2;k=-k*d/2;var p=(l+f)*d;f=(l+f)*e;m?a.moveTo(b-n+p,c-k+f):a.lineTo(b-n+p,c-k+f);a.lineTo(b-n/g+p,c-k/g+f);a.lineTo(b+l*d,c+l*e);a.lineTo(b+n/g+p,c+k/g+f);a.lineTo(b+n+p,c+k+f)};mxArrowConnector.prototype.isArrowRounded=function(){return this.isRounded};mxArrowConnector.prototype.getStartArrowWidth=function(){return mxConstants.ARROW_WIDTH};mxArrowConnector.prototype.getEndArrowWidth=function(){return mxConstants.ARROW_WIDTH};
+mxArrowConnector.prototype.getEdgeWidth=function(){return mxConstants.ARROW_WIDTH/3};mxArrowConnector.prototype.isOpenEnded=function(){return!1};mxArrowConnector.prototype.isMarkerStart=function(){return mxUtils.getValue(this.style,mxConstants.STYLE_STARTARROW,mxConstants.NONE)!=mxConstants.NONE};mxArrowConnector.prototype.isMarkerEnd=function(){return mxUtils.getValue(this.style,mxConstants.STYLE_ENDARROW,mxConstants.NONE)!=mxConstants.NONE};
+function mxText(a,b,c,d,e,f,g,k,l,m,n,p,q,r,t,u,x,y,A,z,v){mxShape.call(this);this.value=a;this.bounds=b;this.color=null!=e?e:"black";this.align=null!=c?c:mxConstants.ALIGN_CENTER;this.valign=null!=d?d:mxConstants.ALIGN_MIDDLE;this.family=null!=f?f:mxConstants.DEFAULT_FONTFAMILY;this.size=null!=g?g:mxConstants.DEFAULT_FONTSIZE;this.fontStyle=null!=k?k:mxConstants.DEFAULT_FONTSTYLE;this.spacing=parseInt(l||2);this.spacingTop=this.spacing+parseInt(m||0);this.spacingRight=this.spacing+parseInt(n||0);
+this.spacingBottom=this.spacing+parseInt(p||0);this.spacingLeft=this.spacing+parseInt(q||0);this.horizontal=null!=r?r:!0;this.background=t;this.border=u;this.wrap=null!=x?x:!1;this.clipped=null!=y?y:!1;this.overflow=null!=A?A:"visible";this.labelPadding=null!=z?z:0;this.textDirection=v;this.rotation=0;this.updateMargin()}mxUtils.extend(mxText,mxShape);mxText.prototype.baseSpacingTop=0;mxText.prototype.baseSpacingBottom=0;mxText.prototype.baseSpacingLeft=0;mxText.prototype.baseSpacingRight=0;
+mxText.prototype.replaceLinefeeds=!0;mxText.prototype.verticalTextRotation=-90;mxText.prototype.ignoreClippedStringSize=!0;mxText.prototype.ignoreStringSize=!1;mxText.prototype.textWidthPadding=8!=document.documentMode||mxClient.IS_EM?3:4;mxText.prototype.lastValue=null;mxText.prototype.cacheEnabled=!0;mxText.prototype.isParseVml=function(){return!1};mxText.prototype.isHtmlAllowed=function(){return 8!=document.documentMode||mxClient.IS_EM};mxText.prototype.getSvgScreenOffset=function(){return 0};
+mxText.prototype.checkBounds=function(){return!isNaN(this.scale)&&isFinite(this.scale)&&0<this.scale&&null!=this.bounds&&!isNaN(this.bounds.x)&&!isNaN(this.bounds.y)&&!isNaN(this.bounds.width)&&!isNaN(this.bounds.height)};
+mxText.prototype.paint=function(a,b){var c=this.scale,d=this.bounds.x/c,e=this.bounds.y/c,f=this.bounds.width/c,c=this.bounds.height/c;this.updateTransform(a,d,e,f,c);this.configureCanvas(a,d,e,f,c);var g=null!=this.state?this.state.unscaledWidth:null;if(b)null==this.node.firstChild||null!=g&&this.lastUnscaledWidth==g||a.invalidateCachedOffsetSize(this.node),a.updateText(d,e,f,c,this.align,this.valign,this.wrap,this.overflow,this.clipped,this.getTextRotation(),this.node);else{var k=mxUtils.isNode(this.value)||
+this.dialect==mxConstants.DIALECT_STRICTHTML,l=k||a instanceof mxVmlCanvas2D?"html":"",m=this.value;k||"html"!=l||(m=mxUtils.htmlEntities(m,!1));"html"!=l||mxUtils.isNode(this.value)||(m=mxUtils.replaceTrailingNewlines(m,"<div><br></div>"));var m=!mxUtils.isNode(this.value)&&this.replaceLinefeeds&&"html"==l?m.replace(/\n/g,"<br/>"):m,n=this.textDirection;n!=mxConstants.TEXT_DIRECTION_AUTO||k||(n=this.getAutoDirection());n!=mxConstants.TEXT_DIRECTION_LTR&&n!=mxConstants.TEXT_DIRECTION_RTL&&(n=null);
+a.text(d,e,f,c,m,this.align,this.valign,this.wrap,l,this.overflow,this.clipped,this.getTextRotation(),n)}this.lastUnscaledWidth=g};
+mxText.prototype.redraw=function(){if(this.visible&&this.checkBounds()&&this.cacheEnabled&&this.lastValue==this.value&&(mxUtils.isNode(this.value)||this.dialect==mxConstants.DIALECT_STRICTHTML))if("DIV"!=this.node.nodeName||!this.isHtmlAllowed()&&mxClient.IS_VML){var a=this.createCanvas();null!=a&&null!=a.updateText&&null!=a.invalidateCachedOffsetSize?(this.paint(a,!0),this.destroyCanvas(a),this.updateBoundingBox()):mxShape.prototype.redraw.apply(this,arguments)}else this.updateSize(this.node,null==
+this.state||null==this.state.view.textDiv),mxClient.IS_IE&&(null==document.documentMode||8>=document.documentMode)?this.updateHtmlFilter():this.updateHtmlTransform(),this.updateBoundingBox();else mxShape.prototype.redraw.apply(this,arguments),mxUtils.isNode(this.value)||this.dialect==mxConstants.DIALECT_STRICTHTML?this.lastValue=this.value:this.lastValue=null};
+mxText.prototype.resetStyles=function(){mxShape.prototype.resetStyles.apply(this,arguments);this.color="black";this.align=mxConstants.ALIGN_CENTER;this.valign=mxConstants.ALIGN_MIDDLE;this.family=mxConstants.DEFAULT_FONTFAMILY;this.size=mxConstants.DEFAULT_FONTSIZE;this.fontStyle=mxConstants.DEFAULT_FONTSTYLE;this.spacingLeft=this.spacingBottom=this.spacingRight=this.spacingTop=this.spacing=2;this.horizontal=!0;delete this.background;delete this.border;this.textDirection=mxConstants.DEFAULT_TEXT_DIRECTION;
+delete this.margin};
+mxText.prototype.apply=function(a){var b=this.spacing;mxShape.prototype.apply.apply(this,arguments);null!=this.style&&(this.fontStyle=mxUtils.getValue(this.style,mxConstants.STYLE_FONTSTYLE,this.fontStyle),this.family=mxUtils.getValue(this.style,mxConstants.STYLE_FONTFAMILY,this.family),this.size=mxUtils.getValue(this.style,mxConstants.STYLE_FONTSIZE,this.size),this.color=mxUtils.getValue(this.style,mxConstants.STYLE_FONTCOLOR,this.color),this.align=mxUtils.getValue(this.style,mxConstants.STYLE_ALIGN,
+this.align),this.valign=mxUtils.getValue(this.style,mxConstants.STYLE_VERTICAL_ALIGN,this.valign),this.spacing=parseInt(mxUtils.getValue(this.style,mxConstants.STYLE_SPACING,this.spacing)),this.spacingTop=parseInt(mxUtils.getValue(this.style,mxConstants.STYLE_SPACING_TOP,this.spacingTop-b))+this.spacing,this.spacingRight=parseInt(mxUtils.getValue(this.style,mxConstants.STYLE_SPACING_RIGHT,this.spacingRight-b))+this.spacing,this.spacingBottom=parseInt(mxUtils.getValue(this.style,mxConstants.STYLE_SPACING_BOTTOM,
+this.spacingBottom-b))+this.spacing,this.spacingLeft=parseInt(mxUtils.getValue(this.style,mxConstants.STYLE_SPACING_LEFT,this.spacingLeft-b))+this.spacing,this.horizontal=mxUtils.getValue(this.style,mxConstants.STYLE_HORIZONTAL,this.horizontal),this.background=mxUtils.getValue(this.style,mxConstants.STYLE_LABEL_BACKGROUNDCOLOR,this.background),this.border=mxUtils.getValue(this.style,mxConstants.STYLE_LABEL_BORDERCOLOR,this.border),this.textDirection=mxUtils.getValue(this.style,mxConstants.STYLE_TEXT_DIRECTION,
+mxConstants.DEFAULT_TEXT_DIRECTION),this.opacity=mxUtils.getValue(this.style,mxConstants.STYLE_TEXT_OPACITY,100),this.updateMargin());this.flipH=this.flipV=null};mxText.prototype.getAutoDirection=function(){var a=/[A-Za-z\u05d0-\u065f\u066a-\u06ef\u06fa-\u07ff\ufb1d-\ufdff\ufe70-\ufefc]/.exec(this.value);return null!=a&&0<a.length&&"z"<a[0]?mxConstants.TEXT_DIRECTION_RTL:mxConstants.TEXT_DIRECTION_LTR};
+mxText.prototype.updateBoundingBox=function(){var a=this.node;this.boundingBox=this.bounds.clone();var b=this.getTextRotation(),c=null!=this.style?mxUtils.getValue(this.style,mxConstants.STYLE_LABEL_POSITION,mxConstants.ALIGN_CENTER):null,d=null!=this.style?mxUtils.getValue(this.style,mxConstants.STYLE_VERTICAL_LABEL_POSITION,mxConstants.ALIGN_MIDDLE):null;if(!(this.ignoreStringSize||null==a||"fill"==this.overflow||this.clipped&&this.ignoreClippedStringSize&&c==mxConstants.ALIGN_CENTER&&d==mxConstants.ALIGN_MIDDLE)){d=
+c=null;if(null!=a.ownerSVGElement)if(null!=a.firstChild&&null!=a.firstChild.firstChild&&"foreignObject"==a.firstChild.firstChild.nodeName)a=a.firstChild.firstChild,c=parseInt(a.getAttribute("width"))*this.scale,d=parseInt(a.getAttribute("height"))*this.scale;else try{var e=a.getBBox();"string"==typeof this.value&&0==mxUtils.trim(this.value)?this.boundingBox=null:this.boundingBox=0==e.width&&0==e.height?null:new mxRectangle(e.x,e.y,e.width,e.height);return}catch(f){}else{c=null!=this.state?this.state.view.textDiv:
+null;if(null==this.offsetWidth||null==this.offsetHeight)null!=c&&(this.updateFont(c),this.updateSize(c,!1),this.updateInnerHtml(c),a=c),e=a,8!=document.documentMode||mxClient.IS_EM?null!=e.firstChild&&"DIV"==e.firstChild.nodeName&&(e=e.firstChild):(d=Math.round(this.bounds.width/this.scale),this.wrap&&0<d?(a.style.wordWrap=mxConstants.WORD_WRAP,a.style.whiteSpace="normal","break-word"!=a.style.wordWrap&&(a=e.getElementsByTagName("div"),0<a.length&&(e=a[a.length-1]),c=e.offsetWidth+2,a=this.node.getElementsByTagName("div"),
+this.clipped&&(c=Math.min(d,c)),1<a.length&&(a[a.length-2].style.width=c+"px"))):a.style.whiteSpace="nowrap"),this.offsetWidth=e.offsetWidth+this.textWidthPadding,this.offsetHeight=e.offsetHeight;c=this.offsetWidth*this.scale;d=this.offsetHeight*this.scale}null!=c&&null!=d&&(this.boundingBox=new mxRectangle(this.bounds.x,this.bounds.y,c,d))}null!=this.boundingBox&&(0!=b?(b=mxUtils.getBoundingBox(new mxRectangle(this.margin.x*this.boundingBox.width,this.margin.y*this.boundingBox.height,this.boundingBox.width,
+this.boundingBox.height),b,new mxPoint(0,0)),this.unrotatedBoundingBox=mxRectangle.fromRectangle(this.boundingBox),this.unrotatedBoundingBox.x+=this.margin.x*this.unrotatedBoundingBox.width,this.unrotatedBoundingBox.y+=this.margin.y*this.unrotatedBoundingBox.height,this.boundingBox.x+=b.x,this.boundingBox.y+=b.y,this.boundingBox.width=b.width,this.boundingBox.height=b.height):(this.boundingBox.x+=this.margin.x*this.boundingBox.width,this.boundingBox.y+=this.margin.y*this.boundingBox.height,this.unrotatedBoundingBox=
+null))};mxText.prototype.getShapeRotation=function(){return 0};mxText.prototype.getTextRotation=function(){return null!=this.state&&null!=this.state.shape?this.state.shape.getTextRotation():0};mxText.prototype.isPaintBoundsInverted=function(){return!this.horizontal&&null!=this.state&&this.state.view.graph.model.isVertex(this.state.cell)};
+mxText.prototype.configureCanvas=function(a,b,c,d,e){mxShape.prototype.configureCanvas.apply(this,arguments);a.setFontColor(this.color);a.setFontBackgroundColor(this.background);a.setFontBorderColor(this.border);a.setFontFamily(this.family);a.setFontSize(this.size);a.setFontStyle(this.fontStyle)};
+mxText.prototype.updateVmlContainer=function(){this.node.style.left=Math.round(this.bounds.x)+"px";this.node.style.top=Math.round(this.bounds.y)+"px";this.node.style.width="1px";this.node.style.height="1px";this.node.style.overflow="visible"};
+mxText.prototype.redrawHtmlShape=function(){var a=this.node.style;a.whiteSpace="normal";a.overflow="";a.width="";a.height="";this.updateValue();this.updateFont(this.node);this.updateSize(this.node,null==this.state||null==this.state.view.textDiv);this.offsetHeight=this.offsetWidth=null;mxClient.IS_IE&&(null==document.documentMode||8>=document.documentMode)?this.updateHtmlFilter():this.updateHtmlTransform()};
+mxText.prototype.updateHtmlTransform=function(){var a=this.getTextRotation(),b=this.node.style,c=this.margin.x,d=this.margin.y;0!=a?(mxUtils.setPrefixedStyle(b,"transformOrigin",100*-c+"% "+100*-d+"%"),mxUtils.setPrefixedStyle(b,"transform","translate("+100*c+"%,"+100*d+"%)scale("+this.scale+") rotate("+a+"deg)")):(mxUtils.setPrefixedStyle(b,"transformOrigin","0% 0%"),mxUtils.setPrefixedStyle(b,"transform","scale("+this.scale+")translate("+100*c+"%,"+100*d+"%)"));b.left=Math.round(this.bounds.x-Math.ceil(c*
+("fill"!=this.overflow&&"width"!=this.overflow?3:1)))+"px";b.top=Math.round(this.bounds.y-d*("fill"!=this.overflow?3:1))+"px";b.opacity=100>this.opacity?this.opacity/100:""};
+mxText.prototype.updateInnerHtml=function(a){if(mxUtils.isNode(this.value))a.innerHTML=this.value.outerHTML;else{var b=this.value;this.dialect!=mxConstants.DIALECT_STRICTHTML&&(b=mxUtils.htmlEntities(b,!1));b=mxUtils.replaceTrailingNewlines(b,"<div>&nbsp;</div>");b=this.replaceLinefeeds?b.replace(/\n/g,"<br/>"):b;a.innerHTML='<div style="display:inline-block;_display:inline;">'+b+"</div>"}};
+mxText.prototype.updateHtmlFilter=function(){var a=this.node.style,b=this.margin.x,c=this.margin.y,d=this.scale;mxUtils.setOpacity(this.node,this.opacity);var e,f=0,g=null!=this.state?this.state.view.textDiv:null,k=this.node;if(null!=g){g.style.overflow="";g.style.height="";g.style.width="";this.updateFont(g);this.updateSize(g,!1);this.updateInnerHtml(g);var l=Math.round(this.bounds.width/this.scale);this.wrap&&0<l?(g.style.whiteSpace="normal",g.style.wordWrap=mxConstants.WORD_WRAP,e=l,this.clipped&&
+(e=Math.min(e,this.bounds.width)),g.style.width=e+"px"):g.style.whiteSpace="nowrap";k=g;null!=k.firstChild&&"DIV"==k.firstChild.nodeName&&(k=k.firstChild,this.wrap&&"break-word"==g.style.wordWrap&&(k.style.width="100%"));!this.clipped&&this.wrap&&0<l&&(e=k.offsetWidth+this.textWidthPadding,g.style.width=e+"px");f=k.offsetHeight+2;mxClient.IS_QUIRKS&&null!=this.border&&this.border!=mxConstants.NONE&&(f+=3)}else null!=k.firstChild&&"DIV"==k.firstChild.nodeName&&(k=k.firstChild,f=k.offsetHeight);e=k.offsetWidth+
+this.textWidthPadding;this.clipped&&(f=Math.min(f,this.bounds.height));l=this.bounds.width/d;g=this.bounds.height/d;"fill"==this.overflow?(f=g,e=l):"width"==this.overflow&&(f=k.scrollHeight,e=l);this.offsetWidth=e;this.offsetHeight=f;mxClient.IS_QUIRKS&&(this.clipped||"width"==this.overflow&&0<g)?(g=Math.min(g,f),a.height=Math.round(g)+"px"):g=f;"fill"!=this.overflow&&"width"!=this.overflow&&(this.clipped&&(e=Math.min(l,e)),l=e,mxClient.IS_QUIRKS&&this.clipped||this.wrap)&&(a.width=Math.round(l)+
+"px");var g=g*d,l=l*d,m=this.getTextRotation()*(Math.PI/180);e=parseFloat(parseFloat(Math.cos(m)).toFixed(8));f=parseFloat(parseFloat(Math.sin(-m)).toFixed(8));m%=2*Math.PI;0>m&&(m+=2*Math.PI);m%=Math.PI;m>Math.PI/2&&(m=Math.PI-m);var k=Math.cos(m),n=Math.sin(-m),b=l*-(b+.5),p=g*-(c+.5);0!=m&&(c="progid:DXImageTransform.Microsoft.Matrix(M11="+e+", M12="+f+", M21="+-f+", M22="+e+", sizingMethod='auto expand')",a.filter=null!=a.filter&&0<a.filter.length?a.filter+(" "+c):c);c=0;"fill"!=this.overflow&&
+mxClient.IS_QUIRKS&&(c=this.valign==mxConstants.ALIGN_TOP?c-1:this.valign==mxConstants.ALIGN_BOTTOM?c+2:c+1);a.zoom=d;a.left=Math.round(this.bounds.x+((l-l*k+g*n)/2-e*b-f*p)-l/2)+"px";a.top=Math.round(this.bounds.y+((g-g*k+l*n)/2+f*b-e*p)-g/2+c)+"px"};
+mxText.prototype.updateValue=function(){if(mxUtils.isNode(this.value))this.node.innerHTML="",this.node.appendChild(this.value);else{var a=this.value;this.dialect!=mxConstants.DIALECT_STRICTHTML&&(a=mxUtils.htmlEntities(a,!1));var a=mxUtils.replaceTrailingNewlines(a,"<div><br></div>"),a=this.replaceLinefeeds?a.replace(/\n/g,"<br/>"):a,b=null!=this.background&&this.background!=mxConstants.NONE?this.background:null,c=null!=this.border&&this.border!=mxConstants.NONE?this.border:null;if("fill"==this.overflow||
+"width"==this.overflow)null!=b&&(this.node.style.backgroundColor=b),null!=c&&(this.node.style.border="1px solid "+c);else{var d="";null!=b&&(d+="background-color:"+b+";");null!=c&&(d+="border:1px solid "+c+";");a='<div style="zoom:1;'+d+"display:inline-block;_display:inline;text-decoration:inherit;padding-bottom:1px;padding-right:1px;line-height:"+(mxConstants.ABSOLUTE_LINE_HEIGHT?this.size*mxConstants.LINE_HEIGHT+"px":mxConstants.LINE_HEIGHT)+'">'+a+"</div>"}this.node.innerHTML=a;a=this.node.getElementsByTagName("div");
+0<a.length&&(b=this.textDirection,b==mxConstants.TEXT_DIRECTION_AUTO&&this.dialect!=mxConstants.DIALECT_STRICTHTML&&(b=this.getAutoDirection()),b==mxConstants.TEXT_DIRECTION_LTR||b==mxConstants.TEXT_DIRECTION_RTL?a[a.length-1].setAttribute("dir",b):a[a.length-1].removeAttribute("dir"))}};
+mxText.prototype.updateFont=function(a){a=a.style;a.lineHeight=mxConstants.ABSOLUTE_LINE_HEIGHT?this.size*mxConstants.LINE_HEIGHT+"px":mxConstants.LINE_HEIGHT;a.fontSize=this.size+"px";a.fontFamily=this.family;a.verticalAlign="top";a.color=this.color;a.fontWeight=(this.fontStyle&mxConstants.FONT_BOLD)==mxConstants.FONT_BOLD?"bold":"";a.fontStyle=(this.fontStyle&mxConstants.FONT_ITALIC)==mxConstants.FONT_ITALIC?"italic":"";a.textDecoration=(this.fontStyle&mxConstants.FONT_UNDERLINE)==mxConstants.FONT_UNDERLINE?
+"underline":"";a.textAlign=this.align==mxConstants.ALIGN_CENTER?"center":this.align==mxConstants.ALIGN_RIGHT?"right":"left"};
+mxText.prototype.updateSize=function(a,b){var c=Math.max(0,Math.round(this.bounds.width/this.scale)),d=Math.max(0,Math.round(this.bounds.height/this.scale)),e=a.style;this.clipped?(e.overflow="hidden",mxClient.IS_QUIRKS?e.width=c+"px":(e.maxHeight=d+"px",e.maxWidth=c+"px")):"fill"==this.overflow?(e.width=c+1+"px",e.height=d+1+"px",e.overflow="hidden"):"width"==this.overflow&&(e.width=c+1+"px",e.maxHeight=d+1+"px",e.overflow="hidden");if(this.wrap&&0<c){if(e.wordWrap=mxConstants.WORD_WRAP,e.whiteSpace=
+"normal",e.width=c+"px",b&&"fill"!=this.overflow&&"width"!=this.overflow){d=a;null!=d.firstChild&&"DIV"==d.firstChild.nodeName&&(d=d.firstChild,"break-word"==a.style.wordWrap&&(d.style.width="100%"));var f=d.offsetWidth;if(0==f){var g=a.parentNode;a.style.visibility="hidden";document.body.appendChild(a);f=d.offsetWidth;a.style.visibility="";g.appendChild(a)}f+=3;this.clipped&&(f=Math.min(f,c));e.width=f+"px"}}else e.whiteSpace="nowrap"};
+mxText.prototype.updateMargin=function(){this.margin=mxUtils.getAlignmentAsPoint(this.align,this.valign)};
+mxText.prototype.getSpacing=function(){return new mxPoint(this.align==mxConstants.ALIGN_CENTER?(this.spacingLeft-this.spacingRight)/2:this.align==mxConstants.ALIGN_RIGHT?-this.spacingRight-this.baseSpacingRight:this.spacingLeft+this.baseSpacingLeft,this.valign==mxConstants.ALIGN_MIDDLE?(this.spacingTop-this.spacingBottom)/2:this.valign==mxConstants.ALIGN_BOTTOM?-this.spacingBottom-this.baseSpacingBottom:this.spacingTop+this.baseSpacingTop)};function mxTriangle(){mxActor.call(this)}
+mxUtils.extend(mxTriangle,mxActor);mxTriangle.prototype.redrawPath=function(a,b,c,d,e){b=mxUtils.getValue(this.style,mxConstants.STYLE_ARCSIZE,mxConstants.LINE_ARCSIZE)/2;this.addPoints(a,[new mxPoint(0,0),new mxPoint(d,.5*e),new mxPoint(0,e)],this.isRounded,b,!0)};function mxHexagon(){mxActor.call(this)}mxUtils.extend(mxHexagon,mxActor);
+mxHexagon.prototype.redrawPath=function(a,b,c,d,e){b=mxUtils.getValue(this.style,mxConstants.STYLE_ARCSIZE,mxConstants.LINE_ARCSIZE)/2;this.addPoints(a,[new mxPoint(.25*d,0),new mxPoint(.75*d,0),new mxPoint(d,.5*e),new mxPoint(.75*d,e),new mxPoint(.25*d,e),new mxPoint(0,.5*e)],this.isRounded,b,!0)};function mxLine(a,b,c){mxShape.call(this);this.bounds=a;this.stroke=b;this.strokewidth=null!=c?c:1}mxUtils.extend(mxLine,mxShape);
+mxLine.prototype.paintVertexShape=function(a,b,c,d,e){c+=e/2;a.begin();a.moveTo(b,c);a.lineTo(b+d,c);a.stroke()};function mxImageShape(a,b,c,d,e){mxShape.call(this);this.bounds=a;this.image=b;this.fill=c;this.stroke=d;this.strokewidth=null!=e?e:1;this.shadow=!1}mxUtils.extend(mxImageShape,mxRectangleShape);mxImageShape.prototype.preserveImageAspect=!0;mxImageShape.prototype.getSvgScreenOffset=function(){return 0};
+mxImageShape.prototype.apply=function(a){mxShape.prototype.apply.apply(this,arguments);this.gradient=this.stroke=this.fill=null;null!=this.style&&(this.preserveImageAspect=1==mxUtils.getNumber(this.style,mxConstants.STYLE_IMAGE_ASPECT,1),this.flipH=this.flipH||1==mxUtils.getValue(this.style,"imageFlipH",0),this.flipV=this.flipV||1==mxUtils.getValue(this.style,"imageFlipV",0))};mxImageShape.prototype.isHtmlAllowed=function(){return!this.preserveImageAspect};
+mxImageShape.prototype.createHtml=function(){var a=document.createElement("div");a.style.position="absolute";return a};
+mxImageShape.prototype.paintVertexShape=function(a,b,c,d,e){if(null!=this.image){var f=mxUtils.getValue(this.style,mxConstants.STYLE_IMAGE_BACKGROUND,null),g=mxUtils.getValue(this.style,mxConstants.STYLE_IMAGE_BORDER,null);null!=f&&(a.setFillColor(f),a.setStrokeColor(g),a.rect(b,c,d,e),a.fillAndStroke());a.image(b,c,d,e,this.image,this.preserveImageAspect,!1,!1);g=mxUtils.getValue(this.style,mxConstants.STYLE_IMAGE_BORDER,null);null!=g&&(a.setShadow(!1),a.setStrokeColor(g),a.rect(b,c,d,e),a.stroke())}else mxRectangleShape.prototype.paintBackground.apply(this,
+arguments)};
+mxImageShape.prototype.redrawHtmlShape=function(){this.node.style.left=Math.round(this.bounds.x)+"px";this.node.style.top=Math.round(this.bounds.y)+"px";this.node.style.width=Math.max(0,Math.round(this.bounds.width))+"px";this.node.style.height=Math.max(0,Math.round(this.bounds.height))+"px";this.node.innerHTML="";if(null!=this.image){var a=mxUtils.getValue(this.style,mxConstants.STYLE_IMAGE_BACKGROUND,""),b=mxUtils.getValue(this.style,mxConstants.STYLE_IMAGE_BORDER,"");this.node.style.backgroundColor=a;
+this.node.style.borderColor=b;a=document.createElement(mxClient.IS_IE6||(null==document.documentMode||8>=document.documentMode)&&0!=this.rotation?mxClient.VML_PREFIX+":image":"img");a.setAttribute("border","0");a.style.position="absolute";a.src=this.image;b=100>this.opacity?"alpha(opacity="+this.opacity+")":"";this.node.style.filter=b;this.flipH&&this.flipV?b+="progid:DXImageTransform.Microsoft.BasicImage(rotation=2)":this.flipH?b+="progid:DXImageTransform.Microsoft.BasicImage(mirror=1)":this.flipV&&
+(b+="progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)");a.style.filter!=b&&(a.style.filter=b);"image"==a.nodeName?a.style.rotation=this.rotation:0!=this.rotation?mxUtils.setPrefixedStyle(a.style,"transform","rotate("+this.rotation+"deg)"):mxUtils.setPrefixedStyle(a.style,"transform","");a.style.width=this.node.style.width;a.style.height=this.node.style.height;this.node.style.backgroundImage="";this.node.appendChild(a)}else this.setTransparentBackgroundImage(this.node)};
+function mxLabel(a,b,c,d){mxRectangleShape.call(this,a,b,c,d)}mxUtils.extend(mxLabel,mxRectangleShape);mxLabel.prototype.imageSize=mxConstants.DEFAULT_IMAGESIZE;mxLabel.prototype.spacing=2;mxLabel.prototype.indicatorSize=10;mxLabel.prototype.indicatorSpacing=2;mxLabel.prototype.init=function(a){mxShape.prototype.init.apply(this,arguments);null!=this.indicatorShape&&(this.indicator=new this.indicatorShape,this.indicator.dialect=this.dialect,this.indicator.init(this.node))};
+mxLabel.prototype.redraw=function(){null!=this.indicator&&(this.indicator.fill=this.indicatorColor,this.indicator.stroke=this.indicatorStrokeColor,this.indicator.gradient=this.indicatorGradientColor,this.indicator.direction=this.indicatorDirection);mxShape.prototype.redraw.apply(this,arguments)};mxLabel.prototype.isHtmlAllowed=function(){return mxRectangleShape.prototype.isHtmlAllowed.apply(this,arguments)&&null==this.indicatorColor&&null==this.indicatorShape};
+mxLabel.prototype.paintForeground=function(a,b,c,d,e){this.paintImage(a,b,c,d,e);this.paintIndicator(a,b,c,d,e);mxRectangleShape.prototype.paintForeground.apply(this,arguments)};mxLabel.prototype.paintImage=function(a,b,c,d,e){null!=this.image&&(b=this.getImageBounds(b,c,d,e),a.image(b.x,b.y,b.width,b.height,this.image,!1,!1,!1))};
+mxLabel.prototype.getImageBounds=function(a,b,c,d){var e=mxUtils.getValue(this.style,mxConstants.STYLE_IMAGE_ALIGN,mxConstants.ALIGN_LEFT),f=mxUtils.getValue(this.style,mxConstants.STYLE_IMAGE_VERTICAL_ALIGN,mxConstants.ALIGN_MIDDLE),g=mxUtils.getNumber(this.style,mxConstants.STYLE_IMAGE_WIDTH,mxConstants.DEFAULT_IMAGESIZE),k=mxUtils.getNumber(this.style,mxConstants.STYLE_IMAGE_HEIGHT,mxConstants.DEFAULT_IMAGESIZE),l=mxUtils.getNumber(this.style,mxConstants.STYLE_SPACING,this.spacing)+5;a=e==mxConstants.ALIGN_CENTER?
+a+(c-g)/2:e==mxConstants.ALIGN_RIGHT?a+(c-g-l):a+l;b=f==mxConstants.ALIGN_TOP?b+l:f==mxConstants.ALIGN_BOTTOM?b+(d-k-l):b+(d-k)/2;return new mxRectangle(a,b,g,k)};mxLabel.prototype.paintIndicator=function(a,b,c,d,e){null!=this.indicator?(this.indicator.bounds=this.getIndicatorBounds(b,c,d,e),this.indicator.paint(a)):null!=this.indicatorImage&&(b=this.getIndicatorBounds(b,c,d,e),a.image(b.x,b.y,b.width,b.height,this.indicatorImage,!1,!1,!1))};
+mxLabel.prototype.getIndicatorBounds=function(a,b,c,d){var e=mxUtils.getValue(this.style,mxConstants.STYLE_IMAGE_ALIGN,mxConstants.ALIGN_LEFT),f=mxUtils.getValue(this.style,mxConstants.STYLE_IMAGE_VERTICAL_ALIGN,mxConstants.ALIGN_MIDDLE),g=mxUtils.getNumber(this.style,mxConstants.STYLE_INDICATOR_WIDTH,this.indicatorSize),k=mxUtils.getNumber(this.style,mxConstants.STYLE_INDICATOR_HEIGHT,this.indicatorSize),l=this.spacing+5;a=e==mxConstants.ALIGN_RIGHT?a+(c-g-l):e==mxConstants.ALIGN_CENTER?a+(c-g)/
+2:a+l;b=f==mxConstants.ALIGN_BOTTOM?b+(d-k-l):f==mxConstants.ALIGN_TOP?b+l:b+(d-k)/2;return new mxRectangle(a,b,g,k)};
+mxLabel.prototype.redrawHtmlShape=function(){for(mxRectangleShape.prototype.redrawHtmlShape.apply(this,arguments);this.node.hasChildNodes();)this.node.removeChild(this.node.lastChild);if(null!=this.image){var a=document.createElement("img");a.style.position="relative";a.setAttribute("border","0");var b=this.getImageBounds(this.bounds.x,this.bounds.y,this.bounds.width,this.bounds.height);b.x-=this.bounds.x;b.y-=this.bounds.y;a.style.left=Math.round(b.x)+"px";a.style.top=Math.round(b.y)+"px";a.style.width=
+Math.round(b.width)+"px";a.style.height=Math.round(b.height)+"px";a.src=this.image;this.node.appendChild(a)}};function mxCylinder(a,b,c,d){mxShape.call(this);this.bounds=a;this.fill=b;this.stroke=c;this.strokewidth=null!=d?d:1}mxUtils.extend(mxCylinder,mxShape);mxCylinder.prototype.maxHeight=40;mxCylinder.prototype.svgStrokeTolerance=0;
+mxCylinder.prototype.paintVertexShape=function(a,b,c,d,e){a.translate(b,c);a.begin();this.redrawPath(a,b,c,d,e,!1);a.fillAndStroke();a.setShadow(!1);a.begin();this.redrawPath(a,b,c,d,e,!0);a.stroke()};
+mxCylinder.prototype.redrawPath=function(a,b,c,d,e,f){b=Math.min(this.maxHeight,Math.round(e/5));if(f&&null!=this.fill||!f&&null==this.fill)a.moveTo(0,b),a.curveTo(0,2*b,d,2*b,d,b),f||(a.stroke(),a.begin());f||(a.moveTo(0,b),a.curveTo(0,-b/3,d,-b/3,d,b),a.lineTo(d,e-b),a.curveTo(d,e+b/3,0,e+b/3,0,e-b),a.close())};function mxConnector(a,b,c){mxPolyline.call(this,a,b,c)}mxUtils.extend(mxConnector,mxPolyline);
+mxConnector.prototype.updateBoundingBox=function(){this.useSvgBoundingBox=null!=this.style&&1==this.style[mxConstants.STYLE_CURVED];mxShape.prototype.updateBoundingBox.apply(this,arguments)};mxConnector.prototype.paintEdgeShape=function(a,b){var c=this.createMarker(a,b,!0),d=this.createMarker(a,b,!1);mxPolyline.prototype.paintEdgeShape.apply(this,arguments);a.setFillColor(this.stroke);a.setShadow(!1);a.setDashed(!1);null!=c&&c();null!=d&&d()};
+mxConnector.prototype.createMarker=function(a,b,c){var d=null,e=b.length,f=mxUtils.getValue(this.style,c?mxConstants.STYLE_STARTARROW:mxConstants.STYLE_ENDARROW),g=c?b[1]:b[e-2],k=c?b[0]:b[e-1];if(null!=f&&null!=g&&null!=k){for(d=1;d<e-1&&0==Math.round(g.x-k.x)&&0==Math.round(g.y-k.y);)g=c?b[1+d]:b[e-2-d],d++;b=k.x-g.x;e=k.y-g.y;d=Math.max(1,Math.sqrt(b*b+e*e));g=b/d;b=e/d;e=mxUtils.getNumber(this.style,c?mxConstants.STYLE_STARTSIZE:mxConstants.STYLE_ENDSIZE,mxConstants.DEFAULT_MARKERSIZE);d=mxMarker.createMarker(a,
+this,f,k,g,b,e,c,this.strokewidth,0!=this.style[c?mxConstants.STYLE_STARTFILL:mxConstants.STYLE_ENDFILL])}return d};
+mxConnector.prototype.augmentBoundingBox=function(a){mxShape.prototype.augmentBoundingBox.apply(this,arguments);var b=0;mxUtils.getValue(this.style,mxConstants.STYLE_STARTARROW,mxConstants.NONE)!=mxConstants.NONE&&(b=mxUtils.getNumber(this.style,mxConstants.STYLE_STARTSIZE,mxConstants.DEFAULT_MARKERSIZE)+1);mxUtils.getValue(this.style,mxConstants.STYLE_ENDARROW,mxConstants.NONE)!=mxConstants.NONE&&(b=Math.max(b,mxUtils.getNumber(this.style,mxConstants.STYLE_ENDSIZE,mxConstants.DEFAULT_MARKERSIZE))+
+1);a.grow(b*this.scale)};function mxSwimlane(a,b,c,d){mxShape.call(this);this.bounds=a;this.fill=b;this.stroke=c;this.strokewidth=null!=d?d:1}mxUtils.extend(mxSwimlane,mxShape);mxSwimlane.prototype.imageSize=16;mxSwimlane.prototype.getTitleSize=function(){return Math.max(0,mxUtils.getValue(this.style,mxConstants.STYLE_STARTSIZE,mxConstants.DEFAULT_STARTSIZE))};
+mxSwimlane.prototype.getLabelBounds=function(a){var b=this.getTitleSize();a=new mxRectangle(a.x,a.y,a.width,a.height);var c=this.isHorizontal(),d=1==mxUtils.getValue(this.style,mxConstants.STYLE_FLIPH,0),e=1==mxUtils.getValue(this.style,mxConstants.STYLE_FLIPV,0),f=this.direction==mxConstants.DIRECTION_NORTH||this.direction==mxConstants.DIRECTION_SOUTH,c=c==!f,d=!c&&d!=(this.direction==mxConstants.DIRECTION_SOUTH||this.direction==mxConstants.DIRECTION_WEST),e=c&&e!=(this.direction==mxConstants.DIRECTION_SOUTH||
+this.direction==mxConstants.DIRECTION_WEST);if(f){b=Math.min(a.width,b*this.scale);if(d||e)a.x+=a.width-b;a.width=b}else{b=Math.min(a.height,b*this.scale);if(d||e)a.y+=a.height-b;a.height=b}return a};mxSwimlane.prototype.getGradientBounds=function(a,b,c,d,e){a=this.getTitleSize();if(this.isHorizontal())return a=Math.min(a,e),new mxRectangle(b,c,d,a);a=Math.min(a,d);return new mxRectangle(b,c,a,e)};
+mxSwimlane.prototype.getArcSize=function(a,b,c){a=mxUtils.getValue(this.style,mxConstants.STYLE_ARCSIZE,100*mxConstants.RECTANGLE_ROUNDING_FACTOR)/100;return c*a*3};mxSwimlane.prototype.isHorizontal=function(){return 1==mxUtils.getValue(this.style,mxConstants.STYLE_HORIZONTAL,1)};
+mxSwimlane.prototype.paintVertexShape=function(a,b,c,d,e){var f=this.getTitleSize(),g=mxUtils.getValue(this.style,mxConstants.STYLE_SWIMLANE_FILLCOLOR,mxConstants.NONE),k=1==mxUtils.getValue(this.style,mxConstants.STYLE_SWIMLANE_LINE,1),l=0,f=this.isHorizontal()?Math.min(f,e):Math.min(f,d);a.translate(b,c);this.isRounded?(l=this.getArcSize(d,e,f),this.paintRoundedSwimlane(a,b,c,d,e,f,l,g,k)):this.paintSwimlane(a,b,c,d,e,f,g,k);g=mxUtils.getValue(this.style,mxConstants.STYLE_SEPARATORCOLOR,mxConstants.NONE);
+this.paintSeparator(a,b,c,d,e,f,g);null!=this.image&&(e=this.getImageBounds(b,c,d,e),a.image(e.x-b,e.y-c,e.width,e.height,this.image,!1,!1,!1));this.glass&&(a.setShadow(!1),this.paintGlassEffect(a,0,0,d,f,l))};
+mxSwimlane.prototype.paintSwimlane=function(a,b,c,d,e,f,g,k){g!=mxConstants.NONE&&(a.save(),a.setFillColor(g),a.rect(0,0,d,e),a.fillAndStroke(),a.restore(),a.setShadow(!1));a.begin();0==f&&(f=e);this.isHorizontal()?(a.moveTo(0,f),a.lineTo(0,0),a.lineTo(d,0),a.lineTo(d,f),(k||f>=e)&&a.close(),a.fillAndStroke(),f<e&&g==mxConstants.NONE&&(a.pointerEvents=!1,a.begin(),a.moveTo(0,f),a.lineTo(0,e),a.lineTo(d,e),a.lineTo(d,f),a.stroke())):(a.moveTo(f,0),a.lineTo(0,0),a.lineTo(0,e),a.lineTo(f,e),(k||f>=d)&&
+a.close(),a.fillAndStroke(),f<d&&g==mxConstants.NONE&&(a.pointerEvents=!1,a.begin(),a.moveTo(f,0),a.lineTo(d,0),a.lineTo(d,e),a.lineTo(f,e),a.stroke()))};
+mxSwimlane.prototype.paintRoundedSwimlane=function(a,b,c,d,e,f,g,k,l){g=Math.min(e-f,Math.min(f,g));k!=mxConstants.NONE&&(a.save(),a.setFillColor(k),a.roundrect(0,0,d,e,g,g),a.fillAndStroke(),a.restore(),a.setShadow(!1));a.begin();this.isHorizontal()?(a.moveTo(d,f),a.lineTo(d,g),a.quadTo(d,0,d-Math.min(d/2,g),0),a.lineTo(Math.min(d/2,g),0),a.quadTo(0,0,0,g),a.lineTo(0,f),(l||f>=e)&&a.close(),a.fillAndStroke(),f<e&&k==mxConstants.NONE&&(a.pointerEvents=!1,a.begin(),a.moveTo(0,f),a.lineTo(0,e-g),a.quadTo(0,
+e,Math.min(d/2,g),e),a.lineTo(d-Math.min(d/2,g),e),a.quadTo(d,e,d,e-g),a.lineTo(d,f),a.stroke())):(a.moveTo(f,0),a.lineTo(g,0),a.quadTo(0,0,0,Math.min(e/2,g)),a.lineTo(0,e-Math.min(e/2,g)),a.quadTo(0,e,g,e),a.lineTo(f,e),(l||f>=d)&&a.close(),a.fillAndStroke(),f<d&&k==mxConstants.NONE&&(a.pointerEvents=!1,a.begin(),a.moveTo(f,e),a.lineTo(d-g,e),a.quadTo(d,e,d,e-Math.min(e/2,g)),a.lineTo(d,Math.min(e/2,g)),a.quadTo(d,0,d-g,0),a.lineTo(f,0),a.stroke()))};
+mxSwimlane.prototype.paintSeparator=function(a,b,c,d,e,f,g){g!=mxConstants.NONE&&(a.setStrokeColor(g),a.setDashed(!0),a.begin(),this.isHorizontal()?(a.moveTo(d,f),a.lineTo(d,e)):(a.moveTo(f,0),a.lineTo(d,0)),a.stroke(),a.setDashed(!1))};mxSwimlane.prototype.getImageBounds=function(a,b,c,d){return this.isHorizontal()?new mxRectangle(a+c-this.imageSize,b,this.imageSize,this.imageSize):new mxRectangle(a,b,this.imageSize,this.imageSize)};function mxGraphLayout(a){this.graph=a}
+mxGraphLayout.prototype.graph=null;mxGraphLayout.prototype.useBoundingBox=!0;mxGraphLayout.prototype.parent=null;mxGraphLayout.prototype.moveCell=function(a,b,c){};mxGraphLayout.prototype.execute=function(a){};mxGraphLayout.prototype.getGraph=function(){return this.graph};mxGraphLayout.prototype.getConstraint=function(a,b,c,d){c=this.graph.view.getState(b);b=null!=c?c.style:this.graph.getCellStyle(b);return null!=b?b[a]:null};
+mxGraphLayout.traverse=function(a,b,c,d,e){if(null!=c&&null!=a&&(b=null!=b?b:!0,e=e||new mxDictionary,!e.get(a)&&(e.put(a,!0),d=c(a,d),null==d||d))&&(d=this.graph.model.getEdgeCount(a),0<d))for(var f=0;f<d;f++){var g=this.graph.model.getEdgeAt(a,f),k=this.graph.model.getTerminal(g,!0)==a;if(!b||k)k=this.graph.view.getVisibleTerminal(g,!k),this.traverse(k,b,c,g,e)}};
+mxGraphLayout.prototype.isAncestor=function(a,b,c){if(!c)return this.graph.model.getParent(cell)==a;if(b==a)return!1;for(;null!=b&&b!=a;)b=this.graph.model.getParent(b);return b==a};mxGraphLayout.prototype.isVertexMovable=function(a){return this.graph.isCellMovable(a)};mxGraphLayout.prototype.isVertexIgnored=function(a){return!this.graph.getModel().isVertex(a)||!this.graph.isCellVisible(a)};
+mxGraphLayout.prototype.isEdgeIgnored=function(a){var b=this.graph.getModel();return!b.isEdge(a)||!this.graph.isCellVisible(a)||null==b.getTerminal(a,!0)||null==b.getTerminal(a,!1)};mxGraphLayout.prototype.setEdgeStyleEnabled=function(a,b){this.graph.setCellStyles(mxConstants.STYLE_NOEDGESTYLE,b?"0":"1",[a])};mxGraphLayout.prototype.setOrthogonalEdge=function(a,b){this.graph.setCellStyles(mxConstants.STYLE_ORTHOGONAL,b?"1":"0",[a])};
+mxGraphLayout.prototype.getParentOffset=function(a){var b=new mxPoint;if(null!=a&&a!=this.parent){var c=this.graph.getModel();if(c.isAncestor(this.parent,a))for(var d=c.getGeometry(a);a!=this.parent;)b.x+=d.x,b.y+=d.y,a=c.getParent(a),d=c.getGeometry(a)}return b};
+mxGraphLayout.prototype.setEdgePoints=function(a,b){if(null!=a){var c=this.graph.model,d=c.getGeometry(a);null==d?(d=new mxGeometry,d.setRelative(!0)):d=d.clone();if(null!=this.parent&&null!=b)for(var e=c.getParent(a),e=this.getParentOffset(e),f=0;f<b.length;f++)b[f].x-=e.x,b[f].y-=e.y;d.points=b;c.setGeometry(a,d)}};
+mxGraphLayout.prototype.setVertexLocation=function(a,b,c){var d=this.graph.getModel(),e=d.getGeometry(a),f=null;if(null!=e){f=new mxRectangle(b,c,e.width,e.height);if(this.useBoundingBox){var g=this.graph.getView().getState(a);if(null!=g&&null!=g.text&&null!=g.text.boundingBox){var k=this.graph.getView().scale,l=g.text.boundingBox;g.text.boundingBox.x<g.x&&(b+=(g.x-l.x)/k,f.width=l.width);g.text.boundingBox.y<g.y&&(c+=(g.y-l.y)/k,f.height=l.height)}}null!=this.parent&&(g=d.getParent(a),null!=g&&g!=
+this.parent&&(g=this.getParentOffset(g),b-=g.x,c-=g.y));if(e.x!=b||e.y!=c)e=e.clone(),e.x=b,e.y=c,d.setGeometry(a,e)}return f};
+mxGraphLayout.prototype.getVertexBounds=function(a){var b=this.graph.getModel().getGeometry(a);if(this.useBoundingBox){var c=this.graph.getView().getState(a);if(null!=c&&null!=c.text&&null!=c.text.boundingBox)var d=this.graph.getView().scale,e=c.text.boundingBox,f=Math.max(c.x-e.x,0)/d,g=Math.max(c.y-e.y,0)/d,b=new mxRectangle(b.x-f,b.y-g,b.width+f+Math.max(e.x+e.width-(c.x+c.width),0)/d,b.height+g+Math.max(e.y+e.height-(c.y+c.height),0)/d)}null!=this.parent&&(a=this.graph.getModel().getParent(a),
+b=b.clone(),null!=a&&a!=this.parent&&(a=this.getParentOffset(a),b.x+=a.x,b.y+=a.y));return new mxRectangle(b.x,b.y,b.width,b.height)};mxGraphLayout.prototype.arrangeGroups=function(a,b,c,d,e,f){return this.graph.updateGroupBounds(a,b,!0,c,d,e,f)};function WeightedCellSorter(a,b){this.cell=a;this.weightedValue=b}WeightedCellSorter.prototype.weightedValue=0;WeightedCellSorter.prototype.nudge=!1;WeightedCellSorter.prototype.visited=!1;WeightedCellSorter.prototype.rankIndex=null;
+WeightedCellSorter.prototype.cell=null;WeightedCellSorter.prototype.compare=function(a,b){return null!=a&&null!=b?b.weightedValue>a.weightedValue?-1:b.weightedValue<a.weightedValue?1:b.nudge?-1:1:0};function mxStackLayout(a,b,c,d,e,f){mxGraphLayout.call(this,a);this.horizontal=null!=b?b:!0;this.spacing=null!=c?c:0;this.x0=null!=d?d:0;this.y0=null!=e?e:0;this.border=null!=f?f:0}mxStackLayout.prototype=new mxGraphLayout;mxStackLayout.prototype.constructor=mxStackLayout;
+mxStackLayout.prototype.horizontal=null;mxStackLayout.prototype.spacing=null;mxStackLayout.prototype.x0=null;mxStackLayout.prototype.y0=null;mxStackLayout.prototype.border=0;mxStackLayout.prototype.marginTop=0;mxStackLayout.prototype.marginLeft=0;mxStackLayout.prototype.marginRight=0;mxStackLayout.prototype.marginBottom=0;mxStackLayout.prototype.keepFirstLocation=!1;mxStackLayout.prototype.fill=!1;mxStackLayout.prototype.resizeParent=!1;mxStackLayout.prototype.resizeParentMax=!1;
+mxStackLayout.prototype.resizeLast=!1;mxStackLayout.prototype.wrap=null;mxStackLayout.prototype.borderCollapse=!0;mxStackLayout.prototype.isHorizontal=function(){return this.horizontal};
+mxStackLayout.prototype.moveCell=function(a,b,c){var d=this.graph.getModel(),e=d.getParent(a),f=this.isHorizontal();if(null!=a&&null!=e){var g=0,k=d.getChildCount(e);c=f?b:c;b=this.graph.getView().getState(e);null!=b&&(c-=f?b.x:b.y);c/=this.graph.view.scale;for(b=0;b<k;b++){var l=d.getChildAt(e,b);if(l!=a&&(l=d.getGeometry(l),null!=l)){l=f?l.x+l.width/2:l.y+l.height/2;if(g<=c&&l>c)break;g=l}}f=e.getIndex(a);f=Math.max(0,b-(b>f?1:0));d.add(e,a,f)}};
+mxStackLayout.prototype.getParentSize=function(a){var b=this.graph.getModel(),c=b.getGeometry(a);null!=this.graph.container&&(null==c&&b.isLayer(a)||a==this.graph.getView().currentRoot)&&(c=new mxRectangle(0,0,this.graph.container.offsetWidth-1,this.graph.container.offsetHeight-1));return c};
+mxStackLayout.prototype.execute=function(a){if(null!=a){var b=this.getParentSize(a),c=this.isHorizontal(),d=this.graph.getModel(),e=null;null!=b&&(e=c?b.height-this.marginTop-this.marginBottom:b.width-this.marginLeft-this.marginRight);var e=e-2*this.border,f=this.x0+this.border+this.marginLeft,g=this.y0+this.border+this.marginTop;if(this.graph.isSwimlane(a)){var k=this.graph.getCellStyle(a),l=mxUtils.getNumber(k,mxConstants.STYLE_STARTSIZE,mxConstants.DEFAULT_STARTSIZE),k=1==mxUtils.getValue(k,mxConstants.STYLE_HORIZONTAL,
+!0);null!=b&&(l=k?Math.min(l,b.height):Math.min(l,b.width));c==k&&(e-=l);k?g+=l:f+=l}d.beginUpdate();try{for(var l=0,k=null,m=0,n=null,p=d.getChildCount(a),q=0;q<p;q++){var r=d.getChildAt(a,q);if(!this.isVertexIgnored(r)&&this.isVertexMovable(r)){var t=d.getGeometry(r);if(null!=t){t=t.clone();null!=this.wrap&&null!=k&&(c&&k.x+k.width+t.width+2*this.spacing>this.wrap||!c&&k.y+k.height+t.height+2*this.spacing>this.wrap)&&(k=null,c?g+=l+this.spacing:f+=l+this.spacing,l=0);var l=Math.max(l,c?t.height:
+t.width),u=0;if(!this.borderCollapse)var x=this.graph.getCellStyle(r),u=mxUtils.getNumber(x,mxConstants.STYLE_STROKEWIDTH,1);null!=k?c?t.x=m+this.spacing+Math.floor(u/2):t.y=m+this.spacing+Math.floor(u/2):this.keepFirstLocation||(c?t.x=f:t.y=g);c?t.y=g:t.x=f;this.fill&&null!=e&&(c?t.height=e:t.width=e);this.setChildGeometry(r,t);n=r;k=t;m=c?k.x+k.width+Math.floor(u/2):k.y+k.height+Math.floor(u/2)}}}this.resizeParent&&null!=b&&null!=k&&!this.graph.isCellCollapsed(a)?this.updateParentGeometry(a,b,k):
+this.resizeLast&&null!=b&&null!=k&&null!=n&&(c?k.width=b.width-k.x-this.spacing-this.marginRight-this.marginLeft:k.height=b.height-k.y-this.spacing-this.marginBottom,this.setChildGeometry(n,k))}finally{d.endUpdate()}}};mxStackLayout.prototype.setChildGeometry=function(a,b){var c=this.graph.getCellGeometry(a);null!=c&&b.x==c.x&&b.y==c.y&&b.width==c.width&&b.height==c.height||this.graph.getModel().setGeometry(a,b)};
+mxStackLayout.prototype.updateParentGeometry=function(a,b,c){var d=this.isHorizontal(),e=this.graph.getModel(),f=b.clone();d?(c=c.x+c.width+this.marginRight+this.border,f.width=this.resizeParentMax?Math.max(f.width,c):c):(c=c.y+c.height+this.marginBottom+this.border,f.height=this.resizeParentMax?Math.max(f.height,c):c);b.x==f.x&&b.y==f.y&&b.width==f.width&&b.height==f.height||e.setGeometry(a,f)};
+function mxPartitionLayout(a,b,c,d){mxGraphLayout.call(this,a);this.horizontal=null!=b?b:!0;this.spacing=c||0;this.border=d||0}mxPartitionLayout.prototype=new mxGraphLayout;mxPartitionLayout.prototype.constructor=mxPartitionLayout;mxPartitionLayout.prototype.horizontal=null;mxPartitionLayout.prototype.spacing=null;mxPartitionLayout.prototype.border=null;mxPartitionLayout.prototype.resizeVertices=!0;mxPartitionLayout.prototype.isHorizontal=function(){return this.horizontal};
+mxPartitionLayout.prototype.moveCell=function(a,b,c){c=this.graph.getModel();var d=c.getParent(a);if(null!=a&&null!=d){var e,f=0,g=c.getChildCount(d);for(e=0;e<g;e++){var k=c.getChildAt(d,e),k=this.getVertexBounds(k);if(null!=k){k=k.x+k.width/2;if(f<b&&k>b)break;f=k}}b=d.getIndex(a);b=Math.max(0,e-(e>b?1:0));c.add(d,a,b)}};
+mxPartitionLayout.prototype.execute=function(a){var b=this.isHorizontal(),c=this.graph.getModel(),d=c.getGeometry(a);null!=this.graph.container&&(null==d&&c.isLayer(a)||a==this.graph.getView().currentRoot)&&(d=new mxRectangle(0,0,this.graph.container.offsetWidth-1,this.graph.container.offsetHeight-1));if(null!=d){for(var e=[],f=c.getChildCount(a),g=0;g<f;g++){var k=c.getChildAt(a,g);!this.isVertexIgnored(k)&&this.isVertexMovable(k)&&e.push(k)}f=e.length;if(0<f){var l=this.border,m=this.border,n=b?
+d.height:d.width,n=n-2*this.border;a=this.graph.isSwimlane(a)?this.graph.getStartSize(a):new mxRectangle;n-=b?a.height:a.width;l+=a.width;m+=a.height;a=this.border+(f-1)*this.spacing;d=b?(d.width-l-a)/f:(d.height-m-a)/f;if(0<d){c.beginUpdate();try{for(g=0;g<f;g++){var k=e[g],p=c.getGeometry(k);null!=p&&(p=p.clone(),p.x=l,p.y=m,b?(this.resizeVertices&&(p.width=d,p.height=n),l+=d+this.spacing):(this.resizeVertices&&(p.height=d,p.width=n),m+=d+this.spacing),c.setGeometry(k,p))}}finally{c.endUpdate()}}}}};
+function mxCompactTreeLayout(a,b,c){mxGraphLayout.call(this,a);this.horizontal=null!=b?b:!0;this.invert=null!=c?c:!1}mxCompactTreeLayout.prototype=new mxGraphLayout;mxCompactTreeLayout.prototype.constructor=mxCompactTreeLayout;mxCompactTreeLayout.prototype.horizontal=null;mxCompactTreeLayout.prototype.invert=null;mxCompactTreeLayout.prototype.resizeParent=!0;mxCompactTreeLayout.prototype.maintainParentLocation=!1;mxCompactTreeLayout.prototype.groupPadding=10;
+mxCompactTreeLayout.prototype.groupPaddingTop=0;mxCompactTreeLayout.prototype.groupPaddingRight=0;mxCompactTreeLayout.prototype.groupPaddingBottom=0;mxCompactTreeLayout.prototype.groupPaddingLeft=0;mxCompactTreeLayout.prototype.parentsChanged=null;mxCompactTreeLayout.prototype.moveTree=!1;mxCompactTreeLayout.prototype.visited=null;mxCompactTreeLayout.prototype.levelDistance=10;mxCompactTreeLayout.prototype.nodeDistance=20;mxCompactTreeLayout.prototype.resetEdges=!0;
+mxCompactTreeLayout.prototype.prefHozEdgeSep=5;mxCompactTreeLayout.prototype.prefVertEdgeOff=4;mxCompactTreeLayout.prototype.minEdgeJetty=8;mxCompactTreeLayout.prototype.channelBuffer=4;mxCompactTreeLayout.prototype.edgeRouting=!0;mxCompactTreeLayout.prototype.sortEdges=!1;mxCompactTreeLayout.prototype.alignRanks=!1;mxCompactTreeLayout.prototype.maxRankHeight=null;mxCompactTreeLayout.prototype.root=null;mxCompactTreeLayout.prototype.node=null;
+mxCompactTreeLayout.prototype.isVertexIgnored=function(a){return mxGraphLayout.prototype.isVertexIgnored.apply(this,arguments)||0==this.graph.getConnections(a).length};mxCompactTreeLayout.prototype.isHorizontal=function(){return this.horizontal};
+mxCompactTreeLayout.prototype.execute=function(a,b){this.parent=a;var c=this.graph.getModel();if(null==b)if(0<this.graph.getEdges(a,c.getParent(a),this.invert,!this.invert,!1).length)this.root=a;else{var d=this.graph.findTreeRoots(a,!0,this.invert);if(0<d.length)for(var e=0;e<d.length;e++)if(!this.isVertexIgnored(d[e])&&0<this.graph.getEdges(d[e],null,this.invert,!this.invert,!1).length){this.root=d[e];break}}else this.root=b;if(null!=this.root){this.parentsChanged=this.resizeParent?{}:null;this.parentY=
+this.parentX=null;if(a!=this.root&&null!=c.isVertex(a)&&this.maintainParentLocation){var f=this.graph.getCellGeometry(a);null!=f&&(this.parentX=f.x,this.parentY=f.y)}c.beginUpdate();try{if(this.visited={},this.node=this.dfs(this.root,a),this.alignRanks&&(this.maxRankHeight=[],this.findRankHeights(this.node,0),this.setCellHeights(this.node,0)),null!=this.node){this.layout(this.node);var g=this.graph.gridSize,d=g;if(!this.moveTree){var k=this.getVertexBounds(this.root);null!=k&&(g=k.x,d=k.y)}k=null;
+k=this.isHorizontal()?this.horizontalLayout(this.node,g,d):this.verticalLayout(this.node,null,g,d);if(null!=k){var l=e=0;0>k.x&&(e=Math.abs(g-k.x));0>k.y&&(l=Math.abs(d-k.y));0==e&&0==l||this.moveNode(this.node,e,l);this.resizeParent&&this.adjustParents();this.edgeRouting&&this.localEdgeProcessing(this.node)}null!=this.parentX&&null!=this.parentY&&(f=this.graph.getCellGeometry(a),null!=f&&(f=f.clone(),f.x=this.parentX,f.y=this.parentY,c.setGeometry(a,f)))}}finally{c.endUpdate()}}};
+mxCompactTreeLayout.prototype.moveNode=function(a,b,c){a.x+=b;a.y+=c;this.apply(a);for(a=a.child;null!=a;)this.moveNode(a,b,c),a=a.next};
+mxCompactTreeLayout.prototype.sortOutgoingEdges=function(a,b){var c=new mxDictionary;b.sort(function(b,e){var d=b.getTerminal(b.getTerminal(!1)==a),g=c.get(d);null==g&&(g=mxCellPath.create(d).split(mxCellPath.PATH_SEPARATOR),c.put(d,g));var d=e.getTerminal(e.getTerminal(!1)==a),k=c.get(d);null==k&&(k=mxCellPath.create(d).split(mxCellPath.PATH_SEPARATOR),c.put(d,k));return mxCellPath.compare(g,k)})};
+mxCompactTreeLayout.prototype.findRankHeights=function(a,b){if(null==this.maxRankHeight[b]||this.maxRankHeight[b]<a.height)this.maxRankHeight[b]=a.height;for(var c=a.child;null!=c;)this.findRankHeights(c,b+1),c=c.next};mxCompactTreeLayout.prototype.setCellHeights=function(a,b){null!=this.maxRankHeight[b]&&this.maxRankHeight[b]>a.height&&(a.height=this.maxRankHeight[b]);for(var c=a.child;null!=c;)this.setCellHeights(c,b+1),c=c.next};
+mxCompactTreeLayout.prototype.dfs=function(a,b){var c=mxCellPath.create(a),d=null;if(null!=a&&null==this.visited[c]&&!this.isVertexIgnored(a)){this.visited[c]=a;var d=this.createNode(a),c=this.graph.getModel(),e=null,f=this.graph.getEdges(a,b,this.invert,!this.invert,!1,!0),g=this.graph.getView();this.sortEdges&&this.sortOutgoingEdges(a,f);for(var k=0;k<f.length;k++){var l=f[k];if(!this.isEdgeIgnored(l)){this.resetEdges&&this.setEdgePoints(l,null);this.edgeRouting&&(this.setEdgeStyleEnabled(l,!1),
+this.setEdgePoints(l,null));var m=g.getState(l),l=null!=m?m.getVisibleTerminal(this.invert):g.getVisibleTerminal(l,this.invert),m=this.dfs(l,b);null!=m&&null!=c.getGeometry(l)&&(null==e?d.child=m:e.next=m,e=m)}}}return d};mxCompactTreeLayout.prototype.layout=function(a){if(null!=a){for(var b=a.child;null!=b;)this.layout(b),b=b.next;null!=a.child?this.attachParent(a,this.join(a)):this.layoutLeaf(a)}};
+mxCompactTreeLayout.prototype.horizontalLayout=function(a,b,c,d){a.x+=b+a.offsetX;a.y+=c+a.offsetY;d=this.apply(a,d);b=a.child;if(null!=b){d=this.horizontalLayout(b,a.x,a.y,d);c=a.y+b.offsetY;for(var e=b.next;null!=e;)d=this.horizontalLayout(e,a.x+b.offsetX,c,d),c+=e.offsetY,e=e.next}return d};
+mxCompactTreeLayout.prototype.verticalLayout=function(a,b,c,d,e){a.x+=c+a.offsetY;a.y+=d+a.offsetX;e=this.apply(a,e);b=a.child;if(null!=b)for(e=this.verticalLayout(b,a,a.x,a.y,e),c=a.x+b.offsetY,d=b.next;null!=d;)e=this.verticalLayout(d,a,c,a.y+b.offsetX,e),c+=d.offsetY,d=d.next;return e};
+mxCompactTreeLayout.prototype.attachParent=function(a,b){var c=this.nodeDistance+this.levelDistance,d=(b-a.width)/2-this.nodeDistance,e=d+a.width+2*this.nodeDistance-b;a.child.offsetX=c+a.height;a.child.offsetY=e;a.contour.upperHead=this.createLine(a.height,0,this.createLine(c,e,a.contour.upperHead));a.contour.lowerHead=this.createLine(a.height,0,this.createLine(c,d,a.contour.lowerHead))};
+mxCompactTreeLayout.prototype.layoutLeaf=function(a){var b=2*this.nodeDistance;a.contour.upperTail=this.createLine(a.height+b,0);a.contour.upperHead=a.contour.upperTail;a.contour.lowerTail=this.createLine(0,-a.width-b);a.contour.lowerHead=this.createLine(a.height+b,0,a.contour.lowerTail)};
+mxCompactTreeLayout.prototype.join=function(a){var b=2*this.nodeDistance,c=a.child;a.contour=c.contour;for(var d=c.width+b,e=d,c=c.next;null!=c;){var f=this.merge(a.contour,c.contour);c.offsetY=f+d;c.offsetX=0;d=c.width+b;e+=f+d;c=c.next}return e};
+mxCompactTreeLayout.prototype.merge=function(a,b){for(var c=0,d=0,e=0,f=a.lowerHead,g=b.upperHead;null!=g&&null!=f;){var k=this.offset(c,d,g.dx,g.dy,f.dx,f.dy),d=d+k,e=e+k;c+g.dx<=f.dx?(c+=g.dx,d+=g.dy,g=g.next):(c-=f.dx,d-=f.dy,f=f.next)}null!=g?(c=this.bridge(a.upperTail,0,0,g,c,d),a.upperTail=null!=c.next?b.upperTail:c,a.lowerTail=b.lowerTail):(c=this.bridge(b.lowerTail,c,d,f,0,0),null==c.next&&(a.lowerTail=c));a.lowerHead=b.lowerHead;return e};
+mxCompactTreeLayout.prototype.offset=function(a,b,c,d,e,f){if(e<=a||0>=a+c)return 0;a=0<e*d-c*f?0>a?a*d/c-b:0<a?a*f/e-b:-b:e<a+c?f-(b+(e-a)*d/c):e>a+c?(c+a)*f/e-(b+d):f-(b+d);return 0<a?a:0};mxCompactTreeLayout.prototype.bridge=function(a,b,c,d,e,f){b=e+d.dx-b;0==d.dx?e=d.dy:(e=b*d.dy,e/=d.dx);b=this.createLine(b,e,d.next);a.next=this.createLine(0,f+d.dy-e-c,b);return b};
+mxCompactTreeLayout.prototype.createNode=function(a){var b={};b.cell=a;b.x=0;b.y=0;b.width=0;b.height=0;a=this.getVertexBounds(a);null!=a&&(this.isHorizontal()?(b.width=a.height,b.height=a.width):(b.width=a.width,b.height=a.height));b.offsetX=0;b.offsetY=0;b.contour={};return b};
+mxCompactTreeLayout.prototype.apply=function(a,b){var c=this.graph.getModel(),d=a.cell,e=c.getGeometry(d);null!=d&&null!=e&&(this.isVertexMovable(d)&&(e=this.setVertexLocation(d,a.x,a.y),this.resizeParent&&(c=c.getParent(d),d=mxCellPath.create(c),null==this.parentsChanged[d]&&(this.parentsChanged[d]=c))),b=null==b?new mxRectangle(e.x,e.y,e.width,e.height):new mxRectangle(Math.min(b.x,e.x),Math.min(b.y,e.y),Math.max(b.x+b.width,e.x+e.width),Math.max(b.y+b.height,e.y+e.height)));return b};
+mxCompactTreeLayout.prototype.createLine=function(a,b,c){var d={};d.dx=a;d.dy=b;d.next=c;return d};mxCompactTreeLayout.prototype.adjustParents=function(){var a=[],b;for(b in this.parentsChanged)a.push(this.parentsChanged[b]);this.arrangeGroups(mxUtils.sortCells(a,!0),this.groupPadding,this.groupPaddingTop,this.groupPaddingRight,this.groupPaddingBottom,this.groupPaddingLeft)};
+mxCompactTreeLayout.prototype.localEdgeProcessing=function(a){this.processNodeOutgoing(a);for(a=a.child;null!=a;)this.localEdgeProcessing(a),a=a.next};
+mxCompactTreeLayout.prototype.processNodeOutgoing=function(a){for(var b=a.child,c=a.cell,d=0,e=[];null!=b;){d++;var f=b.x;this.horizontal&&(f=b.y);e.push(new WeightedCellSorter(b,f));b=b.next}e.sort(WeightedCellSorter.prototype.compare);var f=a.width,g=(d+1)*this.prefHozEdgeSep;f>g+2*this.prefHozEdgeSep&&(f-=2*this.prefHozEdgeSep);a=f/d;b=a/2;f>g+2*this.prefHozEdgeSep&&(b+=this.prefHozEdgeSep);for(var f=this.minEdgeJetty-this.prefVertEdgeOff,g=this.getVertexBounds(c),k=0;k<e.length;k++){for(var l=
+e[k].cell.cell,m=this.getVertexBounds(l),l=this.graph.getEdgesBetween(c,l,!1),n=[],p,q,r=0;r<l.length;r++)this.horizontal?(p=g.x+g.width,q=g.y+b,n.push(new mxPoint(p,q)),p=g.x+g.width+f,n.push(new mxPoint(p,q)),q=m.y+m.height/2):(p=g.x+b,q=g.y+g.height,n.push(new mxPoint(p,q)),q=g.y+g.height+f,n.push(new mxPoint(p,q)),p=m.x+m.width/2),n.push(new mxPoint(p,q)),this.setEdgePoints(l[r],n);k<d/2?f+=this.prefVertEdgeOff:k>d/2&&(f-=this.prefVertEdgeOff);b+=a}};
+function mxRadialTreeLayout(a){mxCompactTreeLayout.call(this,a,!1)}mxUtils.extend(mxRadialTreeLayout,mxCompactTreeLayout);mxRadialTreeLayout.prototype.angleOffset=.5;mxRadialTreeLayout.prototype.rootx=0;mxRadialTreeLayout.prototype.rooty=0;mxRadialTreeLayout.prototype.levelDistance=120;mxRadialTreeLayout.prototype.nodeDistance=10;mxRadialTreeLayout.prototype.autoRadius=!1;mxRadialTreeLayout.prototype.sortEdges=!1;mxRadialTreeLayout.prototype.rowMinX=[];mxRadialTreeLayout.prototype.rowMaxX=[];
+mxRadialTreeLayout.prototype.rowMinCenX=[];mxRadialTreeLayout.prototype.rowMaxCenX=[];mxRadialTreeLayout.prototype.rowRadi=[];mxRadialTreeLayout.prototype.row=[];mxRadialTreeLayout.prototype.isVertexIgnored=function(a){return mxGraphLayout.prototype.isVertexIgnored.apply(this,arguments)||0==this.graph.getConnections(a).length};
+mxRadialTreeLayout.prototype.execute=function(a,b){this.parent=a;this.edgeRouting=this.useBoundingBox=!1;mxCompactTreeLayout.prototype.execute.apply(this,arguments);var c=null,d=this.getVertexBounds(this.root);this.centerX=d.x+d.width/2;this.centerY=d.y+d.height/2;for(var e in this.visited){var f=this.getVertexBounds(this.visited[e]),c=null!=c?c:f.clone();c.add(f)}this.calcRowDims([this.node],0);for(var g=0,k=0,c=0;c<this.row.length;c++)e=(this.rowMaxX[c]-this.centerX-this.nodeDistance)/this.rowRadi[c],
+g=Math.max(g,(this.centerX-this.rowMinX[c]-this.nodeDistance)/this.rowRadi[c]),k=Math.max(k,e);for(c=0;c<this.row.length;c++){var l=this.centerX-this.nodeDistance-g*this.rowRadi[c],m=this.centerX+this.nodeDistance+k*this.rowRadi[c]-l;for(e=0;e<this.row[c].length;e++)f=this.row[c],d=f[e],f=this.getVertexBounds(d.cell),d.theta=(f.x+f.width/2-l)/m*Math.PI*2}for(c=this.row.length-2;0<=c;c--)for(f=this.row[c],e=0;e<f.length;e++){d=f[e];g=d.child;for(l=k=0;null!=g;)l+=g.theta,k++,g=g.next;0<k&&(g=l/k,g>
+d.theta&&e<f.length-1?d.theta=Math.min(g,f[e+1].theta-Math.PI/10):g<d.theta&&0<e&&(d.theta=Math.max(g,f[e-1].theta+Math.PI/10)))}for(c=0;c<this.row.length;c++)for(e=0;e<this.row[c].length;e++)f=this.row[c],d=f[e],f=this.getVertexBounds(d.cell),this.setVertexLocation(d.cell,this.centerX-f.width/2+this.rowRadi[c]*Math.cos(d.theta),this.centerY-f.height/2+this.rowRadi[c]*Math.sin(d.theta))};
+mxRadialTreeLayout.prototype.calcRowDims=function(a,b){if(null!=a&&0!=a.length){this.rowMinX[b]=this.centerX;this.rowMaxX[b]=this.centerX;this.rowMinCenX[b]=this.centerX;this.rowMaxCenX[b]=this.centerX;this.row[b]=[];for(var c=!1,d=0;d<a.length;d++)for(var e=null!=a[d]?a[d].child:null;null!=e;){var f=this.getVertexBounds(e.cell);this.rowMinX[b]=Math.min(f.x,this.rowMinX[b]);this.rowMaxX[b]=Math.max(f.x+f.width,this.rowMaxX[b]);this.rowMinCenX[b]=Math.min(f.x+f.width/2,this.rowMinCenX[b]);this.rowMaxCenX[b]=
+Math.max(f.x+f.width/2,this.rowMaxCenX[b]);this.rowRadi[b]=f.y-this.getVertexBounds(this.root).y;null!=e.child&&(c=!0);this.row[b].push(e);e=e.next}c&&this.calcRowDims(this.row[b],b+1)}};function mxFastOrganicLayout(a){mxGraphLayout.call(this,a)}mxFastOrganicLayout.prototype=new mxGraphLayout;mxFastOrganicLayout.prototype.constructor=mxFastOrganicLayout;mxFastOrganicLayout.prototype.useInputOrigin=!0;mxFastOrganicLayout.prototype.resetEdges=!0;mxFastOrganicLayout.prototype.disableEdgeStyle=!0;
+mxFastOrganicLayout.prototype.forceConstant=50;mxFastOrganicLayout.prototype.forceConstantSquared=0;mxFastOrganicLayout.prototype.minDistanceLimit=2;mxFastOrganicLayout.prototype.maxDistanceLimit=500;mxFastOrganicLayout.prototype.minDistanceLimitSquared=4;mxFastOrganicLayout.prototype.initialTemp=200;mxFastOrganicLayout.prototype.temperature=0;mxFastOrganicLayout.prototype.maxIterations=0;mxFastOrganicLayout.prototype.iteration=0;mxFastOrganicLayout.prototype.allowedToRun=!0;
+mxFastOrganicLayout.prototype.isVertexIgnored=function(a){return mxGraphLayout.prototype.isVertexIgnored.apply(this,arguments)||0==this.graph.getConnections(a).length};
+mxFastOrganicLayout.prototype.execute=function(a){var b=this.graph.getModel();this.vertexArray=[];for(var c=this.graph.getChildVertices(a),d=0;d<c.length;d++)this.isVertexIgnored(c[d])||this.vertexArray.push(c[d]);var e=this.useInputOrigin?this.graph.getBoundingBoxFromGeometry(this.vertexArray):null,f=this.vertexArray.length;this.indices=[];this.dispX=[];this.dispY=[];this.cellLocation=[];this.isMoveable=[];this.neighbours=[];this.radius=[];this.radiusSquared=[];.001>this.forceConstant&&(this.forceConstant=
+.001);this.forceConstantSquared=this.forceConstant*this.forceConstant;for(d=0;d<this.vertexArray.length;d++){var g=this.vertexArray[d];this.cellLocation[d]=[];var k=mxObjectIdentity.get(g);this.indices[k]=d;var l=this.getVertexBounds(g),m=l.width,n=l.height,p=l.x,q=l.y;this.cellLocation[d][0]=p+m/2;this.cellLocation[d][1]=q+n/2;this.radius[d]=Math.min(m,n);this.radiusSquared[d]=this.radius[d]*this.radius[d]}b.beginUpdate();try{for(d=0;d<f;d++){this.dispX[d]=0;this.dispY[d]=0;this.isMoveable[d]=this.isVertexMovable(this.vertexArray[d]);
+var r=this.graph.getConnections(this.vertexArray[d],a),c=this.graph.getOpposites(r,this.vertexArray[d]);this.neighbours[d]=[];for(m=0;m<c.length;m++){this.resetEdges&&this.graph.resetEdge(r[m]);this.disableEdgeStyle&&this.setEdgeStyleEnabled(r[m],!1);var k=mxObjectIdentity.get(c[m]),t=this.indices[k];this.neighbours[d][m]=null!=t?t:d}}this.temperature=this.initialTemp;0==this.maxIterations&&(this.maxIterations=20*Math.sqrt(f));for(this.iteration=0;this.iteration<this.maxIterations;this.iteration++){if(!this.allowedToRun)return;
+this.calcRepulsion();this.calcAttraction();this.calcPositions();this.reduceTemperature()}a=c=null;for(d=0;d<this.vertexArray.length;d++)g=this.vertexArray[d],this.isVertexMovable(g)&&(l=this.getVertexBounds(g),null!=l&&(this.cellLocation[d][0]-=l.width/2,this.cellLocation[d][1]-=l.height/2,p=this.graph.snap(Math.round(this.cellLocation[d][0])),q=this.graph.snap(Math.round(this.cellLocation[d][1])),this.setVertexLocation(g,p,q),c=null==c?p:Math.min(c,p),a=null==a?q:Math.min(a,q)));d=-(c||0)+1;g=-(a||
+0)+1;null!=e&&(d+=e.x,g+=e.y);this.graph.moveCells(this.vertexArray,d,g)}finally{b.endUpdate()}};mxFastOrganicLayout.prototype.calcPositions=function(){for(var a=0;a<this.vertexArray.length;a++)if(this.isMoveable[a]){var b=Math.sqrt(this.dispX[a]*this.dispX[a]+this.dispY[a]*this.dispY[a]);.001>b&&(b=.001);var c=this.dispX[a]/b*Math.min(b,this.temperature),b=this.dispY[a]/b*Math.min(b,this.temperature);this.dispX[a]=0;this.dispY[a]=0;this.cellLocation[a][0]+=c;this.cellLocation[a][1]+=b}};
+mxFastOrganicLayout.prototype.calcAttraction=function(){for(var a=0;a<this.vertexArray.length;a++)for(var b=0;b<this.neighbours[a].length;b++){var c=this.neighbours[a][b];if(a!=c&&this.isMoveable[a]&&this.isMoveable[c]){var d=this.cellLocation[a][0]-this.cellLocation[c][0],e=this.cellLocation[a][1]-this.cellLocation[c][1],f=d*d+e*e-this.radiusSquared[a]-this.radiusSquared[c];f<this.minDistanceLimitSquared&&(f=this.minDistanceLimitSquared);var g=Math.sqrt(f),f=f/this.forceConstant,d=d/g*f,e=e/g*f;
+this.dispX[a]-=d;this.dispY[a]-=e;this.dispX[c]+=d;this.dispY[c]+=e}}};
+mxFastOrganicLayout.prototype.calcRepulsion=function(){for(var a=this.vertexArray.length,b=0;b<a;b++)for(var c=b;c<a;c++){if(!this.allowedToRun)return;if(c!=b&&this.isMoveable[b]&&this.isMoveable[c]){var d=this.cellLocation[b][0]-this.cellLocation[c][0],e=this.cellLocation[b][1]-this.cellLocation[c][1];0==d&&(d=.01+Math.random());0==e&&(e=.01+Math.random());var f=Math.sqrt(d*d+e*e),g=f-this.radius[b]-this.radius[c];g>this.maxDistanceLimit||(g<this.minDistanceLimit&&(g=this.minDistanceLimit),g=this.forceConstantSquared/
+g,d=d/f*g,e=e/f*g,this.dispX[b]+=d,this.dispY[b]+=e,this.dispX[c]-=d,this.dispY[c]-=e)}}};mxFastOrganicLayout.prototype.reduceTemperature=function(){this.temperature=this.initialTemp*(1-this.iteration/this.maxIterations)};function mxCircleLayout(a,b){mxGraphLayout.call(this,a);this.radius=null!=b?b:100}mxCircleLayout.prototype=new mxGraphLayout;mxCircleLayout.prototype.constructor=mxCircleLayout;mxCircleLayout.prototype.radius=null;mxCircleLayout.prototype.moveCircle=!1;
+mxCircleLayout.prototype.x0=0;mxCircleLayout.prototype.y0=0;mxCircleLayout.prototype.resetEdges=!0;mxCircleLayout.prototype.disableEdgeStyle=!0;
+mxCircleLayout.prototype.execute=function(a){var b=this.graph.getModel();b.beginUpdate();try{for(var c=0,d=null,e=null,f=[],g=b.getChildCount(a),k=0;k<g;k++){var l=b.getChildAt(a,k);if(this.isVertexIgnored(l))this.isEdgeIgnored(l)||(this.resetEdges&&this.graph.resetEdge(l),this.disableEdgeStyle&&this.setEdgeStyleEnabled(l,!1));else{f.push(l);var m=this.getVertexBounds(l),d=null==d?m.y:Math.min(d,m.y),e=null==e?m.x:Math.min(e,m.x),c=Math.max(c,Math.max(m.width,m.height))}}var n=this.getRadius(f.length,
+c);this.moveCircle&&(e=this.x0,d=this.y0);this.circle(f,n,e,d)}finally{b.endUpdate()}};mxCircleLayout.prototype.getRadius=function(a,b){return Math.max(a*b/Math.PI,this.radius)};mxCircleLayout.prototype.circle=function(a,b,c,d){for(var e=a.length,f=2*Math.PI/e,g=0;g<e;g++)this.isVertexMovable(a[g])&&this.setVertexLocation(a[g],Math.round(c+b+b*Math.sin(g*f)),Math.round(d+b+b*Math.cos(g*f)))};function mxParallelEdgeLayout(a){mxGraphLayout.call(this,a)}mxParallelEdgeLayout.prototype=new mxGraphLayout;
+mxParallelEdgeLayout.prototype.constructor=mxParallelEdgeLayout;mxParallelEdgeLayout.prototype.spacing=20;mxParallelEdgeLayout.prototype.execute=function(a){a=this.findParallels(a);this.graph.model.beginUpdate();try{for(var b in a){var c=a[b];1<c.length&&this.layout(c)}}finally{this.graph.model.endUpdate()}};
+mxParallelEdgeLayout.prototype.findParallels=function(a){for(var b=this.graph.getModel(),c=[],d=b.getChildCount(a),e=0;e<d;e++){var f=b.getChildAt(a,e);if(!this.isEdgeIgnored(f)){var g=this.getEdgeId(f);null!=g&&(null==c[g]&&(c[g]=[]),c[g].push(f))}}return c};mxParallelEdgeLayout.prototype.getEdgeId=function(a){var b=this.graph.getView(),c=b.getVisibleTerminal(a,!0);a=b.getVisibleTerminal(a,!1);return null!=c&&null!=a?(c=mxObjectIdentity.get(c),a=mxObjectIdentity.get(a),c>a?a+"-"+c:c+"-"+a):null};
+mxParallelEdgeLayout.prototype.layout=function(a){var b=a[0],c=this.graph.getView(),d=this.graph.getModel(),e=d.getGeometry(c.getVisibleTerminal(b,!0)),d=d.getGeometry(c.getVisibleTerminal(b,!1));if(e==d)for(var b=e.x+e.width+this.spacing,c=e.y+e.height/2,f=0;f<a.length;f++)this.route(a[f],b,c),b+=this.spacing;else if(null!=e&&null!=d){var b=e.x+e.width/2,c=e.y+e.height/2,f=d.x+d.width/2-b,g=d.y+d.height/2-c,d=Math.sqrt(f*f+g*g);if(0<d)for(e=g*this.spacing/d,d=f*this.spacing/d,b=b+f/2+e*(a.length-
+1)/2,c=c+g/2-d*(a.length-1)/2,f=0;f<a.length;f++)this.route(a[f],b,c),b-=e,c+=d}};mxParallelEdgeLayout.prototype.route=function(a,b,c){this.graph.isCellMovable(a)&&this.setEdgePoints(a,[new mxPoint(b,c)])};function mxCompositeLayout(a,b,c){mxGraphLayout.call(this,a);this.layouts=b;this.master=c}mxCompositeLayout.prototype=new mxGraphLayout;mxCompositeLayout.prototype.constructor=mxCompositeLayout;mxCompositeLayout.prototype.layouts=null;mxCompositeLayout.prototype.master=null;
+mxCompositeLayout.prototype.moveCell=function(a,b,c){null!=this.master?this.master.move.apply(this.master,arguments):this.layouts[0].move.apply(this.layouts[0],arguments)};mxCompositeLayout.prototype.execute=function(a){var b=this.graph.getModel();b.beginUpdate();try{for(var c=0;c<this.layouts.length;c++)this.layouts[c].execute.apply(this.layouts[c],arguments)}finally{b.endUpdate()}};function mxEdgeLabelLayout(a,b){mxGraphLayout.call(this,a)}mxEdgeLabelLayout.prototype=new mxGraphLayout;
+mxEdgeLabelLayout.prototype.constructor=mxEdgeLabelLayout;mxEdgeLabelLayout.prototype.execute=function(a){for(var b=this.graph.view,c=this.graph.getModel(),d=[],e=[],f=c.getChildCount(a),g=0;g<f;g++){var k=c.getChildAt(a,g),l=b.getState(k);null!=l&&(this.isVertexIgnored(k)?this.isEdgeIgnored(k)||d.push(l):e.push(l))}this.placeLabels(e,d)};
+mxEdgeLabelLayout.prototype.placeLabels=function(a,b){var c=this.graph.getModel();c.beginUpdate();try{for(var d=0;d<b.length;d++){var e=b[d];if(null!=e&&null!=e.text&&null!=e.text.boundingBox)for(var f=0;f<a.length;f++){var g=a[f];null!=g&&this.avoid(e,g)}}}finally{c.endUpdate()}};
+mxEdgeLabelLayout.prototype.avoid=function(a,b){var c=this.graph.getModel(),d=a.text.boundingBox;if(mxUtils.intersects(d,b)){var e=-d.y-d.height+b.y,f=-d.y+b.y+b.height,e=Math.abs(e)<Math.abs(f)?e:f,f=-d.x-d.width+b.x,d=-d.x+b.x+b.width,d=Math.abs(f)<Math.abs(d)?f:d;Math.abs(d)<Math.abs(e)?e=0:d=0;f=c.getGeometry(a.cell);null!=f&&(f=f.clone(),null!=f.offset?(f.offset.x+=d,f.offset.y+=e):f.offset=new mxPoint(d,e),c.setGeometry(a.cell,f))}};
+function mxGraphAbstractHierarchyCell(){this.x=[];this.y=[];this.temp=[]}mxGraphAbstractHierarchyCell.prototype.maxRank=-1;mxGraphAbstractHierarchyCell.prototype.minRank=-1;mxGraphAbstractHierarchyCell.prototype.x=null;mxGraphAbstractHierarchyCell.prototype.y=null;mxGraphAbstractHierarchyCell.prototype.width=0;mxGraphAbstractHierarchyCell.prototype.height=0;mxGraphAbstractHierarchyCell.prototype.nextLayerConnectedCells=null;mxGraphAbstractHierarchyCell.prototype.previousLayerConnectedCells=null;
+mxGraphAbstractHierarchyCell.prototype.temp=null;mxGraphAbstractHierarchyCell.prototype.getNextLayerConnectedCells=function(a){return null};mxGraphAbstractHierarchyCell.prototype.getPreviousLayerConnectedCells=function(a){return null};mxGraphAbstractHierarchyCell.prototype.isEdge=function(){return!1};mxGraphAbstractHierarchyCell.prototype.isVertex=function(){return!1};mxGraphAbstractHierarchyCell.prototype.getGeneralPurposeVariable=function(a){return null};
+mxGraphAbstractHierarchyCell.prototype.setGeneralPurposeVariable=function(a,b){return null};mxGraphAbstractHierarchyCell.prototype.setX=function(a,b){this.isVertex()?this.x[0]=b:this.isEdge()&&(this.x[a-this.minRank-1]=b)};mxGraphAbstractHierarchyCell.prototype.getX=function(a){return this.isVertex()?this.x[0]:this.isEdge()?this.x[a-this.minRank-1]:0};mxGraphAbstractHierarchyCell.prototype.setY=function(a,b){this.isVertex()?this.y[0]=b:this.isEdge()&&(this.y[a-this.minRank-1]=b)};
+function mxGraphHierarchyNode(a){mxGraphAbstractHierarchyCell.apply(this,arguments);this.cell=a;this.id=mxObjectIdentity.get(a);this.connectsAsTarget=[];this.connectsAsSource=[]}mxGraphHierarchyNode.prototype=new mxGraphAbstractHierarchyCell;mxGraphHierarchyNode.prototype.constructor=mxGraphHierarchyNode;mxGraphHierarchyNode.prototype.cell=null;mxGraphHierarchyNode.prototype.id=null;mxGraphHierarchyNode.prototype.connectsAsTarget=null;mxGraphHierarchyNode.prototype.connectsAsSource=null;
+mxGraphHierarchyNode.prototype.hashCode=!1;mxGraphHierarchyNode.prototype.getRankValue=function(a){return this.maxRank};mxGraphHierarchyNode.prototype.getNextLayerConnectedCells=function(a){if(null==this.nextLayerConnectedCells){this.nextLayerConnectedCells=[];this.nextLayerConnectedCells[0]=[];for(var b=0;b<this.connectsAsTarget.length;b++){var c=this.connectsAsTarget[b];-1==c.maxRank||c.maxRank==a+1?this.nextLayerConnectedCells[0].push(c.source):this.nextLayerConnectedCells[0].push(c)}}return this.nextLayerConnectedCells[0]};
+mxGraphHierarchyNode.prototype.getPreviousLayerConnectedCells=function(a){if(null==this.previousLayerConnectedCells){this.previousLayerConnectedCells=[];this.previousLayerConnectedCells[0]=[];for(var b=0;b<this.connectsAsSource.length;b++){var c=this.connectsAsSource[b];-1==c.minRank||c.minRank==a-1?this.previousLayerConnectedCells[0].push(c.target):this.previousLayerConnectedCells[0].push(c)}}return this.previousLayerConnectedCells[0]};mxGraphHierarchyNode.prototype.isVertex=function(){return!0};
+mxGraphHierarchyNode.prototype.getGeneralPurposeVariable=function(a){return this.temp[0]};mxGraphHierarchyNode.prototype.setGeneralPurposeVariable=function(a,b){this.temp[0]=b};mxGraphHierarchyNode.prototype.isAncestor=function(a){if(null!=a&&null!=this.hashCode&&null!=a.hashCode&&this.hashCode.length<a.hashCode.length){if(this.hashCode==a.hashCode)return!0;if(null==this.hashCode||null==this.hashCode)return!1;for(var b=0;b<this.hashCode.length;b++)if(this.hashCode[b]!=a.hashCode[b])return!1;return!0}return!1};
+mxGraphHierarchyNode.prototype.getCoreCell=function(){return this.cell};function mxGraphHierarchyEdge(a){mxGraphAbstractHierarchyCell.apply(this,arguments);this.edges=a;this.ids=[];for(var b=0;b<a.length;b++)this.ids.push(mxObjectIdentity.get(a[b]))}mxGraphHierarchyEdge.prototype=new mxGraphAbstractHierarchyCell;mxGraphHierarchyEdge.prototype.constructor=mxGraphHierarchyEdge;mxGraphHierarchyEdge.prototype.edges=null;mxGraphHierarchyEdge.prototype.ids=null;mxGraphHierarchyEdge.prototype.source=null;
+mxGraphHierarchyEdge.prototype.target=null;mxGraphHierarchyEdge.prototype.isReversed=!1;mxGraphHierarchyEdge.prototype.invert=function(a){a=this.source;this.source=this.target;this.target=a;this.isReversed=!this.isReversed};
+mxGraphHierarchyEdge.prototype.getNextLayerConnectedCells=function(a){if(null==this.nextLayerConnectedCells){this.nextLayerConnectedCells=[];for(var b=0;b<this.temp.length;b++)this.nextLayerConnectedCells[b]=[],b==this.temp.length-1?this.nextLayerConnectedCells[b].push(this.source):this.nextLayerConnectedCells[b].push(this)}return this.nextLayerConnectedCells[a-this.minRank-1]};
+mxGraphHierarchyEdge.prototype.getPreviousLayerConnectedCells=function(a){if(null==this.previousLayerConnectedCells){this.previousLayerConnectedCells=[];for(var b=0;b<this.temp.length;b++)this.previousLayerConnectedCells[b]=[],0==b?this.previousLayerConnectedCells[b].push(this.target):this.previousLayerConnectedCells[b].push(this)}return this.previousLayerConnectedCells[a-this.minRank-1]};mxGraphHierarchyEdge.prototype.isEdge=function(){return!0};
+mxGraphHierarchyEdge.prototype.getGeneralPurposeVariable=function(a){return this.temp[a-this.minRank-1]};mxGraphHierarchyEdge.prototype.setGeneralPurposeVariable=function(a,b){this.temp[a-this.minRank-1]=b};mxGraphHierarchyEdge.prototype.getCoreCell=function(){return null!=this.edges&&0<this.edges.length?this.edges[0]:null};
+function mxGraphHierarchyModel(a,b,c,d,e){a.getGraph();this.tightenToSource=e;this.roots=c;this.parent=d;this.vertexMapper=new mxDictionary;this.edgeMapper=new mxDictionary;this.maxRank=0;c=[];null==b&&(b=this.graph.getChildVertices(d));this.maxRank=this.SOURCESCANSTARTRANK;this.createInternalCells(a,b,c);for(d=0;d<b.length;d++){e=c[d].connectsAsSource;for(var f=0;f<e.length;f++){var g=e[f],k=g.edges;if(null!=k&&0<k.length){var k=k[0],l=a.getVisibleTerminal(k,!1),l=this.vertexMapper.get(l);c[d]==
+l&&(l=a.getVisibleTerminal(k,!0),l=this.vertexMapper.get(l));null!=l&&c[d]!=l&&(g.target=l,0==l.connectsAsTarget.length&&(l.connectsAsTarget=[]),0>mxUtils.indexOf(l.connectsAsTarget,g)&&l.connectsAsTarget.push(g))}}c[d].temp[0]=1}}mxGraphHierarchyModel.prototype.maxRank=null;mxGraphHierarchyModel.prototype.vertexMapper=null;mxGraphHierarchyModel.prototype.edgeMapper=null;mxGraphHierarchyModel.prototype.ranks=null;mxGraphHierarchyModel.prototype.roots=null;mxGraphHierarchyModel.prototype.parent=null;
+mxGraphHierarchyModel.prototype.dfsCount=0;mxGraphHierarchyModel.prototype.SOURCESCANSTARTRANK=1E8;mxGraphHierarchyModel.prototype.tightenToSource=!1;
+mxGraphHierarchyModel.prototype.createInternalCells=function(a,b,c){for(var d=a.getGraph(),e=0;e<b.length;e++){c[e]=new mxGraphHierarchyNode(b[e]);this.vertexMapper.put(b[e],c[e]);var f=a.getEdges(b[e]);c[e].connectsAsSource=[];for(var g=0;g<f.length;g++){var k=a.getVisibleTerminal(f[g],!1);if(k!=b[e]&&a.graph.model.isVertex(k)&&!a.isVertexIgnored(k)){var l=a.getEdgesBetween(b[e],k,!1),k=a.getEdgesBetween(b[e],k,!0);if(null!=l&&0<l.length&&null==this.edgeMapper.get(l[0])&&2*k.length>=l.length){for(var k=
+new mxGraphHierarchyEdge(l),m=0;m<l.length;m++){var n=l[m];this.edgeMapper.put(n,k);d.resetEdge(n);a.disableEdgeStyle&&(a.setEdgeStyleEnabled(n,!1),a.setOrthogonalEdge(n,!0))}k.source=c[e];0>mxUtils.indexOf(c[e].connectsAsSource,k)&&c[e].connectsAsSource.push(k)}}}c[e].temp[0]=0}};
+mxGraphHierarchyModel.prototype.initialRank=function(){var a=[];if(null!=this.roots)for(var b=0;b<this.roots.length;b++){var c=this.vertexMapper.get(this.roots[b]);null!=c&&a.push(c)}for(var d=this.vertexMapper.getValues(),b=0;b<d.length;b++)d[b].temp[0]=-1;for(var e=a.slice();0<a.length;){var c=a[0],f,g;f=c.connectsAsTarget;g=c.connectsAsSource;for(var k=!0,l=this.SOURCESCANSTARTRANK,b=0;b<f.length;b++){var m=f[b];if(5270620==m.temp[0])m=m.source,l=Math.min(l,m.temp[0]-1);else{k=!1;break}}if(k){c.temp[0]=
+l;this.maxRank=Math.min(this.maxRank,l);if(null!=g)for(b=0;b<g.length;b++)m=g[b],m.temp[0]=5270620,m=m.target,-1==m.temp[0]&&(a.push(m),m.temp[0]=-2);a.shift()}else if(b=a.shift(),a.push(c),b==c&&1==a.length)break}for(b=0;b<d.length;b++)d[b].temp[0]-=this.maxRank;for(b=0;b<e.length;b++)for(c=e[b],a=0,f=c.connectsAsSource,d=0;d<f.length;d++)m=f[d],m=m.target,c.temp[0]=Math.max(a,m.temp[0]+1),a=c.temp[0];this.maxRank=this.SOURCESCANSTARTRANK-this.maxRank};
+mxGraphHierarchyModel.prototype.fixRanks=function(){var a=[];this.ranks=[];for(var b=0;b<this.maxRank+1;b++)a[b]=[],this.ranks[b]=a[b];var c=null;if(null!=this.roots)for(var d=this.roots,c=[],b=0;b<d.length;b++){var e=this.vertexMapper.get(d[b]);c[b]=e}this.visit(function(b,c,d,e,m){0==m&&0>c.maxRank&&0>c.minRank&&(a[c.temp[0]].push(c),c.maxRank=c.temp[0],c.minRank=c.temp[0],c.temp[0]=a[c.maxRank].length-1);if(null!=b&&null!=d&&1<b.maxRank-c.maxRank)for(d.maxRank=b.maxRank,d.minRank=c.maxRank,d.temp=
+[],d.x=[],d.y=[],b=d.minRank+1;b<d.maxRank;b++)a[b].push(d),d.setGeneralPurposeVariable(b,a[b].length-1)},c,!1,null)};mxGraphHierarchyModel.prototype.visit=function(a,b,c,d){if(null!=b){for(var e=0;e<b.length;e++){var f=b[e];null!=f&&(null==d&&(d={}),c?(f.hashCode=[],f.hashCode[0]=this.dfsCount,f.hashCode[1]=e,this.extendedDfs(null,f,null,a,d,f.hashCode,e,0)):this.dfs(null,f,null,a,d,0))}this.dfsCount++}};
+mxGraphHierarchyModel.prototype.dfs=function(a,b,c,d,e,f){if(null!=b){var g=b.id;if(null==e[g])for(e[g]=b,d(a,b,c,f,0),a=b.connectsAsSource.slice(),c=0;c<a.length;c++)g=a[c],this.dfs(b,g.target,g,d,e,f+1);else d(a,b,c,f,1)}};
+mxGraphHierarchyModel.prototype.extendedDfs=function(a,b,c,d,e,f,g,k){if(null!=b)if(null==a||null!=b.hashCode&&b.hashCode[0]==a.hashCode[0]||(f=a.hashCode.length+1,b.hashCode=a.hashCode.slice(),b.hashCode[f-1]=g),g=b.id,null==e[g])for(e[g]=b,d(a,b,c,k,0),a=b.connectsAsSource.slice(),c=0;c<a.length;c++)g=a[c],this.extendedDfs(b,g.target,g,d,e,b.hashCode,c,k+1);else d(a,b,c,k,1)};
+function mxSwimlaneModel(a,b,c,d,e){a.getGraph();this.tightenToSource=e;this.roots=c;this.parent=d;this.vertexMapper=new mxDictionary;this.edgeMapper=new mxDictionary;this.maxRank=0;c=[];null==b&&(b=this.graph.getChildVertices(d));this.maxRank=this.SOURCESCANSTARTRANK;this.createInternalCells(a,b,c);for(d=0;d<b.length;d++){e=c[d].connectsAsSource;for(var f=0;f<e.length;f++){var g=e[f],k=g.edges;if(null!=k&&0<k.length){var k=k[0],l=a.getVisibleTerminal(k,!1),l=this.vertexMapper.get(l);c[d]==l&&(l=
+a.getVisibleTerminal(k,!0),l=this.vertexMapper.get(l));null!=l&&c[d]!=l&&(g.target=l,0==l.connectsAsTarget.length&&(l.connectsAsTarget=[]),0>mxUtils.indexOf(l.connectsAsTarget,g)&&l.connectsAsTarget.push(g))}}c[d].temp[0]=1}}mxSwimlaneModel.prototype.maxRank=null;mxSwimlaneModel.prototype.vertexMapper=null;mxSwimlaneModel.prototype.edgeMapper=null;mxSwimlaneModel.prototype.ranks=null;mxSwimlaneModel.prototype.roots=null;mxSwimlaneModel.prototype.parent=null;mxSwimlaneModel.prototype.dfsCount=0;
+mxSwimlaneModel.prototype.SOURCESCANSTARTRANK=1E8;mxSwimlaneModel.prototype.tightenToSource=!1;mxSwimlaneModel.prototype.ranksPerGroup=null;
+mxSwimlaneModel.prototype.createInternalCells=function(a,b,c){for(var d=a.getGraph(),e=a.swimlanes,f=0;f<b.length;f++){c[f]=new mxGraphHierarchyNode(b[f]);this.vertexMapper.put(b[f],c[f]);c[f].swimlaneIndex=-1;for(var g=0;g<e.length;g++)if(d.model.getParent(b[f])==e[g]){c[f].swimlaneIndex=g;break}g=a.getEdges(b[f]);c[f].connectsAsSource=[];for(var k=0;k<g.length;k++){var l=a.getVisibleTerminal(g[k],!1);if(l!=b[f]&&a.graph.model.isVertex(l)&&!a.isVertexIgnored(l)){var m=a.getEdgesBetween(b[f],l,!1),
+l=a.getEdgesBetween(b[f],l,!0);if(null!=m&&0<m.length&&null==this.edgeMapper.get(m[0])&&2*l.length>=m.length){for(var l=new mxGraphHierarchyEdge(m),n=0;n<m.length;n++){var p=m[n];this.edgeMapper.put(p,l);d.resetEdge(p);a.disableEdgeStyle&&(a.setEdgeStyleEnabled(p,!1),a.setOrthogonalEdge(p,!0))}l.source=c[f];0>mxUtils.indexOf(c[f].connectsAsSource,l)&&c[f].connectsAsSource.push(l)}}}c[f].temp[0]=0}};
+mxSwimlaneModel.prototype.initialRank=function(){this.ranksPerGroup=[];var a=[],b={};if(null!=this.roots)for(var c=0;c<this.roots.length;c++){var d=this.vertexMapper.get(this.roots[c]);this.maxChainDfs(null,d,null,b,0);null!=d&&a.push(d)}d=[];b=[];for(c=this.ranksPerGroup.length-1;0<=c;c--)d[c]=c==this.ranksPerGroup.length-1?0:b[c+1]+1,b[c]=d[c]+this.ranksPerGroup[c];this.maxRank=b[0];d=this.vertexMapper.getValues();for(c=0;c<d.length;c++)d[c].temp[0]=-1;for(a.slice();0<a.length;){var d=a[0],e,f;
+e=d.connectsAsTarget;f=d.connectsAsSource;for(var g=!0,k=b[0],c=0;c<e.length;c++){var l=e[c];if(5270620==l.temp[0])l=l.source,k=Math.min(k,l.temp[0]-1);else{g=!1;break}}if(g){k>b[d.swimlaneIndex]&&(k=b[d.swimlaneIndex]);d.temp[0]=k;if(null!=f)for(c=0;c<f.length;c++)l=f[c],l.temp[0]=5270620,l=l.target,-1==l.temp[0]&&(a.push(l),l.temp[0]=-2);a.shift()}else if(c=a.shift(),a.push(d),c==d&&1==a.length)break}};
+mxSwimlaneModel.prototype.maxChainDfs=function(a,b,c,d,e){if(null!=b&&(a=mxCellPath.create(b.cell),null==d[a])){d[a]=b;a=b.swimlaneIndex;if(null==this.ranksPerGroup[a]||this.ranksPerGroup[a]<e)this.ranksPerGroup[a]=e;a=b.connectsAsSource.slice();for(c=0;c<a.length;c++){var f=a[c],g=f.target;b.swimlaneIndex<g.swimlaneIndex?this.maxChainDfs(b,g,f,mxUtils.clone(d,null,!0),0):b.swimlaneIndex==g.swimlaneIndex&&this.maxChainDfs(b,g,f,mxUtils.clone(d,null,!0),e+1)}}};
+mxSwimlaneModel.prototype.fixRanks=function(){var a=[];this.ranks=[];for(var b=0;b<this.maxRank+1;b++)a[b]=[],this.ranks[b]=a[b];var c=null;if(null!=this.roots)for(var d=this.roots,c=[],b=0;b<d.length;b++){var e=this.vertexMapper.get(d[b]);c[b]=e}this.visit(function(b,c,d,e,m){0==m&&0>c.maxRank&&0>c.minRank&&(a[c.temp[0]].push(c),c.maxRank=c.temp[0],c.minRank=c.temp[0],c.temp[0]=a[c.maxRank].length-1);if(null!=b&&null!=d&&1<b.maxRank-c.maxRank)for(d.maxRank=b.maxRank,d.minRank=c.maxRank,d.temp=[],
+d.x=[],d.y=[],b=d.minRank+1;b<d.maxRank;b++)a[b].push(d),d.setGeneralPurposeVariable(b,a[b].length-1)},c,!1,null)};mxSwimlaneModel.prototype.visit=function(a,b,c,d){if(null!=b){for(var e=0;e<b.length;e++){var f=b[e];null!=f&&(null==d&&(d={}),c?(f.hashCode=[],f.hashCode[0]=this.dfsCount,f.hashCode[1]=e,this.extendedDfs(null,f,null,a,d,f.hashCode,e,0)):this.dfs(null,f,null,a,d,0))}this.dfsCount++}};
+mxSwimlaneModel.prototype.dfs=function(a,b,c,d,e,f){if(null!=b){var g=b.id;if(null==e[g])for(e[g]=b,d(a,b,c,f,0),a=b.connectsAsSource.slice(),c=0;c<a.length;c++)g=a[c],this.dfs(b,g.target,g,d,e,f+1);else d(a,b,c,f,1)}};
+mxSwimlaneModel.prototype.extendedDfs=function(a,b,c,d,e,f,g,k){if(null!=b)if(null==a||null!=b.hashCode&&b.hashCode[0]==a.hashCode[0]||(f=a.hashCode.length+1,b.hashCode=a.hashCode.slice(),b.hashCode[f-1]=g),g=b.id,null==e[g]){e[g]=b;d(a,b,c,k,0);a=b.connectsAsSource.slice();c=b.connectsAsTarget.slice();for(g=0;g<a.length;g++){f=a[g];var l=f.target;b.swimlaneIndex<=l.swimlaneIndex&&this.extendedDfs(b,l,f,d,e,b.hashCode,g,k+1)}for(g=0;g<c.length;g++)f=c[g],l=f.source,b.swimlaneIndex<l.swimlaneIndex&&
+this.extendedDfs(b,l,f,d,e,b.hashCode,g,k+1)}else d(a,b,c,k,1)};function mxHierarchicalLayoutStage(){}mxHierarchicalLayoutStage.prototype.execute=function(a){};function mxMedianHybridCrossingReduction(a){this.layout=a}mxMedianHybridCrossingReduction.prototype=new mxHierarchicalLayoutStage;mxMedianHybridCrossingReduction.prototype.constructor=mxMedianHybridCrossingReduction;mxMedianHybridCrossingReduction.prototype.layout=null;mxMedianHybridCrossingReduction.prototype.maxIterations=24;
+mxMedianHybridCrossingReduction.prototype.nestedBestRanks=null;mxMedianHybridCrossingReduction.prototype.currentBestCrossings=0;mxMedianHybridCrossingReduction.prototype.iterationsWithoutImprovement=0;mxMedianHybridCrossingReduction.prototype.maxNoImprovementIterations=2;
+mxMedianHybridCrossingReduction.prototype.execute=function(a){a=this.layout.getModel();this.nestedBestRanks=[];for(var b=0;b<a.ranks.length;b++)this.nestedBestRanks[b]=a.ranks[b].slice();for(var c=0,d=this.calculateCrossings(a),b=0;b<this.maxIterations&&c<this.maxNoImprovementIterations;b++){this.weightedMedian(b,a);this.transpose(b,a);var e=this.calculateCrossings(a);if(e<d)for(d=e,e=c=0;e<this.nestedBestRanks.length;e++)for(var f=a.ranks[e],g=0;g<f.length;g++){var k=f[g];this.nestedBestRanks[e][k.getGeneralPurposeVariable(e)]=
+k}else for(c++,e=0;e<this.nestedBestRanks.length;e++)for(f=a.ranks[e],g=0;g<f.length;g++)k=f[g],k.setGeneralPurposeVariable(e,g);if(0==d)break}c=[];d=[];for(b=0;b<a.maxRank+1;b++)d[b]=[],c[b]=d[b];for(b=0;b<this.nestedBestRanks.length;b++)for(e=0;e<this.nestedBestRanks[b].length;e++)d[b].push(this.nestedBestRanks[b][e]);a.ranks=c};mxMedianHybridCrossingReduction.prototype.calculateCrossings=function(a){for(var b=a.ranks.length,c=0,d=1;d<b;d++)c+=this.calculateRankCrossing(d,a);return c};
+mxMedianHybridCrossingReduction.prototype.calculateRankCrossing=function(a,b){for(var c=0,d=b.ranks[a],e=b.ranks[a-1],f=[],g=0;g<d.length;g++){for(var k=d[g],l=k.getGeneralPurposeVariable(a),k=k.getPreviousLayerConnectedCells(a),m=[],n=0;n<k.length;n++){var p=k[n].getGeneralPurposeVariable(a-1);m.push(p)}m.sort(function(a,b){return a-b});f[l]=m}d=[];for(g=0;g<f.length;g++)d=d.concat(f[g]);for(f=1;f<e.length;)f<<=1;l=2*f-1;--f;e=[];for(g=0;g<l;++g)e[g]=0;for(g=0;g<d.length;g++)for(l=d[g]+f,++e[l];0<
+l;)l%2&&(c+=e[l+1]),l=l-1>>1,++e[l];return c};
+mxMedianHybridCrossingReduction.prototype.transpose=function(a,b){for(var c=!0,d=0;c&&10>d++;)for(var e=1==a%2&&1==d%2,c=!1,f=0;f<b.ranks.length;f++){for(var g=b.ranks[f],k=[],l=0;l<g.length;l++){var m=g[l],n=m.getGeneralPurposeVariable(f);0>n&&(n=l);k[n]=m}for(var p=null,q=null,r,t,u=null,x=null,y,A=null,l=0;l<g.length-1;l++){if(0==l){y=k[l];m=y.getNextLayerConnectedCells(f);n=y.getPreviousLayerConnectedCells(f);r=[];t=[];for(var z=0;z<m.length;z++)r[z]=m[z].getGeneralPurposeVariable(f+1);for(z=
+0;z<n.length;z++)t[z]=n[z].getGeneralPurposeVariable(f-1)}else m=p,n=q,r=u,t=x,y=A;A=k[l+1];p=A.getNextLayerConnectedCells(f);q=A.getPreviousLayerConnectedCells(f);u=[];x=[];for(z=0;z<p.length;z++)u[z]=p[z].getGeneralPurposeVariable(f+1);for(z=0;z<q.length;z++)x[z]=q[z].getGeneralPurposeVariable(f-1);for(var v=0,B=0,z=0;z<r.length;z++)for(var C=0;C<u.length;C++)r[z]>u[C]&&v++,r[z]<u[C]&&B++;for(z=0;z<t.length;z++)for(C=0;C<x.length;C++)t[z]>x[C]&&v++,t[z]<x[C]&&B++;if(B<v||B==v&&e)p=y.getGeneralPurposeVariable(f),
+y.setGeneralPurposeVariable(f,A.getGeneralPurposeVariable(f)),A.setGeneralPurposeVariable(f,p),p=m,q=n,u=r,x=t,A=y,e||(c=!0)}}};mxMedianHybridCrossingReduction.prototype.weightedMedian=function(a,b){var c=0==a%2;if(c)for(var d=b.maxRank-1;0<=d;d--)this.medianRank(d,c);else for(d=1;d<b.maxRank;d++)this.medianRank(d,c)};
+mxMedianHybridCrossingReduction.prototype.medianRank=function(a,b){for(var c=this.nestedBestRanks[a].length,d=[],e=[],f=0;f<c;f++){var g=this.nestedBestRanks[a][f],k=new MedianCellSorter;k.cell=g;var l;l=b?g.getNextLayerConnectedCells(a):g.getPreviousLayerConnectedCells(a);var m;m=b?a+1:a-1;null!=l&&0!=l.length?(k.medianValue=this.medianValue(l,m),d.push(k)):e[g.getGeneralPurposeVariable(a)]=!0}d.sort(MedianCellSorter.prototype.compare);for(f=0;f<c;f++)null==e[f]&&(g=d.shift().cell,g.setGeneralPurposeVariable(a,
+f))};mxMedianHybridCrossingReduction.prototype.medianValue=function(a,b){for(var c=[],d=0,e=0;e<a.length;e++){var f=a[e];c[d++]=f.getGeneralPurposeVariable(b)}c.sort(function(a,b){return a-b});if(1==d%2)return c[Math.floor(d/2)];if(2==d)return(c[0]+c[1])/2;e=d/2;f=c[e-1]-c[0];d=c[d-1]-c[e];return(c[e-1]*d+c[e]*f)/(f+d)};function MedianCellSorter(){}MedianCellSorter.prototype.medianValue=0;MedianCellSorter.prototype.cell=!1;
+MedianCellSorter.prototype.compare=function(a,b){return null!=a&&null!=b?b.medianValue>a.medianValue?-1:b.medianValue<a.medianValue?1:0:0};function mxMinimumCycleRemover(a){this.layout=a}mxMinimumCycleRemover.prototype=new mxHierarchicalLayoutStage;mxMinimumCycleRemover.prototype.constructor=mxMinimumCycleRemover;mxMinimumCycleRemover.prototype.layout=null;
+mxMinimumCycleRemover.prototype.execute=function(a){a=this.layout.getModel();for(var b={},c=a.vertexMapper.getValues(),d={},e=0;e<c.length;e++)d[c[e].id]=c[e];c=null;if(null!=a.roots)for(var f=a.roots,c=[],e=0;e<f.length;e++)c[e]=a.vertexMapper.get(f[e]);a.visit(function(a,c,e,f,n){c.isAncestor(a)&&(e.invert(),mxUtils.remove(e,a.connectsAsSource),a.connectsAsTarget.push(e),mxUtils.remove(e,c.connectsAsTarget),c.connectsAsSource.push(e));b[c.id]=c;delete d[c.id]},c,!0,null);e=mxUtils.clone(b,null,
+!0);a.visit(function(a,c,e,f,n){c.isAncestor(a)&&(e.invert(),mxUtils.remove(e,a.connectsAsSource),c.connectsAsSource.push(e),a.connectsAsTarget.push(e),mxUtils.remove(e,c.connectsAsTarget));b[c.id]=c;delete d[c.id]},d,!0,e)};function mxCoordinateAssignment(a,b,c,d,e,f){this.layout=a;this.intraCellSpacing=b;this.interRankCellSpacing=c;this.orientation=d;this.initialX=e;this.parallelEdgeSpacing=f}mxCoordinateAssignment.prototype=new mxHierarchicalLayoutStage;
+mxCoordinateAssignment.prototype.constructor=mxCoordinateAssignment;mxCoordinateAssignment.prototype.layout=null;mxCoordinateAssignment.prototype.intraCellSpacing=30;mxCoordinateAssignment.prototype.interRankCellSpacing=100;mxCoordinateAssignment.prototype.parallelEdgeSpacing=10;mxCoordinateAssignment.prototype.maxIterations=8;mxCoordinateAssignment.prototype.prefHozEdgeSep=5;mxCoordinateAssignment.prototype.prefVertEdgeOff=2;mxCoordinateAssignment.prototype.minEdgeJetty=12;
+mxCoordinateAssignment.prototype.channelBuffer=4;mxCoordinateAssignment.prototype.jettyPositions=null;mxCoordinateAssignment.prototype.orientation=mxConstants.DIRECTION_NORTH;mxCoordinateAssignment.prototype.initialX=null;mxCoordinateAssignment.prototype.limitX=null;mxCoordinateAssignment.prototype.currentXDelta=null;mxCoordinateAssignment.prototype.widestRank=null;mxCoordinateAssignment.prototype.rankTopY=null;mxCoordinateAssignment.prototype.rankBottomY=null;
+mxCoordinateAssignment.prototype.widestRankValue=null;mxCoordinateAssignment.prototype.rankWidths=null;mxCoordinateAssignment.prototype.rankY=null;mxCoordinateAssignment.prototype.fineTuning=!0;mxCoordinateAssignment.prototype.nextLayerConnectedCache=null;mxCoordinateAssignment.prototype.previousLayerConnectedCache=null;mxCoordinateAssignment.prototype.groupPadding=10;
+mxCoordinateAssignment.prototype.printStatus=function(){var a=this.layout.getModel();mxLog.show();mxLog.writeln("======Coord assignment debug=======");for(var b=0;b<a.ranks.length;b++){mxLog.write("Rank ",b," : ");for(var c=a.ranks[b],d=0;d<c.length;d++)mxLog.write(c[d].getGeneralPurposeVariable(b)," ");mxLog.writeln()}mxLog.writeln("====================================")};
+mxCoordinateAssignment.prototype.execute=function(a){this.jettyPositions={};a=this.layout.getModel();this.currentXDelta=0;this.initialCoords(this.layout.getGraph(),a);this.fineTuning&&this.minNode(a);var b=1E8;if(this.fineTuning)for(var c=0;c<this.maxIterations;c++){0!=c&&(this.medianPos(c,a),this.minNode(a));if(this.currentXDelta<b){for(var d=0;d<a.ranks.length;d++)for(var e=a.ranks[d],f=0;f<e.length;f++){var g=e[f];g.setX(d,g.getGeneralPurposeVariable(d))}b=this.currentXDelta}else for(d=0;d<a.ranks.length;d++)for(e=
+a.ranks[d],f=0;f<e.length;f++)g=e[f],g.setGeneralPurposeVariable(d,g.getX(d));this.minPath(this.layout.getGraph(),a);this.currentXDelta=0}this.setCellLocations(this.layout.getGraph(),a)};
+mxCoordinateAssignment.prototype.minNode=function(a){for(var b=[],c=new mxDictionary,d=[],e=0;e<=a.maxRank;e++){d[e]=a.ranks[e];for(var f=0;f<d[e].length;f++){var g=d[e][f],k=new WeightedCellSorter(g,e);k.rankIndex=f;k.visited=!0;b.push(k);c.put(g,k)}}a=10*b.length;for(f=0;0<b.length&&f<=a;){var g=b.shift(),e=g.cell,l=g.weightedValue,m=parseInt(g.rankIndex),k=e.getNextLayerConnectedCells(l),n=e.getPreviousLayerConnectedCells(l),p=k.length,q=n.length,r=this.medianXValue(k,l+1),t=this.medianXValue(n,
+l-1),u=p+q,x=e.getGeneralPurposeVariable(l),y=x;0<u&&(y=(r*p+t*q)/u);p=!1;y<x-1?0==m?(e.setGeneralPurposeVariable(l,y),p=!0):(m=d[l][m-1],x=m.getGeneralPurposeVariable(l),x=x+m.width/2+this.intraCellSpacing+e.width/2,x<y?(e.setGeneralPurposeVariable(l,y),p=!0):x<e.getGeneralPurposeVariable(l)-1&&(e.setGeneralPurposeVariable(l,x),p=!0)):y>x+1&&(m==d[l].length-1?(e.setGeneralPurposeVariable(l,y),p=!0):(m=d[l][m+1],x=m.getGeneralPurposeVariable(l),x=x-m.width/2-this.intraCellSpacing-e.width/2,x>y?(e.setGeneralPurposeVariable(l,
+y),p=!0):x>e.getGeneralPurposeVariable(l)+1&&(e.setGeneralPurposeVariable(l,x),p=!0)));if(p){for(e=0;e<k.length;e++)l=k[e],l=c.get(l),null!=l&&0==l.visited&&(l.visited=!0,b.push(l));for(e=0;e<n.length;e++)l=n[e],l=c.get(l),null!=l&&0==l.visited&&(l.visited=!0,b.push(l))}g.visited=!1;f++}};mxCoordinateAssignment.prototype.medianPos=function(a,b){if(0==a%2)for(var c=b.maxRank;0<c;c--)this.rankMedianPosition(c-1,b,c);else for(c=0;c<b.maxRank-1;c++)this.rankMedianPosition(c+1,b,c)};
+mxCoordinateAssignment.prototype.rankMedianPosition=function(a,b,c){b=b.ranks[a];for(var d=[],e={},f=0;f<b.length;f++){var g=b[f];d[f]=new WeightedCellSorter;d[f].cell=g;d[f].rankIndex=f;e[g.id]=d[f];var k;k=c<a?g.getPreviousLayerConnectedCells(a):g.getNextLayerConnectedCells(a);d[f].weightedValue=this.calculatedWeightedValue(g,k)}d.sort(WeightedCellSorter.prototype.compare);for(f=0;f<d.length;f++){var l,g=d[f].cell;l=0;k=c<a?g.getPreviousLayerConnectedCells(a).slice():g.getNextLayerConnectedCells(a).slice();
+null!=k&&(l=k.length,l=0<l?this.medianXValue(k,c):g.getGeneralPurposeVariable(a));var m=0;k=-1E8;for(var n=d[f].rankIndex-1;0<=n;){var p=e[b[n].id];if(null!=p){var q=p.cell;p.visited?(k=q.getGeneralPurposeVariable(a)+q.width/2+this.intraCellSpacing+m+g.width/2,n=-1):(m+=q.width+this.intraCellSpacing,n--)}}m=0;q=1E8;for(n=d[f].rankIndex+1;n<d.length;)if(p=e[b[n].id],null!=p){var r=p.cell;p.visited?(q=r.getGeneralPurposeVariable(a)-r.width/2-this.intraCellSpacing-m-g.width/2,n=d.length):(m+=r.width+
+this.intraCellSpacing,n++)}l>=k&&l<=q?g.setGeneralPurposeVariable(a,l):l<k?(g.setGeneralPurposeVariable(a,k),this.currentXDelta+=k-l):l>q&&(g.setGeneralPurposeVariable(a,q),this.currentXDelta+=l-q);d[f].visited=!0}};mxCoordinateAssignment.prototype.calculatedWeightedValue=function(a,b){for(var c=0,d=0;d<b.length;d++){var e=b[d];a.isVertex()&&e.isVertex()?c++:c=a.isEdge()&&e.isEdge()?c+8:c+2}return c};
+mxCoordinateAssignment.prototype.medianXValue=function(a,b){if(0==a.length)return 0;for(var c=[],d=0;d<a.length;d++)c[d]=a[d].getGeneralPurposeVariable(b);c.sort(function(a,b){return a-b});if(1==a.length%2)return c[Math.floor(a.length/2)];d=a.length/2;return(c[d-1]+c[d])/2};
+mxCoordinateAssignment.prototype.initialCoords=function(a,b){this.calculateWidestRank(a,b);for(var c=this.widestRank;0<=c;c--)c<b.maxRank&&this.rankCoordinates(c,a,b);for(c=this.widestRank+1;c<=b.maxRank;c++)0<c&&this.rankCoordinates(c,a,b)};
+mxCoordinateAssignment.prototype.rankCoordinates=function(a,b,c){b=c.ranks[a];c=this.initialX+(this.widestRankValue-this.rankWidths[a])/2;for(var d=!1,e=0;e<b.length;e++){var f=b[e];if(f.isVertex()){var g=this.layout.getVertexBounds(f.cell);null!=g?this.orientation==mxConstants.DIRECTION_NORTH||this.orientation==mxConstants.DIRECTION_SOUTH?(f.width=g.width,f.height=g.height):(f.width=g.height,f.height=g.width):d=!0}else f.isEdge()&&(g=1,null!=f.edges?g=f.edges.length:mxLog.warn("edge.edges is null"),
+f.width=(g-1)*this.parallelEdgeSpacing);c+=f.width/2;f.setX(a,c);f.setGeneralPurposeVariable(a,c);c+=f.width/2;c+=this.intraCellSpacing}1==d&&mxLog.warn("At least one cell has no bounds")};
+mxCoordinateAssignment.prototype.calculateWidestRank=function(a,b){var c=-this.interRankCellSpacing,d=0;this.rankWidths=[];this.rankY=[];for(var e=b.maxRank;0<=e;e--){for(var f=0,g=b.ranks[e],k=this.initialX,l=!1,m=0;m<g.length;m++){var n=g[m];if(n.isVertex()){var p=this.layout.getVertexBounds(n.cell);null!=p?this.orientation==mxConstants.DIRECTION_NORTH||this.orientation==mxConstants.DIRECTION_SOUTH?(n.width=p.width,n.height=p.height):(n.width=p.height,n.height=p.width):l=!0;f=Math.max(f,n.height)}else n.isEdge()&&
+(p=1,null!=n.edges?p=n.edges.length:mxLog.warn("edge.edges is null"),n.width=(p-1)*this.parallelEdgeSpacing);k+=n.width/2;n.setX(e,k);n.setGeneralPurposeVariable(e,k);k+=n.width/2;k+=this.intraCellSpacing;k>this.widestRankValue&&(this.widestRankValue=k,this.widestRank=e);this.rankWidths[e]=k}1==l&&mxLog.warn("At least one cell has no bounds");this.rankY[e]=c;k=f/2+d/2+this.interRankCellSpacing;d=f;c=this.orientation==mxConstants.DIRECTION_NORTH||this.orientation==mxConstants.DIRECTION_WEST?c+k:c-
+k;for(m=0;m<g.length;m++)g[m].setY(e,c)}};
+mxCoordinateAssignment.prototype.minPath=function(a,b){for(var c=b.edgeMapper.getValues(),d=0;d<c.length;d++){var e=c[d];if(!(1>e.maxRank-e.minRank-1)){for(var f=e.getGeneralPurposeVariable(e.minRank+1),g=!0,k=0,l=e.minRank+2;l<e.maxRank;l++){var m=e.getGeneralPurposeVariable(l);f!=m?(g=!1,f=m):k++}if(!g){for(var g=f=0,m=[],n=[],p=e.getGeneralPurposeVariable(e.minRank+1),l=e.minRank+1;l<e.maxRank-1;l++){var q=e.getX(l+1);p==q?(m[l-e.minRank-1]=p,f++):this.repositionValid(b,e,l+1,p)?(m[l-e.minRank-
+1]=p,f++):p=m[l-e.minRank-1]=q}p=e.getX(l);for(l=e.maxRank-1;l>e.minRank+1;l--)q=e.getX(l-1),p==q?(n[l-e.minRank-2]=p,g++):this.repositionValid(b,e,l-1,p)?(n[l-e.minRank-2]=p,g++):(n[l-e.minRank-2]=e.getX(l-1),p=q);if(g>k||f>k)if(g>=f)for(l=e.maxRank-2;l>e.minRank;l--)e.setX(l,n[l-e.minRank-1]);else if(f>g)for(l=e.minRank+2;l<e.maxRank;l++)e.setX(l,m[l-e.minRank-2])}}}};
+mxCoordinateAssignment.prototype.repositionValid=function(a,b,c,d){a=a.ranks[c];for(var e=-1,f=0;f<a.length;f++)if(b==a[f]){e=f;break}if(0>e)return!1;f=b.getGeneralPurposeVariable(c);if(d<f){if(0==e)return!0;a=a[e-1];c=a.getGeneralPurposeVariable(c);c=c+a.width/2+this.intraCellSpacing+b.width/2;if(!(c<=d))return!1}else if(d>f){if(e==a.length-1)return!0;a=a[e+1];c=a.getGeneralPurposeVariable(c);c=c-a.width/2-this.intraCellSpacing-b.width/2;if(!(c>=d))return!1}return!0};
+mxCoordinateAssignment.prototype.setCellLocations=function(a,b){this.rankTopY=[];this.rankBottomY=[];for(var c=0;c<b.ranks.length;c++)this.rankTopY[c]=Number.MAX_VALUE,this.rankBottomY[c]=-Number.MAX_VALUE;for(var d=b.vertexMapper.getValues(),c=0;c<d.length;c++)this.setVertexLocation(d[c]);this.layout.edgeStyle!=mxHierarchicalEdgeStyle.ORTHOGONAL&&this.layout.edgeStyle!=mxHierarchicalEdgeStyle.POLYLINE&&this.layout.edgeStyle!=mxHierarchicalEdgeStyle.CURVE||this.localEdgeProcessing(b);d=b.edgeMapper.getValues();
+for(c=0;c<d.length;c++)this.setEdgePosition(d[c])};
+mxCoordinateAssignment.prototype.localEdgeProcessing=function(a){for(var b=0;b<a.ranks.length;b++)for(var c=a.ranks[b],d=0;d<c.length;d++){var e=c[d];if(e.isVertex())for(var f=e.getPreviousLayerConnectedCells(b),g=b-1,k=0;2>k;k++){if(-1<g&&g<a.ranks.length&&null!=f&&0<f.length){for(var l=[],m=0;m<f.length;m++){var n=new WeightedCellSorter(f[m],f[m].getX(g));l.push(n)}l.sort(WeightedCellSorter.prototype.compare);for(var n=e.x[0]-e.width/2,p=n+e.width,q=f=0,g=[],m=0;m<l.length;m++){var r=l[m].cell,
+t;if(r.isVertex()){t=0==k?e.connectsAsSource:e.connectsAsTarget;for(var u=0;u<t.length;u++)if(t[u].source==r||t[u].target==r)f+=t[u].edges.length,q++,g.push(t[u])}else f+=r.edges.length,q++,g.push(r)}e.width>(f+1)*this.prefHozEdgeSep+2*this.prefHozEdgeSep&&(n+=this.prefHozEdgeSep,p-=this.prefHozEdgeSep);l=(p-n)/f;n+=l/2;p=this.minEdgeJetty-this.prefVertEdgeOff;for(m=0;m<g.length;m++)for(q=g[m].edges.length,r=this.jettyPositions[g[m].ids[0]],null==r&&(r=[],this.jettyPositions[g[m].ids[0]]=r),m<f/2?
+p+=this.prefVertEdgeOff:m>f/2&&(p-=this.prefVertEdgeOff),t=0;t<q;t++)r[4*t+2*k]=n,n+=l,r[4*t+2*k+1]=p}f=e.getNextLayerConnectedCells(b);g=b+1}}};
+mxCoordinateAssignment.prototype.setEdgePosition=function(a){var b=0;if(101207!=a.temp[0]){var c=a.maxRank,d=a.minRank;c==d&&(c=a.source.maxRank,d=a.target.minRank);for(var e=0,f=this.jettyPositions[a.ids[0]],g=a.isReversed?a.target.cell:a.source.cell,k=this.layout.graph,l=this.orientation==mxConstants.DIRECTION_EAST||this.orientation==mxConstants.DIRECTION_SOUTH,m=0;m<a.edges.length;m++){var n=a.edges[m],p=this.layout.getVisibleTerminal(n,!0),q=[],r=a.isReversed;p!=g&&(r=!r);if(null!=f){var t=r?
+2:0,u=r?l?this.rankBottomY[d]:this.rankTopY[d]:l?this.rankTopY[c]:this.rankBottomY[c],x=f[4*e+1+t];r!=l&&(x=-x);var u=u+x,t=f[4*e+t],y=k.model.getTerminal(n,!0);this.layout.isPort(y)&&k.model.getParent(y)==p&&(t=k.view.getState(y),t=null!=t?t.x:p.geometry.x+a.source.width*y.geometry.x);this.orientation==mxConstants.DIRECTION_NORTH||this.orientation==mxConstants.DIRECTION_SOUTH?(q.push(new mxPoint(t,u)),this.layout.edgeStyle==mxHierarchicalEdgeStyle.CURVE&&q.push(new mxPoint(t,u+x))):(q.push(new mxPoint(u,
+t)),this.layout.edgeStyle==mxHierarchicalEdgeStyle.CURVE&&q.push(new mxPoint(u+x,t)))}t=a.x.length-1;u=x=-1;p=a.maxRank-1;r&&(t=0,x=a.x.length,u=1,p=a.minRank+1);for(;a.maxRank!=a.minRank&&t!=x;t+=u){var y=a.x[t]+b,A=(this.rankTopY[p]+this.rankBottomY[p+1])/2,z=(this.rankTopY[p-1]+this.rankBottomY[p])/2;if(r)var v=A,A=z,z=v;this.orientation==mxConstants.DIRECTION_NORTH||this.orientation==mxConstants.DIRECTION_SOUTH?(q.push(new mxPoint(y,A)),q.push(new mxPoint(y,z))):(q.push(new mxPoint(A,y)),q.push(new mxPoint(z,
+y)));this.limitX=Math.max(this.limitX,y);p+=u}null!=f&&(t=r?2:0,u=r?l?this.rankTopY[c]:this.rankBottomY[c]:l?this.rankBottomY[d]:this.rankTopY[d],x=f[4*e+3-t],r!=l&&(x=-x),u-=x,t=f[4*e+2-t],r=k.model.getTerminal(n,!1),p=this.layout.getVisibleTerminal(n,!1),this.layout.isPort(r)&&k.model.getParent(r)==p&&(t=k.view.getState(r),t=null!=t?t.x:p.geometry.x+a.target.width*r.geometry.x),this.orientation==mxConstants.DIRECTION_NORTH||this.orientation==mxConstants.DIRECTION_SOUTH?(this.layout.edgeStyle==mxHierarchicalEdgeStyle.CURVE&&
+q.push(new mxPoint(t,u-x)),q.push(new mxPoint(t,u))):(this.layout.edgeStyle==mxHierarchicalEdgeStyle.CURVE&&q.push(new mxPoint(u-x,t)),q.push(new mxPoint(u,t))));a.isReversed&&this.processReversedEdge(a,n);this.layout.setEdgePoints(n,q);b=0==b?this.parallelEdgeSpacing:0<b?-b:-b+this.parallelEdgeSpacing;e++}a.temp[0]=101207}};
+mxCoordinateAssignment.prototype.setVertexLocation=function(a){var b=a.cell,c=a.x[0]-a.width/2,d=a.y[0]-a.height/2;this.rankTopY[a.minRank]=Math.min(this.rankTopY[a.minRank],d);this.rankBottomY[a.minRank]=Math.max(this.rankBottomY[a.minRank],d+a.height);this.orientation==mxConstants.DIRECTION_NORTH||this.orientation==mxConstants.DIRECTION_SOUTH?this.layout.setVertexLocation(b,c,d):this.layout.setVertexLocation(b,d,c);this.limitX=Math.max(this.limitX,c+a.width)};
+mxCoordinateAssignment.prototype.processReversedEdge=function(a,b){};function mxSwimlaneOrdering(a){this.layout=a}mxSwimlaneOrdering.prototype=new mxHierarchicalLayoutStage;mxSwimlaneOrdering.prototype.constructor=mxSwimlaneOrdering;mxSwimlaneOrdering.prototype.layout=null;
+mxSwimlaneOrdering.prototype.execute=function(a){a=this.layout.getModel();var b=mxUtils.clone(a.vertexMapper,null,!0),c=null;if(null!=a.roots)for(var d=a.roots,c=[],e=0;e<d.length;e++)mxCellPath.create(d[e]),c[e]=a.vertexMapper.get(d[e]);a.visit(function(a,c,d,e,m){e=null!=a&&a.swimlaneIndex==c.swimlaneIndex&&c.isAncestor(a);m=null!=a&&null!=d&&a.swimlaneIndex<c.swimlaneIndex&&d.source==c;e?(d.invert(),mxUtils.remove(d,a.connectsAsSource),c.connectsAsSource.push(d),a.connectsAsTarget.push(d),mxUtils.remove(d,
+c.connectsAsTarget)):m&&(d.invert(),mxUtils.remove(d,a.connectsAsTarget),c.connectsAsTarget.push(d),a.connectsAsSource.push(d),mxUtils.remove(d,c.connectsAsSource));a=mxCellPath.create(c.cell);delete b[a]},c,!0,null)};function mxHierarchicalLayout(a,b,c){mxGraphLayout.call(this,a);this.orientation=null!=b?b:mxConstants.DIRECTION_NORTH;this.deterministic=null!=c?c:!0}var mxHierarchicalEdgeStyle={ORTHOGONAL:1,POLYLINE:2,STRAIGHT:3,CURVE:4};mxHierarchicalLayout.prototype=new mxGraphLayout;
+mxHierarchicalLayout.prototype.constructor=mxHierarchicalLayout;mxHierarchicalLayout.prototype.roots=null;mxHierarchicalLayout.prototype.resizeParent=!1;mxHierarchicalLayout.prototype.maintainParentLocation=!1;mxHierarchicalLayout.prototype.moveParent=!1;mxHierarchicalLayout.prototype.parentBorder=0;mxHierarchicalLayout.prototype.intraCellSpacing=30;mxHierarchicalLayout.prototype.interRankCellSpacing=100;mxHierarchicalLayout.prototype.interHierarchySpacing=60;
+mxHierarchicalLayout.prototype.parallelEdgeSpacing=10;mxHierarchicalLayout.prototype.orientation=mxConstants.DIRECTION_NORTH;mxHierarchicalLayout.prototype.fineTuning=!0;mxHierarchicalLayout.prototype.tightenToSource=!0;mxHierarchicalLayout.prototype.disableEdgeStyle=!0;mxHierarchicalLayout.prototype.traverseAncestors=!0;mxHierarchicalLayout.prototype.model=null;mxHierarchicalLayout.prototype.edgesCache=null;mxHierarchicalLayout.prototype.edgeSourceTermCache=null;
+mxHierarchicalLayout.prototype.edgesTargetTermCache=null;mxHierarchicalLayout.prototype.edgeStyle=mxHierarchicalEdgeStyle.POLYLINE;mxHierarchicalLayout.prototype.getModel=function(){return this.model};
+mxHierarchicalLayout.prototype.execute=function(a,b){this.parent=a;var c=this.graph.model;this.edgesCache=new mxDictionary;this.edgeSourceTermCache=new mxDictionary;this.edgesTargetTermCache=new mxDictionary;null==b||b instanceof Array||(b=[b]);if(null!=b||null!=a){this.parentY=this.parentX=null;if(a!=this.root&&null!=c.isVertex(a)&&this.maintainParentLocation){var d=this.graph.getCellGeometry(a);null!=d&&(this.parentX=d.x,this.parentY=d.y)}if(null!=b){for(var e=[],f=0;f<b.length;f++)(null!=a?c.isAncestor(a,
+b[f]):1)&&c.isVertex(b[f])&&e.push(b[f]);this.roots=e}c.beginUpdate();try{this.run(a),this.resizeParent&&!this.graph.isCellCollapsed(a)&&this.graph.updateGroupBounds([a],this.parentBorder,this.moveParent),null!=this.parentX&&null!=this.parentY&&(d=this.graph.getCellGeometry(a),null!=d&&(d=d.clone(),d.x=this.parentX,d.y=this.parentY,c.setGeometry(a,d)))}finally{c.endUpdate()}}};
+mxHierarchicalLayout.prototype.findRoots=function(a,b){var c=[];if(null!=a&&null!=b){var d=this.graph.model,e=null,f=-1E5,g;for(g in b){var k=b[g];if(d.isVertex(k)&&this.graph.isCellVisible(k)){for(var l=this.getEdges(k),m=0,n=0,p=0;p<l.length;p++)this.getVisibleTerminal(l[p],!0)==k?m++:n++;0==n&&0<m&&c.push(k);l=m-n;l>f&&(f=l,e=k)}}0==c.length&&null!=e&&c.push(e)}return c};
+mxHierarchicalLayout.prototype.getEdges=function(a){var b=this.edgesCache.get(a);if(null!=b)return b;for(var c=this.graph.model,b=[],d=this.graph.isCellCollapsed(a),e=c.getChildCount(a),f=0;f<e;f++){var g=c.getChildAt(a,f);if(this.isPort(g))b=b.concat(c.getEdges(g,!0,!0));else if(d||!this.graph.isCellVisible(g))b=b.concat(c.getEdges(g,!0,!0))}b=b.concat(c.getEdges(a,!0,!0));c=[];for(f=0;f<b.length;f++)d=this.getVisibleTerminal(b[f],!0),e=this.getVisibleTerminal(b[f],!1),(d==e||d!=e&&(e==a&&(null==
+this.parent||this.isAncestor(this.parent,d,this.traverseAncestors))||d==a&&(null==this.parent||this.isAncestor(this.parent,e,this.traverseAncestors))))&&c.push(b[f]);this.edgesCache.put(a,c);return c};
+mxHierarchicalLayout.prototype.getVisibleTerminal=function(a,b){var c=this.edgesTargetTermCache;b&&(c=this.edgeSourceTermCache);var d=c.get(a);if(null!=d)return d;var d=this.graph.view.getState(a),e=null!=d?d.getVisibleTerminal(b):this.graph.view.getVisibleTerminal(a,b);null==e&&(e=null!=d?d.getVisibleTerminal(b):this.graph.view.getVisibleTerminal(a,b));null!=e&&(this.isPort(e)&&(e=this.graph.model.getParent(e)),c.put(a,e));return e};
+mxHierarchicalLayout.prototype.run=function(a){var b=[],c=[];if(null==this.roots&&null!=a){var d={};this.filterDescendants(a,d);this.roots=[];var e=!0,f;for(f in d)if(null!=d[f]){e=!1;break}for(;!e;){for(var g=this.findRoots(a,d),e=0;e<g.length;e++){var k={};b.push(k);this.traverse(g[e],!0,null,c,k,b,d)}for(e=0;e<g.length;e++)this.roots.push(g[e]);e=!0;for(f in d)if(null!=d[f]){e=!1;break}}}else for(e=0;e<this.roots.length;e++)k={},b.push(k),this.traverse(this.roots[e],!0,null,c,k,b,null);for(e=c=
+0;e<b.length;e++){k=b[e];d=[];for(f in k)d.push(k[f]);this.model=new mxGraphHierarchyModel(this,d,this.roots,a,this.tightenToSource);this.cycleStage(a);this.layeringStage();this.crossingStage(a);c=this.placementStage(c,a)}};
+mxHierarchicalLayout.prototype.filterDescendants=function(a,b){var c=this.graph.model;c.isVertex(a)&&a!=this.parent&&this.graph.isCellVisible(a)&&(b[mxObjectIdentity.get(a)]=a);if(this.traverseAncestors||a==this.parent&&this.graph.isCellVisible(a))for(var d=c.getChildCount(a),e=0;e<d;e++){var f=c.getChildAt(a,e);this.isPort(f)||this.filterDescendants(f,b)}};mxHierarchicalLayout.prototype.isPort=function(a){return a.geometry.relative?!0:!1};
+mxHierarchicalLayout.prototype.getEdgesBetween=function(a,b,c){c=null!=c?c:!1;for(var d=this.getEdges(a),e=[],f=0;f<d.length;f++){var g=this.getVisibleTerminal(d[f],!0),k=this.getVisibleTerminal(d[f],!1);(g==a&&k==b||!c&&g==b&&k==a)&&e.push(d[f])}return e};
+mxHierarchicalLayout.prototype.traverse=function(a,b,c,d,e,f,g){if(null!=a&&null!=d){var k=mxObjectIdentity.get(a);if(null==d[k]&&(null==g||null!=g[k])){null==e[k]&&(e[k]=a);null==d[k]&&(d[k]=a);null!==g&&delete g[k];var l=this.getEdges(a),k=[];for(c=0;c<l.length;c++)k[c]=this.getVisibleTerminal(l[c],!0)==a;for(c=0;c<l.length;c++)if(!b||k[c]){a=this.getVisibleTerminal(l[c],!k[c]);for(var m=1,n=0;n<l.length;n++)if(n!=c){var p=k[n];this.getVisibleTerminal(l[n],!p)==a&&(p?m++:m--)}0<=m&&(e=this.traverse(a,
+b,l[c],d,e,f,g))}}else if(null==e[k])for(c=0;c<f.length;c++)if(b=f[c],null!=b[k]){for(l in b)e[l]=b[l];f.splice(c,1);break}}return e};mxHierarchicalLayout.prototype.cycleStage=function(a){(new mxMinimumCycleRemover(this)).execute(a)};mxHierarchicalLayout.prototype.layeringStage=function(){this.model.initialRank();this.model.fixRanks()};mxHierarchicalLayout.prototype.crossingStage=function(a){(new mxMedianHybridCrossingReduction(this)).execute(a)};
+mxHierarchicalLayout.prototype.placementStage=function(a,b){var c=new mxCoordinateAssignment(this,this.intraCellSpacing,this.interRankCellSpacing,this.orientation,a,this.parallelEdgeSpacing);c.fineTuning=this.fineTuning;c.execute(b);return c.limitX+this.interHierarchySpacing};function mxSwimlaneLayout(a,b,c){mxGraphLayout.call(this,a);this.orientation=null!=b?b:mxConstants.DIRECTION_NORTH;this.deterministic=null!=c?c:!0}mxSwimlaneLayout.prototype=new mxGraphLayout;
+mxSwimlaneLayout.prototype.constructor=mxSwimlaneLayout;mxSwimlaneLayout.prototype.roots=null;mxSwimlaneLayout.prototype.swimlanes=null;mxSwimlaneLayout.prototype.dummyVertices=null;mxSwimlaneLayout.prototype.dummyVertexWidth=50;mxSwimlaneLayout.prototype.resizeParent=!1;mxSwimlaneLayout.prototype.maintainParentLocation=!1;mxSwimlaneLayout.prototype.moveParent=!1;mxSwimlaneLayout.prototype.parentBorder=30;mxSwimlaneLayout.prototype.intraCellSpacing=30;
+mxSwimlaneLayout.prototype.interRankCellSpacing=100;mxSwimlaneLayout.prototype.interHierarchySpacing=60;mxSwimlaneLayout.prototype.parallelEdgeSpacing=10;mxSwimlaneLayout.prototype.orientation=mxConstants.DIRECTION_NORTH;mxSwimlaneLayout.prototype.fineTuning=!0;mxSwimlaneLayout.prototype.tightenToSource=!0;mxSwimlaneLayout.prototype.disableEdgeStyle=!0;mxSwimlaneLayout.prototype.traverseAncestors=!0;mxSwimlaneLayout.prototype.model=null;mxSwimlaneLayout.prototype.edgesCache=null;
+mxHierarchicalLayout.prototype.edgeSourceTermCache=null;mxHierarchicalLayout.prototype.edgesTargetTermCache=null;mxHierarchicalLayout.prototype.edgeStyle=mxHierarchicalEdgeStyle.POLYLINE;mxSwimlaneLayout.prototype.getModel=function(){return this.model};
+mxSwimlaneLayout.prototype.execute=function(a,b){this.parent=a;var c=this.graph.model;this.edgesCache=new mxDictionary;this.edgeSourceTermCache=new mxDictionary;this.edgesTargetTermCache=new mxDictionary;if(!(null==b||1>b.length)){null==a&&(a=c.getParent(b[0]));this.parentY=this.parentX=null;if(a!=this.root&&null!=c.isVertex(a)&&this.maintainParentLocation){var d=this.graph.getCellGeometry(a);null!=d&&(this.parentX=d.x,this.parentY=d.y)}this.swimlanes=b;this.dummyVertices=[];for(var e=0;e<b.length;e++){var f=
+this.graph.getChildCells(b[e]);if(null==f||0==f.length)f=this.graph.insertVertex(b[e],null,null,0,0,this.dummyVertexWidth,0),this.dummyVertices.push(f)}c.beginUpdate();try{this.run(a),this.resizeParent&&!this.graph.isCellCollapsed(a)&&this.graph.updateGroupBounds([a],this.parentBorder,this.moveParent),null!=this.parentX&&null!=this.parentY&&(d=this.graph.getCellGeometry(a),null!=d&&(d=d.clone(),d.x=this.parentX,d.y=this.parentY,c.setGeometry(a,d))),this.graph.removeCells(this.dummyVertices)}finally{c.endUpdate()}}};
+mxSwimlaneLayout.prototype.updateGroupBounds=function(){var a=[],b=this.model,c;for(c in b.edgeMapper)for(var d=b.edgeMapper[c],e=0;e<d.edges.length;e++)a.push(d.edges[e]);a=this.graph.getBoundingBoxFromGeometry(a,!0);b=[];for(e=0;e<this.swimlanes.length;e++){var f=this.swimlanes[e];c=this.graph.getCellGeometry(f);if(null!=c){var g=this.graph.getChildCells(f),d=this.graph.isSwimlane(f)?this.graph.getStartSize(f):new mxRectangle,f=this.graph.getBoundingBoxFromGeometry(g);b[e]=f;d=f.y+c.y-d.height-
+this.parentBorder;c=f.y+c.y+f.height;null==a?a=new mxRectangle(0,d,0,c-d):(a.y=Math.min(a.y,d),a.height=Math.max(a.y+a.height,c)-a.y)}}for(e=0;e<this.swimlanes.length;e++)if(f=this.swimlanes[e],c=this.graph.getCellGeometry(f),null!=c){var g=this.graph.getChildCells(f),d=this.graph.isSwimlane(f)?this.graph.getStartSize(f):new mxRectangle,k=c.clone(),l=0==e?this.parentBorder:this.interRankCellSpacing/2;k.x+=b[e].x-d.width-l;k.y=k.y+a.y-c.y-this.parentBorder;k.width=b[e].width+d.width+this.interRankCellSpacing/
+2+l;k.height=a.height+d.height+2*this.parentBorder;this.graph.model.setGeometry(f,k);this.graph.moveCells(g,-b[e].x+d.width+l,c.y-a.y+this.parentBorder)}};
+mxSwimlaneLayout.prototype.findRoots=function(a,b){var c=[];if(null!=a&&null!=b){var d=this.graph.model,e=null,f=-1E5,g;for(g in b){var k=b[g];if(null!=k&&d.isVertex(k)&&this.graph.isCellVisible(k)&&d.isAncestor(a,k)){for(var l=this.getEdges(k),m=0,n=0,p=0;p<l.length;p++){var q=this.getVisibleTerminal(l[p],!0);q==k?(q=this.getVisibleTerminal(l[p],!1),d.isAncestor(a,q)&&m++):d.isAncestor(a,q)&&n++}0==n&&0<m&&c.push(k);l=m-n;l>f&&(f=l,e=k)}}0==c.length&&null!=e&&c.push(e)}return c};
+mxSwimlaneLayout.prototype.getEdges=function(a){var b=this.edgesCache.get(a);if(null!=b)return b;for(var c=this.graph.model,b=[],d=this.graph.isCellCollapsed(a),e=c.getChildCount(a),f=0;f<e;f++){var g=c.getChildAt(a,f);if(this.isPort(g))b=b.concat(c.getEdges(g,!0,!0));else if(d||!this.graph.isCellVisible(g))b=b.concat(c.getEdges(g,!0,!0))}b=b.concat(c.getEdges(a,!0,!0));c=[];for(f=0;f<b.length;f++)d=this.getVisibleTerminal(b[f],!0),e=this.getVisibleTerminal(b[f],!1),(d==e||d!=e&&(e==a&&(null==this.parent||
+this.graph.isValidAncestor(d,this.parent,this.traverseAncestors))||d==a&&(null==this.parent||this.graph.isValidAncestor(e,this.parent,this.traverseAncestors))))&&c.push(b[f]);this.edgesCache.put(a,c);return c};
+mxSwimlaneLayout.prototype.getVisibleTerminal=function(a,b){var c=this.edgesTargetTermCache;b&&(c=this.edgeSourceTermCache);var d=c.get(a);if(null!=d)return d;var d=this.graph.view.getState(a),e=null!=d?d.getVisibleTerminal(b):this.graph.view.getVisibleTerminal(a,b);null==e&&(e=null!=d?d.getVisibleTerminal(b):this.graph.view.getVisibleTerminal(a,b));null!=e&&(this.isPort(e)&&(e=this.graph.model.getParent(e)),c.put(a,e));return e};
+mxSwimlaneLayout.prototype.run=function(a){var b=[],c=[];if(null!=this.swimlanes&&0<this.swimlanes.length&&null!=a){for(var d={},e=0;e<this.swimlanes.length;e++)this.filterDescendants(this.swimlanes[e],d);this.roots=[];var e=!0,f;for(f in d)if(null!=d[f]){e=!1;break}for(var g=0;!e&&g<this.swimlanes.length;){var k=this.findRoots(this.swimlanes[g],d);if(0==k.length)g++;else{for(e=0;e<k.length;e++){var l={};b.push(l);this.traverse(k[e],!0,null,c,l,b,d,g)}for(e=0;e<k.length;e++)this.roots.push(k[e]);
+e=!0;for(f in d)if(null!=d[f]){e=!1;break}}}}else for(e=0;e<this.roots.length;e++)l={},b.push(l),this.traverse(this.roots[e],!0,null,c,l,b,null);b=[];for(f in c)b.push(c[f]);this.model=new mxSwimlaneModel(this,b,this.roots,a,this.tightenToSource);this.cycleStage(a);this.layeringStage();this.crossingStage(a);initialX=this.placementStage(0,a)};
+mxSwimlaneLayout.prototype.filterDescendants=function(a,b){var c=this.graph.model;c.isVertex(a)&&a!=this.parent&&c.getParent(a)!=this.parent&&this.graph.isCellVisible(a)&&(b[mxObjectIdentity.get(a)]=a);if(this.traverseAncestors||a==this.parent&&this.graph.isCellVisible(a))for(var d=c.getChildCount(a),e=0;e<d;e++){var f=c.getChildAt(a,e);this.isPort(f)||this.filterDescendants(f,b)}};mxSwimlaneLayout.prototype.isPort=function(a){return a.geometry.relative?!0:!1};
+mxSwimlaneLayout.prototype.getEdgesBetween=function(a,b,c){c=null!=c?c:!1;for(var d=this.getEdges(a),e=[],f=0;f<d.length;f++){var g=this.getVisibleTerminal(d[f],!0),k=this.getVisibleTerminal(d[f],!1);(g==a&&k==b||!c&&g==b&&k==a)&&e.push(d[f])}return e};
+mxSwimlaneLayout.prototype.traverse=function(a,b,c,d,e,f,g,k){if(null!=a&&null!=d){var l=mxObjectIdentity.get(a);if(null==d[l]&&(null==g||null!=g[l])){null==e[l]&&(e[l]=a);null==d[l]&&(d[l]=a);null!==g&&delete g[l];var m=this.getEdges(a),l=this.graph.model;for(c=0;c<m.length;c++){var n=this.getVisibleTerminal(m[c],!0),p=n==a;p&&(n=this.getVisibleTerminal(m[c],!1));var q;for(q=0;q<this.swimlanes.length&&!l.isAncestor(this.swimlanes[q],n);q++);q>=this.swimlanes.length||!(q>k||(!b||p)&&q==k)||(e=this.traverse(n,
+b,m[c],d,e,f,g,q))}}else if(null==e[l])for(c=0;c<f.length;c++)if(a=f[c],null!=a[l]){for(m in a)e[m]=a[m];f.splice(c,1);break}}return e};mxSwimlaneLayout.prototype.cycleStage=function(a){(new mxSwimlaneOrdering(this)).execute(a)};mxSwimlaneLayout.prototype.layeringStage=function(){this.model.initialRank();this.model.fixRanks()};mxSwimlaneLayout.prototype.crossingStage=function(a){(new mxMedianHybridCrossingReduction(this)).execute(a)};
+mxSwimlaneLayout.prototype.placementStage=function(a,b){var c=new mxCoordinateAssignment(this,this.intraCellSpacing,this.interRankCellSpacing,this.orientation,a,this.parallelEdgeSpacing);c.fineTuning=this.fineTuning;c.execute(b);return c.limitX+this.interHierarchySpacing};function mxGraphModel(a){this.currentEdit=this.createUndoableEdit();null!=a?this.setRoot(a):this.clear()}mxGraphModel.prototype=new mxEventSource;mxGraphModel.prototype.constructor=mxGraphModel;mxGraphModel.prototype.root=null;
+mxGraphModel.prototype.cells=null;mxGraphModel.prototype.maintainEdgeParent=!0;mxGraphModel.prototype.ignoreRelativeEdgeParent=!0;mxGraphModel.prototype.createIds=!0;mxGraphModel.prototype.prefix="";mxGraphModel.prototype.postfix="";mxGraphModel.prototype.nextId=0;mxGraphModel.prototype.currentEdit=null;mxGraphModel.prototype.updateLevel=0;mxGraphModel.prototype.endingUpdate=!1;mxGraphModel.prototype.clear=function(){this.setRoot(this.createRoot())};mxGraphModel.prototype.isCreateIds=function(){return this.createIds};
+mxGraphModel.prototype.setCreateIds=function(a){this.createIds=a};mxGraphModel.prototype.createRoot=function(){var a=new mxCell;a.insert(new mxCell);return a};mxGraphModel.prototype.getCell=function(a){return null!=this.cells?this.cells[a]:null};mxGraphModel.prototype.filterCells=function(a,b){var c=null;if(null!=a)for(var c=[],d=0;d<a.length;d++)b(a[d])&&c.push(a[d]);return c};mxGraphModel.prototype.getDescendants=function(a){return this.filterDescendants(null,a)};
+mxGraphModel.prototype.filterDescendants=function(a,b){var c=[];b=b||this.getRoot();(null==a||a(b))&&c.push(b);for(var d=this.getChildCount(b),e=0;e<d;e++)var f=this.getChildAt(b,e),c=c.concat(this.filterDescendants(a,f));return c};mxGraphModel.prototype.getRoot=function(a){var b=a||this.root;if(null!=a)for(;null!=a;)b=a,a=this.getParent(a);return b};mxGraphModel.prototype.setRoot=function(a){this.execute(new mxRootChange(this,a));return a};
+mxGraphModel.prototype.rootChanged=function(a){var b=this.root;this.root=a;this.nextId=0;this.cells=null;this.cellAdded(a);return b};mxGraphModel.prototype.isRoot=function(a){return null!=a&&this.root==a};mxGraphModel.prototype.isLayer=function(a){return this.isRoot(this.getParent(a))};mxGraphModel.prototype.isAncestor=function(a,b){for(;null!=b&&b!=a;)b=this.getParent(b);return b==a};mxGraphModel.prototype.contains=function(a){return this.isAncestor(this.root,a)};
+mxGraphModel.prototype.getParent=function(a){return null!=a?a.getParent():null};mxGraphModel.prototype.add=function(a,b,c){if(b!=a&&null!=a&&null!=b){null==c&&(c=this.getChildCount(a));var d=a!=this.getParent(b);this.execute(new mxChildChange(this,a,b,c));this.maintainEdgeParent&&d&&this.updateEdgeParents(b)}return b};
+mxGraphModel.prototype.cellAdded=function(a){if(null!=a){null==a.getId()&&this.createIds&&a.setId(this.createId(a));if(null!=a.getId()){var b=this.getCell(a.getId());if(b!=a){for(;null!=b;)a.setId(this.createId(a)),b=this.getCell(a.getId());null==this.cells&&(this.cells={});this.cells[a.getId()]=a}}mxUtils.isNumeric(a.getId())&&(this.nextId=Math.max(this.nextId,a.getId()));for(var b=this.getChildCount(a),c=0;c<b;c++)this.cellAdded(this.getChildAt(a,c))}};
+mxGraphModel.prototype.createId=function(a){a=this.nextId;this.nextId++;return this.prefix+a+this.postfix};mxGraphModel.prototype.updateEdgeParents=function(a,b){b=b||this.getRoot(a);for(var c=this.getChildCount(a),d=0;d<c;d++){var e=this.getChildAt(a,d);this.updateEdgeParents(e,b)}e=this.getEdgeCount(a);c=[];for(d=0;d<e;d++)c.push(this.getEdgeAt(a,d));for(d=0;d<c.length;d++)e=c[d],this.isAncestor(b,e)&&this.updateEdgeParent(e,b)};
+mxGraphModel.prototype.updateEdgeParent=function(a,b){for(var c=this.getTerminal(a,!0),d=this.getTerminal(a,!1);null!=c&&!this.isEdge(c)&&null!=c.geometry&&c.geometry.relative;)c=this.getParent(c);for(;null!=d&&this.ignoreRelativeEdgeParent&&!this.isEdge(d)&&null!=d.geometry&&d.geometry.relative;)d=this.getParent(d);if(this.isAncestor(b,c)&&this.isAncestor(b,d)&&(c=c==d?this.getParent(c):this.getNearestCommonAncestor(c,d),null!=c&&(this.getParent(c)!=this.root||this.isAncestor(c,a))&&this.getParent(a)!=
+c)){d=this.getGeometry(a);if(null!=d){var e=this.getOrigin(this.getParent(a)),f=this.getOrigin(c),g=f.x-e.x,e=f.y-e.y,d=d.clone();d.translate(-g,-e);this.setGeometry(a,d)}this.add(c,a,this.getChildCount(c))}};mxGraphModel.prototype.getOrigin=function(a){var b;null!=a?(b=this.getOrigin(this.getParent(a)),this.isEdge(a)||(a=this.getGeometry(a),null!=a&&(b.x+=a.x,b.y+=a.y))):b=new mxPoint;return b};
+mxGraphModel.prototype.getNearestCommonAncestor=function(a,b){if(null!=a&&null!=b){var c=mxCellPath.create(b);if(null!=c&&0<c.length){var d=a,e=mxCellPath.create(d);if(c.length<e.length)var d=b,f=e,e=c,c=f;for(;null!=d;){f=this.getParent(d);if(0==c.indexOf(e+mxCellPath.PATH_SEPARATOR)&&null!=f)return d;e=mxCellPath.getParentPath(e);d=f}}}return null};mxGraphModel.prototype.remove=function(a){a==this.root?this.setRoot(null):null!=this.getParent(a)&&this.execute(new mxChildChange(this,null,a));return a};
+mxGraphModel.prototype.cellRemoved=function(a){if(null!=a&&null!=this.cells){for(var b=this.getChildCount(a)-1;0<=b;b--)this.cellRemoved(this.getChildAt(a,b));null!=this.cells&&null!=a.getId()&&delete this.cells[a.getId()]}};mxGraphModel.prototype.parentForCellChanged=function(a,b,c){var d=this.getParent(a);null!=b?b==d&&d.getIndex(a)==c||b.insert(a,c):null!=d&&(c=d.getIndex(a),d.remove(c));this.contains(d)||null==b?null==b&&this.cellRemoved(a):this.cellAdded(a);return d};
+mxGraphModel.prototype.getChildCount=function(a){return null!=a?a.getChildCount():0};mxGraphModel.prototype.getChildAt=function(a,b){return null!=a?a.getChildAt(b):null};mxGraphModel.prototype.getChildren=function(a){return null!=a?a.children:null};mxGraphModel.prototype.getChildVertices=function(a){return this.getChildCells(a,!0,!1)};mxGraphModel.prototype.getChildEdges=function(a){return this.getChildCells(a,!1,!0)};
+mxGraphModel.prototype.getChildCells=function(a,b,c){b=null!=b?b:!1;c=null!=c?c:!1;for(var d=this.getChildCount(a),e=[],f=0;f<d;f++){var g=this.getChildAt(a,f);(!c&&!b||c&&this.isEdge(g)||b&&this.isVertex(g))&&e.push(g)}return e};mxGraphModel.prototype.getTerminal=function(a,b){return null!=a?a.getTerminal(b):null};
+mxGraphModel.prototype.setTerminal=function(a,b,c){var d=b!=this.getTerminal(a,c);this.execute(new mxTerminalChange(this,a,b,c));this.maintainEdgeParent&&d&&this.updateEdgeParent(a,this.getRoot());return b};mxGraphModel.prototype.setTerminals=function(a,b,c){this.beginUpdate();try{this.setTerminal(a,b,!0),this.setTerminal(a,c,!1)}finally{this.endUpdate()}};
+mxGraphModel.prototype.terminalForCellChanged=function(a,b,c){var d=this.getTerminal(a,c);null!=b?b.insertEdge(a,c):null!=d&&d.removeEdge(a,c);return d};mxGraphModel.prototype.getEdgeCount=function(a){return null!=a?a.getEdgeCount():0};mxGraphModel.prototype.getEdgeAt=function(a,b){return null!=a?a.getEdgeAt(b):null};mxGraphModel.prototype.getDirectedEdgeCount=function(a,b,c){for(var d=0,e=this.getEdgeCount(a),f=0;f<e;f++){var g=this.getEdgeAt(a,f);g!=c&&this.getTerminal(g,b)==a&&d++}return d};
+mxGraphModel.prototype.getConnections=function(a){return this.getEdges(a,!0,!0,!1)};mxGraphModel.prototype.getIncomingEdges=function(a){return this.getEdges(a,!0,!1,!1)};mxGraphModel.prototype.getOutgoingEdges=function(a){return this.getEdges(a,!1,!0,!1)};
+mxGraphModel.prototype.getEdges=function(a,b,c,d){b=null!=b?b:!0;c=null!=c?c:!0;d=null!=d?d:!0;for(var e=this.getEdgeCount(a),f=[],g=0;g<e;g++){var k=this.getEdgeAt(a,g),l=this.getTerminal(k,!0),m=this.getTerminal(k,!1);(d&&l==m||l!=m&&(b&&m==a||c&&l==a))&&f.push(k)}return f};
+mxGraphModel.prototype.getEdgesBetween=function(a,b,c){c=null!=c?c:!1;var d=this.getEdgeCount(a),e=this.getEdgeCount(b),f=a,g=d;e<d&&(g=e,f=b);d=[];for(e=0;e<g;e++){var k=this.getEdgeAt(f,e),l=this.getTerminal(k,!0),m=this.getTerminal(k,!1),n=m==a&&l==b;(l==a&&m==b||!c&&n)&&d.push(k)}return d};
+mxGraphModel.prototype.getOpposites=function(a,b,c,d){c=null!=c?c:!0;d=null!=d?d:!0;var e=[];if(null!=a)for(var f=0;f<a.length;f++){var g=this.getTerminal(a[f],!0),k=this.getTerminal(a[f],!1);g==b&&null!=k&&k!=b&&d?e.push(k):k==b&&null!=g&&g!=b&&c&&e.push(g)}return e};
+mxGraphModel.prototype.getTopmostCells=function(a){for(var b=new mxDictionary,c=[],d=0;d<a.length;d++)b.put(a[d],!0);for(d=0;d<a.length;d++){for(var e=a[d],f=!0,g=this.getParent(e);null!=g;){if(b.get(g)){f=!1;break}g=this.getParent(g)}f&&c.push(e)}return c};mxGraphModel.prototype.isVertex=function(a){return null!=a?a.isVertex():!1};mxGraphModel.prototype.isEdge=function(a){return null!=a?a.isEdge():!1};mxGraphModel.prototype.isConnectable=function(a){return null!=a?a.isConnectable():!1};
+mxGraphModel.prototype.getValue=function(a){return null!=a?a.getValue():null};mxGraphModel.prototype.setValue=function(a,b){this.execute(new mxValueChange(this,a,b));return b};mxGraphModel.prototype.valueForCellChanged=function(a,b){return a.valueChanged(b)};mxGraphModel.prototype.getGeometry=function(a){return null!=a?a.getGeometry():null};mxGraphModel.prototype.setGeometry=function(a,b){b!=this.getGeometry(a)&&this.execute(new mxGeometryChange(this,a,b));return b};
+mxGraphModel.prototype.geometryForCellChanged=function(a,b){var c=this.getGeometry(a);a.setGeometry(b);return c};mxGraphModel.prototype.getStyle=function(a){return null!=a?a.getStyle():null};mxGraphModel.prototype.setStyle=function(a,b){b!=this.getStyle(a)&&this.execute(new mxStyleChange(this,a,b));return b};mxGraphModel.prototype.styleForCellChanged=function(a,b){var c=this.getStyle(a);a.setStyle(b);return c};mxGraphModel.prototype.isCollapsed=function(a){return null!=a?a.isCollapsed():!1};
+mxGraphModel.prototype.setCollapsed=function(a,b){b!=this.isCollapsed(a)&&this.execute(new mxCollapseChange(this,a,b));return b};mxGraphModel.prototype.collapsedStateForCellChanged=function(a,b){var c=this.isCollapsed(a);a.setCollapsed(b);return c};mxGraphModel.prototype.isVisible=function(a){return null!=a?a.isVisible():!1};mxGraphModel.prototype.setVisible=function(a,b){b!=this.isVisible(a)&&this.execute(new mxVisibleChange(this,a,b));return b};
+mxGraphModel.prototype.visibleStateForCellChanged=function(a,b){var c=this.isVisible(a);a.setVisible(b);return c};mxGraphModel.prototype.execute=function(a){a.execute();this.beginUpdate();this.currentEdit.add(a);this.fireEvent(new mxEventObject(mxEvent.EXECUTE,"change",a));this.fireEvent(new mxEventObject(mxEvent.EXECUTED,"change",a));this.endUpdate()};mxGraphModel.prototype.beginUpdate=function(){this.updateLevel++;this.fireEvent(new mxEventObject(mxEvent.BEGIN_UPDATE));1==this.updateLevel&&this.fireEvent(new mxEventObject(mxEvent.START_EDIT))};
+mxGraphModel.prototype.endUpdate=function(){this.updateLevel--;0==this.updateLevel&&this.fireEvent(new mxEventObject(mxEvent.END_EDIT));if(!this.endingUpdate){this.endingUpdate=0==this.updateLevel;this.fireEvent(new mxEventObject(mxEvent.END_UPDATE,"edit",this.currentEdit));try{if(this.endingUpdate&&!this.currentEdit.isEmpty()){this.fireEvent(new mxEventObject(mxEvent.BEFORE_UNDO,"edit",this.currentEdit));var a=this.currentEdit;this.currentEdit=this.createUndoableEdit();a.notify();this.fireEvent(new mxEventObject(mxEvent.UNDO,
+"edit",a))}}finally{this.endingUpdate=!1}}};mxGraphModel.prototype.createUndoableEdit=function(a){var b=new mxUndoableEdit(this,null!=a?a:!0);b.notify=function(){b.source.fireEvent(new mxEventObject(mxEvent.CHANGE,"edit",b,"changes",b.changes));b.source.fireEvent(new mxEventObject(mxEvent.NOTIFY,"edit",b,"changes",b.changes))};return b};
+mxGraphModel.prototype.mergeChildren=function(a,b,c){c=null!=c?c:!0;this.beginUpdate();try{var d={};this.mergeChildrenImpl(a,b,c,d);for(var e in d){var f=d[e],g=this.getTerminal(f,!0);null!=g&&(g=d[mxCellPath.create(g)],this.setTerminal(f,g,!0));g=this.getTerminal(f,!1);null!=g&&(g=d[mxCellPath.create(g)],this.setTerminal(f,g,!1))}}finally{this.endUpdate()}};
+mxGraphModel.prototype.mergeChildrenImpl=function(a,b,c,d){this.beginUpdate();try{for(var e=a.getChildCount(),f=0;f<e;f++){var g=a.getChildAt(f);if("function"==typeof g.getId){var k=g.getId(),l=null==k||this.isEdge(g)&&c?null:this.getCell(k);if(null==l){var m=g.clone();m.setId(k);m.setTerminal(g.getTerminal(!0),!0);m.setTerminal(g.getTerminal(!1),!1);l=b.insert(m);this.cellAdded(l)}d[mxCellPath.create(g)]=l;this.mergeChildrenImpl(g,l,c,d)}}}finally{this.endUpdate()}};
+mxGraphModel.prototype.getParents=function(a){var b=[];if(null!=a)for(var c=new mxDictionary,d=0;d<a.length;d++){var e=this.getParent(a[d]);null==e||c.get(e)||(c.put(e,!0),b.push(e))}return b};mxGraphModel.prototype.cloneCell=function(a){return null!=a?this.cloneCells([a],!0)[0]:null};
+mxGraphModel.prototype.cloneCells=function(a,b,c){c=null!=c?c:{};for(var d=[],e=0;e<a.length;e++)null!=a[e]?d.push(this.cloneCellImpl(a[e],c,b)):d.push(null);for(e=0;e<d.length;e++)null!=d[e]&&this.restoreClone(d[e],a[e],c);return d};mxGraphModel.prototype.cloneCellImpl=function(a,b,c){var d=this.cellCloned(a);b[mxObjectIdentity.get(a)]=d;if(c){c=this.getChildCount(a);for(var e=0;e<c;e++){var f=this.cloneCellImpl(this.getChildAt(a,e),b,!0);d.insert(f)}}return d};
+mxGraphModel.prototype.cellCloned=function(a){return a.clone()};mxGraphModel.prototype.restoreClone=function(a,b,c){var d=this.getTerminal(b,!0);null!=d&&(d=c[mxObjectIdentity.get(d)],null!=d&&d.insertEdge(a,!0));d=this.getTerminal(b,!1);null!=d&&(d=c[mxObjectIdentity.get(d)],null!=d&&d.insertEdge(a,!1));for(var d=this.getChildCount(a),e=0;e<d;e++)this.restoreClone(this.getChildAt(a,e),this.getChildAt(b,e),c)};function mxRootChange(a,b){this.model=a;this.previous=this.root=b}
+mxRootChange.prototype.execute=function(){this.root=this.previous;this.previous=this.model.rootChanged(this.previous)};function mxChildChange(a,b,c,d){this.model=a;this.previous=this.parent=b;this.child=c;this.previousIndex=this.index=d}
+mxChildChange.prototype.execute=function(){if(null!=this.child){var a=this.model.getParent(this.child),b=null!=a?a.getIndex(this.child):0;null==this.previous&&this.connect(this.child,!1);a=this.model.parentForCellChanged(this.child,this.previous,this.previousIndex);null!=this.previous&&this.connect(this.child,!0);this.parent=this.previous;this.previous=a;this.index=this.previousIndex;this.previousIndex=b}};
+mxChildChange.prototype.connect=function(a,b){b=null!=b?b:!0;var c=a.getTerminal(!0),d=a.getTerminal(!1);null!=c&&(b?this.model.terminalForCellChanged(a,c,!0):this.model.terminalForCellChanged(a,null,!0));null!=d&&(b?this.model.terminalForCellChanged(a,d,!1):this.model.terminalForCellChanged(a,null,!1));a.setTerminal(c,!0);a.setTerminal(d,!1);c=this.model.getChildCount(a);for(d=0;d<c;d++)this.connect(this.model.getChildAt(a,d),b)};
+function mxTerminalChange(a,b,c,d){this.model=a;this.cell=b;this.previous=this.terminal=c;this.source=d}mxTerminalChange.prototype.execute=function(){null!=this.cell&&(this.terminal=this.previous,this.previous=this.model.terminalForCellChanged(this.cell,this.previous,this.source))};function mxValueChange(a,b,c){this.model=a;this.cell=b;this.previous=this.value=c}
+mxValueChange.prototype.execute=function(){null!=this.cell&&(this.value=this.previous,this.previous=this.model.valueForCellChanged(this.cell,this.previous))};function mxStyleChange(a,b,c){this.model=a;this.cell=b;this.previous=this.style=c}mxStyleChange.prototype.execute=function(){null!=this.cell&&(this.style=this.previous,this.previous=this.model.styleForCellChanged(this.cell,this.previous))};function mxGeometryChange(a,b,c){this.model=a;this.cell=b;this.previous=this.geometry=c}
+mxGeometryChange.prototype.execute=function(){null!=this.cell&&(this.geometry=this.previous,this.previous=this.model.geometryForCellChanged(this.cell,this.previous))};function mxCollapseChange(a,b,c){this.model=a;this.cell=b;this.previous=this.collapsed=c}mxCollapseChange.prototype.execute=function(){null!=this.cell&&(this.collapsed=this.previous,this.previous=this.model.collapsedStateForCellChanged(this.cell,this.previous))};
+function mxVisibleChange(a,b,c){this.model=a;this.cell=b;this.previous=this.visible=c}mxVisibleChange.prototype.execute=function(){null!=this.cell&&(this.visible=this.previous,this.previous=this.model.visibleStateForCellChanged(this.cell,this.previous))};function mxCellAttributeChange(a,b,c){this.cell=a;this.attribute=b;this.previous=this.value=c}
+mxCellAttributeChange.prototype.execute=function(){if(null!=this.cell){var a=this.cell.getAttribute(this.attribute);null==this.previous?this.cell.value.removeAttribute(this.attribute):this.cell.setAttribute(this.attribute,this.previous);this.previous=a}};function mxCell(a,b,c){this.value=a;this.setGeometry(b);this.setStyle(c);if(null!=this.onInit)this.onInit()}mxCell.prototype.id=null;mxCell.prototype.value=null;mxCell.prototype.geometry=null;mxCell.prototype.style=null;mxCell.prototype.vertex=!1;
+mxCell.prototype.edge=!1;mxCell.prototype.connectable=!0;mxCell.prototype.visible=!0;mxCell.prototype.collapsed=!1;mxCell.prototype.parent=null;mxCell.prototype.source=null;mxCell.prototype.target=null;mxCell.prototype.children=null;mxCell.prototype.edges=null;mxCell.prototype.mxTransient="id value parent source target children edges".split(" ");mxCell.prototype.getId=function(){return this.id};mxCell.prototype.setId=function(a){this.id=a};mxCell.prototype.getValue=function(){return this.value};
+mxCell.prototype.setValue=function(a){this.value=a};mxCell.prototype.valueChanged=function(a){var b=this.getValue();this.setValue(a);return b};mxCell.prototype.getGeometry=function(){return this.geometry};mxCell.prototype.setGeometry=function(a){this.geometry=a};mxCell.prototype.getStyle=function(){return this.style};mxCell.prototype.setStyle=function(a){this.style=a};mxCell.prototype.isVertex=function(){return 0!=this.vertex};mxCell.prototype.setVertex=function(a){this.vertex=a};
+mxCell.prototype.isEdge=function(){return 0!=this.edge};mxCell.prototype.setEdge=function(a){this.edge=a};mxCell.prototype.isConnectable=function(){return 0!=this.connectable};mxCell.prototype.setConnectable=function(a){this.connectable=a};mxCell.prototype.isVisible=function(){return 0!=this.visible};mxCell.prototype.setVisible=function(a){this.visible=a};mxCell.prototype.isCollapsed=function(){return 0!=this.collapsed};mxCell.prototype.setCollapsed=function(a){this.collapsed=a};
+mxCell.prototype.getParent=function(){return this.parent};mxCell.prototype.setParent=function(a){this.parent=a};mxCell.prototype.getTerminal=function(a){return a?this.source:this.target};mxCell.prototype.setTerminal=function(a,b){b?this.source=a:this.target=a;return a};mxCell.prototype.getChildCount=function(){return null==this.children?0:this.children.length};mxCell.prototype.getIndex=function(a){return mxUtils.indexOf(this.children,a)};
+mxCell.prototype.getChildAt=function(a){return null==this.children?null:this.children[a]};mxCell.prototype.insert=function(a,b){null!=a&&(null==b&&(b=this.getChildCount(),a.getParent()==this&&b--),a.removeFromParent(),a.setParent(this),null==this.children?(this.children=[],this.children.push(a)):this.children.splice(b,0,a));return a};mxCell.prototype.remove=function(a){var b=null;null!=this.children&&0<=a&&(b=this.getChildAt(a),null!=b&&(this.children.splice(a,1),b.setParent(null)));return b};
+mxCell.prototype.removeFromParent=function(){if(null!=this.parent){var a=this.parent.getIndex(this);this.parent.remove(a)}};mxCell.prototype.getEdgeCount=function(){return null==this.edges?0:this.edges.length};mxCell.prototype.getEdgeIndex=function(a){return mxUtils.indexOf(this.edges,a)};mxCell.prototype.getEdgeAt=function(a){return null==this.edges?null:this.edges[a]};
+mxCell.prototype.insertEdge=function(a,b){null!=a&&(a.removeFromTerminal(b),a.setTerminal(this,b),null==this.edges||a.getTerminal(!b)!=this||0>mxUtils.indexOf(this.edges,a))&&(null==this.edges&&(this.edges=[]),this.edges.push(a));return a};mxCell.prototype.removeEdge=function(a,b){if(null!=a){if(a.getTerminal(!b)!=this&&null!=this.edges){var c=this.getEdgeIndex(a);0<=c&&this.edges.splice(c,1)}a.setTerminal(null,b)}return a};
+mxCell.prototype.removeFromTerminal=function(a){var b=this.getTerminal(a);null!=b&&b.removeEdge(this,a)};mxCell.prototype.hasAttribute=function(a){var b=this.getValue();return null!=b&&b.nodeType==mxConstants.NODETYPE_ELEMENT&&b.hasAttribute?b.hasAttribute(a):null!=b.getAttribute(a)};mxCell.prototype.getAttribute=function(a,b){var c=this.getValue();return(null!=c&&c.nodeType==mxConstants.NODETYPE_ELEMENT?c.getAttribute(a):null)||b};
+mxCell.prototype.setAttribute=function(a,b){var c=this.getValue();null!=c&&c.nodeType==mxConstants.NODETYPE_ELEMENT&&c.setAttribute(a,b)};mxCell.prototype.clone=function(){var a=mxUtils.clone(this,this.mxTransient);a.setValue(this.cloneValue());return a};mxCell.prototype.cloneValue=function(){var a=this.getValue();null!=a&&("function"==typeof a.clone?a=a.clone():isNaN(a.nodeType)||(a=a.cloneNode(!0)));return a};function mxGeometry(a,b,c,d){mxRectangle.call(this,a,b,c,d)}mxGeometry.prototype=new mxRectangle;
+mxGeometry.prototype.constructor=mxGeometry;mxGeometry.prototype.TRANSLATE_CONTROL_POINTS=!0;mxGeometry.prototype.alternateBounds=null;mxGeometry.prototype.sourcePoint=null;mxGeometry.prototype.targetPoint=null;mxGeometry.prototype.points=null;mxGeometry.prototype.offset=null;mxGeometry.prototype.relative=!1;
+mxGeometry.prototype.swap=function(){if(null!=this.alternateBounds){var a=new mxRectangle(this.x,this.y,this.width,this.height);this.x=this.alternateBounds.x;this.y=this.alternateBounds.y;this.width=this.alternateBounds.width;this.height=this.alternateBounds.height;this.alternateBounds=a}};mxGeometry.prototype.getTerminalPoint=function(a){return a?this.sourcePoint:this.targetPoint};mxGeometry.prototype.setTerminalPoint=function(a,b){b?this.sourcePoint=a:this.targetPoint=a;return a};
+mxGeometry.prototype.rotate=function(a,b){var c=mxUtils.toRadians(a),d=Math.cos(c),c=Math.sin(c);if(!this.relative){var e=new mxPoint(this.getCenterX(),this.getCenterY()),e=mxUtils.getRotatedPoint(e,d,c,b);this.x=Math.round(e.x-this.width/2);this.y=Math.round(e.y-this.height/2)}null!=this.sourcePoint&&(e=mxUtils.getRotatedPoint(this.sourcePoint,d,c,b),this.sourcePoint.x=Math.round(e.x),this.sourcePoint.y=Math.round(e.y));null!=this.targetPoint&&(e=mxUtils.getRotatedPoint(this.targetPoint,d,c,b),this.targetPoint.x=
+Math.round(e.x),this.targetPoint.y=Math.round(e.y));if(null!=this.points)for(var f=0;f<this.points.length;f++)null!=this.points[f]&&(e=mxUtils.getRotatedPoint(this.points[f],d,c,b),this.points[f].x=Math.round(e.x),this.points[f].y=Math.round(e.y))};
+mxGeometry.prototype.translate=function(a,b){a=parseFloat(a);b=parseFloat(b);this.relative||(this.x=parseFloat(this.x)+a,this.y=parseFloat(this.y)+b);null!=this.sourcePoint&&(this.sourcePoint.x=parseFloat(this.sourcePoint.x)+a,this.sourcePoint.y=parseFloat(this.sourcePoint.y)+b);null!=this.targetPoint&&(this.targetPoint.x=parseFloat(this.targetPoint.x)+a,this.targetPoint.y=parseFloat(this.targetPoint.y)+b);if(this.TRANSLATE_CONTROL_POINTS&&null!=this.points)for(var c=0;c<this.points.length;c++)null!=
+this.points[c]&&(this.points[c].x=parseFloat(this.points[c].x)+a,this.points[c].y=parseFloat(this.points[c].y)+b)};
+mxGeometry.prototype.scale=function(a,b,c){a=parseFloat(a);b=parseFloat(b);null!=this.sourcePoint&&(this.sourcePoint.x=parseFloat(this.sourcePoint.x)*a,this.sourcePoint.y=parseFloat(this.sourcePoint.y)*b);null!=this.targetPoint&&(this.targetPoint.x=parseFloat(this.targetPoint.x)*a,this.targetPoint.y=parseFloat(this.targetPoint.y)*b);if(null!=this.points)for(var d=0;d<this.points.length;d++)null!=this.points[d]&&(this.points[d].x=parseFloat(this.points[d].x)*a,this.points[d].y=parseFloat(this.points[d].y)*
+b);this.relative||(this.x=parseFloat(this.x)*a,this.y=parseFloat(this.y)*b,c&&(b=a=Math.min(a,b)),this.width=parseFloat(this.width)*a,this.height=parseFloat(this.height)*b)};
+mxGeometry.prototype.equals=function(a){return mxRectangle.prototype.equals.apply(this,arguments)&&this.relative==a.relative&&(null==this.sourcePoint&&null==a.sourcePoint||null!=this.sourcePoint&&this.sourcePoint.equals(a.sourcePoint))&&(null==this.targetPoint&&null==a.targetPoint||null!=this.targetPoint&&this.targetPoint.equals(a.targetPoint))&&(null==this.points&&null==a.points||null!=this.points&&mxUtils.equalPoints(this.points,a.points))&&(null==this.alternateBounds&&null==a.alternateBounds||
+null!=this.alternateBounds&&this.alternateBounds.equals(a.alternateBounds))&&(null==this.offset&&null==a.offset||null!=this.offset&&this.offset.equals(a.offset))};
+var mxCellPath={PATH_SEPARATOR:".",create:function(a){var b="";if(null!=a)for(var c=a.getParent();null!=c;)b=c.getIndex(a)+mxCellPath.PATH_SEPARATOR+b,a=c,c=a.getParent();a=b.length;1<a&&(b=b.substring(0,a-1));return b},getParentPath:function(a){if(null!=a){var b=a.lastIndexOf(mxCellPath.PATH_SEPARATOR);if(0<=b)return a.substring(0,b);if(0<a.length)return""}return null},resolve:function(a,b){var c=a;if(null!=b)for(var d=b.split(mxCellPath.PATH_SEPARATOR),e=0;e<d.length;e++)c=c.getChildAt(parseInt(d[e]));
+return c},compare:function(a,b){for(var c=Math.min(a.length,b.length),d=0,e=0;e<c;e++)if(a[e]!=b[e]){0==a[e].length||0==b[e].length?d=a[e]==b[e]?0:a[e]>b[e]?1:-1:(c=parseInt(a[e]),e=parseInt(b[e]),d=c==e?0:c>e?1:-1);break}0==d&&(c=a.length,e=b.length,c!=e&&(d=c>e?1:-1));return d}},mxPerimeter={RectanglePerimeter:function(a,b,c,d){b=a.getCenterX();var e=a.getCenterY(),f=Math.atan2(c.y-e,c.x-b),g=new mxPoint(0,0),k=Math.PI,l=Math.PI/2-f,m=Math.atan2(a.height,a.width);f<-k+m||f>k-m?(g.x=a.x,g.y=e-a.width*
+Math.tan(f)/2):f<-m?(g.y=a.y,g.x=b-a.height*Math.tan(l)/2):f<m?(g.x=a.x+a.width,g.y=e+a.width*Math.tan(f)/2):(g.y=a.y+a.height,g.x=b+a.height*Math.tan(l)/2);d&&(c.x>=a.x&&c.x<=a.x+a.width?g.x=c.x:c.y>=a.y&&c.y<=a.y+a.height&&(g.y=c.y),c.x<a.x?g.x=a.x:c.x>a.x+a.width&&(g.x=a.x+a.width),c.y<a.y?g.y=a.y:c.y>a.y+a.height&&(g.y=a.y+a.height));return g},EllipsePerimeter:function(a,b,c,d){var e=a.x,f=a.y,g=a.width/2,k=a.height/2,l=e+g,m=f+k;b=c.x;c=c.y;var n=parseInt(b-l),p=parseInt(c-m);if(0==n&&0!=p)return new mxPoint(l,
+m+k*p/Math.abs(p));if(0==n&&0==p)return new mxPoint(b,c);if(d){if(c>=f&&c<=f+a.height)return a=c-m,a=Math.sqrt(g*g*(1-a*a/(k*k)))||0,b<=e&&(a=-a),new mxPoint(l+a,c);if(b>=e&&b<=e+a.width)return a=b-l,a=Math.sqrt(k*k*(1-a*a/(g*g)))||0,c<=f&&(a=-a),new mxPoint(b,m+a)}e=p/n;m-=e*l;f=g*g*e*e+k*k;a=-2*l*f;k=Math.sqrt(a*a-4*f*(g*g*e*e*l*l+k*k*l*l-g*g*k*k));g=(-a+k)/(2*f);l=(-a-k)/(2*f);k=e*g+m;m=e*l+m;Math.sqrt(Math.pow(g-b,2)+Math.pow(k-c,2))<Math.sqrt(Math.pow(l-b,2)+Math.pow(m-c,2))?(b=g,c=k):(b=l,c=
+m);return new mxPoint(b,c)},RhombusPerimeter:function(a,b,c,d){b=a.x;var e=a.y,f=a.width;a=a.height;var g=b+f/2,k=e+a/2,l=c.x;c=c.y;if(g==l)return k>c?new mxPoint(g,e):new mxPoint(g,e+a);if(k==c)return g>l?new mxPoint(b,k):new mxPoint(b+f,k);var m=g,n=k;d&&(l>=b&&l<=b+f?m=l:c>=e&&c<=e+a&&(n=c));return l<g?c<k?mxUtils.intersection(l,c,m,n,g,e,b,k):mxUtils.intersection(l,c,m,n,g,e+a,b,k):c<k?mxUtils.intersection(l,c,m,n,g,e,b+f,k):mxUtils.intersection(l,c,m,n,g,e+a,b+f,k)},TrianglePerimeter:function(a,
+b,c,d){b=null!=b?b.style[mxConstants.STYLE_DIRECTION]:null;var e=b==mxConstants.DIRECTION_NORTH||b==mxConstants.DIRECTION_SOUTH,f=a.x,g=a.y,k=a.width,l=a.height;a=f+k/2;var m=g+l/2,n=new mxPoint(f,g),p=new mxPoint(f+k,m),q=new mxPoint(f,g+l);b==mxConstants.DIRECTION_NORTH?(n=q,p=new mxPoint(a,g),q=new mxPoint(f+k,g+l)):b==mxConstants.DIRECTION_SOUTH?(p=new mxPoint(a,g+l),q=new mxPoint(f+k,g)):b==mxConstants.DIRECTION_WEST&&(n=new mxPoint(f+k,g),p=new mxPoint(f,m),q=new mxPoint(f+k,g+l));var r=c.x-
+a,t=c.y-m,r=e?Math.atan2(r,t):Math.atan2(t,r),t=e?Math.atan2(k,l):Math.atan2(l,k);(b==mxConstants.DIRECTION_NORTH||b==mxConstants.DIRECTION_WEST?r>-t&&r<t:r<-Math.PI+t||r>Math.PI-t)?c=d&&(e&&c.x>=n.x&&c.x<=q.x||!e&&c.y>=n.y&&c.y<=q.y)?e?new mxPoint(c.x,n.y):new mxPoint(n.x,c.y):b==mxConstants.DIRECTION_NORTH?new mxPoint(f+k/2+l*Math.tan(r)/2,g+l):b==mxConstants.DIRECTION_SOUTH?new mxPoint(f+k/2-l*Math.tan(r)/2,g):b==mxConstants.DIRECTION_WEST?new mxPoint(f+k,g+l/2+k*Math.tan(r)/2):new mxPoint(f,g+
+l/2-k*Math.tan(r)/2):(d&&(d=new mxPoint(a,m),c.y>=g&&c.y<=g+l?(d.x=e?a:b==mxConstants.DIRECTION_WEST?f+k:f,d.y=c.y):c.x>=f&&c.x<=f+k&&(d.x=c.x,d.y=e?b==mxConstants.DIRECTION_NORTH?g+l:g:m),a=d.x,m=d.y),c=e&&c.x<=f+k/2||!e&&c.y<=g+l/2?mxUtils.intersection(c.x,c.y,a,m,n.x,n.y,p.x,p.y):mxUtils.intersection(c.x,c.y,a,m,p.x,p.y,q.x,q.y));null==c&&(c=new mxPoint(a,m));return c},HexagonPerimeter:function(a,b,c,d){var e=a.x,f=a.y,g=a.width,k=a.height,l=a.getCenterX();a=a.getCenterY();var m=c.x,n=c.y,p=-Math.atan2(n-
+a,m-l),q=Math.PI,r=Math.PI/2;new mxPoint(l,a);b=null!=b?mxUtils.getValue(b.style,mxConstants.STYLE_DIRECTION,mxConstants.DIRECTION_EAST):mxConstants.DIRECTION_EAST;var t=b==mxConstants.DIRECTION_NORTH||b==mxConstants.DIRECTION_SOUTH;b=new mxPoint;var u=new mxPoint;if(m<e&&n<f||m<e&&n>f+k||m>e+g&&n<f||m>e+g&&n>f+k)d=!1;if(d){if(t){if(m==l){if(n<=f)return new mxPoint(l,f);if(n>=f+k)return new mxPoint(l,f+k)}else if(m<e){if(n==f+k/4)return new mxPoint(e,f+k/4);if(n==f+3*k/4)return new mxPoint(e,f+3*
+k/4)}else if(m>e+g){if(n==f+k/4)return new mxPoint(e+g,f+k/4);if(n==f+3*k/4)return new mxPoint(e+g,f+3*k/4)}else if(m==e){if(n<a)return new mxPoint(e,f+k/4);if(n>a)return new mxPoint(e,f+3*k/4)}else if(m==e+g){if(n<a)return new mxPoint(e+g,f+k/4);if(n>a)return new mxPoint(e+g,f+3*k/4)}if(n==f)return new mxPoint(l,f);if(n==f+k)return new mxPoint(l,f+k);m<l?n>f+k/4&&n<f+3*k/4?(b=new mxPoint(e,f),u=new mxPoint(e,f+k)):n<f+k/4?(b=new mxPoint(e-Math.floor(.5*g),f+Math.floor(.5*k)),u=new mxPoint(e+g,f-
+Math.floor(.25*k))):n>f+3*k/4&&(b=new mxPoint(e-Math.floor(.5*g),f+Math.floor(.5*k)),u=new mxPoint(e+g,f+Math.floor(1.25*k))):m>l&&(n>f+k/4&&n<f+3*k/4?(b=new mxPoint(e+g,f),u=new mxPoint(e+g,f+k)):n<f+k/4?(b=new mxPoint(e,f-Math.floor(.25*k)),u=new mxPoint(e+Math.floor(1.5*g),f+Math.floor(.5*k))):n>f+3*k/4&&(b=new mxPoint(e+Math.floor(1.5*g),f+Math.floor(.5*k)),u=new mxPoint(e,f+Math.floor(1.25*k))))}else{if(n==a){if(m<=e)return new mxPoint(e,f+k/2);if(m>=e+g)return new mxPoint(e+g,f+k/2)}else if(n<
+f){if(m==e+g/4)return new mxPoint(e+g/4,f);if(m==e+3*g/4)return new mxPoint(e+3*g/4,f)}else if(n>f+k){if(m==e+g/4)return new mxPoint(e+g/4,f+k);if(m==e+3*g/4)return new mxPoint(e+3*g/4,f+k)}else if(n==f){if(m<l)return new mxPoint(e+g/4,f);if(m>l)return new mxPoint(e+3*g/4,f)}else if(n==f+k){if(m<l)return new mxPoint(e+g/4,f+k);if(n>a)return new mxPoint(e+3*g/4,f+k)}if(m==e)return new mxPoint(e,a);if(m==e+g)return new mxPoint(e+g,a);n<a?m>e+g/4&&m<e+3*g/4?(b=new mxPoint(e,f),u=new mxPoint(e+g,f)):
+m<e+g/4?(b=new mxPoint(e-Math.floor(.25*g),f+k),u=new mxPoint(e+Math.floor(.5*g),f-Math.floor(.5*k))):m>e+3*g/4&&(b=new mxPoint(e+Math.floor(.5*g),f-Math.floor(.5*k)),u=new mxPoint(e+Math.floor(1.25*g),f+k)):n>a&&(m>e+g/4&&m<e+3*g/4?(b=new mxPoint(e,f+k),u=new mxPoint(e+g,f+k)):m<e+g/4?(b=new mxPoint(e-Math.floor(.25*g),f),u=new mxPoint(e+Math.floor(.5*g),f+Math.floor(1.5*k))):m>e+3*g/4&&(b=new mxPoint(e+Math.floor(.5*g),f+Math.floor(1.5*k)),u=new mxPoint(e+Math.floor(1.25*g),f)))}d=l;p=a;m>=e&&m<=
+e+g?(d=m,p=n<a?f+k:f):n>=f&&n<=f+k&&(p=n,d=m<l?e+g:e);c=mxUtils.intersection(d,p,c.x,c.y,b.x,b.y,u.x,u.y)}else{if(t){m=Math.atan2(k/4,g/2);if(p==m)return new mxPoint(e+g,f+Math.floor(.25*k));if(p==r)return new mxPoint(e+Math.floor(.5*g),f);if(p==q-m)return new mxPoint(e,f+Math.floor(.25*k));if(p==-m)return new mxPoint(e+g,f+Math.floor(.75*k));if(p==-r)return new mxPoint(e+Math.floor(.5*g),f+k);if(p==-q+m)return new mxPoint(e,f+Math.floor(.75*k));p<m&&p>-m?(b=new mxPoint(e+g,f),u=new mxPoint(e+g,f+
+k)):p>m&&p<r?(b=new mxPoint(e,f-Math.floor(.25*k)),u=new mxPoint(e+Math.floor(1.5*g),f+Math.floor(.5*k))):p>r&&p<q-m?(b=new mxPoint(e-Math.floor(.5*g),f+Math.floor(.5*k)),u=new mxPoint(e+g,f-Math.floor(.25*k))):p>q-m&&p<=q||p<-q+m&&p>=-q?(b=new mxPoint(e,f),u=new mxPoint(e,f+k)):p<-m&&p>-r?(b=new mxPoint(e+Math.floor(1.5*g),f+Math.floor(.5*k)),u=new mxPoint(e,f+Math.floor(1.25*k))):p<-r&&p>-q+m&&(b=new mxPoint(e-Math.floor(.5*g),f+Math.floor(.5*k)),u=new mxPoint(e+g,f+Math.floor(1.25*k)))}else{m=
+Math.atan2(k/2,g/4);if(p==m)return new mxPoint(e+Math.floor(.75*g),f);if(p==q-m)return new mxPoint(e+Math.floor(.25*g),f);if(p==q||p==-q)return new mxPoint(e,f+Math.floor(.5*k));if(0==p)return new mxPoint(e+g,f+Math.floor(.5*k));if(p==-m)return new mxPoint(e+Math.floor(.75*g),f+k);if(p==-q+m)return new mxPoint(e+Math.floor(.25*g),f+k);0<p&&p<m?(b=new mxPoint(e+Math.floor(.5*g),f-Math.floor(.5*k)),u=new mxPoint(e+Math.floor(1.25*g),f+k)):p>m&&p<q-m?(b=new mxPoint(e,f),u=new mxPoint(e+g,f)):p>q-m&&
+p<q?(b=new mxPoint(e-Math.floor(.25*g),f+k),u=new mxPoint(e+Math.floor(.5*g),f-Math.floor(.5*k))):0>p&&p>-m?(b=new mxPoint(e+Math.floor(.5*g),f+Math.floor(1.5*k)),u=new mxPoint(e+Math.floor(1.25*g),f)):p<-m&&p>-q+m?(b=new mxPoint(e,f+k),u=new mxPoint(e+g,f+k)):p<-q+m&&p>-q&&(b=new mxPoint(e-Math.floor(.25*g),f),u=new mxPoint(e+Math.floor(.5*g),f+Math.floor(1.5*k)))}c=mxUtils.intersection(l,a,c.x,c.y,b.x,b.y,u.x,u.y)}return null==c?new mxPoint(l,a):c}};
+function mxPrintPreview(a,b,c,d,e,f,g,k,l){this.graph=a;this.scale=null!=b?b:1/a.pageScale;this.border=null!=d?d:0;this.pageFormat=mxRectangle.fromRectangle(null!=c?c:a.pageFormat);this.title=null!=k?k:"Printer-friendly version";this.x0=null!=e?e:0;this.y0=null!=f?f:0;this.borderColor=g;this.pageSelector=null!=l?l:!0}mxPrintPreview.prototype.graph=null;mxPrintPreview.prototype.pageFormat=null;mxPrintPreview.prototype.scale=null;mxPrintPreview.prototype.border=0;
+mxPrintPreview.prototype.marginTop=0;mxPrintPreview.prototype.marginBottom=0;mxPrintPreview.prototype.x0=0;mxPrintPreview.prototype.y0=0;mxPrintPreview.prototype.autoOrigin=!0;mxPrintPreview.prototype.printOverlays=!1;mxPrintPreview.prototype.printControls=!1;mxPrintPreview.prototype.printBackgroundImage=!1;mxPrintPreview.prototype.backgroundColor="#ffffff";mxPrintPreview.prototype.borderColor=null;mxPrintPreview.prototype.title=null;mxPrintPreview.prototype.pageSelector=null;
+mxPrintPreview.prototype.wnd=null;mxPrintPreview.prototype.targetWindow=null;mxPrintPreview.prototype.pageCount=0;mxPrintPreview.prototype.clipping=!0;mxPrintPreview.prototype.getWindow=function(){return this.wnd};
+mxPrintPreview.prototype.getDoctype=function(){var a="";5==document.documentMode?a='<meta http-equiv="X-UA-Compatible" content="IE=5">':8==document.documentMode?a='<meta http-equiv="X-UA-Compatible" content="IE=8">':8<document.documentMode&&(a='\x3c!--[if IE]><meta http-equiv="X-UA-Compatible" content="IE=edge"><![endif]--\x3e');return a};mxPrintPreview.prototype.appendGraph=function(a,b,c,d,e,f){this.graph=a;this.scale=null!=b?b:1/a.pageScale;this.x0=c;this.y0=d;this.open(null,null,e,f)};
+mxPrintPreview.prototype.open=function(a,b,c,d){var e=this.graph.cellRenderer.initializeOverlay,f=null;try{this.printOverlays&&(this.graph.cellRenderer.initializeOverlay=function(a,b){b.init(a.view.getDrawPane())});this.printControls&&(this.graph.cellRenderer.initControl=function(a,b,c,d){b.dialect=a.view.graph.dialect;b.init(a.view.getDrawPane())});this.wnd=null!=b?b:this.wnd;var g=!1;null==this.wnd&&(g=!0,this.wnd=window.open());var k=this.wnd.document;if(g){var l=this.getDoctype();null!=l&&0<l.length&&
+k.writeln(l);mxClient.IS_VML?k.writeln('<html xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office">'):("CSS1Compat"===document.compatMode&&k.writeln("<!DOCTYPE html>"),k.writeln("<html>"));k.writeln("<head>");this.writeHead(k,a);k.writeln("</head>");k.writeln('<body class="mxPage">')}var m=this.graph.getGraphBounds().clone(),n=this.graph.getView().getScale(),p=n/this.scale,q=this.graph.getView().getTranslate();this.autoOrigin||(this.x0-=q.x*this.scale,this.y0-=
+q.y*this.scale,m.width+=m.x,m.height+=m.y,m.x=0,this.border=m.y=0);var r=this.pageFormat.width-2*this.border,t=this.pageFormat.height-2*this.border;this.pageFormat.height+=this.marginTop+this.marginBottom;m.width/=p;m.height/=p;var u=Math.max(1,Math.ceil((m.width+this.x0)/r)),x=Math.max(1,Math.ceil((m.height+this.y0)/t));this.pageCount=u*x;var y=mxUtils.bind(this,function(){if(this.pageSelector&&(1<x||1<u)){var a=this.createPageSelector(x,u);k.body.appendChild(a);if(mxClient.IS_IE&&null==k.documentMode||
+5==k.documentMode||8==k.documentMode||7==k.documentMode){a.style.position="absolute";var b=function(){a.style.top=(k.body.scrollTop||k.documentElement.scrollTop)+10+"px"};mxEvent.addListener(this.wnd,"scroll",function(a){b()});mxEvent.addListener(this.wnd,"resize",function(a){b()})}}}),A=mxUtils.bind(this,function(a,b){null!=this.borderColor&&(a.style.borderColor=this.borderColor,a.style.borderStyle="solid",a.style.borderWidth="1px");a.style.background=this.backgroundColor;if(c||b)a.style.pageBreakAfter=
+"always";g&&(mxClient.IS_IE||11<=document.documentMode||mxClient.IS_EDGE)?(k.writeln(a.outerHTML),a.parentNode.removeChild(a)):(a.parentNode.removeChild(a),k.body.appendChild(a));(c||b)&&this.addPageBreak(k)}),z=this.getCoverPages(this.pageFormat.width,this.pageFormat.height);if(null!=z)for(var v=0;v<z.length;v++)A(z[v],!0);for(var B=this.getAppendices(this.pageFormat.width,this.pageFormat.height),v=0;v<x;v++){var C=v*t/this.scale-this.y0/this.scale+(m.y-q.y*n)/n;for(a=0;a<u;a++){if(null==this.wnd)return null;
+var F=a*r/this.scale-this.x0/this.scale+(m.x-q.x*n)/n,D=v*u+a+1,I=new mxRectangle(F,C,r,t),f=this.renderPage(this.pageFormat.width,this.pageFormat.height,0,0,mxUtils.bind(this,function(a){this.addGraphFragment(-F,-C,this.scale,D,a,I);this.printBackgroundImage&&this.insertBackgroundImage(a,-F,-C)}),D);f.setAttribute("id","mxPage-"+D);A(f,null!=B||v<x-1||a<u-1)}}if(null!=B)for(v=0;v<B.length;v++)A(B[v],v<B.length-1);g&&!d&&(this.closeDocument(),y());this.wnd.focus()}catch(E){null!=f&&null!=f.parentNode&&
+f.parentNode.removeChild(f)}finally{this.graph.cellRenderer.initializeOverlay=e}return this.wnd};mxPrintPreview.prototype.addPageBreak=function(a){var b=a.createElement("hr");b.className="mxPageBreak";a.body.appendChild(b)};mxPrintPreview.prototype.closeDocument=function(){if(null!=this.wnd&&null!=this.wnd.document){var a=this.wnd.document;this.writePostfix(a);a.writeln("</body>");a.writeln("</html>");a.close();mxEvent.release(a.body)}};
+mxPrintPreview.prototype.writeHead=function(a,b){null!=this.title&&a.writeln("<title>"+this.title+"</title>");mxClient.IS_VML&&a.writeln('<style type="text/css">v\\:*{behavior:url(#default#VML)}o\\:*{behavior:url(#default#VML)}</style>');mxClient.link("stylesheet",mxClient.basePath+"/css/common.css",a);a.writeln('<style type="text/css">');a.writeln("@media print {");a.writeln(" table.mxPageSelector { display: none; }");a.writeln(" hr.mxPageBreak { display: none; }");a.writeln("}");a.writeln("@media screen {");
+a.writeln(" table.mxPageSelector { position: fixed; right: 10px; top: 10px;font-family: Arial; font-size:10pt; border: solid 1px darkgray;background: white; border-collapse:collapse; }");a.writeln(" table.mxPageSelector td { border: solid 1px gray; padding:4px; }");a.writeln(" body.mxPage { background: gray; }");a.writeln("}");null!=b&&a.writeln(b);a.writeln("</style>")};mxPrintPreview.prototype.writePostfix=function(a){};
+mxPrintPreview.prototype.createPageSelector=function(a,b){var c=this.wnd.document,d=c.createElement("table");d.className="mxPageSelector";d.setAttribute("border","0");for(var e=c.createElement("tbody"),f=0;f<a;f++){for(var g=c.createElement("tr"),k=0;k<b;k++){var l=f*b+k+1,m=c.createElement("td"),n=c.createElement("a");n.setAttribute("href","#mxPage-"+l);!mxClient.IS_NS||mxClient.IS_SF||mxClient.IS_GC||n.setAttribute("onclick","var page = document.getElementById('mxPage-"+l+"');page.scrollIntoView(true);event.preventDefault();");
+mxUtils.write(n,l,c);m.appendChild(n);g.appendChild(m)}e.appendChild(g)}d.appendChild(e);return d};
+mxPrintPreview.prototype.renderPage=function(a,b,c,d,e,f){f=this.wnd.document;var g=document.createElement("div"),k=null;try{if(0!=c||0!=d){g.style.position="relative";g.style.width=a+"px";g.style.height=b+"px";g.style.pageBreakInside="avoid";var l=document.createElement("div");l.style.position="relative";l.style.top=this.border+"px";l.style.left=this.border+"px";l.style.width=a-2*this.border+"px";l.style.height=b-2*this.border+"px";l.style.overflow="hidden";var m=document.createElement("div");m.style.position=
+"relative";m.style.marginLeft=c+"px";m.style.marginTop=d+"px";8==f.documentMode&&(l.style.position="absolute",m.style.position="absolute");10==f.documentMode&&(m.style.width="100%",m.style.height="100%");l.appendChild(m);g.appendChild(l);document.body.appendChild(g);k=m}else g.style.width=a+"px",g.style.height=b+"px",g.style.overflow="hidden",g.style.pageBreakInside="avoid",8==f.documentMode&&(g.style.position="relative"),l=document.createElement("div"),l.style.width=a-2*this.border+"px",l.style.height=
+b-2*this.border+"px",l.style.overflow="hidden",!mxClient.IS_IE||null!=f.documentMode&&5!=f.documentMode&&8!=f.documentMode&&7!=f.documentMode?(l.style.top=this.border+"px",l.style.left=this.border+"px"):(l.style.marginTop=this.border+"px",l.style.marginLeft=this.border+"px"),this.graph.dialect==mxConstants.DIALECT_VML&&(l.style.position="absolute"),g.appendChild(l),document.body.appendChild(g),k=l}catch(n){throw g.parentNode.removeChild(g),n;}e(k);return g};
+mxPrintPreview.prototype.getRoot=function(){var a=this.graph.view.currentRoot;null==a&&(a=this.graph.getModel().getRoot());return a};
+mxPrintPreview.prototype.addGraphFragment=function(a,b,c,d,e,f){var g=this.graph.getView();d=this.graph.container;this.graph.container=e;var k=g.getCanvas(),l=g.getBackgroundPane(),m=g.getDrawPane(),n=g.getOverlayPane();this.graph.dialect==mxConstants.DIALECT_SVG?g.createSvg():this.graph.dialect==mxConstants.DIALECT_VML?g.createVml():g.createHtml();var p=g.isEventsEnabled();g.setEventsEnabled(!1);var q=this.graph.isEnabled();this.graph.setEnabled(!1);var r=g.getTranslate();g.translate=new mxPoint(a,
+b);var t=this.graph.cellRenderer.redraw,u=g.states;a=g.scale;if(this.clipping){var x=new mxRectangle((f.x+r.x)*a,(f.y+r.y)*a,f.width*a/c,f.height*a/c);this.graph.cellRenderer.redraw=function(a,b,c){if(null!=a){var d=u.get(a.cell);if(null!=d&&(d=g.getBoundingBox(d,!1),null!=d&&!mxUtils.intersects(x,d)))return}t.apply(this,arguments)}}a=null;try{var y=[this.getRoot()];a=new mxTemporaryCellStates(g,c,y,null,mxUtils.bind(this,function(a){return this.getLinkForCellState(a)}))}finally{if(mxClient.IS_IE)g.overlayPane.innerHTML=
+"",g.canvas.style.overflow="hidden",g.canvas.style.position="relative",g.canvas.style.top=this.marginTop+"px",g.canvas.style.width=f.width+"px",g.canvas.style.height=f.height+"px";else for(c=e.firstChild;null!=c;)y=c.nextSibling,b=c.nodeName.toLowerCase(),"svg"==b?(c.style.overflow="hidden",c.style.position="relative",c.style.top=this.marginTop+"px",c.setAttribute("width",f.width),c.setAttribute("height",f.height),c.style.width="",c.style.height=""):"default"!=c.style.cursor&&"div"!=b&&c.parentNode.removeChild(c),
+c=y;this.printBackgroundImage&&(e=e.getElementsByTagName("svg"),0<e.length&&(e[0].style.position="absolute"));g.overlayPane.parentNode.removeChild(g.overlayPane);this.graph.setEnabled(q);this.graph.container=d;this.graph.cellRenderer.redraw=t;g.canvas=k;g.backgroundPane=l;g.drawPane=m;g.overlayPane=n;g.translate=r;a.destroy();g.setEventsEnabled(p)}};mxPrintPreview.prototype.getLinkForCellState=function(a){return this.graph.getLinkForCell(a.cell)};
+mxPrintPreview.prototype.insertBackgroundImage=function(a,b,c){var d=this.graph.backgroundImage;if(null!=d){var e=document.createElement("img");e.style.position="absolute";e.style.marginLeft=Math.round(b*this.scale)+"px";e.style.marginTop=Math.round(c*this.scale)+"px";e.setAttribute("width",Math.round(this.scale*d.width));e.setAttribute("height",Math.round(this.scale*d.height));e.src=d.src;a.insertBefore(e,a.firstChild)}};mxPrintPreview.prototype.getCoverPages=function(){return null};
+mxPrintPreview.prototype.getAppendices=function(){return null};mxPrintPreview.prototype.print=function(a){a=this.open(a);null!=a&&a.print()};mxPrintPreview.prototype.close=function(){null!=this.wnd&&(this.wnd.close(),this.wnd=null)};function mxStylesheet(){this.styles={};this.putDefaultVertexStyle(this.createDefaultVertexStyle());this.putDefaultEdgeStyle(this.createDefaultEdgeStyle())}
+mxStylesheet.prototype.createDefaultVertexStyle=function(){var a={};a[mxConstants.STYLE_SHAPE]=mxConstants.SHAPE_RECTANGLE;a[mxConstants.STYLE_PERIMETER]=mxPerimeter.RectanglePerimeter;a[mxConstants.STYLE_VERTICAL_ALIGN]=mxConstants.ALIGN_MIDDLE;a[mxConstants.STYLE_ALIGN]=mxConstants.ALIGN_CENTER;a[mxConstants.STYLE_FILLCOLOR]="#C3D9FF";a[mxConstants.STYLE_STROKECOLOR]="#6482B9";a[mxConstants.STYLE_FONTCOLOR]="#774400";return a};
+mxStylesheet.prototype.createDefaultEdgeStyle=function(){var a={};a[mxConstants.STYLE_SHAPE]=mxConstants.SHAPE_CONNECTOR;a[mxConstants.STYLE_ENDARROW]=mxConstants.ARROW_CLASSIC;a[mxConstants.STYLE_VERTICAL_ALIGN]=mxConstants.ALIGN_MIDDLE;a[mxConstants.STYLE_ALIGN]=mxConstants.ALIGN_CENTER;a[mxConstants.STYLE_STROKECOLOR]="#6482B9";a[mxConstants.STYLE_FONTCOLOR]="#446299";return a};mxStylesheet.prototype.putDefaultVertexStyle=function(a){this.putCellStyle("defaultVertex",a)};
+mxStylesheet.prototype.putDefaultEdgeStyle=function(a){this.putCellStyle("defaultEdge",a)};mxStylesheet.prototype.getDefaultVertexStyle=function(){return this.styles.defaultVertex};mxStylesheet.prototype.getDefaultEdgeStyle=function(){return this.styles.defaultEdge};mxStylesheet.prototype.putCellStyle=function(a,b){this.styles[a]=b};
+mxStylesheet.prototype.getCellStyle=function(a,b){var c=b;if(null!=a&&0<a.length)for(var d=a.split(";"),c=null!=c&&";"!=a.charAt(0)?mxUtils.clone(c):{},e=0;e<d.length;e++){var f=d[e],g=f.indexOf("=");if(0<=g){var k=f.substring(0,g),f=f.substring(g+1);f==mxConstants.NONE?delete c[k]:mxUtils.isNumeric(f)?c[k]=parseFloat(f):c[k]=f}else if(f=this.styles[f],null!=f)for(k in f)c[k]=f[k]}return c};
+function mxCellState(a,b,c){this.view=a;this.cell=b;this.style=c;this.origin=new mxPoint;this.absoluteOffset=new mxPoint}mxCellState.prototype=new mxRectangle;mxCellState.prototype.constructor=mxCellState;mxCellState.prototype.view=null;mxCellState.prototype.cell=null;mxCellState.prototype.style=null;mxCellState.prototype.invalid=!0;mxCellState.prototype.origin=null;mxCellState.prototype.absolutePoints=null;mxCellState.prototype.absoluteOffset=null;mxCellState.prototype.visibleSourceState=null;
+mxCellState.prototype.visibleTargetState=null;mxCellState.prototype.terminalDistance=0;mxCellState.prototype.length=0;mxCellState.prototype.segments=null;mxCellState.prototype.shape=null;mxCellState.prototype.text=null;mxCellState.prototype.unscaledWidth=null;
+mxCellState.prototype.getPerimeterBounds=function(a,b){a=a||0;b=null!=b?b:new mxRectangle(this.x,this.y,this.width,this.height);if(null!=this.shape&&null!=this.shape.stencil&&"fixed"==this.shape.stencil.aspect){var c=this.shape.stencil.computeAspect(this.style,b.x,b.y,b.width,b.height);b.x=c.x;b.y=c.y;b.width=this.shape.stencil.w0*c.width;b.height=this.shape.stencil.h0*c.height}0!=a&&b.grow(a);return b};
+mxCellState.prototype.setAbsoluteTerminalPoint=function(a,b){b?(null==this.absolutePoints&&(this.absolutePoints=[]),0==this.absolutePoints.length?this.absolutePoints.push(a):this.absolutePoints[0]=a):null==this.absolutePoints?(this.absolutePoints=[],this.absolutePoints.push(null),this.absolutePoints.push(a)):1==this.absolutePoints.length?this.absolutePoints.push(a):this.absolutePoints[this.absolutePoints.length-1]=a};
+mxCellState.prototype.setCursor=function(a){null!=this.shape&&this.shape.setCursor(a);null!=this.text&&this.text.setCursor(a)};mxCellState.prototype.getVisibleTerminal=function(a){a=this.getVisibleTerminalState(a);return null!=a?a.cell:null};mxCellState.prototype.getVisibleTerminalState=function(a){return a?this.visibleSourceState:this.visibleTargetState};mxCellState.prototype.setVisibleTerminalState=function(a,b){b?this.visibleSourceState=a:this.visibleTargetState=a};
+mxCellState.prototype.getCellBounds=function(){return this.cellBounds};mxCellState.prototype.getPaintBounds=function(){return this.paintBounds};mxCellState.prototype.updateCachedBounds=function(){var a=this.view.translate,b=this.view.scale;this.cellBounds=new mxRectangle(this.x/b-a.x,this.y/b-a.y,this.width/b,this.height/b);this.paintBounds=mxRectangle.fromRectangle(this.cellBounds);null!=this.shape&&this.shape.isPaintBoundsInverted()&&this.paintBounds.rotate90()};
+mxCellState.prototype.setState=function(a){this.view=a.view;this.cell=a.cell;this.style=a.style;this.absolutePoints=a.absolutePoints;this.origin=a.origin;this.absoluteOffset=a.absoluteOffset;this.boundingBox=a.boundingBox;this.terminalDistance=a.terminalDistance;this.segments=a.segments;this.length=a.length;this.x=a.x;this.y=a.y;this.width=a.width;this.height=a.height;this.unscaledWidth=a.unscaledWidth};
+mxCellState.prototype.clone=function(){var a=new mxCellState(this.view,this.cell,this.style);if(null!=this.absolutePoints){a.absolutePoints=[];for(var b=0;b<this.absolutePoints.length;b++)a.absolutePoints[b]=this.absolutePoints[b].clone()}null!=this.origin&&(a.origin=this.origin.clone());null!=this.absoluteOffset&&(a.absoluteOffset=this.absoluteOffset.clone());null!=this.boundingBox&&(a.boundingBox=this.boundingBox.clone());a.terminalDistance=this.terminalDistance;a.segments=this.segments;a.length=
+this.length;a.x=this.x;a.y=this.y;a.width=this.width;a.height=this.height;a.unscaledWidth=this.unscaledWidth;return a};mxCellState.prototype.destroy=function(){this.view.graph.cellRenderer.destroy(this)};function mxGraphSelectionModel(a){this.graph=a;this.cells=[]}mxGraphSelectionModel.prototype=new mxEventSource;mxGraphSelectionModel.prototype.constructor=mxGraphSelectionModel;mxGraphSelectionModel.prototype.doneResource="none"!=mxClient.language?"done":"";
+mxGraphSelectionModel.prototype.updatingSelectionResource="none"!=mxClient.language?"updatingSelection":"";mxGraphSelectionModel.prototype.graph=null;mxGraphSelectionModel.prototype.singleSelection=!1;mxGraphSelectionModel.prototype.isSingleSelection=function(){return this.singleSelection};mxGraphSelectionModel.prototype.setSingleSelection=function(a){this.singleSelection=a};mxGraphSelectionModel.prototype.isSelected=function(a){return null!=a?0<=mxUtils.indexOf(this.cells,a):!1};
+mxGraphSelectionModel.prototype.isEmpty=function(){return 0==this.cells.length};mxGraphSelectionModel.prototype.clear=function(){this.changeSelection(null,this.cells)};mxGraphSelectionModel.prototype.setCell=function(a){null!=a&&this.setCells([a])};mxGraphSelectionModel.prototype.setCells=function(a){if(null!=a){this.singleSelection&&(a=[this.getFirstSelectableCell(a)]);for(var b=[],c=0;c<a.length;c++)this.graph.isCellSelectable(a[c])&&b.push(a[c]);this.changeSelection(b,this.cells)}};
+mxGraphSelectionModel.prototype.getFirstSelectableCell=function(a){if(null!=a)for(var b=0;b<a.length;b++)if(this.graph.isCellSelectable(a[b]))return a[b];return null};mxGraphSelectionModel.prototype.addCell=function(a){null!=a&&this.addCells([a])};
+mxGraphSelectionModel.prototype.addCells=function(a){if(null!=a){var b=null;this.singleSelection&&(b=this.cells,a=[this.getFirstSelectableCell(a)]);for(var c=[],d=0;d<a.length;d++)!this.isSelected(a[d])&&this.graph.isCellSelectable(a[d])&&c.push(a[d]);this.changeSelection(c,b)}};mxGraphSelectionModel.prototype.removeCell=function(a){null!=a&&this.removeCells([a])};
+mxGraphSelectionModel.prototype.removeCells=function(a){if(null!=a){for(var b=[],c=0;c<a.length;c++)this.isSelected(a[c])&&b.push(a[c]);this.changeSelection(null,b)}};mxGraphSelectionModel.prototype.changeSelection=function(a,b){if(null!=a&&0<a.length&&null!=a[0]||null!=b&&0<b.length&&null!=b[0]){var c=new mxSelectionChange(this,a,b);c.execute();var d=new mxUndoableEdit(this,!1);d.add(c);this.fireEvent(new mxEventObject(mxEvent.UNDO,"edit",d))}};
+mxGraphSelectionModel.prototype.cellAdded=function(a){null==a||this.isSelected(a)||this.cells.push(a)};mxGraphSelectionModel.prototype.cellRemoved=function(a){null!=a&&(a=mxUtils.indexOf(this.cells,a),0<=a&&this.cells.splice(a,1))};function mxSelectionChange(a,b,c){this.selectionModel=a;this.added=null!=b?b.slice():null;this.removed=null!=c?c.slice():null}
+mxSelectionChange.prototype.execute=function(){var a=mxLog.enter("mxSelectionChange.execute");window.status=mxResources.get(this.selectionModel.updatingSelectionResource)||this.selectionModel.updatingSelectionResource;if(null!=this.removed)for(var b=0;b<this.removed.length;b++)this.selectionModel.cellRemoved(this.removed[b]);if(null!=this.added)for(b=0;b<this.added.length;b++)this.selectionModel.cellAdded(this.added[b]);b=this.added;this.added=this.removed;this.removed=b;window.status=mxResources.get(this.selectionModel.doneResource)||
+this.selectionModel.doneResource;mxLog.leave("mxSelectionChange.execute",a);this.selectionModel.fireEvent(new mxEventObject(mxEvent.CHANGE,"added",this.added,"removed",this.removed))};
+function mxCellEditor(a){this.graph=a;this.zoomHandler=mxUtils.bind(this,function(){this.graph.isEditing()&&this.resize()});this.graph.view.addListener(mxEvent.SCALE,this.zoomHandler);this.graph.view.addListener(mxEvent.SCALE_AND_TRANSLATE,this.zoomHandler);this.changeHandler=mxUtils.bind(this,function(a){null!=this.editingCell&&null==this.graph.getView().getState(this.editingCell)&&this.stopEditing(!0)});this.graph.getModel().addListener(mxEvent.CHANGE,this.changeHandler)}
+mxCellEditor.prototype.graph=null;mxCellEditor.prototype.textarea=null;mxCellEditor.prototype.editingCell=null;mxCellEditor.prototype.trigger=null;mxCellEditor.prototype.modified=!1;mxCellEditor.prototype.autoSize=!0;mxCellEditor.prototype.selectText=!0;mxCellEditor.prototype.emptyLabelText=mxClient.IS_FF?"<br>":"";mxCellEditor.prototype.escapeCancelsEditing=!0;mxCellEditor.prototype.textNode="";mxCellEditor.prototype.zIndex=5;mxCellEditor.prototype.minResize=new mxRectangle(0,20);
+mxCellEditor.prototype.wordWrapPadding=mxClient.IS_QUIRKS?2:mxClient.IS_IE11?0:1;mxCellEditor.prototype.blurEnabled=!1;mxCellEditor.prototype.initialValue=null;mxCellEditor.prototype.init=function(){this.textarea=document.createElement("div");this.textarea.className="mxCellEditor mxPlainTextEditor";this.textarea.contentEditable=!0;mxClient.IS_GC&&(this.textarea.style.minHeight="1em");this.textarea.style.position=this.isLegacyEditor()?"absolute":"relative";this.installListeners(this.textarea)};
+mxCellEditor.prototype.applyValue=function(a,b){this.graph.labelChanged(a.cell,b,this.trigger)};mxCellEditor.prototype.getInitialValue=function(a,b){var c=mxUtils.htmlEntities(this.graph.getEditingValue(a.cell,b),!1);mxClient.IS_QUIRKS||8==document.documentMode||9==document.documentMode||10==document.documentMode||(c=mxUtils.replaceTrailingNewlines(c,"<div><br></div>"));return c.replace(/\n/g,"<br>")};mxCellEditor.prototype.getCurrentValue=function(a){return mxUtils.extractTextWithWhitespace(this.textarea.childNodes)};
+mxCellEditor.prototype.isCancelEditingKeyEvent=function(a){return this.escapeCancelsEditing||mxEvent.isShiftDown(a)||mxEvent.isControlDown(a)||mxEvent.isMetaDown(a)};
+mxCellEditor.prototype.installListeners=function(a){mxEvent.addListener(a,"blur",mxUtils.bind(this,function(a){this.blurEnabled&&this.focusLost(a)}));mxEvent.addListener(a,"keydown",mxUtils.bind(this,function(a){mxEvent.isConsumed(a)||(this.isStopEditingEvent(a)?(this.graph.stopEditing(!1),mxEvent.consume(a)):27==a.keyCode&&(this.graph.stopEditing(this.isCancelEditingKeyEvent(a)),mxEvent.consume(a)))}));var b=mxUtils.bind(this,function(b){null!=this.editingCell&&this.clearOnChange&&a.innerHTML==this.getEmptyLabelText()&&
+(!mxClient.IS_FF||8!=b.keyCode&&46!=b.keyCode)&&(this.clearOnChange=!1,a.innerHTML="")});mxEvent.addListener(a,"keypress",b);mxEvent.addListener(a,"paste",b);b=mxUtils.bind(this,function(a){null!=this.editingCell&&(0==this.textarea.innerHTML.length||"<br>"==this.textarea.innerHTML?(this.textarea.innerHTML=this.getEmptyLabelText(),this.clearOnChange=0<this.textarea.innerHTML.length):this.clearOnChange=!1)});mxEvent.addListener(a,mxClient.IS_IE11||mxClient.IS_IE?"keyup":"input",b);mxEvent.addListener(a,
+"cut",b);mxEvent.addListener(a,"paste",b);var b=mxClient.IS_IE11||mxClient.IS_IE?"keydown":"input",c=mxUtils.bind(this,function(a){null!=this.editingCell&&this.autoSize&&!mxEvent.isConsumed(a)&&(null!=this.resizeThread&&window.clearTimeout(this.resizeThread),this.resizeThread=window.setTimeout(mxUtils.bind(this,function(){this.resizeThread=null;this.resize()}),0))});mxEvent.addListener(a,b,c);9<=document.documentMode?(mxEvent.addListener(a,"DOMNodeRemoved",c),mxEvent.addListener(a,"DOMNodeInserted",
+c)):(mxEvent.addListener(a,"cut",c),mxEvent.addListener(a,"paste",c))};mxCellEditor.prototype.isStopEditingEvent=function(a){return 113==a.keyCode||this.graph.isEnterStopsCellEditing()&&13==a.keyCode&&!mxEvent.isControlDown(a)&&!mxEvent.isShiftDown(a)};mxCellEditor.prototype.isEventSource=function(a){return mxEvent.getSource(a)==this.textarea};
+mxCellEditor.prototype.resize=function(){var a=this.graph.getView().getState(this.editingCell);if(null==a)this.stopEditing(!0);else if(null!=this.textarea){var b=this.graph.getModel().isEdge(a.cell),c=this.graph.getView().scale,d=null;if(this.autoSize&&"fill"!=a.style[mxConstants.STYLE_OVERFLOW]){var e=mxUtils.getValue(a.style,mxConstants.STYLE_LABEL_WIDTH,null),d=null!=a.text?a.text.margin:null;null==d&&(d=mxUtils.getAlignmentAsPoint(mxUtils.getValue(a.style,mxConstants.STYLE_ALIGN,mxConstants.ALIGN_CENTER),
+mxUtils.getValue(a.style,mxConstants.STYLE_VERTICAL_ALIGN,mxConstants.ALIGN_MIDDLE)));if(b)this.bounds=new mxRectangle(a.absoluteOffset.x,a.absoluteOffset.y,0,0),null!=e&&(e=(parseFloat(e)+2)*c,this.bounds.width=e,this.bounds.x+=d.x*e);else{var b=mxRectangle.fromRectangle(a),f=mxUtils.getValue(a.style,mxConstants.STYLE_LABEL_POSITION,mxConstants.ALIGN_CENTER),g=mxUtils.getValue(a.style,mxConstants.STYLE_VERTICAL_LABEL_POSITION,mxConstants.ALIGN_MIDDLE),b=null!=a.shape&&f==mxConstants.ALIGN_CENTER&&
+g==mxConstants.ALIGN_MIDDLE?a.shape.getLabelBounds(b):b;null!=e&&(b.width=parseFloat(e)*c);if(!a.view.graph.cellRenderer.legacySpacing||"width"!=a.style[mxConstants.STYLE_OVERFLOW])var f=parseInt(a.style[mxConstants.STYLE_SPACING]||2)*c,k=(parseInt(a.style[mxConstants.STYLE_SPACING_TOP]||0)+mxText.prototype.baseSpacingTop)*c+f,l=(parseInt(a.style[mxConstants.STYLE_SPACING_RIGHT]||0)+mxText.prototype.baseSpacingRight)*c+f,m=(parseInt(a.style[mxConstants.STYLE_SPACING_BOTTOM]||0)+mxText.prototype.baseSpacingBottom)*
+c+f,n=(parseInt(a.style[mxConstants.STYLE_SPACING_LEFT]||0)+mxText.prototype.baseSpacingLeft)*c+f,f=mxUtils.getValue(a.style,mxConstants.STYLE_LABEL_POSITION,mxConstants.ALIGN_CENTER),g=mxUtils.getValue(a.style,mxConstants.STYLE_VERTICAL_LABEL_POSITION,mxConstants.ALIGN_MIDDLE),b=new mxRectangle(b.x+n,b.y+k,b.width-(f==mxConstants.ALIGN_CENTER&&null==e?n+l:0),b.height-(g==mxConstants.ALIGN_MIDDLE?k+m:0));this.bounds=new mxRectangle(b.x+a.absoluteOffset.x,b.y+a.absoluteOffset.y,b.width,b.height)}this.graph.isWrapping(a.cell)&&
+(2<=this.bounds.width||2<=this.bounds.height)&&this.textarea.innerHTML!=this.getEmptyLabelText()?(this.textarea.style.wordWrap=mxConstants.WORD_WRAP,this.textarea.style.whiteSpace="normal",e=Math.round(this.bounds.width/c)+this.wordWrapPadding,"relative"!=this.textarea.style.position?(this.textarea.style.width=e+"px",this.textarea.scrollWidth>e&&(this.textarea.style.width=this.textarea.scrollWidth+"px")):this.textarea.style.maxWidth=e+"px"):(this.textarea.style.whiteSpace="nowrap",this.textarea.style.width=
+"");8==document.documentMode&&(this.textarea.style.zoom="1",this.textarea.style.height="auto");a=this.textarea.scrollWidth;e=this.textarea.scrollHeight;8==document.documentMode?(this.textarea.style.left=Math.max(0,Math.ceil((this.bounds.x-d.x*(this.bounds.width-(a+1)*c)+a*(c-1)*0+2*(d.x+.5))/c))+"px",this.textarea.style.top=Math.max(0,Math.ceil((this.bounds.y-d.y*(this.bounds.height-(e+.5)*c)+e*(c-1)*0+1*Math.abs(d.y+.5))/c))+"px",this.textarea.style.width=Math.round(a*c)+"px",this.textarea.style.height=
+Math.round(e*c)+"px"):mxClient.IS_QUIRKS?(this.textarea.style.left=Math.max(0,Math.ceil(this.bounds.x-d.x*(this.bounds.width-(a+1)*c)+a*(c-1)*0+2*(d.x+.5)))+"px",this.textarea.style.top=Math.max(0,Math.ceil(this.bounds.y-d.y*(this.bounds.height-(e+.5)*c)+e*(c-1)*0+1*Math.abs(d.y+.5)))+"px"):(this.textarea.style.left=Math.max(0,Math.round(this.bounds.x-d.x*(this.bounds.width-2))+1)+"px",this.textarea.style.top=Math.max(0,Math.round(this.bounds.y-d.y*(this.bounds.height-4)+(-1==d.y?3:0))+1)+"px")}else this.bounds=
+this.getEditorBounds(a),this.textarea.style.width=Math.round(this.bounds.width/c)+"px",this.textarea.style.height=Math.round(this.bounds.height/c)+"px",8==document.documentMode||mxClient.IS_QUIRKS?(this.textarea.style.left=Math.round(this.bounds.x)+"px",this.textarea.style.top=Math.round(this.bounds.y)+"px"):(this.textarea.style.left=Math.max(0,Math.round(this.bounds.x+1))+"px",this.textarea.style.top=Math.max(0,Math.round(this.bounds.y+1))+"px"),this.graph.isWrapping(a.cell)&&(2<=this.bounds.width||
+2<=this.bounds.height)&&this.textarea.innerHTML!=this.getEmptyLabelText()?(this.textarea.style.wordWrap=mxConstants.WORD_WRAP,this.textarea.style.whiteSpace="normal","fill"!=a.style[mxConstants.STYLE_OVERFLOW]&&(this.textarea.style.width=Math.round(this.bounds.width/c)+this.wordWrapPadding+"px")):(this.textarea.style.whiteSpace="nowrap","fill"!=a.style[mxConstants.STYLE_OVERFLOW]&&(this.textarea.style.width=""));mxClient.IS_VML?this.textarea.style.zoom=c:(mxUtils.setPrefixedStyle(this.textarea.style,
+"transformOrigin","0px 0px"),mxUtils.setPrefixedStyle(this.textarea.style,"transform","scale("+c+","+c+")"+(null==d?"":" translate("+100*d.x+"%,"+100*d.y+"%)")))}};mxCellEditor.prototype.focusLost=function(){this.stopEditing(!this.graph.isInvokesStopCellEditing())};mxCellEditor.prototype.getBackgroundColor=function(a){return null};
+mxCellEditor.prototype.isLegacyEditor=function(){if(mxClient.IS_VML)return!0;var a=!1;if(mxClient.IS_SVG){var b=this.graph.view.getDrawPane().ownerSVGElement;null!=b&&(a="absolute"==mxUtils.getCurrentStyle(b).position)}return!a};
+mxCellEditor.prototype.startEditing=function(a,b){this.stopEditing(!0);null==this.textarea&&this.init();null!=this.graph.tooltipHandler&&this.graph.tooltipHandler.hideTooltip();var c=this.graph.getView().getState(a);if(null!=c){this.graph.getView();var d=mxUtils.getValue(c.style,mxConstants.STYLE_FONTSIZE,mxConstants.DEFAULT_FONTSIZE),e=mxUtils.getValue(c.style,mxConstants.STYLE_FONTFAMILY,mxConstants.DEFAULT_FONTFAMILY),f=mxUtils.getValue(c.style,mxConstants.STYLE_FONTCOLOR,"black"),g=mxUtils.getValue(c.style,
+mxConstants.STYLE_ALIGN,mxConstants.ALIGN_LEFT),k=(mxUtils.getValue(c.style,mxConstants.STYLE_FONTSTYLE,0)&mxConstants.FONT_BOLD)==mxConstants.FONT_BOLD,l=(mxUtils.getValue(c.style,mxConstants.STYLE_FONTSTYLE,0)&mxConstants.FONT_ITALIC)==mxConstants.FONT_ITALIC,m=(mxUtils.getValue(c.style,mxConstants.STYLE_FONTSTYLE,0)&mxConstants.FONT_UNDERLINE)==mxConstants.FONT_UNDERLINE;this.textarea.style.lineHeight=mxConstants.ABSOLUTE_LINE_HEIGHT?Math.round(d*mxConstants.LINE_HEIGHT)+"px":mxConstants.LINE_HEIGHT;
+this.textarea.style.backgroundColor=this.getBackgroundColor(c);this.textarea.style.textDecoration=m?"underline":"";this.textarea.style.fontWeight=k?"bold":"normal";this.textarea.style.fontStyle=l?"italic":"";this.textarea.style.fontSize=Math.round(d)+"px";this.textarea.style.zIndex=this.zIndex;this.textarea.style.fontFamily=e;this.textarea.style.textAlign=g;this.textarea.style.outline="none";this.textarea.style.color=f;d=this.textDirection=mxUtils.getValue(c.style,mxConstants.STYLE_TEXT_DIRECTION,
+mxConstants.DEFAULT_TEXT_DIRECTION);d==mxConstants.TEXT_DIRECTION_AUTO&&(null==c||null==c.text||c.text.dialect==mxConstants.DIALECT_STRICTHTML||mxUtils.isNode(c.text.value)||(d=c.text.getAutoDirection()));d==mxConstants.TEXT_DIRECTION_LTR||d==mxConstants.TEXT_DIRECTION_RTL?this.textarea.setAttribute("dir",d):this.textarea.removeAttribute("dir");this.textarea.innerHTML=this.getInitialValue(c,b)||"";this.initialValue=this.textarea.innerHTML;0==this.textarea.innerHTML.length||"<br>"==this.textarea.innerHTML?
+(this.textarea.innerHTML=this.getEmptyLabelText(),this.clearOnChange=!0):this.clearOnChange=this.textarea.innerHTML==this.getEmptyLabelText();this.graph.container.appendChild(this.textarea);this.editingCell=a;this.trigger=b;this.textNode=null;null!=c.text&&this.isHideLabel(c)&&(this.textNode=c.text.node,this.textNode.style.visibility="hidden");this.autoSize&&(this.graph.model.isEdge(c.cell)||"fill"!=c.style[mxConstants.STYLE_OVERFLOW])&&window.setTimeout(mxUtils.bind(this,function(){this.resize()}),
+0);this.resize();try{this.textarea.focus(),this.isSelectText()&&0<this.textarea.innerHTML.length&&(this.textarea.innerHTML!=this.getEmptyLabelText()||!this.clearOnChange)&&document.execCommand("selectAll",!1,null)}catch(n){}}};mxCellEditor.prototype.isSelectText=function(){return this.selectText};mxCellEditor.prototype.clearSelection=function(){var a=null;window.getSelection?a=window.getSelection():document.selection&&(a=document.selection);null!=a&&(a.empty?a.empty():a.removeAllRanges&&a.removeAllRanges())};
+mxCellEditor.prototype.stopEditing=function(a){if(null!=this.editingCell){null!=this.textNode&&(this.textNode.style.visibility="visible",this.textNode=null);a=a?null:this.graph.view.getState(this.editingCell);var b=this.initialValue;this.bounds=this.trigger=this.editingCell=this.initialValue=null;this.textarea.blur();this.clearSelection();null!=this.textarea.parentNode&&this.textarea.parentNode.removeChild(this.textarea);this.clearOnChange&&this.textarea.innerHTML==this.getEmptyLabelText()&&(this.textarea.innerHTML=
+"",this.clearOnChange=!1);null!=a&&this.textarea.innerHTML!=b&&(this.prepareTextarea(),b=this.getCurrentValue(a),null!=b&&this.applyValue(a,b));mxEvent.release(this.textarea);this.textarea=null}};mxCellEditor.prototype.prepareTextarea=function(){mxClient.IS_FF&&null!=this.textarea.lastChild&&"BR"==this.textarea.lastChild.nodeName&&this.textarea.removeChild(this.textarea.lastChild)};mxCellEditor.prototype.isHideLabel=function(a){return!0};
+mxCellEditor.prototype.getMinimumSize=function(a){var b=this.graph.getView().scale;return new mxRectangle(0,0,null==a.text?30:a.text.size*b+20,"left"==this.textarea.style.textAlign?120:40)};
+mxCellEditor.prototype.getEditorBounds=function(a){var b=this.graph.getModel().isEdge(a.cell),c=this.graph.getView().scale,d=this.getMinimumSize(a),e=d.width,d=d.height;if(!b&&a.view.graph.cellRenderer.legacySpacing&&"fill"==a.style[mxConstants.STYLE_OVERFLOW])c=a.shape.getLabelBounds(mxRectangle.fromRectangle(a));else{var f=parseInt(a.style[mxConstants.STYLE_SPACING]||0)*c,g=(parseInt(a.style[mxConstants.STYLE_SPACING_TOP]||0)+mxText.prototype.baseSpacingTop)*c+f,k=(parseInt(a.style[mxConstants.STYLE_SPACING_RIGHT]||
+0)+mxText.prototype.baseSpacingRight)*c+f,l=(parseInt(a.style[mxConstants.STYLE_SPACING_BOTTOM]||0)+mxText.prototype.baseSpacingBottom)*c+f,f=(parseInt(a.style[mxConstants.STYLE_SPACING_LEFT]||0)+mxText.prototype.baseSpacingLeft)*c+f,c=new mxRectangle(a.x,a.y,Math.max(e,a.width-f-k),Math.max(d,a.height-g-l)),k=mxUtils.getValue(a.style,mxConstants.STYLE_LABEL_POSITION,mxConstants.ALIGN_CENTER),l=mxUtils.getValue(a.style,mxConstants.STYLE_VERTICAL_LABEL_POSITION,mxConstants.ALIGN_MIDDLE),c=null!=a.shape&&
+k==mxConstants.ALIGN_CENTER&&l==mxConstants.ALIGN_MIDDLE?a.shape.getLabelBounds(c):c;b?(c.x=a.absoluteOffset.x,c.y=a.absoluteOffset.y,null!=a.text&&null!=a.text.boundingBox&&(0<a.text.boundingBox.x&&(c.x=a.text.boundingBox.x),0<a.text.boundingBox.y&&(c.y=a.text.boundingBox.y))):null!=a.text&&null!=a.text.boundingBox&&(c.x=Math.min(c.x,a.text.boundingBox.x),c.y=Math.min(c.y,a.text.boundingBox.y));c.x+=f;c.y+=g;null!=a.text&&null!=a.text.boundingBox&&(b?(c.width=Math.max(e,a.text.boundingBox.width),
+c.height=Math.max(d,a.text.boundingBox.height)):(c.width=Math.max(c.width,a.text.boundingBox.width),c.height=Math.max(c.height,a.text.boundingBox.height)));this.graph.getModel().isVertex(a.cell)&&(b=mxUtils.getValue(a.style,mxConstants.STYLE_LABEL_POSITION,mxConstants.ALIGN_CENTER),b==mxConstants.ALIGN_LEFT?c.x-=a.width:b==mxConstants.ALIGN_RIGHT&&(c.x+=a.width),b=mxUtils.getValue(a.style,mxConstants.STYLE_VERTICAL_LABEL_POSITION,mxConstants.ALIGN_MIDDLE),b==mxConstants.ALIGN_TOP?c.y-=a.height:b==
+mxConstants.ALIGN_BOTTOM&&(c.y+=a.height))}return new mxRectangle(Math.round(c.x),Math.round(c.y),Math.round(c.width),Math.round(c.height))};mxCellEditor.prototype.getEmptyLabelText=function(a){return this.emptyLabelText};mxCellEditor.prototype.getEditingCell=function(){return this.editingCell};
+mxCellEditor.prototype.destroy=function(){null!=this.textarea&&(mxEvent.release(this.textarea),null!=this.textarea.parentNode&&this.textarea.parentNode.removeChild(this.textarea),this.textarea=null);null!=this.changeHandler&&(this.graph.getModel().removeListener(this.changeHandler),this.changeHandler=null);this.zoomHandler&&(this.graph.view.removeListener(this.zoomHandler),this.zoomHandler=null)};function mxCellRenderer(){}mxCellRenderer.defaultShapes={};
+mxCellRenderer.prototype.defaultEdgeShape=mxConnector;mxCellRenderer.prototype.defaultVertexShape=mxRectangleShape;mxCellRenderer.prototype.defaultTextShape=mxText;mxCellRenderer.prototype.legacyControlPosition=!0;mxCellRenderer.prototype.legacySpacing=!0;mxCellRenderer.prototype.antiAlias=!0;mxCellRenderer.prototype.minSvgStrokeWidth=1;mxCellRenderer.prototype.forceControlClickHandler=!1;mxCellRenderer.registerShape=function(a,b){mxCellRenderer.defaultShapes[a]=b};
+mxCellRenderer.registerShape(mxConstants.SHAPE_RECTANGLE,mxRectangleShape);mxCellRenderer.registerShape(mxConstants.SHAPE_ELLIPSE,mxEllipse);mxCellRenderer.registerShape(mxConstants.SHAPE_RHOMBUS,mxRhombus);mxCellRenderer.registerShape(mxConstants.SHAPE_CYLINDER,mxCylinder);mxCellRenderer.registerShape(mxConstants.SHAPE_CONNECTOR,mxConnector);mxCellRenderer.registerShape(mxConstants.SHAPE_ACTOR,mxActor);mxCellRenderer.registerShape(mxConstants.SHAPE_TRIANGLE,mxTriangle);
+mxCellRenderer.registerShape(mxConstants.SHAPE_HEXAGON,mxHexagon);mxCellRenderer.registerShape(mxConstants.SHAPE_CLOUD,mxCloud);mxCellRenderer.registerShape(mxConstants.SHAPE_LINE,mxLine);mxCellRenderer.registerShape(mxConstants.SHAPE_ARROW,mxArrow);mxCellRenderer.registerShape(mxConstants.SHAPE_ARROW_CONNECTOR,mxArrowConnector);mxCellRenderer.registerShape(mxConstants.SHAPE_DOUBLE_ELLIPSE,mxDoubleEllipse);mxCellRenderer.registerShape(mxConstants.SHAPE_SWIMLANE,mxSwimlane);
+mxCellRenderer.registerShape(mxConstants.SHAPE_IMAGE,mxImageShape);mxCellRenderer.registerShape(mxConstants.SHAPE_LABEL,mxLabel);mxCellRenderer.prototype.initializeShape=function(a){a.shape.dialect=a.view.graph.dialect;this.configureShape(a);a.shape.init(a.view.getDrawPane())};mxCellRenderer.prototype.createShape=function(a){var b=null;null!=a.style&&(b=mxStencilRegistry.getStencil(a.style[mxConstants.STYLE_SHAPE]),b=null!=b?new mxShape(b):new (this.getShapeConstructor(a)));return b};
+mxCellRenderer.prototype.createIndicatorShape=function(a){a.shape.indicatorShape=this.getShape(a.view.graph.getIndicatorShape(a))};mxCellRenderer.prototype.getShape=function(a){return null!=a?mxCellRenderer.defaultShapes[a]:null};mxCellRenderer.prototype.getShapeConstructor=function(a){var b=this.getShape(a.style[mxConstants.STYLE_SHAPE]);null==b&&(b=a.view.graph.getModel().isEdge(a.cell)?this.defaultEdgeShape:this.defaultVertexShape);return b};
+mxCellRenderer.prototype.configureShape=function(a){a.shape.apply(a);a.shape.image=a.view.graph.getImage(a);a.shape.indicatorColor=a.view.graph.getIndicatorColor(a);a.shape.indicatorStrokeColor=a.style[mxConstants.STYLE_INDICATOR_STROKECOLOR];a.shape.indicatorGradientColor=a.view.graph.getIndicatorGradientColor(a);a.shape.indicatorDirection=a.style[mxConstants.STYLE_INDICATOR_DIRECTION];a.shape.indicatorImage=a.view.graph.getIndicatorImage(a);this.postConfigureShape(a)};
+mxCellRenderer.prototype.postConfigureShape=function(a){null!=a.shape&&(this.resolveColor(a,"indicatorColor",mxConstants.STYLE_FILLCOLOR),this.resolveColor(a,"indicatorGradientColor",mxConstants.STYLE_GRADIENTCOLOR),this.resolveColor(a,"fill",mxConstants.STYLE_FILLCOLOR),this.resolveColor(a,"stroke",mxConstants.STYLE_STROKECOLOR),this.resolveColor(a,"gradient",mxConstants.STYLE_GRADIENTCOLOR))};
+mxCellRenderer.prototype.checkPlaceholderStyles=function(a){if(null!=a.style)for(var b=["inherit","swimlane","indicated"],c=[mxConstants.STYLE_FILLCOLOR,mxConstants.STYLE_STROKECOLOR,mxConstants.STYLE_GRADIENTCOLOR],d=0;d<c.length;d++)if(0<=mxUtils.indexOf(b,a.style[c[d]]))return!0;return!1};
+mxCellRenderer.prototype.resolveColor=function(a,b,c){var d=a.shape[b],e=a.view.graph,f=null;"inherit"==d?f=e.model.getParent(a.cell):"swimlane"==d?(a.shape[b]=c==mxConstants.STYLE_STROKECOLOR?"#000000":"#ffffff",f=null!=e.model.getTerminal(a.cell,!1)?e.model.getTerminal(a.cell,!1):a.cell,f=e.getSwimlane(f),c=e.swimlaneIndicatorColorAttribute):"indicated"==d&&(a.shape[b]=a.shape.indicatorColor);null!=f&&(d=e.getView().getState(f),a.shape[b]=null,null!=d&&(a.shape[b]=null!=d.shape&&"indicatorColor"!=
+b?d.shape[b]:d.style[c]))};mxCellRenderer.prototype.getLabelValue=function(a){return a.view.graph.getLabel(a.cell)};
+mxCellRenderer.prototype.createLabel=function(a,b){var c=a.view.graph;c.getModel().isEdge(a.cell);if(0<a.style[mxConstants.STYLE_FONTSIZE]||null==a.style[mxConstants.STYLE_FONTSIZE]){var d=c.isHtmlLabel(a.cell)||null!=b&&mxUtils.isNode(b);a.text=new this.defaultTextShape(b,new mxRectangle,a.style[mxConstants.STYLE_ALIGN]||mxConstants.ALIGN_CENTER,c.getVerticalAlign(a),a.style[mxConstants.STYLE_FONTCOLOR],a.style[mxConstants.STYLE_FONTFAMILY],a.style[mxConstants.STYLE_FONTSIZE],a.style[mxConstants.STYLE_FONTSTYLE],
+a.style[mxConstants.STYLE_SPACING],a.style[mxConstants.STYLE_SPACING_TOP],a.style[mxConstants.STYLE_SPACING_RIGHT],a.style[mxConstants.STYLE_SPACING_BOTTOM],a.style[mxConstants.STYLE_SPACING_LEFT],a.style[mxConstants.STYLE_HORIZONTAL],a.style[mxConstants.STYLE_LABEL_BACKGROUNDCOLOR],a.style[mxConstants.STYLE_LABEL_BORDERCOLOR],c.isWrapping(a.cell)&&c.isHtmlLabel(a.cell),c.isLabelClipped(a.cell),a.style[mxConstants.STYLE_OVERFLOW],a.style[mxConstants.STYLE_LABEL_PADDING],mxUtils.getValue(a.style,mxConstants.STYLE_TEXT_DIRECTION,
+mxConstants.DEFAULT_TEXT_DIRECTION));a.text.opacity=mxUtils.getValue(a.style,mxConstants.STYLE_TEXT_OPACITY,100);a.text.dialect=d?mxConstants.DIALECT_STRICTHTML:a.view.graph.dialect;a.text.style=a.style;a.text.state=a;this.initializeLabel(a,a.text);var e=!1,f=function(b){var d=a;if(mxClient.IS_TOUCH||e)d=mxEvent.getClientX(b),b=mxEvent.getClientY(b),b=mxUtils.convertPoint(c.container,d,b),d=c.view.getState(c.getCellAt(b.x,b.y));return d};mxEvent.addGestureListeners(a.text.node,mxUtils.bind(this,function(b){this.isLabelEvent(a,
+b)&&(c.fireMouseEvent(mxEvent.MOUSE_DOWN,new mxMouseEvent(b,a)),e=c.dialect!=mxConstants.DIALECT_SVG&&"IMG"==mxEvent.getSource(b).nodeName)}),mxUtils.bind(this,function(b){this.isLabelEvent(a,b)&&c.fireMouseEvent(mxEvent.MOUSE_MOVE,new mxMouseEvent(b,f(b)))}),mxUtils.bind(this,function(b){this.isLabelEvent(a,b)&&(c.fireMouseEvent(mxEvent.MOUSE_UP,new mxMouseEvent(b,f(b))),e=!1)}));c.nativeDblClickEnabled&&mxEvent.addListener(a.text.node,"dblclick",mxUtils.bind(this,function(b){this.isLabelEvent(a,
+b)&&(c.dblClick(b,a.cell),mxEvent.consume(b))}))}};mxCellRenderer.prototype.initializeLabel=function(a,b){mxClient.IS_SVG&&mxClient.NO_FO&&b.dialect!=mxConstants.DIALECT_SVG?b.init(a.view.graph.container):b.init(a.view.getDrawPane())};
+mxCellRenderer.prototype.createCellOverlays=function(a){var b=a.view.graph.getCellOverlays(a.cell),c=null;if(null!=b)for(var c=new mxDictionary,d=0;d<b.length;d++){var e=null!=a.overlays?a.overlays.remove(b[d]):null;null==e&&(e=new mxImageShape(new mxRectangle,b[d].image.src),e.dialect=a.view.graph.dialect,e.preserveImageAspect=!1,e.overlay=b[d],this.initializeOverlay(a,e),this.installCellOverlayListeners(a,b[d],e),null!=b[d].cursor&&(e.node.style.cursor=b[d].cursor));c.put(b[d],e)}null!=a.overlays&&
+a.overlays.visit(function(a,b){b.destroy()});a.overlays=c};mxCellRenderer.prototype.initializeOverlay=function(a,b){b.init(a.view.getOverlayPane())};
+mxCellRenderer.prototype.installCellOverlayListeners=function(a,b,c){var d=a.view.graph;mxEvent.addListener(c.node,"click",function(c){d.isEditing()&&d.stopEditing(!d.isInvokesStopCellEditing());b.fireEvent(new mxEventObject(mxEvent.CLICK,"event",c,"cell",a.cell))});mxEvent.addGestureListeners(c.node,function(a){mxEvent.consume(a)},function(b){d.fireMouseEvent(mxEvent.MOUSE_MOVE,new mxMouseEvent(b,a))});mxClient.IS_TOUCH&&mxEvent.addListener(c.node,"touchend",function(c){b.fireEvent(new mxEventObject(mxEvent.CLICK,
+"event",c,"cell",a.cell))})};mxCellRenderer.prototype.createControl=function(a){var b=a.view.graph,c=b.getFoldingImage(a);if(b.foldingEnabled&&null!=c){if(null==a.control){var d=new mxRectangle(0,0,c.width,c.height);a.control=new mxImageShape(d,c.src);a.control.preserveImageAspect=!1;a.control.dialect=b.dialect;this.initControl(a,a.control,!0,this.createControlClickHandler(a))}}else null!=a.control&&(a.control.destroy(),a.control=null)};
+mxCellRenderer.prototype.createControlClickHandler=function(a){var b=a.view.graph;return mxUtils.bind(this,function(c){if(this.forceControlClickHandler||b.isEnabled()){var d=!b.isCellCollapsed(a.cell);b.foldCells(d,!1,[a.cell],null,c);mxEvent.consume(c)}})};
+mxCellRenderer.prototype.initControl=function(a,b,c,d){var e=a.view.graph;e.isHtmlLabel(a.cell)&&mxClient.NO_FO&&e.dialect==mxConstants.DIALECT_SVG?(b.dialect=mxConstants.DIALECT_PREFERHTML,b.init(e.container),b.node.style.zIndex=1):b.init(a.view.getOverlayPane());b=b.innerNode||b.node;null==d||mxClient.IS_IOS||(e.isEnabled()&&(b.style.cursor="pointer"),mxEvent.addListener(b,"click",d));if(c){var f=null;mxEvent.addGestureListeners(b,function(b){f=new mxPoint(mxEvent.getClientX(b),mxEvent.getClientY(b));
+e.fireMouseEvent(mxEvent.MOUSE_DOWN,new mxMouseEvent(b,a));mxEvent.consume(b)},function(b){e.fireMouseEvent(mxEvent.MOUSE_MOVE,new mxMouseEvent(b,a))},function(b){e.fireMouseEvent(mxEvent.MOUSE_UP,new mxMouseEvent(b,a));mxEvent.consume(b)});null!=d&&mxClient.IS_IOS&&b.addEventListener("touchend",function(a){if(null!=f){var b=e.tolerance;Math.abs(f.x-mxEvent.getClientX(a))<b&&Math.abs(f.y-mxEvent.getClientY(a))<b&&(d.call(d,a),mxEvent.consume(a))}},!0)}return b};
+mxCellRenderer.prototype.isShapeEvent=function(a,b){return!0};mxCellRenderer.prototype.isLabelEvent=function(a,b){return!0};
+mxCellRenderer.prototype.installListeners=function(a){var b=a.view.graph,c=function(c){var d=a;if(b.dialect!=mxConstants.DIALECT_SVG&&"IMG"==mxEvent.getSource(c).nodeName||mxClient.IS_TOUCH)d=mxEvent.getClientX(c),c=mxEvent.getClientY(c),c=mxUtils.convertPoint(b.container,d,c),d=b.view.getState(b.getCellAt(c.x,c.y));return d};mxEvent.addGestureListeners(a.shape.node,mxUtils.bind(this,function(c){this.isShapeEvent(a,c)&&b.fireMouseEvent(mxEvent.MOUSE_DOWN,new mxMouseEvent(c,a))}),mxUtils.bind(this,
+function(d){this.isShapeEvent(a,d)&&b.fireMouseEvent(mxEvent.MOUSE_MOVE,new mxMouseEvent(d,c(d)))}),mxUtils.bind(this,function(d){this.isShapeEvent(a,d)&&b.fireMouseEvent(mxEvent.MOUSE_UP,new mxMouseEvent(d,c(d)))}));b.nativeDblClickEnabled&&mxEvent.addListener(a.shape.node,"dblclick",mxUtils.bind(this,function(c){this.isShapeEvent(a,c)&&(b.dblClick(c,a.cell),mxEvent.consume(c))}))};
+mxCellRenderer.prototype.redrawLabel=function(a,b){var c=a.view.graph,d=this.getLabelValue(a),e=c.isWrapping(a.cell),f=c.isLabelClipped(a.cell),g=a.view.graph.isHtmlLabel(a.cell)||null!=d&&mxUtils.isNode(d)?mxConstants.DIALECT_STRICTHTML:a.view.graph.dialect,k=a.style[mxConstants.STYLE_OVERFLOW]||"visible";null==a.text||a.text.wrap==e&&a.text.clipped==f&&a.text.overflow==k&&a.text.dialect==g||(a.text.destroy(),a.text=null);null==a.text&&null!=d&&(mxUtils.isNode(d)||0<d.length)?this.createLabel(a,
+d):null==a.text||null!=d&&0!=d.length||(a.text.destroy(),a.text=null);if(null!=a.text){b&&(null!=a.text.lastValue&&this.isTextShapeInvalid(a,a.text)&&(a.text.lastValue=null),a.text.resetStyles(),a.text.apply(a),a.text.valign=c.getVerticalAlign(a));var c=this.getLabelBounds(a),l=this.getTextScale(a);if(b||a.text.value!=d||a.text.isWrapping!=e||a.text.overflow!=k||a.text.isClipping!=f||a.text.scale!=l||a.text.dialect!=g||!a.text.bounds.equals(c))0!=a.text.bounds.width&&null!=a.unscaledWidth&&0!=Math.round(a.text.bounds.width/
+a.text.scale*l-c.width)&&(a.unscaledWidth=null),a.text.dialect=g,a.text.value=d,a.text.bounds=c,a.text.scale=l,a.text.wrap=e,a.text.clipped=f,a.text.overflow=k,d=a.text.node.style.visibility,this.redrawLabelShape(a.text),a.text.node.style.visibility=d}};
+mxCellRenderer.prototype.isTextShapeInvalid=function(a,b){function c(c,e,f){return result="spacingTop"==e||"spacingRight"==e||"spacingBottom"==e||"spacingLeft"==e?parseFloat(b[c])-parseFloat(b.spacing)!=(a.style[e]||f):b[c]!=(a.style[e]||f)}return c("fontStyle",mxConstants.STYLE_FONTSTYLE,mxConstants.DEFAULT_FONTSTYLE)||c("family",mxConstants.STYLE_FONTFAMILY,mxConstants.DEFAULT_FONTFAMILY)||c("size",mxConstants.STYLE_FONTSIZE,mxConstants.DEFAULT_FONTSIZE)||c("color",mxConstants.STYLE_FONTCOLOR,"black")||
+c("align",mxConstants.STYLE_ALIGN,"")||c("valign",mxConstants.STYLE_VERTICAL_ALIGN,"")||c("spacing",mxConstants.STYLE_SPACING,2)||c("spacingTop",mxConstants.STYLE_SPACING_TOP,0)||c("spacingRight",mxConstants.STYLE_SPACING_RIGHT,0)||c("spacingBottom",mxConstants.STYLE_SPACING_BOTTOM,0)||c("spacingLeft",mxConstants.STYLE_SPACING_LEFT,0)||c("horizontal",mxConstants.STYLE_HORIZONTAL,!0)||c("background",mxConstants.STYLE_LABEL_BACKGROUNDCOLOR)||c("border",mxConstants.STYLE_LABEL_BORDERCOLOR)||c("opacity",
+mxConstants.STYLE_TEXT_OPACITY,100)||c("textDirection",mxConstants.STYLE_TEXT_DIRECTION,mxConstants.DEFAULT_TEXT_DIRECTION)};mxCellRenderer.prototype.redrawLabelShape=function(a){a.redraw()};mxCellRenderer.prototype.getTextScale=function(a){return a.view.scale};
+mxCellRenderer.prototype.getLabelBounds=function(a){var b=a.view.graph,c=a.view.scale,d=b.getModel().isEdge(a.cell),e=new mxRectangle(a.absoluteOffset.x,a.absoluteOffset.y);if(d){var f=a.text.getSpacing();e.x+=f.x*c;e.y+=f.y*c;b=b.getCellGeometry(a.cell);null!=b&&(e.width=Math.max(0,b.width*c),e.height=Math.max(0,b.height*c))}else a.text.isPaintBoundsInverted()&&(b=e.x,e.x=e.y,e.y=b),e.x+=a.x,e.y+=a.y,e.width=Math.max(1,a.width),e.height=Math.max(1,a.height),b=mxUtils.getValue(a.style,mxConstants.STYLE_STROKECOLOR,
+mxConstants.NONE),b!=mxConstants.NONE&&""!=b&&(f=parseFloat(mxUtils.getValue(a.style,mxConstants.STYLE_STROKEWIDTH,1))*c,b=1+Math.floor((f-1)/2),f=Math.floor(f+1),e.x+=b,e.y+=b,e.width-=f,e.height-=f);a.text.isPaintBoundsInverted()&&(b=(a.width-a.height)/2,e.x+=b,e.y-=b,b=e.width,e.width=e.height,e.height=b);null!=a.shape&&(b=mxUtils.getValue(a.style,mxConstants.STYLE_LABEL_POSITION,mxConstants.ALIGN_CENTER),f=mxUtils.getValue(a.style,mxConstants.STYLE_VERTICAL_LABEL_POSITION,mxConstants.ALIGN_MIDDLE),
+b==mxConstants.ALIGN_CENTER&&f==mxConstants.ALIGN_MIDDLE&&(e=a.shape.getLabelBounds(e)));b=mxUtils.getValue(a.style,mxConstants.STYLE_LABEL_WIDTH,null);null!=b&&(e.width=parseFloat(b)*c);d||this.rotateLabelBounds(a,e);return e};
+mxCellRenderer.prototype.rotateLabelBounds=function(a,b){b.y-=a.text.margin.y*b.height;b.x-=a.text.margin.x*b.width;if(!this.legacySpacing||"fill"!=a.style[mxConstants.STYLE_OVERFLOW]&&"width"!=a.style[mxConstants.STYLE_OVERFLOW]){var c=a.view.scale,d=a.text.getSpacing();b.x+=d.x*c;b.y+=d.y*c;var d=mxUtils.getValue(a.style,mxConstants.STYLE_LABEL_POSITION,mxConstants.ALIGN_CENTER),e=mxUtils.getValue(a.style,mxConstants.STYLE_VERTICAL_LABEL_POSITION,mxConstants.ALIGN_MIDDLE),f=mxUtils.getValue(a.style,
+mxConstants.STYLE_LABEL_WIDTH,null);b.width=Math.max(0,b.width-(d==mxConstants.ALIGN_CENTER&&null==f?a.text.spacingLeft*c+a.text.spacingRight*c:0));b.height=Math.max(0,b.height-(e==mxConstants.ALIGN_MIDDLE?a.text.spacingTop*c+a.text.spacingBottom*c:0))}e=a.text.getTextRotation();0!=e&&null!=a&&a.view.graph.model.isVertex(a.cell)&&(c=a.getCenterX(),d=a.getCenterY(),b.x!=c||b.y!=d)&&(e*=Math.PI/180,pt=mxUtils.getRotatedPoint(new mxPoint(b.x,b.y),Math.cos(e),Math.sin(e),new mxPoint(c,d)),b.x=pt.x,b.y=
+pt.y)};
+mxCellRenderer.prototype.redrawCellOverlays=function(a,b){this.createCellOverlays(a);if(null!=a.overlays){var c=mxUtils.mod(mxUtils.getValue(a.style,mxConstants.STYLE_ROTATION,0),90),d=mxUtils.toRadians(c),e=Math.cos(d),f=Math.sin(d);a.overlays.visit(function(d,k){var g=k.overlay.getBounds(a);if(!a.view.graph.getModel().isEdge(a.cell)&&null!=a.shape&&0!=c){var m=g.getCenterX(),n=g.getCenterY(),n=mxUtils.getRotatedPoint(new mxPoint(m,n),e,f,new mxPoint(a.getCenterX(),a.getCenterY())),m=n.x,n=n.y;g.x=
+Math.round(m-g.width/2);g.y=Math.round(n-g.height/2)}if(b||null==k.bounds||k.scale!=a.view.scale||!k.bounds.equals(g))k.bounds=g,k.scale=a.view.scale,k.redraw()})}};
+mxCellRenderer.prototype.redrawControl=function(a,b){var c=a.view.graph.getFoldingImage(a);if(null!=a.control&&null!=c){var c=this.getControlBounds(a,c.width,c.height),d=this.legacyControlPosition?mxUtils.getValue(a.style,mxConstants.STYLE_ROTATION,0):a.shape.getTextRotation(),e=a.view.scale;if(b||a.control.scale!=e||!a.control.bounds.equals(c)||a.control.rotation!=d)a.control.rotation=d,a.control.bounds=c,a.control.scale=e,a.control.redraw()}};
+mxCellRenderer.prototype.getControlBounds=function(a,b,c){if(null!=a.control){var d=a.view.scale,e=a.getCenterX(),f=a.getCenterY();if(!a.view.graph.getModel().isEdge(a.cell)&&(e=a.x+b*d,f=a.y+c*d,null!=a.shape)){var g=a.shape.getShapeRotation();if(this.legacyControlPosition)g=mxUtils.getValue(a.style,mxConstants.STYLE_ROTATION,0);else if(a.shape.isPaintBoundsInverted())var k=(a.width-a.height)/2,e=e+k,f=f-k;0!=g&&(k=mxUtils.toRadians(g),g=Math.cos(k),k=Math.sin(k),f=mxUtils.getRotatedPoint(new mxPoint(e,
+f),g,k,new mxPoint(a.getCenterX(),a.getCenterY())),e=f.x,f=f.y)}return a.view.graph.getModel().isEdge(a.cell),new mxRectangle(Math.round(e-b/2*d),Math.round(f-c/2*d),Math.round(b*d),Math.round(c*d))}return null};
+mxCellRenderer.prototype.insertStateAfter=function(a,b,c){for(var d=this.getShapesForState(a),e=0;e<d.length;e++)if(null!=d[e]&&null!=d[e].node){var f=d[e].node.parentNode!=a.view.getDrawPane()&&d[e].node.parentNode!=a.view.getOverlayPane(),g=f?c:b;if(null!=g&&g.nextSibling!=d[e].node)null==g.nextSibling?g.parentNode.appendChild(d[e].node):g.parentNode.insertBefore(d[e].node,g.nextSibling);else if(null==g)if(d[e].node.parentNode==a.view.graph.container){for(g=a.view.canvas;null!=g&&g.parentNode!=
+a.view.graph.container;)g=g.parentNode;null!=g&&null!=g.nextSibling?g.nextSibling!=d[e].node&&d[e].node.parentNode.insertBefore(d[e].node,g.nextSibling):d[e].node.parentNode.appendChild(d[e].node)}else null!=d[e].node.parentNode.firstChild&&d[e].node.parentNode.firstChild!=d[e].node&&d[e].node.parentNode.insertBefore(d[e].node,d[e].node.parentNode.firstChild);f?c=d[e].node:b=d[e].node}return[b,c]};mxCellRenderer.prototype.getShapesForState=function(a){return[a.shape,a.text,a.control]};
+mxCellRenderer.prototype.redraw=function(a,b,c){b=this.redrawShape(a,b,c);null==a.shape||null!=c&&!c||(this.redrawLabel(a,b),this.redrawCellOverlays(a,b),this.redrawControl(a,b))};
+mxCellRenderer.prototype.redrawShape=function(a,b,c){var d=a.view.graph.model,e=!1;null!=a.shape&&null!=a.shape.style&&null!=a.style&&a.shape.style[mxConstants.STYLE_SHAPE]!=a.style[mxConstants.STYLE_SHAPE]&&(a.shape.destroy(),a.shape=null);null==a.shape&&null!=a.view.graph.container&&a.cell!=a.view.currentRoot&&(d.isVertex(a.cell)||d.isEdge(a.cell))?(a.shape=this.createShape(a),null!=a.shape&&(a.shape.minSvgStrokeWidth=this.minSvgStrokeWidth,a.shape.antiAlias=this.antiAlias,this.createIndicatorShape(a),
+this.initializeShape(a),this.createCellOverlays(a),this.installListeners(a),a.view.graph.selectionCellsHandler.updateHandler(a))):b||null==a.shape||mxUtils.equalEntries(a.shape.style,a.style)&&!this.checkPlaceholderStyles(a)||(a.shape.resetStyles(),this.configureShape(a),a.view.graph.selectionCellsHandler.updateHandler(a),b=!0);null!=a.shape&&(this.createControl(a),b||this.isShapeInvalid(a,a.shape))&&(null!=a.absolutePoints?(a.shape.points=a.absolutePoints.slice(),a.shape.bounds=null):(a.shape.points=
+null,a.shape.bounds=new mxRectangle(a.x,a.y,a.width,a.height)),a.shape.scale=a.view.scale,null==c||c?this.doRedrawShape(a):a.shape.updateBoundingBox(),e=!0);return e};mxCellRenderer.prototype.doRedrawShape=function(a){a.shape.redraw()};mxCellRenderer.prototype.isShapeInvalid=function(a,b){return null==b.bounds||b.scale!=a.view.scale||null==a.absolutePoints&&!b.bounds.equals(a)||null!=a.absolutePoints&&!mxUtils.equalPoints(b.points,a.absolutePoints)};
+mxCellRenderer.prototype.destroy=function(a){null!=a.shape&&(null!=a.text&&(a.text.destroy(),a.text=null),null!=a.overlays&&(a.overlays.visit(function(a,c){c.destroy()}),a.overlays=null),null!=a.control&&(a.control.destroy(),a.control=null),a.shape.destroy(),a.shape=null)};
+var mxEdgeStyle={EntityRelation:function(a,b,c,d,e){var f=a.view,g=f.graph;d=mxUtils.getValue(a.style,mxConstants.STYLE_SEGMENT,mxConstants.ENTITY_SEGMENT)*f.scale;var k=a.absolutePoints,l=k[0],m=k[k.length-1],k=!1;if(null!=l)b=new mxCellState,b.x=l.x,b.y=l.y;else if(null!=b){var n=mxUtils.getPortConstraints(b,a,!0,mxConstants.DIRECTION_MASK_NONE);n!=mxConstants.DIRECTION_MASK_NONE&&n!=mxConstants.DIRECTION_MASK_WEST+mxConstants.DIRECTION_MASK_EAST?k=n==mxConstants.DIRECTION_MASK_WEST:(l=g.getCellGeometry(b.cell),
+l.relative?k=.5>=l.x:null!=c&&(k=c.x+c.width<b.x))}else return;l=!0;null!=m?(c=new mxCellState,c.x=m.x,c.y=m.y):null!=c&&(n=mxUtils.getPortConstraints(c,a,!1,mxConstants.DIRECTION_MASK_NONE),n!=mxConstants.DIRECTION_MASK_NONE&&n!=mxConstants.DIRECTION_MASK_WEST+mxConstants.DIRECTION_MASK_EAST?l=n==mxConstants.DIRECTION_MASK_WEST:(a=g.getCellGeometry(c.cell),a.relative?l=.5>=a.x:null!=b&&(l=b.x+b.width<c.x)));null!=b&&null!=c&&(a=k?b.x:b.x+b.width,b=f.getRoutingCenterY(b),g=l?c.x:c.x+c.width,c=f.getRoutingCenterY(c),
+f=new mxPoint(a+(k?-d:d),b),m=new mxPoint(g+(l?-d:d),c),k==l?(d=k?Math.min(a,g)-d:Math.max(a,g)+d,e.push(new mxPoint(d,b)),e.push(new mxPoint(d,c))):(f.x<m.x==k?(d=b+(c-b)/2,e.push(f),e.push(new mxPoint(f.x,d)),e.push(new mxPoint(m.x,d))):e.push(f),e.push(m)))},Loop:function(a,b,c,d,e){c=a.absolutePoints;var f=c[c.length-1];if(null!=c[0]&&null!=f){if(null!=d&&0<d.length)for(b=0;b<d.length;b++)c=d[b],c=a.view.transformControlPoint(a,c),e.push(new mxPoint(c.x,c.y))}else if(null!=b){var f=a.view,g=f.graph;
+c=null!=d&&0<d.length?d[0]:null;null!=c&&(c=f.transformControlPoint(a,c),mxUtils.contains(b,c.x,c.y)&&(c=null));var k=d=0,l=0,m=0,g=mxUtils.getValue(a.style,mxConstants.STYLE_SEGMENT,g.gridSize)*f.scale;a=mxUtils.getValue(a.style,mxConstants.STYLE_DIRECTION,mxConstants.DIRECTION_WEST);a==mxConstants.DIRECTION_NORTH||a==mxConstants.DIRECTION_SOUTH?(d=f.getRoutingCenterX(b),k=g):(l=f.getRoutingCenterY(b),m=g);null==c||c.x<b.x||c.x>b.x+b.width?null!=c?(d=c.x,m=Math.max(Math.abs(l-c.y),m)):a==mxConstants.DIRECTION_NORTH?
+l=b.y-2*k:a==mxConstants.DIRECTION_SOUTH?l=b.y+b.height+2*k:d=a==mxConstants.DIRECTION_EAST?b.x-2*m:b.x+b.width+2*m:null!=c&&(d=f.getRoutingCenterX(b),k=Math.max(Math.abs(d-c.x),m),l=c.y,m=0);e.push(new mxPoint(d-k,l-m));e.push(new mxPoint(d+k,l+m))}},ElbowConnector:function(a,b,c,d,e){var f=null!=d&&0<d.length?d[0]:null,g=!1,k=!1;if(null!=b&&null!=c)if(null!=f)var l=Math.min(b.x,c.x),m=Math.max(b.x+b.width,c.x+c.width),k=Math.min(b.y,c.y),n=Math.max(b.y+b.height,c.y+c.height),f=a.view.transformControlPoint(a,
+f),g=f.y<k||f.y>n,k=f.x<l||f.x>m;else l=Math.max(b.x,c.x),m=Math.min(b.x+b.width,c.x+c.width),g=l==m,g||(k=Math.max(b.y,c.y),n=Math.min(b.y+b.height,c.y+c.height),k=k==n);k||!g&&a.style[mxConstants.STYLE_ELBOW]!=mxConstants.ELBOW_VERTICAL?mxEdgeStyle.SideToSide(a,b,c,d,e):mxEdgeStyle.TopToBottom(a,b,c,d,e)},SideToSide:function(a,b,c,d,e){var f=a.view;d=null!=d&&0<d.length?d[0]:null;var g=a.absolutePoints,k=g[0],g=g[g.length-1];null!=d&&(d=f.transformControlPoint(a,d));null!=k&&(b=new mxCellState,
+b.x=k.x,b.y=k.y);null!=g&&(c=new mxCellState,c.x=g.x,c.y=g.y);null!=b&&null!=c&&(a=Math.max(b.x,c.x),k=Math.min(b.x+b.width,c.x+c.width),a=null!=d?d.x:Math.round(k+(a-k)/2),k=f.getRoutingCenterY(b),f=f.getRoutingCenterY(c),null!=d&&(d.y>=b.y&&d.y<=b.y+b.height&&(k=d.y),d.y>=c.y&&d.y<=c.y+c.height&&(f=d.y)),mxUtils.contains(c,a,k)||mxUtils.contains(b,a,k)||e.push(new mxPoint(a,k)),mxUtils.contains(c,a,f)||mxUtils.contains(b,a,f)||e.push(new mxPoint(a,f)),1==e.length&&(null!=d?mxUtils.contains(c,a,
+d.y)||mxUtils.contains(b,a,d.y)||e.push(new mxPoint(a,d.y)):(f=Math.max(b.y,c.y),e.push(new mxPoint(a,f+(Math.min(b.y+b.height,c.y+c.height)-f)/2)))))},TopToBottom:function(a,b,c,d,e){var f=a.view;d=null!=d&&0<d.length?d[0]:null;var g=a.absolutePoints,k=g[0],g=g[g.length-1];null!=d&&(d=f.transformControlPoint(a,d));null!=k&&(b=new mxCellState,b.x=k.x,b.y=k.y);null!=g&&(c=new mxCellState,c.x=g.x,c.y=g.y);null!=b&&null!=c&&(k=Math.max(b.y,c.y),g=Math.min(b.y+b.height,c.y+c.height),a=f.getRoutingCenterX(b),
+null!=d&&d.x>=b.x&&d.x<=b.x+b.width&&(a=d.x),k=null!=d?d.y:Math.round(g+(k-g)/2),mxUtils.contains(c,a,k)||mxUtils.contains(b,a,k)||e.push(new mxPoint(a,k)),a=null!=d&&d.x>=c.x&&d.x<=c.x+c.width?d.x:f.getRoutingCenterX(c),mxUtils.contains(c,a,k)||mxUtils.contains(b,a,k)||e.push(new mxPoint(a,k)),1==e.length&&(null!=d&&1==e.length?mxUtils.contains(c,d.x,k)||mxUtils.contains(b,d.x,k)||e.push(new mxPoint(d.x,k)):(f=Math.max(b.x,c.x),e.push(new mxPoint(f+(Math.min(b.x+b.width,c.x+c.width)-f)/2,k)))))},
+SegmentConnector:function(a,b,c,d,e){function f(a){if(null==l||Math.abs(l.x-a.x)>=k||Math.abs(l.y-a.y)>=k)e.push(a),l=a;return l}var g=a.absolutePoints,k=Math.max(1,a.view.scale),l=0<e.length?e[0]:null,m=!0,n=null,p=g[0];null==p&&null!=b?p=new mxPoint(a.view.getRoutingCenterX(b),a.view.getRoutingCenterY(b)):null!=p&&(p=p.clone());p.x=Math.round(p.x);p.y=Math.round(p.y);var q=g.length-1;if(null!=d&&0<d.length){for(var n=[],r=0;r<d.length;r++){var t=a.view.transformControlPoint(a,d[r]);null!=t&&(t.x=
+Math.round(t.x),t.y=Math.round(t.y),n.push(t))}if(0==n.length)return;d=n;null!=p&&null!=d[0]&&(Math.abs(d[0].x-p.x)<k&