aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/account/__init__.py8
-rw-r--r--src/account/admin.py19
-rw-r--r--src/account/apps.py15
-rw-r--r--src/account/forms.py29
-rw-r--r--src/account/middleware.py35
-rw-r--r--src/account/migrations/0001_initial.py65
-rw-r--r--src/account/migrations/0002_lab_description.py19
-rw-r--r--src/account/migrations/0003_publicnetwork.py25
-rw-r--r--src/account/migrations/0004_downtime.py24
-rw-r--r--src/account/migrations/0005_auto_20200723_2100.py23
-rw-r--r--src/account/migrations/0006_auto_20201109_1947.py23
-rw-r--r--src/account/migrations/0007_userprofile_pulic_user.py18
-rw-r--r--src/account/migrations/0008_auto_20210324_2106.py18
-rw-r--r--src/account/migrations/0009_auto_20210324_2107.py18
-rw-r--r--src/account/migrations/__init__.py0
-rw-r--r--src/account/models.py297
-rw-r--r--src/account/tests/__init__.py8
-rw-r--r--src/account/tests/test_general.py60
-rw-r--r--src/account/urls.py59
-rw-r--r--src/account/views.py226
-rw-r--r--src/analytics/__init__.py0
-rw-r--r--src/analytics/admin.py13
-rw-r--r--src/analytics/apps.py14
-rw-r--r--src/analytics/migrations/0001_initial.py22
-rw-r--r--src/analytics/migrations/0002_auto_20201109_2149.py27
-rw-r--r--src/analytics/migrations/__init__.py0
-rw-r--r--src/analytics/models.py30
-rw-r--r--src/analytics/tests.py10
-rw-r--r--src/analytics/views.py10
-rw-r--r--src/api/__init__.py8
-rw-r--r--src/api/admin.py43
-rw-r--r--src/api/forms.py16
-rw-r--r--src/api/migrations/0001_initial.py185
-rw-r--r--src/api/migrations/0002_remove_job_delta.py17
-rw-r--r--src/api/migrations/0003_auto_20190102_1956.py18
-rw-r--r--src/api/migrations/0004_snapshotconfig_snapshotrelation.py42
-rw-r--r--src/api/migrations/0005_snapshotconfig_delta.py18
-rw-r--r--src/api/migrations/0006_auto_20190313_1729.py23
-rw-r--r--src/api/migrations/0007_auto_20190417_1511.py25
-rw-r--r--src/api/migrations/0007_opnfvapiconfig_opnfv_config.py20
-rw-r--r--src/api/migrations/0008_auto_20190419_1414.py28
-rw-r--r--src/api/migrations/0009_merge_20190508_1317.py14
-rw-r--r--src/api/migrations/0010_auto_20191219_2004.py23
-rw-r--r--src/api/migrations/0011_auto_20200218_1536.py29
-rw-r--r--src/api/migrations/0012_manual_20200218_1536.py22
-rw-r--r--src/api/migrations/0013_manual_20200218_1536.py29
-rw-r--r--src/api/migrations/0014_manual_20200220.py18
-rw-r--r--src/api/migrations/0015_auto_20201109_1947.py18
-rw-r--r--src/api/migrations/0016_auto_20201109_2149.py41
-rw-r--r--src/api/migrations/0017_apilog.py27
-rw-r--r--src/api/migrations/0017_auto_20210630_1629.py18
-rw-r--r--src/api/migrations/0018_apilog_ip_addr.py18
-rw-r--r--src/api/migrations/0018_cloudinitfile.py25
-rw-r--r--src/api/migrations/0019_auto_20210322_1823.py19
-rw-r--r--src/api/migrations/0019_auto_20210907_1448.py29
-rw-r--r--src/api/migrations/0020_auto_20210322_2218.py23
-rw-r--r--src/api/migrations/0021_auto_20210405_1943.py18
-rw-r--r--src/api/migrations/0022_add_cifile_generated_field.py15
-rw-r--r--src/api/migrations/0022_merge_20211102_2136.py14
-rw-r--r--src/api/migrations/__init__.py8
-rw-r--r--src/api/models.py1453
-rw-r--r--src/api/serializers/__init__.py8
-rw-r--r--src/api/serializers/booking_serializer.py173
-rw-r--r--src/api/serializers/old_serializers.py21
-rw-r--r--src/api/tests/__init__.py8
-rw-r--r--src/api/tests/test_models_unittest.py271
-rw-r--r--src/api/urls.py108
-rw-r--r--src/api/views.py756
-rw-r--r--src/booking/__init__.py8
-rw-r--r--src/booking/admin.py16
-rw-r--r--src/booking/apps.py15
-rw-r--r--src/booking/forms.py123
-rw-r--r--src/booking/lib.py36
-rw-r--r--src/booking/migrations/0001_initial.py68
-rw-r--r--src/booking/migrations/0002_booking_pdf.py18
-rw-r--r--src/booking/migrations/0003_auto_20190115_1733.py30
-rw-r--r--src/booking/migrations/0004_auto_20190124_1700.py20
-rw-r--r--src/booking/migrations/0005_booking_idf.py18
-rw-r--r--src/booking/migrations/0006_booking_opnfv_config.py20
-rw-r--r--src/booking/migrations/0007_remove_booking_config_bundle.py17
-rw-r--r--src/booking/migrations/0008_auto_20201109_1947.py30
-rw-r--r--src/booking/migrations/0009_booking_complete.py18
-rw-r--r--src/booking/migrations/__init__.py0
-rw-r--r--src/booking/models.py72
-rw-r--r--src/booking/quick_deployer.py343
-rw-r--r--src/booking/stats.py109
-rw-r--r--src/booking/tests/__init__.py8
-rw-r--r--src/booking/tests/test_models.py210
-rw-r--r--src/booking/tests/test_quick_booking.py180
-rw-r--r--src/booking/tests/test_stats.py59
-rw-r--r--src/booking/urls.py53
-rw-r--r--src/booking/views.py211
-rw-r--r--src/dashboard/__init__.py8
-rw-r--r--src/dashboard/admin.py16
-rw-r--r--src/dashboard/admin_utils.py811
-rw-r--r--src/dashboard/apps.py15
-rw-r--r--src/dashboard/context_processors.py13
-rw-r--r--src/dashboard/exceptions.py52
-rw-r--r--src/dashboard/fixtures/dashboard.json164
-rw-r--r--src/dashboard/models.py9
-rw-r--r--src/dashboard/populate_db_iol.py352
-rw-r--r--src/dashboard/tasks.py98
-rw-r--r--src/dashboard/templatetags/__init__.py8
-rw-r--r--src/dashboard/testing_utils.py315
-rw-r--r--src/dashboard/tests/__init__.py8
-rw-r--r--src/dashboard/tests/test_views.py30
-rw-r--r--src/dashboard/urls.py42
-rw-r--r--src/dashboard/utils.py52
-rw-r--r--src/dashboard/views.py119
-rw-r--r--src/laas_dashboard/__init__.py13
-rw-r--r--src/laas_dashboard/celery.py31
-rw-r--r--src/laas_dashboard/model_test.py110
-rw-r--r--src/laas_dashboard/settings.py254
-rw-r--r--src/laas_dashboard/urls.py49
-rw-r--r--src/laas_dashboard/wsgi.py26
-rwxr-xr-xsrc/manage.py33
-rw-r--r--src/notifier/__init__.py8
-rw-r--r--src/notifier/admin.py15
-rw-r--r--src/notifier/apps.py15
-rw-r--r--src/notifier/manager.py162
-rw-r--r--src/notifier/migrations/0001_initial.py47
-rw-r--r--src/notifier/migrations/0002_auto_20181102_1631.py44
-rw-r--r--src/notifier/migrations/0003_auto_20190123_1741.py23
-rw-r--r--src/notifier/migrations/0004_auto_20190124_2115.py23
-rw-r--r--src/notifier/migrations/0005_auto_20190306_1616.py18
-rw-r--r--src/notifier/migrations/0006_emailed.py24
-rw-r--r--src/notifier/migrations/0007_email.py23
-rw-r--r--src/notifier/migrations/__init__.py0
-rw-r--r--src/notifier/models.py56
-rw-r--r--src/notifier/tasks.py51
-rw-r--r--src/notifier/tests/test_dispatcher.py15
-rw-r--r--src/notifier/tests/test_models.py30
-rw-r--r--src/notifier/urls.py19
-rw-r--r--src/notifier/views.py58
-rw-r--r--src/resource_inventory/__init__.py8
-rw-r--r--src/resource_inventory/admin.py70
-rw-r--r--src/resource_inventory/apps.py14
-rw-r--r--src/resource_inventory/forms.py31
-rw-r--r--src/resource_inventory/idf_templater.py148
-rw-r--r--src/resource_inventory/migrations/0001_initial.py328
-rw-r--r--src/resource_inventory/migrations/0002_auto_20180919_1459.py18
-rw-r--r--src/resource_inventory/migrations/0003_vlan_public.py18
-rw-r--r--src/resource_inventory/migrations/0004_auto_20181017_1532.py28
-rw-r--r--src/resource_inventory/migrations/0005_image_os.py19
-rw-r--r--src/resource_inventory/migrations/0006_auto_20190124_1700.py76
-rw-r--r--src/resource_inventory/migrations/0007_auto_20190306_1616.py31
-rw-r--r--src/resource_inventory/migrations/0008_host_remote_management.py19
-rw-r--r--src/resource_inventory/migrations/0009_auto_20190315_1757.py73
-rw-r--r--src/resource_inventory/migrations/0010_auto_20190430_1405.py54
-rw-r--r--src/resource_inventory/migrations/0011_auto_20191106_2024.py33
-rw-r--r--src/resource_inventory/migrations/0012_auto_20200103_1850.py59
-rw-r--r--src/resource_inventory/migrations/0012_manual_20200218_1536.py25
-rw-r--r--src/resource_inventory/migrations/0013_auto_20200218_1536.py407
-rw-r--r--src/resource_inventory/migrations/0014_auto_20200305_1415.py18
-rw-r--r--src/resource_inventory/migrations/0015_resourcetemplate_copy_of.py19
-rw-r--r--src/resource_inventory/migrations/0016_auto_20201109_1947.py59
-rw-r--r--src/resource_inventory/migrations/0017_auto_20201218_1516.py18
-rw-r--r--src/resource_inventory/migrations/0018_auto_20210630_1629.py101
-rw-r--r--src/resource_inventory/migrations/0019_auto_20210701_1947.py43
-rw-r--r--src/resource_inventory/migrations/0020_cloudinitfile.py21
-rw-r--r--src/resource_inventory/migrations/0021_resourceconfiguration_cloud_init_files.py18
-rw-r--r--src/resource_inventory/migrations/0022_auto_20210925_2028.py23
-rw-r--r--src/resource_inventory/migrations/0023_cloudinitfile_generated.py18
-rw-r--r--src/resource_inventory/migrations/__init__.py0
-rw-r--r--src/resource_inventory/models.py705
-rw-r--r--src/resource_inventory/pdf_templater.py176
-rw-r--r--src/resource_inventory/resource_manager.py197
-rw-r--r--src/resource_inventory/tests/test_managers.py301
-rw-r--r--src/resource_inventory/tests/test_models.py173
-rw-r--r--src/resource_inventory/urls.py36
-rw-r--r--src/resource_inventory/views.py38
-rw-r--r--src/static/bower.json29
-rw-r--r--src/static/css/anuket.css115
-rw-r--r--src/static/css/base.css93
-rw-r--r--src/static/css/lfedge.css14
-rw-r--r--src/static/css/theme.css19
-rw-r--r--src/static/img/Anuket-logo-reverse.pngbin8612 -> 0 bytes
-rw-r--r--src/static/img/Anuket-logo.svg1
-rw-r--r--src/static/img/akraino_logo.logobin4059 -> 0 bytes
-rw-r--r--src/static/img/lfedge-logo.pngbin6633 -> 0 bytes
-rw-r--r--src/static/js/dashboard.js1664
-rw-r--r--src/static/js/flot-pie-chart.js30
-rw-r--r--src/static/package-lock.json163
-rw-r--r--src/static/package.json26
-rw-r--r--src/templates/README25
-rw-r--r--src/templates/base/account/booking_list.html146
-rw-r--r--src/templates/base/account/details.html8
-rw-r--r--src/templates/base/account/image_list.html132
-rw-r--r--src/templates/base/account/resource_list.html126
-rw-r--r--src/templates/base/account/user_list.html55
-rw-r--r--src/templates/base/account/userprofile_update_form.html31
-rw-r--r--src/templates/base/base.html191
-rw-r--r--src/templates/base/booking/booking_calendar.html207
-rw-r--r--src/templates/base/booking/booking_delete.html10
-rw-r--r--src/templates/base/booking/booking_detail.html373
-rw-r--r--src/templates/base/booking/booking_grid_item.html11
-rw-r--r--src/templates/base/booking/booking_list.html44
-rw-r--r--src/templates/base/booking/booking_table.html37
-rw-r--r--src/templates/base/booking/quick_deploy.html157
-rw-r--r--src/templates/base/booking/stats.html288
-rw-r--r--src/templates/base/booking/steps/booking_meta.html38
-rw-r--r--src/templates/base/config_bundle/steps/assign_host_roles.html22
-rw-r--r--src/templates/base/config_bundle/steps/assign_network_roles.html22
-rw-r--r--src/templates/base/config_bundle/steps/config_software.html15
-rw-r--r--src/templates/base/config_bundle/steps/define_software.html44
-rw-r--r--src/templates/base/config_bundle/steps/pick_installer.html22
-rw-r--r--src/templates/base/config_bundle/steps/table_formset.html53
-rw-r--r--src/templates/base/dashboard/genericselect.html30
-rw-r--r--src/templates/base/dashboard/host_profile_detail.html70
-rw-r--r--src/templates/base/dashboard/idf.yaml46
-rw-r--r--src/templates/base/dashboard/lab_detail.html165
-rw-r--r--src/templates/base/dashboard/lab_list.html29
-rw-r--r--src/templates/base/dashboard/landing.html101
-rw-r--r--src/templates/base/dashboard/login.html7
-rw-r--r--src/templates/base/dashboard/multiple_select_filter_widget.html54
-rw-r--r--src/templates/base/dashboard/pdf.yaml92
-rw-r--r--src/templates/base/dashboard/resource.html57
-rw-r--r--src/templates/base/dashboard/resource_all.html70
-rw-r--r--src/templates/base/dashboard/resource_detail.html151
-rw-r--r--src/templates/base/dashboard/searchable_select_multiple.html73
-rw-r--r--src/templates/base/dashboard/server_table.html30
-rw-r--r--src/templates/base/dashboard/table.html39
-rw-r--r--src/templates/base/layout.html59
-rw-r--r--src/templates/base/notifier/email_ended.txt25
-rw-r--r--src/templates/base/notifier/email_expiring.txt25
-rw-r--r--src/templates/base/notifier/email_fulfilled.txt21
-rw-r--r--src/templates/base/notifier/end_booking.html134
-rw-r--r--src/templates/base/notifier/expiring_booking.html134
-rw-r--r--src/templates/base/notifier/inbox.html87
-rw-r--r--src/templates/base/notifier/new_booking.html133
-rw-r--r--src/templates/base/notifier/notification.html50
-rw-r--r--src/templates/base/resource/hostprofile_detail.html104
-rw-r--r--src/templates/base/resource/hosts.html42
-rw-r--r--src/templates/base/resource/mxClient.min.js1808
-rw-r--r--src/templates/base/resource/steps/define_hardware.html17
-rw-r--r--src/templates/base/resource/steps/host_info.html34
-rw-r--r--src/templates/base/resource/steps/meta_info.html14
-rw-r--r--src/templates/base/resource/steps/pod_definition.html72
-rw-r--r--src/templates/base/resource/uncommon.css162
-rw-r--r--src/templates/base/rest_framework/api.html9
-rw-r--r--src/templates/base/snapshot_workflow/steps/meta.html17
-rw-r--r--src/templates/base/snapshot_workflow/steps/select_host.html83
-rw-r--r--src/templates/base/workflow/confirm.html56
-rw-r--r--src/templates/base/workflow/no_workflow.html3
-rw-r--r--src/templates/base/workflow/viewport-base.html82
-rw-r--r--src/templates/base/workflow/viewport-element.html17
-rw-r--r--src/templates/laas/base.html89
-rw-r--r--src/templates/laas/dashboard/landing.html12
-rw-r--r--src/templates/laas/layout.html5
-rw-r--r--src/templates/lfedge/base.html31
-rw-r--r--src/templates/lfedge/booking/booking_table.html41
-rw-r--r--src/templates/lfedge/booking/quick_deploy.html28
-rw-r--r--src/templates/lfedge/dashboard/landing.html23
-rw-r--r--src/templates/lfedge/layout.html5
-rw-r--r--src/workflow/README31
-rw-r--r--src/workflow/__init__.py8
-rw-r--r--src/workflow/apps.py15
-rw-r--r--src/workflow/booking_workflow.py182
-rw-r--r--src/workflow/forms.py489
-rw-r--r--src/workflow/models.py693
-rw-r--r--src/workflow/opnfv_workflow.py292
-rw-r--r--src/workflow/resource_bundle_workflow.py614
-rw-r--r--src/workflow/snapshot_workflow.py116
-rw-r--r--src/workflow/tests/__init__.py8
-rw-r--r--src/workflow/tests/constants.py198
-rw-r--r--src/workflow/tests/test_fixtures.py2
-rw-r--r--src/workflow/tests/test_steps.py269
-rw-r--r--src/workflow/tests/test_workflows.py99
-rw-r--r--src/workflow/urls.py23
-rw-r--r--src/workflow/views.py112
-rw-r--r--src/workflow/workflow_factory.py126
-rw-r--r--src/workflow/workflow_manager.py270
272 files changed, 0 insertions, 25322 deletions
diff --git a/src/account/__init__.py b/src/account/__init__.py
deleted file mode 100644
index b6fef6c..0000000
--- a/src/account/__init__.py
+++ /dev/null
@@ -1,8 +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/src/account/admin.py b/src/account/admin.py
deleted file mode 100644
index b4c142c..0000000
--- a/src/account/admin.py
+++ /dev/null
@@ -1,19 +0,0 @@
-##############################################################################
-# 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 django.contrib import admin
-
-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/src/account/apps.py b/src/account/apps.py
deleted file mode 100644
index 9814648..0000000
--- a/src/account/apps.py
+++ /dev/null
@@ -1,15 +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.apps import AppConfig
-
-
-class AccountsConfig(AppConfig):
- name = 'account'
diff --git a/src/account/forms.py b/src/account/forms.py
deleted file mode 100644
index 28cb27d..0000000
--- a/src/account/forms.py
+++ /dev/null
@@ -1,29 +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
-import pytz as pytz
-from django.utils.translation import gettext_lazy as _
-
-from account.models import UserProfile
-
-
-class AccountSettingsForm(forms.ModelForm):
- class Meta:
- model = UserProfile
- fields = ['company', 'email_addr', 'public_user', 'ssh_public_key', 'pgp_public_key', 'timezone']
- labels = {
- 'email_addr': _('Email Address'),
- 'ssh_public_key': _('SSH Public Key'),
- 'pgp_public_key': _('PGP Public Key'),
- 'public_user': _('Public User')
- }
-
- timezone = forms.ChoiceField(choices=[(x, x) for x in pytz.common_timezones], initial='UTC')
diff --git a/src/account/middleware.py b/src/account/middleware.py
deleted file mode 100644
index 6a46dfe..0000000
--- a/src/account/middleware.py
+++ /dev/null
@@ -1,35 +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.utils import timezone
-from django.utils.deprecation import MiddlewareMixin
-
-from account.models import UserProfile
-
-
-class TimezoneMiddleware(MiddlewareMixin):
- """
- Manage user's Timezone preference.
-
- Activate the timezone from request.user.userprofile if user is authenticated,
- deactivate the timezone otherwise and use default (UTC)
- """
-
- def process_request(self, request):
- if request.user.is_authenticated:
- try:
- tz = request.user.userprofile.timezone
- timezone.activate(tz)
- except UserProfile.DoesNotExist:
- UserProfile.objects.create(user=request.user)
- tz = request.user.userprofile.timezone
- timezone.activate(tz)
- else:
- timezone.deactivate()
diff --git a/src/account/migrations/0001_initial.py b/src/account/migrations/0001_initial.py
deleted file mode 100644
index c8b5bdc..0000000
--- a/src/account/migrations/0001_initial.py
+++ /dev/null
@@ -1,65 +0,0 @@
-# Generated by Django 2.1 on 2018-09-14 14:48
-
-import account.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),
- ]
-
- 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/src/account/migrations/0002_lab_description.py b/src/account/migrations/0002_lab_description.py
deleted file mode 100644
index 445501a..0000000
--- a/src/account/migrations/0002_lab_description.py
+++ /dev/null
@@ -1,19 +0,0 @@
-# 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/src/account/migrations/0003_publicnetwork.py b/src/account/migrations/0003_publicnetwork.py
deleted file mode 100644
index 71e5caa..0000000
--- a/src/account/migrations/0003_publicnetwork.py
+++ /dev/null
@@ -1,25 +0,0 @@
-# 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/src/account/migrations/0004_downtime.py b/src/account/migrations/0004_downtime.py
deleted file mode 100644
index fc700d1..0000000
--- a/src/account/migrations/0004_downtime.py
+++ /dev/null
@@ -1,24 +0,0 @@
-# Generated by Django 2.2 on 2019-08-13 16:45
-
-from django.db import migrations, models
-import django.db.models.deletion
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('account', '0003_publicnetwork'),
- ]
-
- operations = [
- migrations.CreateModel(
- name='Downtime',
- fields=[
- ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('start', models.DateTimeField()),
- ('end', models.DateTimeField()),
- ('description', models.TextField(default='This lab will be down for maintenance')),
- ('lab', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='account.Lab')),
- ],
- ),
- ]
diff --git a/src/account/migrations/0005_auto_20200723_2100.py b/src/account/migrations/0005_auto_20200723_2100.py
deleted file mode 100644
index d995f80..0000000
--- a/src/account/migrations/0005_auto_20200723_2100.py
+++ /dev/null
@@ -1,23 +0,0 @@
-# Generated by Django 2.2 on 2020-07-23 21:00
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('account', '0004_downtime'),
- ]
-
- operations = [
- migrations.AddField(
- model_name='lab',
- name='lab_info_link',
- field=models.URLField(null=True),
- ),
- migrations.AddField(
- model_name='lab',
- name='project',
- field=models.CharField(default='LaaS', max_length=100),
- ),
- ]
diff --git a/src/account/migrations/0006_auto_20201109_1947.py b/src/account/migrations/0006_auto_20201109_1947.py
deleted file mode 100644
index d08c426..0000000
--- a/src/account/migrations/0006_auto_20201109_1947.py
+++ /dev/null
@@ -1,23 +0,0 @@
-# Generated by Django 2.2 on 2020-11-09 19:47
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('account', '0005_auto_20200723_2100'),
- ]
-
- operations = [
- migrations.AlterField(
- model_name='userprofile',
- name='full_name',
- field=models.CharField(blank=True, default='', max_length=100, null=True),
- ),
- migrations.AlterField(
- model_name='userprofile',
- name='jira_url',
- field=models.CharField(blank=True, default='', max_length=100, null=True),
- ),
- ]
diff --git a/src/account/migrations/0007_userprofile_pulic_user.py b/src/account/migrations/0007_userprofile_pulic_user.py
deleted file mode 100644
index 6a229e6..0000000
--- a/src/account/migrations/0007_userprofile_pulic_user.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# Generated by Django 2.2 on 2021-03-24 21:06
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('account', '0006_auto_20201109_1947'),
- ]
-
- operations = [
- migrations.AddField(
- model_name='userprofile',
- name='pulic_user',
- field=models.BooleanField(default=False),
- ),
- ]
diff --git a/src/account/migrations/0008_auto_20210324_2106.py b/src/account/migrations/0008_auto_20210324_2106.py
deleted file mode 100644
index 9ff513d..0000000
--- a/src/account/migrations/0008_auto_20210324_2106.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# Generated by Django 2.2 on 2021-03-24 21:06
-
-from django.db import migrations
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('account', '0007_userprofile_pulic_user'),
- ]
-
- operations = [
- migrations.RenameField(
- model_name='userprofile',
- old_name='pulic_user',
- new_name='public_user',
- ),
- ]
diff --git a/src/account/migrations/0009_auto_20210324_2107.py b/src/account/migrations/0009_auto_20210324_2107.py
deleted file mode 100644
index baa7382..0000000
--- a/src/account/migrations/0009_auto_20210324_2107.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# Generated by Django 2.2 on 2021-03-24 21:07
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('account', '0008_auto_20210324_2106'),
- ]
-
- operations = [
- migrations.AlterField(
- model_name='userprofile',
- name='public_user',
- field=models.BooleanField(default=False),
- ),
- ]
diff --git a/src/account/migrations/__init__.py b/src/account/migrations/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/src/account/migrations/__init__.py
+++ /dev/null
diff --git a/src/account/models.py b/src/account/models.py
deleted file mode 100644
index 32229b1..0000000
--- a/src/account/models.py
+++ /dev/null
@@ -1,297 +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.contrib.auth.models import User
-from django.db import models
-from django.apps import apps
-import json
-import random
-
-from collections import Counter
-
-from dashboard.exceptions import ResourceAvailabilityException
-
-
-class LabStatus(object):
- """
- A Poor man's enum for the status of a lab.
-
- If everything is working fine at a lab, it is UP.
- If it is down temporarily e.g. for maintenance, it is TEMP_DOWN
- If its broken, its DOWN
- """
-
- UP = 0
- TEMP_DOWN = 100
- DOWN = 200
-
-
-def upload_to(object, filename):
- return object.user.username + '/' + filename
-
-
-class UserProfile(models.Model):
- """Extend the Django User model."""
-
- user = models.OneToOneField(User, on_delete=models.CASCADE)
- timezone = models.CharField(max_length=100, blank=False, default='UTC')
- ssh_public_key = models.FileField(upload_to=upload_to, null=True, blank=True)
- pgp_public_key = models.FileField(upload_to=upload_to, null=True, blank=True)
- email_addr = models.CharField(max_length=300, blank=False, default='email@mail.com')
- company = models.CharField(max_length=200, blank=False)
-
- oauth_token = models.CharField(max_length=1024, blank=False)
- oauth_secret = models.CharField(max_length=1024, blank=False)
-
- jira_url = models.CharField(max_length=100, null=True, blank=True, default='')
-
- full_name = models.CharField(max_length=100, null=True, blank=True, default='')
- booking_privledge = models.BooleanField(default=False)
-
- public_user = models.BooleanField(default=False)
-
- class Meta:
- db_table = 'user_profile'
-
- def __str__(self):
- return self.user.username
-
-
-class VlanManager(models.Model):
- """
- Keeps track of the vlans for a lab.
-
- Vlans are represented as indexes into a 4096 element list.
- This list is serialized to JSON for storing in the DB.
- """
-
- # list of length 4096 containing either 0 (not available) or 1 (available)
- vlans = models.TextField()
- # list of length 4096 containing either 0 (not reserved) or 1 (reserved)
- reserved_vlans = models.TextField()
-
- block_size = models.IntegerField()
-
- # True if the lab allows two different users to have the same private vlans
- # if they use QinQ or a vxlan overlay, for example
- allow_overlapping = models.BooleanField()
-
- def get_vlans(self, count=1, within=None):
- """
- Return the IDs of available vlans as a list[int], but does not reserve them.
-
- Will throw index exception if not enough vlans are available.
- Always returns a list of ints
-
- If `within` is not none, will filter against that as a set, requiring that any vlans returned are within that set
- """
- allocated = []
- vlans = json.loads(self.vlans)
- reserved = json.loads(self.reserved_vlans)
-
- for i in range(0, len(vlans) - 1):
- if len(allocated) >= count:
- break
-
- if vlans[i] == 0 and self.allow_overlapping is False:
- continue
-
- if reserved[i] == 1:
- continue
-
- # vlan is available and not reserved, so safe to add
- if within is not None:
- if i in within:
- allocated.append(i)
- else:
- allocated.append(i)
- continue
-
- if len(allocated) != count:
- raise ResourceAvailabilityException("There were not enough available private vlans for the allocation. Please contact the administrators.")
-
- return allocated
-
- def get_public_vlan(self, within=None):
- """Return reference to an available public network without reserving it."""
- r = PublicNetwork.objects.filter(lab=self.lab_set.first(), in_use=False)
- if within is not None:
- r = r.filter(vlan__in=within)
-
- if r.count() < 1:
- raise ResourceAvailabilityException("There were not enough available public vlans for the allocation. Please contact the administrators.")
-
- return r.first()
-
- def reserve_public_vlan(self, vlan):
- """Reserves the Public Network that has the given 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):
- """Un-reserves a public network with the given 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):
- """
- Whether the public vlan is available.
-
- returns true if the network with the given vlan is free to use,
- False otherwise
- """
- net = PublicNetwork.objects.get(lab=self.lab_set.first(), vlan=vlan)
- return not net.in_use
-
- def is_available(self, vlans):
- """
- If the vlans are available.
-
- '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 Exception:
- vlans = [vlans]
-
- for vlan in vlans:
- if not vlan_master_list[vlan] or reserved[vlan]:
- return False
- return True
-
- def release_vlans(self, vlans):
- """
- Make the vlans available for another booking.
-
- '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 Exception:
- vlans = [vlans]
-
- for vlan in vlans:
- my_vlans[vlan] = 1
- self.vlans = json.dumps(my_vlans)
- self.save()
-
- def reserve_vlans(self, vlans):
- """
- Reserves all given vlans or throws a ValueError.
-
- vlans can be an integer or a list of integers.
- """
- my_vlans = json.loads(self.vlans)
-
- reserved = json.loads(self.reserved_vlans)
-
- try:
- iter(vlans)
- except Exception:
- vlans = [vlans]
-
- vlans = set(vlans)
-
- for vlan in vlans:
- if my_vlans[vlan] == 0 or reserved[vlan] == 1:
- raise ValueError("vlan " + str(vlan) + " is not available")
-
- my_vlans[vlan] = 0
- self.vlans = json.dumps(my_vlans)
- self.save()
-
-
-class Lab(models.Model):
- """
- Model representing a Hosting Lab.
-
- Anybody that wants to host resources for LaaS needs to have a Lab model
- We associate hardware with Labs so we know what is available and where.
- """
-
- 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")
- # This token must apear in API requests from this lab
- api_token = models.CharField(max_length=50)
- description = models.CharField(max_length=240)
- lab_info_link = models.URLField(null=True)
- project = models.CharField(default='LaaS', max_length=100)
-
- @staticmethod
- def make_api_token():
- """Generate random 45 character string for API token."""
- alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
- key = ""
- for i in range(45):
- key += random.choice(alphabet)
- return key
-
- def get_available_resources(self):
- # Cannot import model normally due to ciruclar import
- Server = apps.get_model('resource_inventory', 'Server') # TODO: Find way to import ResourceQuery
- resources = [str(resource.profile) for resource in Server.objects.filter(lab=self, working=True, booked=False)]
- return dict(Counter(resources))
-
- def __str__(self):
- return self.name
-
-
-class PublicNetwork(models.Model):
- """L2/L3 network that can reach the internet."""
-
- 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")
-
-
-class Downtime(models.Model):
- """
- A Downtime event.
-
- Labs can create Downtime objects so the dashboard can
- alert users that the lab is down, etc
- """
-
- start = models.DateTimeField()
- end = models.DateTimeField()
- lab = models.ForeignKey(Lab, on_delete=models.CASCADE)
- description = models.TextField(default="This lab will be down for maintenance")
-
- def save(self, *args, **kwargs):
- if self.start >= self.end:
- raise ValueError('Start date is after end date')
-
- # check for overlapping downtimes
- overlap_start = Downtime.objects.filter(lab=self.lab, start__gt=self.start, start__lt=self.end).exists()
- overlap_end = Downtime.objects.filter(lab=self.lab, end__lt=self.end, end__gt=self.start).exists()
-
- if overlap_start or overlap_end:
- raise ValueError('Overlapping Downtime')
-
- return super(Downtime, self).save(*args, **kwargs)
diff --git a/src/account/tests/__init__.py b/src/account/tests/__init__.py
deleted file mode 100644
index b6fef6c..0000000
--- a/src/account/tests/__init__.py
+++ /dev/null
@@ -1,8 +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/src/account/tests/test_general.py b/src/account/tests/test_general.py
deleted file mode 100644
index 4020d89..0000000
--- a/src/account/tests/test_general.py
+++ /dev/null
@@ -1,60 +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.contrib.auth.models import User
-from django.test import Client
-from django.test import TestCase
-from django.urls import reverse
-from django.utils import timezone
-
-from account.models import UserProfile
-
-
-class AccountMiddlewareTestCase(TestCase):
- def setUp(self):
- self.client = Client()
- self.user1 = User.objects.create(username='user1')
- self.user1.set_password('user1')
- self.user1profile = UserProfile.objects.create(user=self.user1)
- self.user1.save()
-
- def test_timezone_middleware(self):
- """
- Verify timezone is being set by Middleware.
-
- The timezone should be UTC for anonymous users,
- for authenticated users it should be set to user.userprofile.timezone
- """
- # default
- self.assertEqual(timezone.get_current_timezone_name(), 'UTC')
-
- url = reverse('account:settings')
- # anonymous request
- self.client.get(url)
- self.assertEqual(timezone.get_current_timezone_name(), 'UTC')
-
- # authenticated user with UTC timezone (userprofile default)
- self.client.login(username='user1', password='user1')
- self.client.get(url)
- self.assertEqual(timezone.get_current_timezone_name(), 'UTC')
-
- # authenticated user with custom timezone (userprofile default)
- self.user1profile.timezone = 'Etc/Greenwich'
- self.user1profile.save()
- self.client.get(url)
- self.assertEqual(timezone.get_current_timezone_name(), 'GMT')
-
- # if there is no profile for a user, it should be created
- user2 = User.objects.create(username='user2')
- user2.set_password('user2')
- user2.save()
- self.client.login(username='user2', password='user2')
- self.client.get(url)
- self.assertTrue(user2.userprofile)
diff --git a/src/account/urls.py b/src/account/urls.py
deleted file mode 100644
index 6d4ef2f..0000000
--- a/src/account/urls.py
+++ /dev/null
@@ -1,59 +0,0 @@
-##############################################################################
-# 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
-##############################################################################
-
-
-"""
-laas_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 django.urls import path
-
-from account.views import (
- AccountSettingsView,
- OIDCLoginView,
- LogoutView,
- UserListView,
- account_resource_view,
- account_booking_view,
- account_images_view,
- account_detail_view,
- template_delete_view,
- booking_cancel_view,
- image_delete_view,
-)
-
-app_name = 'account'
-
-urlpatterns = [
- url(r'^settings/', AccountSettingsView.as_view(), name='settings'),
- url(r'^login/$', OIDCLoginView.as_view(), name='login'),
- url(r'^logout/$', LogoutView.as_view(), name='logout'),
- url(r'^users/$', UserListView.as_view(), name='users'),
- url(r'^my/resources/$', account_resource_view, name='my-resources'),
- path('my/resources/delete/<int:resource_id>', template_delete_view),
- url(r'^my/bookings/$', account_booking_view, name='my-bookings'),
- path('my/bookings/cancel/<int:booking_id>', booking_cancel_view),
- url(r'^my/images/$', account_images_view, name='my-images'),
- path('my/images/delete/<int:image_id>', image_delete_view),
- url(r'^my/$', account_detail_view, name='my-account'),
-]
diff --git a/src/account/views.py b/src/account/views.py
deleted file mode 100644
index 8976ff9..0000000
--- a/src/account/views.py
+++ /dev/null
@@ -1,226 +0,0 @@
-##############################################################################
-# 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
-##############################################################################
-
-
-import os
-
-from django.utils import timezone
-from django.contrib import messages
-from django.contrib.auth import logout
-from django.contrib.auth.decorators import login_required
-from django.contrib.auth.mixins import LoginRequiredMixin
-from django.contrib.auth.models import User
-from django.urls import reverse
-from django.http import HttpResponse
-from django.shortcuts import get_object_or_404
-from django.utils.decorators import method_decorator
-from django.views.generic import RedirectView, TemplateView, UpdateView
-from django.shortcuts import render
-from rest_framework.authtoken.models import Token
-from mozilla_django_oidc.auth import OIDCAuthenticationBackend
-
-
-from account.forms import AccountSettingsForm
-from account.models import UserProfile
-from booking.models import Booking
-from resource_inventory.models import ResourceTemplate, Image
-
-
-@method_decorator(login_required, name='dispatch')
-class AccountSettingsView(UpdateView):
- model = UserProfile
- form_class = AccountSettingsForm
- template_name_suffix = '_update_form'
-
- def get_success_url(self):
- messages.add_message(self.request, messages.INFO,
- 'Settings saved')
- return '/'
-
- def get_object(self, queryset=None):
- return self.request.user.userprofile
-
- def get_context_data(self, **kwargs):
- token, created = Token.objects.get_or_create(user=self.request.user)
- context = super(AccountSettingsView, self).get_context_data(**kwargs)
- context.update({'title': "Settings", 'token': token})
- return context
-
-
-class MyOIDCAB(OIDCAuthenticationBackend):
- def filter_users_by_claims(self, claims):
- """
- Checks to see if user exists and create user if not
-
- Linux foundation does not allow users to change their
- username, so chose to match users based on their username.
- If this changes we will need to match users based on some
- other criterea.
- """
- username = claims.get(os.environ.get('CLAIMS_ENDPOINT') + 'username')
-
- if not username:
- return HttpResponse('No username provided, contact support.')
-
- try:
- # For literally no (good) reason user needs to be a queryset
- user = User.objects.filter(username=username)
- return user
- except User.DoesNotExist:
- return self.UserModel.objects.none()
-
- def create_user(self, claims):
- """ This creates a user and user profile"""
- user = super(MyOIDCAB, self).create_user(claims)
- user.username = claims.get(os.environ['CLAIMS_ENDPOINT'] + 'username')
- user.save()
-
- up = UserProfile()
- up.user = user
- up.email_addr = claims.get('email')
- up.save()
- return user
-
- def update_user(self, user, claims):
- """ If their account has different email, change the email """
- up = UserProfile.objects.get(user=user)
- up.email_addr = claims.get('email')
- up.save()
- return user
-
-
-class OIDCLoginView(RedirectView):
- def get_redirect_url(self, *args, **kwargs):
- return reverse('oidc_authentication_init')
-
-
-class LogoutView(LoginRequiredMixin, RedirectView):
- def get_redirect_url(self, *args, **kwargs):
- logout(self.request)
- return '/'
-
-
-@method_decorator(login_required, name='dispatch')
-class UserListView(TemplateView):
- template_name = "account/user_list.html"
-
- def get_context_data(self, **kwargs):
- users = UserProfile.objects.filter(public_user=True).select_related('user')
- 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):
- """
- Display a user's resources.
-
- 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"
-
- active_bundles = [book.resource for book in Booking.objects.filter(
- owner=request.user, end__gte=timezone.now(), resource__template__temporary=False)]
- active_resources = [bundle.template.id for bundle in active_bundles]
- resource_list = list(ResourceTemplate.objects.filter(owner=request.user, temporary=False))
-
- context = {
- "resources": resource_list,
- "active_resources": active_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, end__gt=timezone.now()).order_by("-start"))
- my_old_bookings = Booking.objects.filter(owner=request.user, end__lt=timezone.now()).order_by("-start")
- collab_old_bookings = request.user.collaborators.filter(end__lt=timezone.now()).order_by("-start")
- expired_bookings = list(my_old_bookings.union(collab_old_bookings))
- collab_bookings = list(request.user.collaborators.filter(end__gt=timezone.now()).order_by("-start"))
- context = {
- "title": "My Bookings",
- "bookings": bookings,
- "collab_bookings": collab_bookings,
- "expired_bookings": expired_bookings
- }
- 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)
- used_images = {}
- for image in my_images:
- if image.in_use():
- used_images[image.id] = "true"
- context = {
- "title": "Images",
- "images": my_images,
- "public_images": public_images,
- "used_images": used_images
- }
- return render(request, template, context=context)
-
-
-def template_delete_view(request, resource_id=None):
- if not request.user.is_authenticated:
- return HttpResponse(status=403)
- template = get_object_or_404(ResourceTemplate, pk=resource_id)
- if not request.user.id == template.owner.id:
- return HttpResponse(status=403)
- if Booking.objects.filter(resource__template=template, end__gt=timezone.now()).exists():
- return HttpResponse(status=403)
- template.public = False
- template.temporary = True
- template.save()
- return HttpResponse(status=200)
-
-
-def booking_cancel_view(request, booking_id=None):
- if not request.user.is_authenticated:
- return HttpResponse('no') # 403?
- booking = get_object_or_404(Booking, pk=booking_id)
- if not request.user.id == booking.owner.id:
- return HttpResponse('no') # 403?
-
- if booking.end < timezone.now(): # booking already over
- return HttpResponse('')
-
- booking.end = timezone.now()
- booking.save()
- return HttpResponse('')
-
-
-def image_delete_view(request, image_id=None):
- if not request.user.is_authenticated:
- return HttpResponse('no') # 403?
- image = get_object_or_404(Image, pk=image_id)
- if image.public or image.owner.id != request.user.id:
- return HttpResponse('no') # 403?
- # check if used in booking
- if image.in_use():
- return HttpResponse('no') # 403?
- image.delete()
- return HttpResponse('')
diff --git a/src/analytics/__init__.py b/src/analytics/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/src/analytics/__init__.py
+++ /dev/null
diff --git a/src/analytics/admin.py b/src/analytics/admin.py
deleted file mode 100644
index 63f139f..0000000
--- a/src/analytics/admin.py
+++ /dev/null
@@ -1,13 +0,0 @@
-##############################################################################
-# Copyright (c) 2020 Sean Smith 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 analytics.models import ActiveVPNUser
-
-admin.site.register(ActiveVPNUser)
diff --git a/src/analytics/apps.py b/src/analytics/apps.py
deleted file mode 100644
index fe1b11f..0000000
--- a/src/analytics/apps.py
+++ /dev/null
@@ -1,14 +0,0 @@
-##############################################################################
-# Copyright (c) 2020 Sean Smith 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
-
-
-class AnalyticsConfig(AppConfig):
- name = 'analytics'
diff --git a/src/analytics/migrations/0001_initial.py b/src/analytics/migrations/0001_initial.py
deleted file mode 100644
index 05a7ec8..0000000
--- a/src/analytics/migrations/0001_initial.py
+++ /dev/null
@@ -1,22 +0,0 @@
-# Generated by Django 2.2 on 2020-08-10 20:10
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- initial = True
-
- dependencies = [
- ]
-
- operations = [
- migrations.CreateModel(
- name='ActiveVPNUsers',
- fields=[
- ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('time_stamp', models.DateTimeField(auto_now_add=True)),
- ('active_users', models.IntegerField()),
- ],
- ),
- ]
diff --git a/src/analytics/migrations/0002_auto_20201109_2149.py b/src/analytics/migrations/0002_auto_20201109_2149.py
deleted file mode 100644
index a845ff8..0000000
--- a/src/analytics/migrations/0002_auto_20201109_2149.py
+++ /dev/null
@@ -1,27 +0,0 @@
-# Generated by Django 2.2 on 2020-11-09 21:49
-
-from django.db import migrations, models
-import django.db.models.deletion
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('account', '0006_auto_20201109_1947'),
- ('analytics', '0001_initial'),
- ]
-
- operations = [
- migrations.CreateModel(
- name='ActiveVPNUser',
- fields=[
- ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('time_stamp', models.DateTimeField(auto_now_add=True)),
- ('active_users', models.IntegerField()),
- ('lab', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='account.Lab')),
- ],
- ),
- migrations.DeleteModel(
- name='ActiveVPNUsers',
- ),
- ]
diff --git a/src/analytics/migrations/__init__.py b/src/analytics/migrations/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/src/analytics/migrations/__init__.py
+++ /dev/null
diff --git a/src/analytics/models.py b/src/analytics/models.py
deleted file mode 100644
index 10baa0c..0000000
--- a/src/analytics/models.py
+++ /dev/null
@@ -1,30 +0,0 @@
-##############################################################################
-# Copyright (c) 2020 Sean Smith 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 account.models import Lab
-
-
-class ActiveVPNUser(models.Model):
- """ Keeps track of how many VPN Users are connected to Lab """
- time_stamp = models.DateTimeField(auto_now_add=True)
- lab = models.ForeignKey(Lab, on_delete=models.CASCADE, null=False)
- active_users = models.IntegerField()
-
- @classmethod
- def create(cls, lab_name, active_users):
- """
- This creates an Active VPN Users entry from
- from lab_name as a string
- """
-
- lab = Lab.objects.get(name=lab_name)
- avu = cls(lab=lab, active_users=active_users)
- avu.save()
- return avu
diff --git a/src/analytics/tests.py b/src/analytics/tests.py
deleted file mode 100644
index d234f48..0000000
--- a/src/analytics/tests.py
+++ /dev/null
@@ -1,10 +0,0 @@
-##############################################################################
-# Copyright (c) 2020 Sean Smith 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
diff --git a/src/analytics/views.py b/src/analytics/views.py
deleted file mode 100644
index 160bc59..0000000
--- a/src/analytics/views.py
+++ /dev/null
@@ -1,10 +0,0 @@
-##############################################################################
-# Copyright (c) 2020 Sean Smith 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
diff --git a/src/api/__init__.py b/src/api/__init__.py
deleted file mode 100644
index b6fef6c..0000000
--- a/src/api/__init__.py
+++ /dev/null
@@ -1,8 +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/src/api/admin.py b/src/api/admin.py
deleted file mode 100644
index 1e243a0..0000000
--- a/src/api/admin.py
+++ /dev/null
@@ -1,43 +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.apps import AppConfig
-from django.contrib import admin
-
-from api.models import (
- Job,
- OpnfvApiConfig,
- HardwareConfig,
- NetworkConfig,
- SoftwareConfig,
- AccessConfig,
- AccessRelation,
- SoftwareRelation,
- HostHardwareRelation,
- HostNetworkRelation,
- APILog
-)
-
-
-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(AccessConfig)
-admin.site.register(AccessRelation)
-admin.site.register(SoftwareRelation)
-admin.site.register(HostHardwareRelation)
-admin.site.register(HostNetworkRelation)
-admin.site.register(APILog)
diff --git a/src/api/forms.py b/src/api/forms.py
deleted file mode 100644
index 1b74a9b..0000000
--- a/src/api/forms.py
+++ /dev/null
@@ -1,16 +0,0 @@
-##############################################################################
-# Copyright (c) 2019 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
-#############################################################################
-
-import django.forms as forms
-
-
-class DowntimeForm(forms.Form):
- start = forms.DateTimeField()
- end = forms.DateTimeField()
- description = forms.CharField(max_length=1000, required=False)
diff --git a/src/api/migrations/0001_initial.py b/src/api/migrations/0001_initial.py
deleted file mode 100644
index abe6f5e..0000000
--- a/src/api/migrations/0001_initial.py
+++ /dev/null
@@ -1,185 +0,0 @@
-##############################################################################
-# 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/src/api/migrations/0002_remove_job_delta.py b/src/api/migrations/0002_remove_job_delta.py
deleted file mode 100644
index 157a40f..0000000
--- a/src/api/migrations/0002_remove_job_delta.py
+++ /dev/null
@@ -1,17 +0,0 @@
-# Generated by Django 2.1 on 2018-10-17 15:32
-
-from django.db import migrations
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('api', '0001_initial'),
- ]
-
- operations = [
- migrations.RemoveField(
- model_name='job',
- name='delta',
- ),
- ]
diff --git a/src/api/migrations/0003_auto_20190102_1956.py b/src/api/migrations/0003_auto_20190102_1956.py
deleted file mode 100644
index 2ea5d70..0000000
--- a/src/api/migrations/0003_auto_20190102_1956.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# Generated by Django 2.1 on 2019-01-02 19:56
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('api', '0002_remove_job_delta'),
- ]
-
- operations = [
- migrations.AlterField(
- model_name='accessconfig',
- name='delta',
- field=models.TextField(default='{}'),
- ),
- ]
diff --git a/src/api/migrations/0004_snapshotconfig_snapshotrelation.py b/src/api/migrations/0004_snapshotconfig_snapshotrelation.py
deleted file mode 100644
index 62bc7af..0000000
--- a/src/api/migrations/0004_snapshotconfig_snapshotrelation.py
+++ /dev/null
@@ -1,42 +0,0 @@
-# Generated by Django 2.1 on 2019-01-17 15:54
-
-import api.models
-from django.db import migrations, models
-import django.db.models.deletion
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('resource_inventory', '0004_auto_20181017_1532'),
- ('api', '0003_auto_20190102_1956'),
- ]
-
- operations = [
- migrations.CreateModel(
- name='SnapshotConfig',
- 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.IntegerField(null=True)),
- ('dashboard_id', models.IntegerField()),
- ('host', models.ForeignKey(null=True, on_delete=django.db.models.deletion.DO_NOTHING, to='resource_inventory.Host')),
- ],
- bases=('api.taskconfig',),
- ),
- migrations.CreateModel(
- name='SnapshotRelation',
- 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='')),
- ('config', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='api.SnapshotConfig')),
- ('job', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='api.Job')),
- ('snapshot', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='resource_inventory.Image')),
- ],
- options={
- 'abstract': False,
- },
- ),
- ]
diff --git a/src/api/migrations/0005_snapshotconfig_delta.py b/src/api/migrations/0005_snapshotconfig_delta.py
deleted file mode 100644
index 559af90..0000000
--- a/src/api/migrations/0005_snapshotconfig_delta.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# Generated by Django 2.1 on 2019-01-17 16:07
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('api', '0004_snapshotconfig_snapshotrelation'),
- ]
-
- operations = [
- migrations.AddField(
- model_name='snapshotconfig',
- name='delta',
- field=models.TextField(default='{}'),
- ),
- ]
diff --git a/src/api/migrations/0006_auto_20190313_1729.py b/src/api/migrations/0006_auto_20190313_1729.py
deleted file mode 100644
index ec148bd..0000000
--- a/src/api/migrations/0006_auto_20190313_1729.py
+++ /dev/null
@@ -1,23 +0,0 @@
-# Generated by Django 2.1 on 2019-03-13 17:29
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('api', '0005_snapshotconfig_delta'),
- ]
-
- operations = [
- migrations.AlterField(
- model_name='opnfvapiconfig',
- name='installer',
- field=models.CharField(max_length=200),
- ),
- migrations.AlterField(
- model_name='opnfvapiconfig',
- name='scenario',
- field=models.CharField(max_length=300),
- ),
- ]
diff --git a/src/api/migrations/0007_auto_20190417_1511.py b/src/api/migrations/0007_auto_20190417_1511.py
deleted file mode 100644
index e7d2c59..0000000
--- a/src/api/migrations/0007_auto_20190417_1511.py
+++ /dev/null
@@ -1,25 +0,0 @@
-# Generated by Django 2.1 on 2019-04-17 15:11
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('api', '0006_auto_20190313_1729'),
- ]
-
- operations = [
- migrations.AddField(
- model_name='opnfvapiconfig',
- name='idf',
- field=models.CharField(default='', max_length=100),
- preserve_default=False,
- ),
- migrations.AddField(
- model_name='opnfvapiconfig',
- name='pdf',
- field=models.CharField(default='', max_length=100),
- preserve_default=False,
- ),
- ]
diff --git a/src/api/migrations/0007_opnfvapiconfig_opnfv_config.py b/src/api/migrations/0007_opnfvapiconfig_opnfv_config.py
deleted file mode 100644
index 46f3631..0000000
--- a/src/api/migrations/0007_opnfvapiconfig_opnfv_config.py
+++ /dev/null
@@ -1,20 +0,0 @@
-# Generated by Django 2.1 on 2019-05-01 18:53
-
-from django.db import migrations, models
-import django.db.models.deletion
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('resource_inventory', '0010_auto_20190430_1405'),
- ('api', '0006_auto_20190313_1729'),
- ]
-
- operations = [
- migrations.AddField(
- model_name='opnfvapiconfig',
- name='opnfv_config',
- field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='resource_inventory.OPNFVConfig'),
- ),
- ]
diff --git a/src/api/migrations/0008_auto_20190419_1414.py b/src/api/migrations/0008_auto_20190419_1414.py
deleted file mode 100644
index 03c3865..0000000
--- a/src/api/migrations/0008_auto_20190419_1414.py
+++ /dev/null
@@ -1,28 +0,0 @@
-# Generated by Django 2.1 on 2019-04-19 14:14
-
-from django.db import migrations, models
-import django.db.models.deletion
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('resource_inventory', '0009_auto_20190315_1757'),
- ('api', '0007_auto_20190417_1511'),
- ]
-
- operations = [
- migrations.CreateModel(
- name='BridgeConfig',
- fields=[
- ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('interfaces', models.ManyToManyField(to='resource_inventory.Interface')),
- ('opnfv_config', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='resource_inventory.OPNFVConfig')),
- ],
- ),
- migrations.AddField(
- model_name='opnfvapiconfig',
- name='bridge_config',
- field=models.OneToOneField(null=True, on_delete=django.db.models.deletion.CASCADE, to='api.BridgeConfig'),
- ),
- ]
diff --git a/src/api/migrations/0009_merge_20190508_1317.py b/src/api/migrations/0009_merge_20190508_1317.py
deleted file mode 100644
index 1a34380..0000000
--- a/src/api/migrations/0009_merge_20190508_1317.py
+++ /dev/null
@@ -1,14 +0,0 @@
-# Generated by Django 2.1 on 2019-05-08 13:17
-
-from django.db import migrations
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('api', '0008_auto_20190419_1414'),
- ('api', '0007_opnfvapiconfig_opnfv_config'),
- ]
-
- operations = [
- ]
diff --git a/src/api/migrations/0010_auto_20191219_2004.py b/src/api/migrations/0010_auto_20191219_2004.py
deleted file mode 100644
index ec48584..0000000
--- a/src/api/migrations/0010_auto_20191219_2004.py
+++ /dev/null
@@ -1,23 +0,0 @@
-# Generated by Django 2.2 on 2019-12-19 20:04
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('api', '0009_merge_20190508_1317'),
- ]
-
- operations = [
- migrations.AddField(
- model_name='taskconfig',
- name='delta_keys_list',
- field=models.CharField(default='[]', max_length=200),
- ),
- migrations.AddField(
- model_name='taskconfig',
- name='state',
- field=models.IntegerField(default=200),
- ),
- ]
diff --git a/src/api/migrations/0011_auto_20200218_1536.py b/src/api/migrations/0011_auto_20200218_1536.py
deleted file mode 100644
index 0fd7029..0000000
--- a/src/api/migrations/0011_auto_20200218_1536.py
+++ /dev/null
@@ -1,29 +0,0 @@
-# Generated by Django 2.2 on 2020-02-18 15:36
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('api', '0010_auto_20191219_2004'),
- # ('resource_inventory', '0013_auto_20200218_1536')
- ]
-
- operations = [
- migrations.AddField(
- model_name='hosthardwarerelation',
- name='resource_id',
- field=models.CharField(default='default_id', max_length=200),
- ),
- migrations.AddField(
- model_name='hostnetworkrelation',
- name='resource_id',
- field=models.CharField(default='default_id', max_length=200),
- ),
- migrations.AddField(
- model_name='snapshotconfig',
- name='resource_id',
- field=models.CharField(default='default_id', max_length=200),
- ),
- ]
diff --git a/src/api/migrations/0012_manual_20200218_1536.py b/src/api/migrations/0012_manual_20200218_1536.py
deleted file mode 100644
index 55befbd..0000000
--- a/src/api/migrations/0012_manual_20200218_1536.py
+++ /dev/null
@@ -1,22 +0,0 @@
-# Generated by Django 2.2 on 2020-02-18 15:36
-
-from django.db import migrations
-
-
-def set_resource_id(apps, schema_editor):
- for cls in ["HostHardwareRelation", "HostNetworkRelation", "SnapshotConfig"]:
- model = apps.get_model('api', cls)
- for m in model.objects.all():
- m.resource_id = m.host.labid
- m.save()
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('api', '0011_auto_20200218_1536'),
- ]
-
- operations = [
- migrations.RunPython(set_resource_id),
- ]
diff --git a/src/api/migrations/0013_manual_20200218_1536.py b/src/api/migrations/0013_manual_20200218_1536.py
deleted file mode 100644
index 3ac427e..0000000
--- a/src/api/migrations/0013_manual_20200218_1536.py
+++ /dev/null
@@ -1,29 +0,0 @@
-# Generated by Django 2.2 on 2020-02-18 15:36
-
-from django.db import migrations
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('api', '0012_manual_20200218_1536'),
- ]
-
- operations = [
- migrations.RemoveField(
- model_name='hosthardwarerelation',
- name='host',
- ),
- migrations.RemoveField(
- model_name='hostnetworkrelation',
- name='host',
- ),
- migrations.RemoveField(
- model_name='snapshotconfig',
- name='host',
- ),
- migrations.RemoveField(
- model_name='opnfvapiconfig',
- name='roles',
- ),
- ]
diff --git a/src/api/migrations/0014_manual_20200220.py b/src/api/migrations/0014_manual_20200220.py
deleted file mode 100644
index 2e2cd58..0000000
--- a/src/api/migrations/0014_manual_20200220.py
+++ /dev/null
@@ -1,18 +0,0 @@
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('api', '0013_manual_20200218_1536'),
- ('resource_inventory', '0013_auto_20200218_1536')
- ]
-
- operations = [
- migrations.AddField(
- model_name='opnfvapiconfig',
- name='roles',
- field=models.ManyToManyField(to='resource_inventory.ResourceOPNFVConfig'),
- ),
- ]
diff --git a/src/api/migrations/0015_auto_20201109_1947.py b/src/api/migrations/0015_auto_20201109_1947.py
deleted file mode 100644
index bbd51e5..0000000
--- a/src/api/migrations/0015_auto_20201109_1947.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# Generated by Django 2.2 on 2020-11-09 19:47
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('api', '0014_manual_20200220'),
- ]
-
- operations = [
- migrations.AlterField(
- model_name='taskconfig',
- name='state',
- field=models.IntegerField(default=0),
- ),
- ]
diff --git a/src/api/migrations/0016_auto_20201109_2149.py b/src/api/migrations/0016_auto_20201109_2149.py
deleted file mode 100644
index a430659..0000000
--- a/src/api/migrations/0016_auto_20201109_2149.py
+++ /dev/null
@@ -1,41 +0,0 @@
-# Generated by Django 2.2 on 2020-11-09 21:49
-
-import api.models
-from django.db import migrations, models
-import django.db.models.deletion
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('api', '0015_auto_20201109_1947'),
- ]
-
- operations = [
- migrations.CreateModel(
- name='ActiveUsersConfig',
- fields=[
- ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ],
- ),
- migrations.AddField(
- model_name='job',
- name='job_type',
- field=models.CharField(choices=[('BOOK', 'Booking'), ('DATA', 'Analytics')], default='BOOK', max_length=4),
- ),
- migrations.CreateModel(
- name='ActiveUsersRelation',
- 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='')),
- ('config', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='api.ActiveUsersConfig')),
- ('job', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='api.Job')),
- ],
- options={
- 'abstract': False,
- },
- ),
- ]
diff --git a/src/api/migrations/0017_apilog.py b/src/api/migrations/0017_apilog.py
deleted file mode 100644
index d209aef..0000000
--- a/src/api/migrations/0017_apilog.py
+++ /dev/null
@@ -1,27 +0,0 @@
-# Generated by Django 2.2 on 2021-03-19 20:45
-
-from django.conf import settings
-import django.contrib.postgres.fields.jsonb
-from django.db import migrations, models
-import django.db.models.deletion
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- migrations.swappable_dependency(settings.AUTH_USER_MODEL),
- ('api', '0016_auto_20201109_2149'),
- ]
-
- operations = [
- migrations.CreateModel(
- name='APILog',
- fields=[
- ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('call_time', models.DateTimeField(auto_now=True)),
- ('endpoint', models.CharField(max_length=300)),
- ('body', django.contrib.postgres.fields.jsonb.JSONField()),
- ('user', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to=settings.AUTH_USER_MODEL)),
- ],
- ),
- ]
diff --git a/src/api/migrations/0017_auto_20210630_1629.py b/src/api/migrations/0017_auto_20210630_1629.py
deleted file mode 100644
index 643ff5f..0000000
--- a/src/api/migrations/0017_auto_20210630_1629.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# Generated by Django 2.2 on 2021-06-30 16:29
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('api', '0016_auto_20201109_2149'),
- ]
-
- operations = [
- migrations.AlterField(
- model_name='snapshotconfig',
- name='image',
- field=models.CharField(max_length=200, null=True),
- ),
- ]
diff --git a/src/api/migrations/0018_apilog_ip_addr.py b/src/api/migrations/0018_apilog_ip_addr.py
deleted file mode 100644
index 4b7ce39..0000000
--- a/src/api/migrations/0018_apilog_ip_addr.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# Generated by Django 2.2 on 2021-03-22 18:12
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('api', '0017_apilog'),
- ]
-
- operations = [
- migrations.AddField(
- model_name='apilog',
- name='ip_addr',
- field=models.GenericIPAddressField(null=True),
- ),
- ]
diff --git a/src/api/migrations/0018_cloudinitfile.py b/src/api/migrations/0018_cloudinitfile.py
deleted file mode 100644
index 4e41b39..0000000
--- a/src/api/migrations/0018_cloudinitfile.py
+++ /dev/null
@@ -1,25 +0,0 @@
-# Generated by Django 2.2 on 2021-07-01 20:45
-
-from django.db import migrations, models
-import django.db.models.deletion
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('resource_inventory', '0019_auto_20210701_1947'),
- ('booking', '0008_auto_20201109_1947'),
- ('api', '0017_auto_20210630_1629'),
- ]
-
- operations = [
- migrations.CreateModel(
- name='CloudInitFile',
- fields=[
- ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('resource_id', models.CharField(max_length=200)),
- ('booking', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='booking.Booking')),
- ('rconfig', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='resource_inventory.ResourceConfiguration')),
- ],
- ),
- ]
diff --git a/src/api/migrations/0019_auto_20210322_1823.py b/src/api/migrations/0019_auto_20210322_1823.py
deleted file mode 100644
index b3c4cdf..0000000
--- a/src/api/migrations/0019_auto_20210322_1823.py
+++ /dev/null
@@ -1,19 +0,0 @@
-# Generated by Django 2.2 on 2021-03-22 18:23
-
-import django.contrib.postgres.fields.jsonb
-from django.db import migrations
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('api', '0018_apilog_ip_addr'),
- ]
-
- operations = [
- migrations.AlterField(
- model_name='apilog',
- name='body',
- field=django.contrib.postgres.fields.jsonb.JSONField(null=True),
- ),
- ]
diff --git a/src/api/migrations/0019_auto_20210907_1448.py b/src/api/migrations/0019_auto_20210907_1448.py
deleted file mode 100644
index 92140fb..0000000
--- a/src/api/migrations/0019_auto_20210907_1448.py
+++ /dev/null
@@ -1,29 +0,0 @@
-# Generated by Django 2.2 on 2021-09-07 14:48
-
-from django.db import migrations, models
-import django.db.models.deletion
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('booking', '0008_auto_20201109_1947'),
- ('resource_inventory', '0020_cloudinitfile'),
- ('api', '0018_cloudinitfile'),
- ]
-
- operations = [
- migrations.CreateModel(
- name='GeneratedCloudConfig',
- fields=[
- ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('resource_id', models.CharField(max_length=200)),
- ('text', models.TextField(blank=True, null=True)),
- ('booking', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='booking.Booking')),
- ('rconfig', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='resource_inventory.ResourceConfiguration')),
- ],
- ),
- migrations.DeleteModel(
- name='CloudInitFile',
- ),
- ]
diff --git a/src/api/migrations/0020_auto_20210322_2218.py b/src/api/migrations/0020_auto_20210322_2218.py
deleted file mode 100644
index 0252c79..0000000
--- a/src/api/migrations/0020_auto_20210322_2218.py
+++ /dev/null
@@ -1,23 +0,0 @@
-# Generated by Django 2.2 on 2021-03-22 22:18
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('api', '0019_auto_20210322_1823'),
- ]
-
- operations = [
- migrations.AddField(
- model_name='apilog',
- name='method',
- field=models.CharField(max_length=4, null=True),
- ),
- migrations.AlterField(
- model_name='apilog',
- name='endpoint',
- field=models.CharField(max_length=300, null=True),
- ),
- ]
diff --git a/src/api/migrations/0021_auto_20210405_1943.py b/src/api/migrations/0021_auto_20210405_1943.py
deleted file mode 100644
index ca6e741..0000000
--- a/src/api/migrations/0021_auto_20210405_1943.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# Generated by Django 2.2 on 2021-04-05 19:43
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('api', '0020_auto_20210322_2218'),
- ]
-
- operations = [
- migrations.AlterField(
- model_name='apilog',
- name='method',
- field=models.CharField(max_length=6, null=True),
- ),
- ]
diff --git a/src/api/migrations/0022_add_cifile_generated_field.py b/src/api/migrations/0022_add_cifile_generated_field.py
deleted file mode 100644
index f83a102..0000000
--- a/src/api/migrations/0022_add_cifile_generated_field.py
+++ /dev/null
@@ -1,15 +0,0 @@
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
- dependencies = [
- ('api', '0018_cloudinitfile'),
- ]
-
- operations = [
- migrations.AddField(
- model_name="CloudInitFile",
- name="generated",
- field=models.BooleanField(default=False)
- ),
- ]
diff --git a/src/api/migrations/0022_merge_20211102_2136.py b/src/api/migrations/0022_merge_20211102_2136.py
deleted file mode 100644
index bb27ae4..0000000
--- a/src/api/migrations/0022_merge_20211102_2136.py
+++ /dev/null
@@ -1,14 +0,0 @@
-# Generated by Django 2.2 on 2021-11-02 21:36
-
-from django.db import migrations
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('api', '0019_auto_20210907_1448'),
- ('api', '0021_auto_20210405_1943'),
- ]
-
- operations = [
- ]
diff --git a/src/api/migrations/__init__.py b/src/api/migrations/__init__.py
deleted file mode 100644
index e0408fa..0000000
--- a/src/api/migrations/__init__.py
+++ /dev/null
@@ -1,8 +0,0 @@
-##############################################################################
-# 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/src/api/models.py b/src/api/models.py
deleted file mode 100644
index 93168f5..0000000
--- a/src/api/models.py
+++ /dev/null
@@ -1,1453 +0,0 @@
-##############################################################################
-# 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.exceptions import PermissionDenied, ValidationError
-from django.shortcuts import get_object_or_404
-from django.contrib.postgres.fields import JSONField
-from django.http import HttpResponseNotFound
-from django.urls import reverse
-from django.utils import timezone
-
-import json
-import uuid
-import yaml
-import re
-
-from booking.models import Booking
-from resource_inventory.models import (
- Lab,
- ResourceProfile,
- Image,
- Opsys,
- Interface,
- ResourceOPNFVConfig,
- RemoteInfo,
- OPNFVConfig,
- ConfigState,
- ResourceQuery,
- ResourceConfiguration,
- CloudInitFile
-)
-from resource_inventory.idf_templater import IDFTemplater
-from resource_inventory.pdf_templater import PDFTemplater
-from account.models import Downtime, UserProfile
-from dashboard.utils import AbstractModelQuery
-
-
-class JobStatus:
- """
- A poor man's enum for a job's status.
-
- A job is NEW if it has not been started or recognized by the Lab
- A job is CURRENT if it has been started by the lab but it is not yet completed
- a job is DONE if all the tasks are complete and the booking is ready to use
- """
-
- NEW = 0
- CURRENT = 100
- DONE = 200
- ERROR = 300
-
-
-class LabManagerTracker:
-
- @classmethod
- def get(cls, lab_name, token):
- """
- Get a LabManager.
-
- Takes in a lab name (from a url path)
- returns a lab manager instance for that lab, if it exists
- Also checks that the given API token is correct
- """
- try:
- lab = Lab.objects.get(name=lab_name)
- except Exception:
- raise PermissionDenied("Lab not found")
- if lab.api_token == token:
- return LabManager(lab)
- raise PermissionDenied("Lab not authorized")
-
-
-class LabManager:
- """
- Handles all lab REST calls.
-
- handles jobs, inventory, status, etc
- may need to create helper classes
- """
-
- def __init__(self, lab):
- self.lab = lab
-
- def get_opsyss(self):
- return Opsys.objects.filter(from_lab=self.lab)
-
- def get_images(self):
- return Image.objects.filter(from_lab=self.lab)
-
- def get_image(self, image_id):
- return Image.objects.filter(from_lab=self.lab, lab_id=image_id)
-
- def get_opsys(self, opsys_id):
- return Opsys.objects.filter(from_lab=self.lab, lab_id=opsys_id)
-
- def get_downtime(self):
- return Downtime.objects.filter(start__lt=timezone.now(), end__gt=timezone.now(), lab=self.lab)
-
- def get_downtime_json(self):
- downtime = self.get_downtime().first() # should only be one item in queryset
- if downtime:
- return {
- "is_down": True,
- "start": downtime.start,
- "end": downtime.end,
- "description": downtime.description
- }
- return {"is_down": False}
-
- def create_downtime(self, form):
- """
- Create a downtime event.
-
- Takes in a dictionary that describes the model.
- {
- "start": utc timestamp
- "end": utc timestamp
- "description": human text (optional)
- }
- For timestamp structure, https://docs.djangoproject.com/en/2.2/ref/forms/fields/#datetimefield
- """
- Downtime.objects.create(
- start=form.cleaned_data['start'],
- end=form.cleaned_data['end'],
- description=form.cleaned_data['description'],
- lab=self.lab
- )
- return self.get_downtime_json()
-
- def update_host_remote_info(self, data, res_id):
- resource = ResourceQuery.filter(labid=res_id, lab=self.lab)
- if len(resource) != 1:
- return HttpResponseNotFound("Could not find single host with id " + str(res_id))
- resource = resource[0]
- info = {}
- try:
- info['address'] = data['address']
- info['mac_address'] = data['mac_address']
- info['password'] = data['password']
- info['user'] = data['user']
- info['type'] = data['type']
- info['versions'] = json.dumps(data['versions'])
- except Exception as e:
- return {"error": "invalid arguement: " + str(e)}
- remote_info = resource.remote_management
- if "default" in remote_info.mac_address:
- remote_info = RemoteInfo()
- remote_info.address = info['address']
- remote_info.mac_address = info['mac_address']
- remote_info.password = info['password']
- remote_info.user = info['user']
- remote_info.type = info['type']
- remote_info.versions = info['versions']
- remote_info.save()
- resource.remote_management = remote_info
- resource.save()
- booking = Booking.objects.get(resource=resource.bundle)
- self.update_xdf(booking)
- return {"status": "success"}
-
- def update_xdf(self, booking):
- booking.pdf = PDFTemplater.makePDF(booking)
- booking.idf = IDFTemplater().makeIDF(booking)
- booking.save()
-
- def get_pdf(self, booking_id):
- booking = get_object_or_404(Booking, pk=booking_id, lab=self.lab)
- return booking.pdf
-
- def get_idf(self, booking_id):
- booking = get_object_or_404(Booking, pk=booking_id, lab=self.lab)
- return booking.idf
-
- 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'] = [{
- "type": profile.name,
- "count": len(profile.get_resources(lab=self.lab))}
- for profile in ResourceProfile.objects.filter(labs=self.lab)]
- return prof
-
- def format_user(self, userprofile):
- return {
- "id": userprofile.user.id,
- "username": userprofile.user.username,
- "email": userprofile.email_addr,
- "first_name": userprofile.user.first_name,
- "last_name": userprofile.user.last_name,
- "company": userprofile.company
- }
-
- def get_users(self):
- userlist = [self.format_user(profile) for profile in UserProfile.objects.select_related("user").all()]
-
- return json.dumps({"users": userlist})
-
- def get_user(self, user_id):
- user = User.objects.get(pk=user_id)
-
- profile = get_object_or_404(UserProfile, user=user)
-
- return json.dumps(self.format_user(profile))
-
- def get_inventory(self):
- inventory = {}
- resources = ResourceQuery.filter(lab=self.lab)
- images = Image.objects.filter(from_lab=self.lab)
- profiles = ResourceProfile.objects.filter(labs=self.lab)
- inventory['resources'] = self.serialize_resources(resources)
- inventory['images'] = self.serialize_images(images)
- inventory['host_types'] = self.serialize_host_profiles(profiles)
- return inventory
-
- def get_host(self, hostname):
- resource = ResourceQuery.filter(labid=hostname, lab=self.lab)
- if len(resource) != 1:
- return HttpResponseNotFound("Could not find single host with id " + str(hostname))
- resource = resource[0]
- return {
- "booked": resource.booked,
- "working": resource.working,
- "type": resource.profile.name
- }
-
- def update_host(self, hostname, data):
- resource = ResourceQuery.filter(labid=hostname, lab=self.lab)
- if len(resource) != 1:
- return HttpResponseNotFound("Could not find single host with id " + str(hostname))
- resource = resource[0]
- if "working" in data:
- working = data['working'] == "true"
- resource.working = working
- resource.save()
- return self.get_host(hostname)
-
- 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_analytics_job(self):
- """ Get analytics job with status new """
- jobs = Job.objects.filter(
- booking__lab=self.lab,
- job_type='DATA'
- )
-
- return self.serialize_jobs(jobs, status=JobStatus.NEW)
-
- 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_resources(self, resources):
- # TODO: rewrite for Resource model
- host_ser = []
- for res in resources:
- r = {
- 'interfaces': [],
- 'hostname': res.name,
- 'host_type': res.profile.name
- }
- for iface in res.get_interfaces():
- r['interfaces'].append({
- 'mac': iface.mac_address,
- 'busaddr': iface.bus_address,
- 'name': iface.name,
- 'switchport': {"switch_name": iface.switch_name, "port_name": iface.port_name}
- })
- 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_resource_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 GeneratedCloudConfig(models.Model):
- resource_id = models.CharField(max_length=200)
- booking = models.ForeignKey(Booking, on_delete=models.CASCADE)
- rconfig = models.ForeignKey(ResourceConfiguration, on_delete=models.CASCADE)
- text = models.TextField(null=True, blank=True)
-
- def _normalize_username(self, username: str) -> str:
- # TODO: make usernames posix compliant
- s = re.sub(r'\W+', '', username)
- return s
-
- def _get_ssh_string(self, username: str) -> str:
- user = User.objects.get(username=username)
- uprofile = user.userprofile
-
- ssh_file = uprofile.ssh_public_key
-
- escaped_file = ssh_file.open().read().decode(encoding="UTF-8").replace("\n", " ")
-
- return escaped_file
-
- def _serialize_users(self):
- """
- returns the dictionary to be placed behind the `users` field of the toplevel c-i dict
- """
- # conserves distro default user
- user_array = ["default"]
-
- users = list(self.booking.collaborators.all())
- users.append(self.booking.owner)
- for collaborator in users:
- userdict = {}
-
- # TODO: validate if usernames are valid as linux usernames (and provide an override potentially)
- userdict['name'] = self._normalize_username(collaborator.username)
-
- userdict['groups'] = "sudo"
- userdict['sudo'] = "ALL=(ALL) NOPASSWD:ALL"
-
- userdict['ssh_authorized_keys'] = [self._get_ssh_string(collaborator.username)]
-
- user_array.append(userdict)
-
- # user_array.append({
- # "name": "opnfv",
- # "passwd": "$6$k54L.vim1cLaEc4$5AyUIrufGlbtVBzuCWOlA1yV6QdD7Gr2MzwIs/WhuYR9ebSfh3Qlb7djkqzjwjxpnSAonK1YOabPP6NxUDccu.",
- # "ssh_redirect_user": True,
- # "sudo": "ALL=(ALL) NOPASSWD:ALL",
- # "groups": "sudo",
- # })
-
- return user_array
-
- # TODO: make this configurable
- def _serialize_sysinfo(self):
- defuser = {}
- defuser['name'] = 'opnfv'
- defuser['plain_text_passwd'] = 'OPNFV_HOST'
- defuser['home'] = '/home/opnfv'
- defuser['shell'] = '/bin/bash'
- defuser['lock_passwd'] = True
- defuser['gecos'] = 'Lab Manager User'
- defuser['groups'] = 'sudo'
-
- return {'default_user': defuser}
-
- # TODO: make this configurable
- def _serialize_runcmds(self):
- cmdlist = []
-
- # have hosts run dhcp on boot
- cmdlist.append(['sudo', 'dhclient', '-r'])
- cmdlist.append(['sudo', 'dhclient'])
-
- return cmdlist
-
- def _serialize_netconf_v1(self):
- # interfaces = {} # map from iface_name => dhcp_config
- # vlans = {} # map from vlan_id => dhcp_config
-
- config_arr = []
-
- for interface in self._resource().interfaces.all():
- interface_name = interface.profile.name
- interface_mac = interface.mac_address
-
- iface_dict_entry = {
- "type": "physical",
- "name": interface_name,
- "mac_address": interface_mac,
- }
-
- for vlan in interface.config.all():
- if vlan.tagged:
- vlan_dict_entry = {'type': 'vlan'}
- vlan_dict_entry['name'] = str(interface_name) + "." + str(vlan.vlan_id)
- vlan_dict_entry['vlan_link'] = str(interface_name)
- vlan_dict_entry['vlan_id'] = int(vlan.vlan_id)
- vlan_dict_entry['mac_address'] = str(interface_mac)
- if vlan.public:
- vlan_dict_entry["subnets"] = [{"type": "dhcp"}]
- config_arr.append(vlan_dict_entry)
- if (not vlan.tagged) and vlan.public:
- iface_dict_entry["subnets"] = [{"type": "dhcp"}]
-
- # vlan_dict_entry['mtu'] = # TODO, determine override MTU if needed
-
- config_arr.append(iface_dict_entry)
-
- ns_dict = {
- 'type': 'nameserver',
- 'address': ['10.64.0.1', '8.8.8.8']
- }
-
- config_arr.append(ns_dict)
-
- full_dict = {'version': 1, 'config': config_arr}
-
- return full_dict
-
- @classmethod
- def get(cls, booking_id: int, resource_lab_id: str, file_id: int):
- return GeneratedCloudConfig.objects.get(resource_id=resource_lab_id, booking__id=booking_id, file_id=file_id)
-
- def _resource(self):
- return ResourceQuery.get(labid=self.resource_id, lab=self.booking.lab)
-
- # def _get_facts(self):
- # resource = self._resource()
-
- # hostname = self.rconfig.name
- # iface_configs = for_config.interface_configs.all()
-
- def _to_dict(self):
- main_dict = {}
-
- main_dict['users'] = self._serialize_users()
- main_dict['network'] = self._serialize_netconf_v1()
- main_dict['hostname'] = self.rconfig.name
-
- # add first startup commands
- main_dict['runcmd'] = self._serialize_runcmds()
-
- # configure distro default user
- main_dict['system_info'] = self._serialize_sysinfo()
-
- return main_dict
-
- def serialize(self) -> str:
- return yaml.dump(self._to_dict(), width=float("inf"))
-
-
-class APILog(models.Model):
- user = models.ForeignKey(User, on_delete=models.PROTECT)
- call_time = models.DateTimeField(auto_now=True)
- method = models.CharField(null=True, max_length=6)
- endpoint = models.CharField(null=True, max_length=300)
- ip_addr = models.GenericIPAddressField(protocol="both", null=True, unpack_ipv4=False)
- body = JSONField(null=True)
-
- def __str__(self):
- return "Call to {} at {} by {}".format(
- self.endpoint,
- self.call_time,
- self.user.username
- )
-
-
-class AutomationAPIManager:
- @staticmethod
- def serialize_booking(booking):
- sbook = {}
- sbook['id'] = booking.pk
- sbook['owner'] = booking.owner.username
- sbook['collaborators'] = [user.username for user in booking.collaborators.all()]
- sbook['start'] = booking.start
- sbook['end'] = booking.end
- sbook['lab'] = AutomationAPIManager.serialize_lab(booking.lab)
- sbook['purpose'] = booking.purpose
- sbook['resourceBundle'] = AutomationAPIManager.serialize_bundle(booking.resource)
- return sbook
-
- @staticmethod
- def serialize_lab(lab):
- slab = {}
- slab['id'] = lab.pk
- slab['name'] = lab.name
- return slab
-
- @staticmethod
- def serialize_bundle(bundle):
- sbundle = {}
- sbundle['id'] = bundle.pk
- sbundle['resources'] = [
- AutomationAPIManager.serialize_server(server)
- for server in bundle.get_resources()]
- return sbundle
-
- @staticmethod
- def serialize_server(server):
- sserver = {}
- sserver['id'] = server.pk
- sserver['name'] = server.name
- return sserver
-
- @staticmethod
- def serialize_resource_profile(profile):
- sprofile = {}
- sprofile['id'] = profile.pk
- sprofile['name'] = profile.name
- return sprofile
-
- @staticmethod
- def serialize_template(rec_temp_and_count):
- template = rec_temp_and_count[0]
- count = rec_temp_and_count[1]
-
- stemplate = {}
- stemplate['id'] = template.pk
- stemplate['name'] = template.name
- stemplate['count_available'] = count
- stemplate['resourceProfiles'] = [
- AutomationAPIManager.serialize_resource_profile(config.profile)
- for config in template.getConfigs()
- ]
- return stemplate
-
- @staticmethod
- def serialize_image(image):
- simage = {}
- simage['id'] = image.pk
- simage['name'] = image.name
- return simage
-
- @staticmethod
- def serialize_userprofile(up):
- sup = {}
- sup['id'] = up.pk
- sup['username'] = up.user.username
- return sup
-
-
-class Job(models.Model):
- """
- A Job to be performed by the Lab.
-
- The API uses Jobs and Tasks to communicate actions that need to be taken to the Lab
- that is hosting a booking. A booking from a user has an associated Job which tells
- the lab how to configure the hardware, networking, etc to fulfill the booking
- for the user.
- This is the class that is serialized and put into the api
- """
-
- JOB_TYPES = (
- ('BOOK', 'Booking'),
- ('DATA', 'Analytics')
- )
-
- booking = models.OneToOneField(Booking, on_delete=models.CASCADE, null=True)
- status = models.IntegerField(default=JobStatus.NEW)
- complete = models.BooleanField(default=False)
- job_type = models.CharField(
- max_length=4,
- choices=JOB_TYPES,
- default='BOOK'
- )
-
- def to_dict(self):
- d = {}
- for relation in self.get_tasklist():
- if relation.job_key not in d:
- d[relation.job_key] = {}
- d[relation.job_key][relation.task_id] = relation.config.to_dict()
-
- return {"id": self.id, "payload": d}
-
- def get_tasklist(self, status="all"):
- if status != "all":
- return JobTaskQuery.filter(job=self, status=status)
- return JobTaskQuery.filter(job=self)
-
- def is_fulfilled(self):
- """
- If a job has been completed by the lab.
-
- This method should return true if all of the job's tasks are done,
- and false otherwise
- """
- my_tasks = self.get_tasklist()
- for task in my_tasks:
- if task.status != JobStatus.DONE:
- return False
- return True
-
- def get_delta(self, status):
- d = {}
- for relation in self.get_tasklist(status=status):
- if relation.job_key not in d:
- d[relation.job_key] = {}
- d[relation.job_key][relation.task_id] = relation.config.get_delta()
-
- return {"id": self.id, "payload": d}
-
- def to_json(self):
- return json.dumps(self.to_dict())
-
-
-class TaskConfig(models.Model):
- state = models.IntegerField(default=ConfigState.NEW)
-
- keys = set() # TODO: This needs to be an instance variable, not a class variable
- delta_keys_list = models.CharField(max_length=200, default="[]")
-
- @property
- def delta_keys(self):
- return list(set(json.loads(self.delta_keys_list)))
-
- @delta_keys.setter
- def delta_keys(self, keylist):
- self.delta_keys_list = json.dumps(keylist)
-
- def to_dict(self):
- raise NotImplementedError
-
- def get_delta(self):
- raise NotImplementedError
-
- def format_delta(self, config, token):
- delta = {k: config[k] for k in self.delta_keys}
- delta['lab_token'] = token
- return delta
-
- def to_json(self):
- return json.dumps(self.to_dict())
-
- def clear_delta(self):
- self.delta_keys = []
-
- def set(self, *args):
- dkeys = self.delta_keys
- for arg in args:
- if arg in self.keys:
- dkeys.append(arg)
- self.delta_keys = dkeys
-
-
-class BridgeConfig(models.Model):
- """Displays mapping between jumphost interfaces and bridges."""
-
- interfaces = models.ManyToManyField(Interface)
- opnfv_config = models.ForeignKey(OPNFVConfig, on_delete=models.CASCADE)
-
- def to_dict(self):
- d = {}
- hid = ResourceQuery.get(interface__pk=self.interfaces.first().pk).labid
- d[hid] = {}
- for interface in self.interfaces.all():
- d[hid][interface.mac_address] = []
- for vlan in interface.config.all():
- network_role = self.opnfv_model.networks().filter(network=vlan.network)
- bridge = IDFTemplater.bridge_names[network_role.name]
- br_config = {
- "vlan_id": vlan.vlan_id,
- "tagged": vlan.tagged,
- "bridge": bridge
- }
- d[hid][interface.mac_address].append(br_config)
- return d
-
- def to_json(self):
- return json.dumps(self.to_dict())
-
-
-class ActiveUsersConfig(models.Model):
- """
- Task for getting active VPN users
-
- StackStorm needs no information to run this job
- so this task is very bare, but neccessary to fit
- job creation convention.
- """
-
- def clear_delta(self):
- self.delta = '{}'
-
- def get_delta(self):
- return json.loads(self.to_json())
-
- def to_json(self):
- return json.dumps(self.to_dict())
-
- def to_dict(self):
- return {}
-
-
-class OpnfvApiConfig(models.Model):
-
- installer = models.CharField(max_length=200)
- scenario = models.CharField(max_length=300)
- roles = models.ManyToManyField(ResourceOPNFVConfig)
- # pdf and idf are url endpoints, not the actual file
- pdf = models.CharField(max_length=100)
- idf = models.CharField(max_length=100)
- bridge_config = models.OneToOneField(BridgeConfig, on_delete=models.CASCADE, null=True)
- delta = models.TextField()
- opnfv_config = models.ForeignKey(OPNFVConfig, null=True, on_delete=models.SET_NULL)
-
- def to_dict(self):
- d = {}
- if not self.opnfv_config:
- return d
- if self.installer:
- d['installer'] = self.installer
- if self.scenario:
- d['scenario'] = self.scenario
- if self.pdf:
- d['pdf'] = self.pdf
- if self.idf:
- d['idf'] = self.idf
- if self.bridge_config:
- d['bridged_interfaces'] = self.bridge_config.to_dict()
-
- hosts = self.roles.all()
- if hosts.exists():
- d['roles'] = []
- for host in hosts:
- d['roles'].append({
- host.labid: self.opnfv_config.host_opnfv_config.get(
- host_config__pk=host.config.pk
- ).role.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 set_xdf(self, booking, update_delta=True):
- kwargs = {'lab_name': booking.lab.name, 'booking_id': booking.id}
- self.pdf = reverse('get-pdf', kwargs=kwargs)
- self.idf = reverse('get-idf', kwargs=kwargs)
- if update_delta:
- d = json.loads(self.delta)
- d['pdf'] = self.pdf
- d['idf'] = self.idf
- 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):
- return json.loads(self.to_json())
-
-
-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(default="{}")
-
- def to_dict(self):
- d = {}
- d['access_type'] = self.access_type
- d['user'] = self.user.id
- d['revoke'] = self.revoke
- try:
- d['context'] = json.loads(self.context)
- except Exception:
- pass
- return d
-
- def get_delta(self):
- d = json.loads(self.to_json())
- 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 = json.dumps(context)
- d = json.loads(self.delta)
- d['context'] = context
- self.delta = json.dumps(d)
-
-
-class SoftwareConfig(TaskConfig):
- """Handles software installations, such as OPNFV or ONAP."""
-
- 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):
- """Describes the desired configuration of the hardware."""
-
- 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()
-
- keys = set(["id", "image", "power", "hostname", "ipmi_create"])
-
- def to_dict(self):
- return self.get_delta()
-
- def get_delta(self):
- # TODO: grab the GeneratedCloudConfig urls from self.hosthardwarerelation.get_resource()
- return self.format_delta(
- self.hosthardwarerelation.get_resource().get_configuration(self.state),
- self.hosthardwarerelation.lab_token)
-
-
-class NetworkConfig(TaskConfig):
- """Handles network configuration."""
-
- interfaces = models.ManyToManyField(Interface)
- delta = models.TextField()
-
- def to_dict(self):
- d = {}
- hid = self.hostnetworkrelation.resource_id
- d[hid] = {}
- for interface in self.interfaces.all():
- d[hid][interface.mac_address] = []
- if self.state != ConfigState.CLEAN:
- for vlan in interface.config.all():
- # TODO: should this come from the interface?
- # e.g. will different interfaces for different resources need different configs?
- 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):
- d = json.loads(self.to_json())
- d['lab_token'] = self.hostnetworkrelation.lab_token
- return d
-
- def clear_delta(self):
- self.delta = json.dumps(self.to_dict())
- self.save()
-
- def add_interface(self, interface):
- self.interfaces.add(interface)
- d = json.loads(self.delta)
- hid = self.hostnetworkrelation.resource_id
- 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)
-
-
-class SnapshotConfig(TaskConfig):
-
- resource_id = models.CharField(max_length=200, default="default_id")
- image = models.CharField(max_length=200, null=True) # cobbler ID
- dashboard_id = models.IntegerField()
- delta = models.TextField(default="{}")
-
- def to_dict(self):
- d = {}
- if self.host:
- d['host'] = self.host.labid
- if self.image:
- d['image'] = self.image
- d['dashboard_id'] = self.dashboard_id
- return d
-
- def to_json(self):
- return json.dumps(self.to_dict())
-
- def get_delta(self):
- d = json.loads(self.to_json())
- return d
-
- def clear_delta(self):
- self.delta = json.dumps(self.to_dict())
- self.save()
-
- def set_host(self, host):
- self.host = host
- d = json.loads(self.delta)
- d['host'] = host.labid
- 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 clear_image(self):
- self.image = None
- d = json.loads(self.delta)
- d.pop("image", None)
- self.delta = json.dumps(d)
-
- def set_dashboard_id(self, dash):
- self.dashboard_id = dash
- d = json.loads(self.delta)
- d['dashboard_id'] = self.dashboard_id
- self.delta = json.dumps(d)
-
- def save(self, *args, **kwargs):
- if len(ResourceQuery.filter(labid=self.resource_id)) != 1:
- raise ValidationError("resource_id " + str(self.resource_id) + " does not refer to a single resource")
- super().save(*args, **kwargs)
-
-
-def get_task(task_id):
- for taskclass in [AccessRelation, SoftwareRelation, HostHardwareRelation, HostNetworkRelation, SnapshotRelation]:
- 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):
- """
- Relates a Job to a TaskConfig.
-
- superclass that relates a Job to tasks anc maintains information
- like status and messages from the lab
- """
-
- 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="")
-
- job_key = None
-
- 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)
- job_key = "access"
-
- 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)
- job_key = "software"
-
- 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):
- resource_id = models.CharField(max_length=200, default="default_id")
- config = models.OneToOneField(HardwareConfig, on_delete=models.CASCADE)
- job_key = "hardware"
-
- 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)
-
- def save(self, *args, **kwargs):
- if len(ResourceQuery.filter(labid=self.resource_id)) != 1:
- raise ValidationError("resource_id " + str(self.resource_id) + " does not refer to a single resource")
- super().save(*args, **kwargs)
-
- def get_resource(self):
- return ResourceQuery.get(labid=self.resource_id)
-
-
-class HostNetworkRelation(TaskRelation):
- resource_id = models.CharField(max_length=200, default="default_id")
- config = models.OneToOneField(NetworkConfig, on_delete=models.CASCADE)
- job_key = "network"
-
- def type_str(self):
- return "Network Configuration Task"
-
- def delete(self, *args, **kwargs):
- self.config.delete()
- return super(self.__class__, self).delete(*args, **kwargs)
-
- def save(self, *args, **kwargs):
- if len(ResourceQuery.filter(labid=self.resource_id)) != 1:
- raise ValidationError("resource_id " + str(self.resource_id) + " does not refer to a single resource")
- super().save(*args, **kwargs)
-
- def get_resource(self):
- return ResourceQuery.get(labid=self.resource_id)
-
-
-class SnapshotRelation(TaskRelation):
- snapshot = models.ForeignKey(Image, on_delete=models.CASCADE)
- config = models.OneToOneField(SnapshotConfig, on_delete=models.CASCADE)
- job_key = "snapshot"
-
- def type_str(self):
- return "Snapshot 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 ActiveUsersRelation(TaskRelation):
- config = models.OneToOneField(ActiveUsersConfig, on_delete=models.CASCADE)
- job_key = "active users task"
-
- def type_str(self):
- return "Active Users Task"
-
-
-class JobFactory(object):
- """This class creates all the API models (jobs, tasks, etc) needed to fulfill a booking."""
-
- @classmethod
- def reimageHost(cls, new_image, booking, host):
- """Modify an existing job to reimage the given host."""
- job = Job.objects.get(booking=booking)
- # make hardware task new
- hardware_relation = HostHardwareRelation.objects.get(resource_id=host, job=job)
- hardware_relation.config.image = new_image.lab_id
- hardware_relation.config.save()
- hardware_relation.status = JobStatus.NEW
-
- # re-apply networking after host is reset
- net_relation = HostNetworkRelation.objects.get(resource_id=host, job=job)
- net_relation.status = JobStatus.NEW
-
- # re-apply ssh access after host is reset
- for relation in AccessRelation.objects.filter(job=job, config__access_type="ssh"):
- relation.status = JobStatus.NEW
- relation.save()
-
- hardware_relation.save()
- net_relation.save()
-
- @classmethod
- def makeSnapshotTask(cls, image, booking, host):
- relation = SnapshotRelation()
- job = Job.objects.get(booking=booking)
- config = SnapshotConfig.objects.create(dashboard_id=image.id)
-
- relation.job = job
- relation.config = config
- relation.config.save()
- relation.config = relation.config
- relation.snapshot = image
- relation.save()
-
- config.clear_delta()
- config.set_host(host)
- config.save()
-
- @classmethod
- def makeActiveUsersTask(cls):
- """ Append active users task to analytics job """
- config = ActiveUsersConfig()
- relation = ActiveUsersRelation()
- job = Job.objects.get(job_type='DATA')
-
- job.status = JobStatus.NEW
-
- relation.job = job
- relation.config = config
- relation.config.save()
- relation.config = relation.config
- relation.save()
- config.save()
-
- @classmethod
- def makeAnalyticsJob(cls, booking):
- """
- Create the analytics job
-
- This will only run once since there will only be one analytics job.
- All analytics tasks get appended to analytics job.
- """
-
- if len(Job.objects.filter(job_type='DATA')) > 0:
- raise Exception("Cannot have more than one analytics job")
-
- if booking.resource:
- raise Exception("Booking is not marker for analytics job, has resoure")
-
- job = Job()
- job.booking = booking
- job.job_type = 'DATA'
- job.save()
-
- cls.makeActiveUsersTask()
-
- @classmethod
- def makeCompleteJob(cls, booking):
- """Create everything that is needed to fulfill the given booking."""
- resources = booking.resource.get_resources()
- job = None
- try:
- job = Job.objects.get(booking=booking)
- except Exception:
- job = Job.objects.create(status=JobStatus.NEW, booking=booking)
- cls.makeHardwareConfigs(
- resources=resources,
- job=job
- )
- cls.makeNetworkConfigs(
- resources=resources,
- job=job
- )
- cls.makeSoftware(
- booking=booking,
- job=job
- )
- cls.makeGeneratedCloudConfigs(
- resources=resources,
- job=job
- )
- all_users = list(booking.collaborators.all())
- all_users.append(booking.owner)
- cls.makeAccessConfig(
- users=all_users,
- access_type="vpn",
- revoke=False,
- job=job
- )
- for user in all_users:
- try:
- cls.makeAccessConfig(
- users=[user],
- access_type="ssh",
- revoke=False,
- job=job,
- context={
- "key": user.userprofile.ssh_public_key.open().read().decode(encoding="UTF-8"),
- "hosts": [r.labid for r in resources]
- }
- )
- except Exception:
- continue
-
- @classmethod
- def makeGeneratedCloudConfigs(cls, resources=[], job=Job()):
- for res in resources:
- cif = GeneratedCloudConfig.objects.create(resource_id=res.labid, booking=job.booking, rconfig=res.config)
- cif.save()
-
- cif = CloudInitFile.create(priority=0, text=cif.serialize())
- cif.save()
-
- res.config.cloud_init_files.add(cif)
- res.config.save()
-
- @classmethod
- def makeHardwareConfigs(cls, resources=[], job=Job()):
- """
- Create and save HardwareConfig.
-
- Helper function to create the tasks related to
- configuring the hardware
- """
- for res in resources:
- hardware_config = None
- try:
- hardware_config = HardwareConfig.objects.get(relation__resource_id=res.labid)
- except Exception:
- hardware_config = HardwareConfig()
-
- relation = HostHardwareRelation()
- relation.resource_id = res.labid
- relation.job = job
- relation.config = hardware_config
- relation.config.save()
- relation.config = relation.config
- relation.save()
-
- hardware_config.set("id", "image", "hostname", "power", "ipmi_create")
- hardware_config.save()
-
- @classmethod
- def makeAccessConfig(cls, users, access_type, revoke=False, job=Job(), context=False):
- """
- Create and save AccessConfig.
-
- Helper function to create the tasks related to
- configuring the VPN, SSH, etc access for users
- """
- 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()
- if context:
- config.set_context(context)
- config.set_access_type(access_type)
- config.set_revoke(revoke)
- config.set_user(user)
- config.save()
-
- @classmethod
- def makeNetworkConfigs(cls, resources=[], job=Job()):
- """
- Create and save NetworkConfig.
-
- Helper function to create the tasks related to
- configuring the networking
- """
- for res in resources:
- network_config = None
- try:
- network_config = NetworkConfig.objects.get(relation__host=res)
- except Exception:
- network_config = NetworkConfig.objects.create()
-
- relation = HostNetworkRelation()
- relation.resource_id = res.labid
- relation.job = job
- network_config.save()
- relation.config = network_config
- relation.save()
- network_config.clear_delta()
-
- # TODO: use get_interfaces() on resource
- for interface in res.interfaces.all():
- network_config.add_interface(interface)
- network_config.save()
-
- @classmethod
- def make_bridge_config(cls, booking):
- if len(booking.resource.get_resources()) < 2:
- return None
- try:
- jumphost_config = ResourceOPNFVConfig.objects.filter(
- role__name__iexact="jumphost"
- )
- jumphost = ResourceQuery.filter(
- bundle=booking.resource,
- config=jumphost_config.resource_config
- )[0]
- except Exception:
- return None
- br_config = BridgeConfig.objects.create(opnfv_config=booking.opnfv_config)
- for iface in jumphost.interfaces.all():
- br_config.interfaces.add(iface)
- return br_config
-
- @classmethod
- def makeSoftware(cls, booking=None, job=Job()):
- """
- Create and save SoftwareConfig.
-
- Helper function to create the tasks related to
- configuring the desired software, e.g. an OPNFV deployment
- """
- if not booking.opnfv_config:
- return None
-
- opnfv_api_config = OpnfvApiConfig.objects.create(
- opnfv_config=booking.opnfv_config,
- installer=booking.opnfv_config.installer.name,
- scenario=booking.opnfv_config.scenario.name,
- bridge_config=cls.make_bridge_config(booking)
- )
-
- opnfv_api_config.set_xdf(booking, False)
- opnfv_api_config.save()
-
- for host in booking.resource.get_resources():
- opnfv_api_config.roles.add(host)
- software_config = SoftwareConfig.objects.create(opnfv=opnfv_api_config)
- software_relation = SoftwareRelation.objects.create(job=job, config=software_config)
- return software_relation
-
-
-JOB_TASK_CLASSLIST = [
- HostHardwareRelation,
- AccessRelation,
- HostNetworkRelation,
- SoftwareRelation,
- SnapshotRelation,
- ActiveUsersRelation
-]
-
-
-class JobTaskQuery(AbstractModelQuery):
- model_list = JOB_TASK_CLASSLIST
diff --git a/src/api/serializers/__init__.py b/src/api/serializers/__init__.py
deleted file mode 100644
index e0408fa..0000000
--- a/src/api/serializers/__init__.py
+++ /dev/null
@@ -1,8 +0,0 @@
-##############################################################################
-# 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/src/api/serializers/booking_serializer.py b/src/api/serializers/booking_serializer.py
deleted file mode 100644
index 993eb22..0000000
--- a/src/api/serializers/booking_serializer.py
+++ /dev/null
@@ -1,173 +0,0 @@
-##############################################################################
-# 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 (
- ResourceConfiguration,
- CpuProfile,
- DiskProfile,
- InterfaceProfile,
- RamProfile,
- Image,
- Interface
-)
-
-
-class BookingField(serializers.Field):
-
- def to_representation(self, booking):
- """
- Take 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] = ResourceConfiguration.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):
- """
- Take in a dictionary of primitives, and return 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):
- """Take 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/src/api/serializers/old_serializers.py b/src/api/serializers/old_serializers.py
deleted file mode 100644
index 0944881..0000000
--- a/src/api/serializers/old_serializers.py
+++ /dev/null
@@ -1,21 +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
-
-
-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/src/api/tests/__init__.py b/src/api/tests/__init__.py
deleted file mode 100644
index 2435a9f..0000000
--- a/src/api/tests/__init__.py
+++ /dev/null
@@ -1,8 +0,0 @@
-##############################################################################
-# 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
-##############################################################################
diff --git a/src/api/tests/test_models_unittest.py b/src/api/tests/test_models_unittest.py
deleted file mode 100644
index 2dee29b..0000000
--- a/src/api/tests/test_models_unittest.py
+++ /dev/null
@@ -1,271 +0,0 @@
-# Copyright (c) 2019 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 api.models import (
- Job,
- JobStatus,
- JobFactory,
- HostNetworkRelation,
- HostHardwareRelation,
- SoftwareRelation,
- AccessConfig,
- SnapshotRelation
-)
-
-from resource_inventory.models import (
- OPNFVRole,
- HostProfile,
- ConfigState,
-)
-
-from django.test import TestCase, Client
-
-from dashboard.testing_utils import (
- make_host,
- make_user,
- make_user_profile,
- make_lab,
- make_installer,
- make_image,
- make_scenario,
- make_os,
- make_complete_host_profile,
- make_booking,
-)
-
-
-class ValidBookingCreatesValidJob(TestCase):
- @classmethod
- def setUpTestData(cls):
- cls.user = make_user(False, username="newtestuser", password="testpassword")
- cls.userprofile = make_user_profile(cls.user)
- cls.lab = make_lab()
-
- cls.host_profile = make_complete_host_profile(cls.lab)
- cls.scenario = make_scenario()
- cls.installer = make_installer([cls.scenario])
- os = make_os([cls.installer])
- cls.image = make_image(cls.lab, 1, cls.user, os, cls.host_profile)
- for i in range(30):
- make_host(cls.host_profile, cls.lab, name="host" + str(i), labid="host" + str(i))
- cls.client = Client()
-
- def setUp(self):
- self.booking, self.compute_hostnames, self.jump_hostname = self.create_multinode_generic_booking()
-
- def create_multinode_generic_booking(self):
- topology = {}
-
- compute_hostnames = ["cmp01", "cmp02", "cmp03"]
-
- host_type = HostProfile.objects.first()
-
- universal_networks = [
- {"name": "public", "tagged": False, "public": True},
- {"name": "admin", "tagged": True, "public": False}]
- compute_networks = [{"name": "private", "tagged": True, "public": False}]
- jumphost_networks = [{"name": "external", "tagged": True, "public": True}]
-
- # generate a bunch of extra networks
- for i in range(10):
- net = {"tagged": False, "public": False}
- net["name"] = "net" + str(i)
- universal_networks.append(net)
-
- jumphost_info = {
- "type": host_type,
- "role": OPNFVRole.objects.get_or_create(name="Jumphost")[0],
- "nets": self.make_networks(host_type, jumphost_networks + universal_networks),
- "image": self.image
- }
- topology["jump"] = jumphost_info
-
- for hostname in compute_hostnames:
- host_info = {
- "type": host_type,
- "role": OPNFVRole.objects.get_or_create(name="Compute")[0],
- "nets": self.make_networks(host_type, compute_networks + universal_networks),
- "image": self.image
- }
- topology[hostname] = host_info
-
- booking = make_booking(
- owner=self.user,
- lab=self.lab,
- topology=topology,
- installer=self.installer,
- scenario=self.scenario
- )
-
- if not booking.resource:
- raise Exception("Booking does not have a resource when trying to pass to makeCompleteJob")
- return booking, compute_hostnames, "jump"
-
- def make_networks(self, hostprofile, nets):
- """
- Distribute nets accross hostprofile's interfaces.
-
- returns a 2D array
- """
- network_struct = []
- count = hostprofile.interfaceprofile.all().count()
- for i in range(count):
- network_struct.append([])
- while (nets):
- index = len(nets) % count
- network_struct[index].append(nets.pop())
-
- return network_struct
-
- #################################################################
- # Complete Job Tests
- #################################################################
-
- def test_complete_job_makes_access_configs(self):
- JobFactory.makeCompleteJob(self.booking)
- job = Job.objects.get(booking=self.booking)
- self.assertIsNotNone(job)
-
- access_configs = AccessConfig.objects.filter(accessrelation__job=job)
-
- vpn_configs = access_configs.filter(access_type="vpn")
- ssh_configs = access_configs.filter(access_type="ssh")
-
- self.assertFalse(AccessConfig.objects.exclude(access_type__in=["vpn", "ssh"]).exists())
-
- all_users = list(self.booking.collaborators.all())
- all_users.append(self.booking.owner)
-
- for user in all_users:
- self.assertTrue(vpn_configs.filter(user=user).exists())
- self.assertTrue(ssh_configs.filter(user=user).exists())
-
- def test_complete_job_makes_network_configs(self):
- JobFactory.makeCompleteJob(self.booking)
- job = Job.objects.get(booking=self.booking)
- self.assertIsNotNone(job)
-
- booking_hosts = self.booking.resource.hosts.all()
-
- netrelations = HostNetworkRelation.objects.filter(job=job)
- netconfigs = [r.config for r in netrelations]
-
- netrelation_hosts = [r.host for r in netrelations]
-
- for config in netconfigs:
- for interface in config.interfaces.all():
- self.assertTrue(interface.host in booking_hosts)
-
- # if no interfaces are referenced that shouldn't have vlans,
- # and no vlans exist outside those accounted for in netconfigs,
- # then the api is faithfully representing networks
- # as netconfigs reference resource_inventory models directly
-
- # this test relies on the assumption that
- # every interface is configured, whether it does or does not have vlans
- # if this is not true, the test fails
-
- for host in booking_hosts:
- self.assertTrue(host in netrelation_hosts)
- relation = HostNetworkRelation.objects.filter(job=job).get(host=host)
-
- # do 2 direction matching that interfaces are one to one
- config = relation.config
- for interface in config.interfaces.all():
- self.assertTrue(interface in host.interfaces)
- for interface in host.interfaces.all():
- self.assertTrue(interface in config.interfaces)
-
- for host in netrelation_hosts:
- self.assertTrue(host in booking_hosts)
-
- def test_complete_job_makes_hardware_configs(self):
- JobFactory.makeCompleteJob(self.booking)
- job = Job.objects.get(booking=self.booking)
- self.assertIsNotNone(job)
-
- hardware_relations = HostHardwareRelation.objects.filter(job=job)
-
- job_hosts = [r.host for r in hardware_relations]
-
- booking_hosts = self.booking.resource.hosts.all()
-
- self.assertEqual(len(booking_hosts), len(job_hosts))
-
- for relation in hardware_relations:
- self.assertTrue(relation.host in booking_hosts)
- self.assertEqual(relation.status, JobStatus.NEW)
- config = relation.config
- host = relation.host
- self.assertEqual(config.get_delta()["hostname"], host.template.resource.name)
-
- def test_complete_job_makes_software_configs(self):
- JobFactory.makeCompleteJob(self.booking)
- job = Job.objects.get(booking=self.booking)
- self.assertIsNotNone(job)
-
- srelation = SoftwareRelation.objects.filter(job=job).first()
- self.assertIsNotNone(srelation)
-
- sconfig = srelation.config
- self.assertIsNotNone(sconfig)
-
- oconfig = sconfig.opnfv
- self.assertIsNotNone(oconfig)
-
- # not onetoone in models, but first() is safe here based on how ConfigBundle and a matching OPNFVConfig are created
- # this should, however, be made explicit
- self.assertEqual(oconfig.installer, self.booking.config_bundle.opnfv_config.first().installer.name)
- self.assertEqual(oconfig.scenario, self.booking.config_bundle.opnfv_config.first().scenario.name)
-
- for host in oconfig.roles.all():
- role_name = host.config.host_opnfv_config.first().role.name
- if str(role_name).lower() == "jumphost":
- self.assertEqual(host.template.resource.name, self.jump_hostname)
- elif str(role_name).lower() == "compute":
- self.assertTrue(host.template.resource.name in self.compute_hostnames)
- else:
- self.fail(msg="Host with non-configured role name related to job: " + str(role_name))
-
- def test_make_snapshot_task(self):
- host = self.booking.resource.hosts.first()
- image = make_image(self.lab, -1, None, None, host.profile)
-
- Job.objects.create(booking=self.booking)
-
- JobFactory.makeSnapshotTask(image, self.booking, host)
-
- snap_relation = SnapshotRelation.objects.get(job=self.booking.job)
- config = snap_relation.config
- self.assertEqual(host.id, config.host.id)
- self.assertEqual(config.dashboard_id, image.id)
- self.assertEqual(snap_relation.snapshot.id, image.id)
-
- def test_make_hardware_configs(self):
- hosts = self.booking.resource.hosts.all()
- job = Job.objects.create(booking=self.booking)
- JobFactory.makeHardwareConfigs(hosts=hosts, job=job)
-
- hardware_relations = HostHardwareRelation.objects.filter(job=job)
-
- self.assertEqual(hardware_relations.count(), hosts.count())
-
- host_set = set([h.id for h in hosts])
-
- for relation in hardware_relations:
- try:
- host_set.remove(relation.host.id)
- except KeyError:
- self.fail("Hardware Relation/Config not created for host " + str(relation.host))
- # TODO: ConfigState needs to be fixed in factory methods
- relation.config.state = ConfigState.NEW
- self.assertEqual(relation.config.get_delta()["power"], "on")
- self.assertTrue(relation.config.get_delta()["ipmi_create"])
- # TODO: the rest of hwconf attrs
-
- self.assertEqual(len(host_set), 0)
diff --git a/src/api/urls.py b/src/api/urls.py
deleted file mode 100644
index cbb453c..0000000
--- a/src/api/urls.py
+++ /dev/null
@@ -1,108 +0,0 @@
-##############################################################################
-# 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
-##############################################################################
-
-
-"""
-laas_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 django.urls import path
-
-from api.views import (
- lab_profile,
- lab_status,
- lab_inventory,
- lab_downtime,
- specific_job,
- specific_task,
- new_jobs,
- current_jobs,
- done_jobs,
- update_host_bmc,
- lab_host,
- get_pdf,
- get_idf,
- lab_users,
- lab_user,
- GenerateTokenView,
- analytics_job,
- user_bookings,
- specific_booking,
- extend_booking,
- make_booking,
- list_labs,
- all_users,
- images_for_template,
- available_templates,
- resource_ci_metadata,
- resource_ci_userdata,
- resource_ci_userdata_directory,
- all_images,
- all_opsyss,
- single_image,
- single_opsys,
- create_ci_file,
- booking_details,
-)
-
-urlpatterns = [
- path('labs/<slug:lab_name>/opsys/<slug:opsys_id>', single_opsys),
- path('labs/<slug:lab_name>/image/<slug:image_id>', single_image),
- path('labs/<slug:lab_name>/opsys', all_opsyss),
- path('labs/<slug:lab_name>/image', all_images),
- 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>/downtime', lab_downtime),
- path('labs/<slug:lab_name>/hosts/<slug:host_id>', lab_host),
- path('labs/<slug:lab_name>/hosts/<slug:host_id>/bmc', update_host_bmc),
- path('labs/<slug:lab_name>/booking/<int:booking_id>/pdf', get_pdf, name="get-pdf"),
- path('labs/<slug:lab_name>/booking/<int:booking_id>/idf', get_idf, name="get-idf"),
- 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/<int:job_id>/cidata/<slug:resource_id>/user-data', resource_ci_userdata_directory, name="specific-user-data"),
- path('labs/<slug:lab_name>/jobs/<int:job_id>/cidata/<slug:resource_id>/meta-data', resource_ci_metadata, name="specific-meta-data"),
- path('labs/<slug:lab_name>/jobs/<int:job_id>/cidata/<slug:resource_id>/<int:file_id>/user-data', resource_ci_userdata, name="user-data-dir"),
- 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),
- path('labs/<slug:lab_name>/jobs/getByType/DATA', analytics_job),
- path('labs/<slug:lab_name>/users', lab_users),
- path('labs/<slug:lab_name>/users/<int:user_id>', lab_user),
-
- path('booking', user_bookings),
- path('booking/<int:booking_id>', specific_booking),
- path('booking/<int:booking_id>/extendBooking/<int:days>', extend_booking),
- path('booking/makeBooking', make_booking),
- path('booking/<int:booking_id>/details', booking_details),
-
- path('resource_inventory/availableTemplates', available_templates),
- path('resource_inventory/<int:template_id>/images', images_for_template),
-
- path('resource_inventory/cloud/create', create_ci_file),
-
- path('users', all_users),
- path('labs', list_labs),
-
- url(r'^token$', GenerateTokenView.as_view(), name='generate_token'),
-]
diff --git a/src/api/views.py b/src/api/views.py
deleted file mode 100644
index d5966ed..0000000
--- a/src/api/views.py
+++ /dev/null
@@ -1,756 +0,0 @@
-##############################################################################
-# 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
-##############################################################################
-
-import json
-import math
-import traceback
-import sys
-from datetime import timedelta
-
-from django.contrib.auth.decorators import login_required
-from django.shortcuts import redirect, get_object_or_404
-from django.utils.decorators import method_decorator
-from django.utils import timezone
-from django.views import View
-from django.http import HttpResponseNotFound
-from django.http.response import JsonResponse, HttpResponse
-from rest_framework import viewsets
-from rest_framework.authtoken.models import Token
-from django.views.decorators.csrf import csrf_exempt
-from django.core.exceptions import ObjectDoesNotExist
-from django.db.models import Q
-
-from api.serializers.booking_serializer import BookingSerializer
-from api.serializers.old_serializers import UserSerializer
-from api.forms import DowntimeForm
-from account.models import UserProfile, Lab
-from booking.models import Booking
-from booking.quick_deployer import create_from_API
-from api.models import LabManagerTracker, get_task, Job, AutomationAPIManager, APILog, GeneratedCloudConfig
-from notifier.manager import NotificationHandler
-from analytics.models import ActiveVPNUser
-from resource_inventory.models import (
- Image,
- Opsys,
- CloudInitFile,
- ResourceQuery,
- ResourceTemplate,
-)
-
-import yaml
-import uuid
-from deepmerge import Merger
-
-"""
-API views.
-
-All functions return a Json blob
-Most functions that deal with info from a specific lab (tasks, host info)
-requires the Lab auth token.
- for example, curl -H auth-token:mylabsauthtoken url
-
-Most functions let you GET or POST to the same endpoint, and
-the correct thing will happen
-"""
-
-
-class BookingViewSet(viewsets.ModelViewSet):
- queryset = Booking.objects.all()
- serializer_class = BookingSerializer
- filter_fields = ('resource', 'id')
-
-
-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):
- user = self.request.user
- token, created = Token.objects.get_or_create(user=user)
- if not created:
- 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)
-
-
-@csrf_exempt
-def lab_host(request, lab_name="", host_id=""):
- lab_token = request.META.get('HTTP_AUTH_TOKEN')
- lab_manager = LabManagerTracker.get(lab_name, lab_token)
- if request.method == "GET":
- return JsonResponse(lab_manager.get_host(host_id), safe=False)
- if request.method == "POST":
- return JsonResponse(lab_manager.update_host(host_id, request.POST), safe=False)
-
-# API extension for Cobbler integration
-
-
-def all_images(request, lab_name=""):
- a = []
- for i in Image.objects.all():
- a.append(i.serialize())
- return JsonResponse(a, safe=False)
-
-
-def all_opsyss(request, lab_name=""):
- a = []
- for opsys in Opsys.objects.all():
- a.append(opsys.serialize())
-
- return JsonResponse(a, safe=False)
-
-
-@csrf_exempt
-def single_image(request, lab_name="", image_id=""):
- lab_token = request.META.get('HTTP_AUTH_TOKEN')
- lab_manager = LabManagerTracker.get(lab_name, lab_token)
- img = lab_manager.get_image(image_id).first()
-
- if request.method == "GET":
- if not img:
- return HttpResponse(status=404)
- return JsonResponse(img.serialize(), safe=False)
-
- if request.method == "POST":
- # get POST data
- data = json.loads(request.body.decode('utf-8'))
- if img:
- img.update(data)
- else:
- # append lab name and the ID from the URL
- data['from_lab_id'] = lab_name
- data['lab_id'] = image_id
-
- # create and save a new Image object
- img = Image.new_from_data(data)
-
- img.save()
-
- # indicate success in response
- return HttpResponse(status=200)
- return HttpResponse(status=405)
-
-
-@csrf_exempt
-def single_opsys(request, lab_name="", opsys_id=""):
- lab_token = request.META.get('HTTP_AUTH_TOKEN')
- lab_manager = LabManagerTracker.get(lab_name, lab_token)
- opsys = lab_manager.get_opsys(opsys_id).first()
-
- if request.method == "GET":
- if not opsys:
- return HttpResponse(status=404)
- return JsonResponse(opsys.serialize(), safe=False)
-
- if request.method == "POST":
- data = json.loads(request.body.decode('utf-8'))
- if opsys:
- opsys.update(data)
- else:
- # only name, available, and obsolete are needed to create an Opsys
- # other fields are derived from the URL parameters
- data['from_lab_id'] = lab_name
- data['lab_id'] = opsys_id
- opsys = Opsys.new_from_data(data)
-
- opsys.save()
- return HttpResponse(status=200)
- return HttpResponse(status=405)
-
-# end API extension
-
-
-def get_pdf(request, lab_name="", booking_id=""):
- lab_token = request.META.get('HTTP_AUTH_TOKEN')
- lab_manager = LabManagerTracker.get(lab_name, lab_token)
- return HttpResponse(lab_manager.get_pdf(booking_id), content_type="text/plain")
-
-
-def get_idf(request, lab_name="", booking_id=""):
- lab_token = request.META.get('HTTP_AUTH_TOKEN')
- lab_manager = LabManagerTracker.get(lab_name, lab_token)
- return HttpResponse(lab_manager.get_idf(booking_id), content_type="text/plain")
-
-
-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_users(request, lab_name=""):
- lab_token = request.META.get('HTTP_AUTH_TOKEN')
- lab_manager = LabManagerTracker.get(lab_name, lab_token)
- return HttpResponse(lab_manager.get_users(), content_type="text/plain")
-
-
-def lab_user(request, lab_name="", user_id=-1):
- lab_token = request.META.get('HTTP_AUTH_TOKEN')
- lab_manager = LabManagerTracker.get(lab_name, lab_token)
- return HttpResponse(lab_manager.get_user(user_id), content_type="text/plain")
-
-
-@csrf_exempt
-def update_host_bmc(request, lab_name="", host_id=""):
- lab_token = request.META.get('HTTP_AUTH_TOKEN')
- lab_manager = LabManagerTracker.get(lab_name, lab_token)
- if request.method == "POST":
- # update / create RemoteInfo for host
- return JsonResponse(
- lab_manager.update_host_remote_info(request.POST, host_id),
- 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')
- if 'lab_token' in request.POST:
- task.lab_token = request.POST.get('lab_token')
- task.save()
- NotificationHandler.task_updated(task)
- d = {}
- d['task'] = task.config.get_delta()
- m = {}
- m['status'] = task.status
- m['job'] = str(task.job)
- m['message'] = task.message
- d['meta'] = m
- return JsonResponse(d, safe=False)
- elif request.method == "GET":
- return JsonResponse(get_task(task_id).config.get_delta())
-
-
-@csrf_exempt
-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)
-
-
-@csrf_exempt
-def resource_ci_userdata(request, lab_name="", job_id="", resource_id="", file_id=0):
- # lab_token = request.META.get('HTTP_AUTH_TOKEN')
- # lab_manager = LabManagerTracker.get(lab_name, lab_token)
-
- # job = lab_manager.get_job(job_id)
- Job.objects.get(id=job_id) # verify a valid job was given, even if we don't use it
-
- cifile = None
- try:
- cifile = CloudInitFile.objects.get(id=file_id)
- except ObjectDoesNotExist:
- return HttpResponseNotFound("Could not find a matching resource by id " + str(resource_id))
-
- text = cifile.text
-
- prepended_text = "#cloud-config\n"
- # mstrat = CloudInitFile.merge_strategy()
- # prepended_text = prepended_text + yaml.dump({"merge_strategy": mstrat}) + "\n"
- # print("in cloudinitfile create")
- text = prepended_text + text
- cloud_dict = {
- "datasource": {
- "None": {
- "metadata": {
- "instance-id": str(uuid.uuid4())
- },
- "userdata_raw": text,
- },
- },
- "datasource_list": ["None"],
- }
-
- return HttpResponse(yaml.dump(cloud_dict, width=float("inf")), status=200)
-
-
-@csrf_exempt
-def resource_ci_metadata(request, lab_name="", job_id="", resource_id="", file_id=0):
- return HttpResponse("#cloud-config", status=200)
-
-
-@csrf_exempt
-def resource_ci_userdata_directory(request, lab_name="", job_id="", resource_id=""):
- # files = [{"id": file.file_id, "priority": file.priority} for file in CloudInitFile.objects.filter(job__id=job_id, resource_id=resource_id).order_by("priority").all()]
- resource = ResourceQuery.get(labid=resource_id, lab=Lab.objects.get(name=lab_name))
- files = resource.config.cloud_init_files
- files = [{"id": file.id, "priority": file.priority} for file in files.order_by("priority").all()]
-
- d = {}
-
- merge_failures = []
-
- merger = Merger(
- [
- (list, ["append"]),
- (dict, ["merge"]),
- ],
- ["override"], # fallback
- ["override"], # if types conflict (shouldn't happen in CI, but handle case)
- )
-
- for f in resource.config.cloud_init_files.order_by("priority").all():
- try:
- other_dict = yaml.safe_load(f.text)
- if not (type(d) is dict):
- raise Exception("CI file was valid yaml but was not a dict")
-
- merger.merge(d, other_dict)
- except Exception as e:
- # if fail to merge, then just skip
- print("Failed to merge file in, as it had invalid content:", f.id)
- print("File text was:")
- print(f.text)
- merge_failures.append({f.id: str(e)})
-
- if len(merge_failures) > 0:
- d['merge_failures'] = merge_failures
-
- file = CloudInitFile.create(text=yaml.dump(d, width=float("inf")), priority=0)
-
- return HttpResponse(json.dumps([{"id": file.id, "priority": file.priority}]), status=200)
-
-
-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)
-
-
-@csrf_exempt
-def analytics_job(request, lab_name=""):
- """ returns all jobs with type booking"""
- lab_token = request.META.get('HTTP_AUTH_TOKEN')
- lab_manager = LabManagerTracker.get(lab_name, lab_token)
- if request.method == "GET":
- return JsonResponse(lab_manager.get_analytics_job(), safe=False)
- if request.method == "POST":
- users = json.loads(request.body.decode('utf-8'))['active_users']
- try:
- ActiveVPNUser.create(lab_name, users)
- except ObjectDoesNotExist:
- return JsonResponse('Lab does not exist!', safe=False)
- return HttpResponse(status=200)
- return HttpResponse(status=405)
-
-
-def lab_downtime(request, lab_name=""):
- lab_token = request.META.get('HTTP_AUTH_TOKEN')
- lab_manager = LabManagerTracker.get(lab_name, lab_token)
- if request.method == "GET":
- return JsonResponse(lab_manager.get_downtime_json())
- if request.method == "POST":
- return post_lab_downtime(request, lab_manager)
- if request.method == "DELETE":
- return delete_lab_downtime(lab_manager)
- return HttpResponse(status=405)
-
-
-def post_lab_downtime(request, lab_manager):
- current_downtime = lab_manager.get_downtime()
- if current_downtime.exists():
- return JsonResponse({"error": "Lab is already in downtime"}, status=422)
- form = DowntimeForm(request.POST)
- if form.is_valid():
- return JsonResponse(lab_manager.create_downtime(form))
- else:
- return JsonResponse(form.errors.get_json_data(), status=400)
-
-
-def delete_lab_downtime(lab_manager):
- current_downtime = lab_manager.get_downtime()
- if current_downtime.exists():
- dt = current_downtime.first()
- dt.end = timezone.now()
- dt.save()
- return JsonResponse(lab_manager.get_downtime_json(), safe=False)
- else:
- return JsonResponse({"error": "Lab is not in downtime"}, status=422)
-
-
-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)
-
-
-def auth_and_log(request, endpoint):
- """
- Function to authenticate an API user and log info
- in the API log model. This is to keep record of
- all calls to the dashboard
- """
- user_token = request.META.get('HTTP_AUTH_TOKEN')
- response = None
-
- if user_token is None:
- return HttpResponse('Unauthorized', status=401)
-
- try:
- token = Token.objects.get(key=user_token)
- except Token.DoesNotExist:
- token = None
- # Added logic to detect malformed token
- if len(str(user_token)) != 40:
- response = HttpResponse('Malformed Token', status=401)
- else:
- response = HttpResponse('Unauthorized', status=401)
-
- x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
- if x_forwarded_for:
- ip = x_forwarded_for.split(',')[0]
- else:
- ip = request.META.get('REMOTE_ADDR')
-
- body = None
-
- if request.method in ['POST', 'PUT']:
- try:
- body = json.loads(request.body.decode('utf-8')),
- except Exception:
- response = HttpResponse('Invalid Request Body', status=400)
-
- APILog.objects.create(
- user=token.user,
- call_time=timezone.now(),
- method=request.method,
- endpoint=endpoint,
- body=body,
- ip_addr=ip
- )
-
- if response:
- return response
- else:
- return token
-
-
-"""
-Booking API Views
-"""
-
-
-def user_bookings(request):
- token = auth_and_log(request, 'booking')
-
- if isinstance(token, HttpResponse):
- return token
-
- bookings = Booking.objects.filter(owner=token.user, end__gte=timezone.now())
- output = [AutomationAPIManager.serialize_booking(booking)
- for booking in bookings]
- return JsonResponse(output, safe=False)
-
-
-@csrf_exempt
-def specific_booking(request, booking_id=""):
- token = auth_and_log(request, 'booking/{}'.format(booking_id))
-
- if isinstance(token, HttpResponse):
- return token
-
- booking = get_object_or_404(Booking, pk=booking_id, owner=token.user)
- if request.method == "GET":
- sbooking = AutomationAPIManager.serialize_booking(booking)
- return JsonResponse(sbooking, safe=False)
-
- if request.method == "DELETE":
-
- if booking.end < timezone.now():
- return HttpResponse("Booking already over", status=400)
-
- booking.end = timezone.now()
- booking.save()
- return HttpResponse("Booking successfully cancelled")
-
-
-@csrf_exempt
-def extend_booking(request, booking_id="", days=""):
- token = auth_and_log(request, 'booking/{}/extendBooking/{}'.format(booking_id, days))
-
- if isinstance(token, HttpResponse):
- return token
-
- booking = get_object_or_404(Booking, pk=booking_id, owner=token.user)
-
- if booking.end < timezone.now():
- return HttpResponse("This booking is already over, cannot extend")
-
- if days > 30:
- return HttpResponse("Cannot extend a booking longer than 30 days")
-
- if booking.ext_count == 0:
- return HttpResponse("Booking has already been extended 2 times, cannot extend again")
-
- booking.end += timedelta(days=days)
- booking.ext_count -= 1
- booking.save()
-
- return HttpResponse("Booking successfully extended")
-
-
-@csrf_exempt
-def make_booking(request):
- token = auth_and_log(request, 'booking/makeBooking')
-
- if isinstance(token, HttpResponse):
- return token
-
- try:
- booking = create_from_API(request.body, token.user)
-
- except Exception:
- finalTrace = ''
- exc_type, exc_value, exc_traceback = sys.exc_info()
- for i in traceback.format_exception(exc_type, exc_value, exc_traceback):
- finalTrace += '<br>' + i.strip()
- return HttpResponse(finalTrace, status=400)
-
- sbooking = AutomationAPIManager.serialize_booking(booking)
- return JsonResponse(sbooking, safe=False)
-
-
-"""
-Resource Inventory API Views
-"""
-
-
-def available_templates(request):
- token = auth_and_log(request, 'resource_inventory/availableTemplates')
-
- if isinstance(token, HttpResponse):
- return token
-
- # get available templates
- # mirrors MultipleSelectFilter Widget
- avt = []
- for lab in Lab.objects.all():
- for template in ResourceTemplate.objects.filter(Q(owner=token.user) | Q(public=True), lab=lab, temporary=False):
- available_resources = lab.get_available_resources()
- required_resources = template.get_required_resources()
- least_available = 100
-
- for resource, count_required in required_resources.items():
- try:
- curr_count = math.floor(available_resources[str(resource)] / count_required)
- if curr_count < least_available:
- least_available = curr_count
- except KeyError:
- least_available = 0
-
- if least_available > 0:
- avt.append((template, least_available))
-
- savt = [AutomationAPIManager.serialize_template(temp)
- for temp in avt]
-
- return JsonResponse(savt, safe=False)
-
-
-def images_for_template(request, template_id=""):
- _ = auth_and_log(request, 'resource_inventory/{}/images'.format(template_id))
-
- template = get_object_or_404(ResourceTemplate, pk=template_id)
- images = [AutomationAPIManager.serialize_image(config.image)
- for config in template.getConfigs()]
- return JsonResponse(images, safe=False)
-
-
-"""
-User API Views
-"""
-
-
-def all_users(request):
- token = auth_and_log(request, 'users')
-
- if token is None:
- return HttpResponse('Unauthorized', status=401)
-
- users = [AutomationAPIManager.serialize_userprofile(up)
- for up in UserProfile.objects.filter(public_user=True)]
-
- return JsonResponse(users, safe=False)
-
-
-def create_ci_file(request):
- token = auth_and_log(request, 'booking/makeCloudConfig')
-
- if isinstance(token, HttpResponse):
- return token
-
- try:
- cconf = request.body
- d = yaml.load(cconf)
- if not (type(d) is dict):
- raise Exception()
-
- cconf = CloudInitFile.create(text=cconf, priority=CloudInitFile.objects.count())
-
- return JsonResponse({"id": cconf.id})
- except Exception:
- return JsonResponse({"error": "Provided config file was not valid yaml or was not a dict at the top level"})
-
-
-"""
-Lab API Views
-"""
-
-
-def list_labs(request):
- lab_list = []
- for lab in Lab.objects.all():
- lab_info = {
- 'name': lab.name,
- 'username': lab.lab_user.username,
- 'status': lab.status,
- 'project': lab.project,
- 'description': lab.description,
- 'location': lab.location,
- 'info': lab.lab_info_link,
- 'email': lab.contact_email,
- 'phone': lab.contact_phone
- }
- lab_list.append(lab_info)
-
- return JsonResponse(lab_list, safe=False)
-
-
-"""
-Booking Details API Views
-"""
-
-
-def booking_details(request, booking_id=""):
- token = auth_and_log(request, 'booking/{}/details'.format(booking_id))
-
- if isinstance(token, HttpResponse):
- return token
-
- booking = get_object_or_404(Booking, pk=booking_id, owner=token.user)
-
- # overview
- overview = {
- 'username': GeneratedCloudConfig._normalize_username(None, str(token.user)),
- 'purpose': booking.purpose,
- 'project': booking.project,
- 'start_time': booking.start,
- 'end_time': booking.end,
- 'pod_definitions': booking.resource.template,
- 'lab': booking.lab
- }
-
- # deployment progress
- task_list = []
- for task in booking.job.get_tasklist():
- task_info = {
- 'name': str(task),
- 'status': 'DONE',
- 'lab_response': 'No response provided (yet)'
- }
- if task.status < 100:
- task_info['status'] = 'PENDING'
- elif task.status < 200:
- task_info['status'] = 'IN PROGRESS'
-
- if task.message:
- if task.type_str == "Access Task" and request.user.id != task.config.user.id:
- task_info['lab_response'] = '--secret--'
- else:
- task_info['lab_response'] = str(task.message)
- task_list.append(task_info)
-
- # pods
- pod_list = []
- for host in booking.resource.get_resources():
- pod_info = {
- 'hostname': host.config.name,
- 'machine': host.name,
- 'role': '',
- 'is_headnode': host.config.is_head_node,
- 'image': host.config.image,
- 'ram': {'amount': str(host.profile.ramprofile.first().amount) + 'G', 'channels': host.profile.ramprofile.first().channels},
- 'cpu': {'arch': host.profile.cpuprofile.first().architecture, 'cores': host.profile.cpuprofile.first().cores, 'sockets': host.profile.cpuprofile.first().cpus},
- 'disk': {'size': str(host.profile.storageprofile.first().size) + 'GiB', 'type': host.profile.storageprofile.first().media_type, 'mount_point': host.profile.storageprofile.first().name},
- 'interfaces': [],
- }
- try:
- pod_info['role'] = host.template.opnfvRole
- except Exception:
- pass
- for intprof in host.profile.interfaceprofile.all():
- int_info = {
- 'name': intprof.name,
- 'speed': intprof.speed
- }
- pod_info['interfaces'].append(int_info)
- pod_list.append(pod_info)
-
- # diagnostic info
- diagnostic_info = {
- 'job_id': booking.job.id,
- 'ci_files': '',
- 'pods': []
- }
- for host in booking.resource.get_resources():
- pod = {
- 'host': host.name,
- 'configs': [],
-
- }
- for ci_file in host.config.cloud_init_files.all():
- ci_info = {
- 'id': ci_file.id,
- 'text': ci_file.text
- }
- pod['configs'].append(ci_info)
- diagnostic_info['pods'].append(pod)
-
- details = {
- 'overview': overview,
- 'deployment_progress': task_list,
- 'pods': pod_list,
- 'diagnostic_info': diagnostic_info,
- 'pdf': booking.pdf
- }
- return JsonResponse(str(details), safe=False)
diff --git a/src/booking/__init__.py b/src/booking/__init__.py
deleted file mode 100644
index b6fef6c..0000000
--- a/src/booking/__init__.py
+++ /dev/null
@@ -1,8 +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/src/booking/admin.py b/src/booking/admin.py
deleted file mode 100644
index 162777e..0000000
--- a/src/booking/admin.py
+++ /dev/null
@@ -1,16 +0,0 @@
-##############################################################################
-# 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 django.contrib import admin
-
-from booking.models import Booking
-
-admin.site.register(Booking)
diff --git a/src/booking/apps.py b/src/booking/apps.py
deleted file mode 100644
index 99bf115..0000000
--- a/src/booking/apps.py
+++ /dev/null
@@ -1,15 +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.apps import AppConfig
-
-
-class BookingConfig(AppConfig):
- name = 'booking'
diff --git a/src/booking/forms.py b/src/booking/forms.py
deleted file mode 100644
index 9c9b053..0000000
--- a/src/booking/forms.py
+++ /dev/null
@@ -1,123 +0,0 @@
-##############################################################################
-# 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
-##############################################################################
-import django.forms as forms
-from django.forms.widgets import NumberInput
-
-from workflow.forms import (
- MultipleSelectFilterField,
- MultipleSelectFilterWidget)
-from account.models import UserProfile
-from resource_inventory.models import Image, Installer, Scenario
-from workflow.forms import SearchableSelectMultipleField
-from booking.lib import get_user_items, get_user_field_opts
-
-
-class QuickBookingForm(forms.Form):
- # Django Form class for Express Booking
- purpose = forms.CharField(max_length=1000)
- project = forms.CharField(max_length=400)
- hostname = forms.CharField(required=False, max_length=400)
- global_cloud_config = forms.CharField(widget=forms.Textarea, required=False)
-
- installer = forms.ModelChoiceField(queryset=Installer.objects.all(), required=False)
- scenario = forms.ModelChoiceField(queryset=Scenario.objects.all(), required=False)
-
- def __init__(self, data=None, user=None, lab_data=None, *args, **kwargs):
- if "default_user" in kwargs:
- default_user = kwargs.pop("default_user")
- else:
- default_user = "you"
- self.default_user = default_user
-
- super(QuickBookingForm, self).__init__(data=data, **kwargs)
-
- image_help_text = 'Image can be set only for single-node bookings. For multi-node bookings set image through Design a POD.'
- self.fields["image"] = forms.ModelChoiceField(
- Image.objects.filter(public=True) | Image.objects.filter(owner=user), required=False
- )
-
- self.fields['image'].widget.attrs.update({
- 'class': 'has-popover',
- 'data-content': image_help_text,
- 'data-placement': 'bottom',
- 'data-container': 'body'
- })
-
- self.fields['users'] = SearchableSelectMultipleField(
- queryset=UserProfile.objects.filter(public_user=True).select_related('user').exclude(user=user),
- items=get_user_items(exclude=user),
- required=False,
- **get_user_field_opts()
- )
-
- self.fields['length'] = forms.IntegerField(
- widget=NumberInput(
- attrs={
- "type": "range",
- 'min': "1",
- "max": "21",
- "value": "1"
- }
- )
- )
-
- self.fields['filter_field'] = MultipleSelectFilterField(widget=MultipleSelectFilterWidget(**lab_data))
-
- hostname_help_text = 'Hostname can be set only for single-node bookings. For multi-node bookings set hostname through Design a POD.'
- self.fields['hostname'].widget.attrs.update({
- 'class': 'has-popover',
- 'data-content': hostname_help_text,
- 'data-placement': 'top',
- 'data-container': 'body'
- })
-
- def build_user_list(self):
- """
- Build list of UserProfiles.
-
- returns a mapping of UserProfile ids to displayable objects expected by
- searchable multiple select widget
- """
- try:
- users = {}
- d_qset = UserProfile.objects.select_related('user').all().exclude(user__username=self.default_user)
- for userprofile in d_qset:
- user = {
- 'id': userprofile.user.id,
- 'expanded_name': userprofile.full_name,
- 'small_name': userprofile.user.username,
- 'string': userprofile.email_addr
- }
-
- users[userprofile.user.id] = user
-
- return users
- except Exception:
- pass
-
- def build_search_widget_attrs(self, chosen_users, default_user="you"):
-
- attrs = {
- 'set': self.build_user_list(),
- 'show_from_noentry': "false",
- 'show_x_results': 10,
- 'scrollable': "false",
- 'selectable_limit': -1,
- 'name': "users",
- 'placeholder': "username",
- 'initial': chosen_users,
- 'edit': False
- }
- return attrs
-
-
-class HostReImageForm(forms.Form):
-
- image_id = forms.IntegerField()
- host_id = forms.IntegerField()
diff --git a/src/booking/lib.py b/src/booking/lib.py
deleted file mode 100644
index 8c87979..0000000
--- a/src/booking/lib.py
+++ /dev/null
@@ -1,36 +0,0 @@
-##############################################################################
-# Copyright (c) 2019 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 account.models import UserProfile
-
-
-def get_user_field_opts():
- return {
- 'show_from_noentry': False,
- 'show_x_results': 5,
- 'results_scrollable': True,
- 'selectable_limit': -1,
- 'placeholder': 'Search for other users',
- 'name': 'users',
- 'disabled': False
- }
-
-
-def get_user_items(exclude=None):
- qs = UserProfile.objects.filter(public_user=True).select_related('user').exclude(user=exclude)
- items = {}
- for up in qs:
- item = {
- 'id': up.id,
- 'expanded_name': up.full_name if up.full_name else up.user.username,
- 'small_name': up.user.username,
- 'string': up.email_addr if up.email_addr else up.user.username,
- }
- items[up.id] = item
- return items
diff --git a/src/booking/migrations/0001_initial.py b/src/booking/migrations/0001_initial.py
deleted file mode 100644
index 20415fe..0000000
--- a/src/booking/migrations/0001_initial.py
+++ /dev/null
@@ -1,68 +0,0 @@
-# Generated by Django 2.1 on 2018-09-14 14:48
-
-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),
- ('account', '0001_initial'),
- ('resource_inventory', '__first__'),
- ]
-
- operations = [
- migrations.CreateModel(
- name='Booking',
- fields=[
- ('id', models.AutoField(primary_key=True, serialize=False)),
- ('start', models.DateTimeField()),
- ('end', models.DateTimeField()),
- ('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',
- },
- ),
- migrations.CreateModel(
- name='Installer',
- fields=[
- ('id', models.AutoField(primary_key=True, serialize=False)),
- ('name', models.CharField(max_length=30)),
- ],
- ),
- 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)),
- ('name', models.CharField(max_length=300)),
- ],
- ),
- migrations.AddField(
- model_name='installer',
- name='sup_scenarios',
- field=models.ManyToManyField(blank=True, to='booking.Scenario'),
- ),
- ]
diff --git a/src/booking/migrations/0002_booking_pdf.py b/src/booking/migrations/0002_booking_pdf.py
deleted file mode 100644
index 53232c9..0000000
--- a/src/booking/migrations/0002_booking_pdf.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# Generated by Django 2.1 on 2018-11-09 16:09
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('booking', '0001_initial'),
- ]
-
- operations = [
- migrations.AddField(
- model_name='booking',
- name='pdf',
- field=models.TextField(blank=True, default=''),
- ),
- ]
diff --git a/src/booking/migrations/0003_auto_20190115_1733.py b/src/booking/migrations/0003_auto_20190115_1733.py
deleted file mode 100644
index 70eecfe..0000000
--- a/src/booking/migrations/0003_auto_20190115_1733.py
+++ /dev/null
@@ -1,30 +0,0 @@
-# Generated by Django 2.1 on 2019-01-15 17:33
-
-from django.db import migrations
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('booking', '0002_booking_pdf'),
- ]
-
- operations = [
- migrations.RemoveField(
- model_name='installer',
- name='sup_scenarios',
- ),
- migrations.RemoveField(
- model_name='opsys',
- name='sup_installers',
- ),
- migrations.DeleteModel(
- name='Installer',
- ),
- migrations.DeleteModel(
- name='Opsys',
- ),
- migrations.DeleteModel(
- name='Scenario',
- ),
- ]
diff --git a/src/booking/migrations/0004_auto_20190124_1700.py b/src/booking/migrations/0004_auto_20190124_1700.py
deleted file mode 100644
index baa32d2..0000000
--- a/src/booking/migrations/0004_auto_20190124_1700.py
+++ /dev/null
@@ -1,20 +0,0 @@
-# Generated by Django 2.1 on 2019-01-24 17:00
-
-from django.conf import settings
-from django.db import migrations, models
-import django.db.models.deletion
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('booking', '0003_auto_20190115_1733'),
- ]
-
- operations = [
- migrations.AlterField(
- model_name='booking',
- name='owner',
- field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='owner', to=settings.AUTH_USER_MODEL),
- ),
- ]
diff --git a/src/booking/migrations/0005_booking_idf.py b/src/booking/migrations/0005_booking_idf.py
deleted file mode 100644
index 31e9170..0000000
--- a/src/booking/migrations/0005_booking_idf.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# Generated by Django 2.1 on 2019-04-12 19:27
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('booking', '0004_auto_20190124_1700'),
- ]
-
- operations = [
- migrations.AddField(
- model_name='booking',
- name='idf',
- field=models.TextField(blank=True, default=''),
- ),
- ]
diff --git a/src/booking/migrations/0006_booking_opnfv_config.py b/src/booking/migrations/0006_booking_opnfv_config.py
deleted file mode 100644
index e5ffc71..0000000
--- a/src/booking/migrations/0006_booking_opnfv_config.py
+++ /dev/null
@@ -1,20 +0,0 @@
-# Generated by Django 2.1 on 2019-05-01 18:02
-
-from django.db import migrations, models
-import django.db.models.deletion
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('resource_inventory', '0010_auto_20190430_1405'),
- ('booking', '0005_booking_idf'),
- ]
-
- operations = [
- migrations.AddField(
- model_name='booking',
- name='opnfv_config',
- field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='resource_inventory.OPNFVConfig'),
- ),
- ]
diff --git a/src/booking/migrations/0007_remove_booking_config_bundle.py b/src/booking/migrations/0007_remove_booking_config_bundle.py
deleted file mode 100644
index dcd2e1c..0000000
--- a/src/booking/migrations/0007_remove_booking_config_bundle.py
+++ /dev/null
@@ -1,17 +0,0 @@
-# Generated by Django 2.2 on 2020-02-18 15:36
-
-from django.db import migrations
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('booking', '0006_booking_opnfv_config'),
- ]
-
- operations = [
- migrations.RemoveField(
- model_name='booking',
- name='config_bundle',
- ),
- ]
diff --git a/src/booking/migrations/0008_auto_20201109_1947.py b/src/booking/migrations/0008_auto_20201109_1947.py
deleted file mode 100644
index 289e476..0000000
--- a/src/booking/migrations/0008_auto_20201109_1947.py
+++ /dev/null
@@ -1,30 +0,0 @@
-# Generated by Django 2.2 on 2020-11-09 19:47
-
-from django.conf import settings
-from django.db import migrations, models
-import django.db.models.deletion
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('booking', '0007_remove_booking_config_bundle'),
- ]
-
- operations = [
- migrations.AlterField(
- model_name='booking',
- name='collaborators',
- field=models.ManyToManyField(blank=True, related_name='collaborators', to=settings.AUTH_USER_MODEL),
- ),
- migrations.AlterField(
- model_name='booking',
- name='opnfv_config',
- field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='resource_inventory.OPNFVConfig'),
- ),
- migrations.AlterField(
- model_name='booking',
- name='resource',
- field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='resource_inventory.ResourceBundle'),
- ),
- ]
diff --git a/src/booking/migrations/0009_booking_complete.py b/src/booking/migrations/0009_booking_complete.py
deleted file mode 100644
index e291a83..0000000
--- a/src/booking/migrations/0009_booking_complete.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# Generated by Django 2.2 on 2021-09-07 15:14
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('booking', '0008_auto_20201109_1947'),
- ]
-
- operations = [
- migrations.AddField(
- model_name='booking',
- name='complete',
- field=models.BooleanField(default=False),
- ),
- ]
diff --git a/src/booking/migrations/__init__.py b/src/booking/migrations/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/src/booking/migrations/__init__.py
+++ /dev/null
diff --git a/src/booking/models.py b/src/booking/models.py
deleted file mode 100644
index 966f1c2..0000000
--- a/src/booking/models.py
+++ /dev/null
@@ -1,72 +0,0 @@
-##############################################################################
-# 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
-##############################################################################
-
-
-from resource_inventory.models import ResourceBundle, OPNFVConfig
-from account.models import Lab
-from django.contrib.auth.models import User
-from django.db import models
-import resource_inventory.resource_manager
-
-
-class Booking(models.Model):
- id = models.AutoField(primary_key=True)
- # All bookings are owned by the user who requested it
- owner = models.ForeignKey(User, on_delete=models.PROTECT, related_name='owner')
- # an owner can add other users to the booking
- collaborators = models.ManyToManyField(User, blank=True, related_name='collaborators')
- # start and end time
- start = models.DateTimeField()
- end = models.DateTimeField()
- reset = models.BooleanField(default=False)
- 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)
- # bookings can be extended a limited number of times
- ext_count = models.IntegerField(default=2)
- # the hardware that the user has booked
- resource = models.ForeignKey(ResourceBundle, on_delete=models.SET_NULL, null=True, blank=True)
- opnfv_config = models.ForeignKey(OPNFVConfig, on_delete=models.SET_NULL, null=True, blank=True)
- project = models.CharField(max_length=100, default="", blank=True, null=True)
- lab = models.ForeignKey(Lab, null=True, on_delete=models.SET_NULL)
- pdf = models.TextField(blank=True, default="")
- idf = models.TextField(blank=True, default="")
-
- complete = models.BooleanField(default=False)
-
- class Meta:
- db_table = 'booking'
-
- def save(self, *args, **kwargs):
- """
- Save the booking if self.user is authorized and there is no overlapping booking.
-
- Raise PermissionError if the user is not authorized
- Raise ValueError if there is an overlapping booking
- """
- if self.start >= self.end:
- raise ValueError('Start date is after end date')
- # conflicts end after booking starts, and start before booking ends
- conflicting_dates = Booking.objects.filter(resource=self.resource).exclude(id=self.id)
- conflicting_dates = conflicting_dates.filter(end__gt=self.start)
- conflicting_dates = conflicting_dates.filter(start__lt=self.end)
- if conflicting_dates.count() > 0:
- raise ValueError('This booking overlaps with another booking')
- 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.purpose) + ' from ' + str(self.start) + ' until ' + str(self.end)
diff --git a/src/booking/quick_deployer.py b/src/booking/quick_deployer.py
deleted file mode 100644
index 4b85d76..0000000
--- a/src/booking/quick_deployer.py
+++ /dev/null
@@ -1,343 +0,0 @@
-##############################################################################
-# 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
-##############################################################################
-
-
-import json
-import yaml
-from django.db.models import Q
-from django.db import transaction
-from datetime import timedelta
-from django.utils import timezone
-from django.core.exceptions import ValidationError
-from account.models import Lab, UserProfile
-
-from resource_inventory.models import (
- ResourceTemplate,
- Image,
- OPNFVRole,
- OPNFVConfig,
- ResourceOPNFVConfig,
- ResourceConfiguration,
- NetworkConnection,
- InterfaceConfiguration,
- Network,
- CloudInitFile,
-)
-from resource_inventory.resource_manager import ResourceManager
-from resource_inventory.pdf_templater import PDFTemplater
-from notifier.manager import NotificationHandler
-from booking.models import Booking
-from dashboard.exceptions import BookingLengthException
-from api.models import JobFactory
-
-
-def parse_resource_field(resource_json):
- """
- Parse the json from the frontend.
-
- returns a reference to the selected Lab and ResourceTemplate objects
- """
- lab, template = (None, None)
- lab_dict = resource_json['lab']
- for lab_info in lab_dict.values():
- if lab_info['selected']:
- lab = Lab.objects.get(lab_user__id=lab_info['id'])
-
- resource_dict = resource_json['resource']
- for resource_info in resource_dict.values():
- if resource_info['selected']:
- template = ResourceTemplate.objects.get(pk=resource_info['id'])
-
- if lab is None:
- raise ValidationError("No lab was selected")
- if template is None:
- raise ValidationError("No Host was selected")
-
- return lab, template
-
-
-def update_template(old_template, image, hostname, user, global_cloud_config=None):
- """
- Duplicate a template to the users account and update configured fields.
-
- The dashboard presents users with preconfigured resource templates,
- but the user might want to make small modifications, e.g hostname and
- linux distro. So we copy the public template and create a private version
- to the user's profile, and mark it temporary. When the booking ends the
- new template is deleted
- """
- name = user.username + "'s Copy of '" + old_template.name + "'"
- num_copies = ResourceTemplate.objects.filter(name__startswith=name).count()
- template = ResourceTemplate.objects.create(
- name=name if num_copies == 0 else name + " (" + str(num_copies) + ")",
- xml=old_template.xml,
- owner=user,
- lab=old_template.lab,
- description=old_template.description,
- public=False,
- temporary=True,
- private_vlan_pool=old_template.private_vlan_pool,
- public_vlan_pool=old_template.public_vlan_pool,
- copy_of=old_template
- )
-
- for old_network in old_template.networks.all():
- Network.objects.create(
- name=old_network.name,
- bundle=template,
- is_public=old_network.is_public
- )
- # We are assuming there is only one opnfv config per public resource template
- old_opnfv = template.opnfv_config.first()
- if old_opnfv:
- opnfv_config = OPNFVConfig.objects.create(
- installer=old_opnfv.installer,
- scenario=old_opnfv.installer,
- template=template,
- name=old_opnfv.installer,
- )
- # I am explicitly leaving opnfv_config.networks empty to avoid
- # problems with duplicated / shared networks. In the quick deploy,
- # there is never multiple networks anyway. This may have to change in the future
-
- for old_config in old_template.getConfigs():
- image_to_set = image
- if not image:
- image_to_set = old_config.image
-
- config = ResourceConfiguration.objects.create(
- profile=old_config.profile,
- image=image_to_set,
- template=template,
- is_head_node=old_config.is_head_node,
- name=hostname if len(old_template.getConfigs()) == 1 else old_config.name,
- # cloud_init_files=old_config.cloud_init_files.set()
- )
-
- for file in old_config.cloud_init_files.all():
- config.cloud_init_files.add(file)
-
- if global_cloud_config:
- config.cloud_init_files.add(global_cloud_config)
- config.save()
-
- for old_iface_config in old_config.interface_configs.all():
- iface_config = InterfaceConfiguration.objects.create(
- profile=old_iface_config.profile,
- resource_config=config
- )
-
- for old_connection in old_iface_config.connections.all():
- iface_config.connections.add(NetworkConnection.objects.create(
- network=template.networks.get(name=old_connection.network.name),
- vlan_is_tagged=old_connection.vlan_is_tagged
- ))
-
- for old_res_opnfv in old_config.resource_opnfv_config.all():
- if old_opnfv:
- ResourceOPNFVConfig.objects.create(
- role=old_opnfv.role,
- resource_config=config,
- opnfv_config=opnfv_config
- )
- return template
-
-
-def generate_opnfvconfig(scenario, installer, template):
- return OPNFVConfig.objects.create(
- scenario=scenario,
- installer=installer,
- template=template
- )
-
-
-def generate_hostopnfv(hostconfig, opnfvconfig):
- role = None
- try:
- role = OPNFVRole.objects.get(name="Jumphost")
- except Exception:
- role = OPNFVRole.objects.create(
- name="Jumphost",
- description="Single server jumphost role"
- )
- return ResourceOPNFVConfig.objects.create(
- role=role,
- host_config=hostconfig,
- opnfv_config=opnfvconfig
- )
-
-
-def generate_resource_bundle(template):
- resource_manager = ResourceManager.getInstance()
- resource_bundle = resource_manager.instantiateTemplate(template)
- return resource_bundle
-
-
-def check_invariants(**kwargs):
- # TODO: This should really happen in the BookingForm validation methods
- image = kwargs['image']
- lab = kwargs['lab']
- length = kwargs['length']
- # check that image os is compatible with installer
- if image:
- if image.from_lab != lab:
- raise ValidationError("The chosen image is not available at the chosen hosting lab")
- # TODO
- # if image.host_type != host_profile:
- # raise ValidationError("The chosen image is not available for the chosen host type")
- if not image.public and image.owner != kwargs['owner']:
- raise ValidationError("You are not the owner of the chosen private image")
- if length < 1 or length > 21:
- raise BookingLengthException("Booking must be between 1 and 21 days long")
-
-
-def create_from_form(form, request):
- """
- Parse data from QuickBookingForm to create booking
- """
- resource_field = form.cleaned_data['filter_field']
- # users_field = form.cleaned_data['users']
- hostname = 'opnfv_host' if not form.cleaned_data['hostname'] else form.cleaned_data['hostname']
-
- global_cloud_config = None if not form.cleaned_data['global_cloud_config'] else form.cleaned_data['global_cloud_config']
-
- if global_cloud_config:
- form.cleaned_data['global_cloud_config'] = create_ci_file(global_cloud_config)
-
- # image = form.cleaned_data['image']
- # scenario = form.cleaned_data['scenario']
- # installer = form.cleaned_data['installer']
-
- lab, resource_template = parse_resource_field(resource_field)
- data = form.cleaned_data
- data['hostname'] = hostname
- data['lab'] = lab
- data['resource_template'] = resource_template
- data['owner'] = request.user
-
- return _create_booking(data)
-
-
-def create_from_API(body, user):
- """
- Parse data from Automation API to create booking
- """
- booking_info = json.loads(body.decode('utf-8'))
-
- data = {}
- data['purpose'] = booking_info['purpose']
- data['project'] = booking_info['project']
- data['users'] = [UserProfile.objects.get(user__username=username)
- for username in booking_info['collaborators']]
- data['hostname'] = booking_info['hostname']
- data['length'] = booking_info['length']
- data['installer'] = None
- data['scenario'] = None
-
- data['image'] = Image.objects.get(pk=booking_info['imageLabID'])
-
- data['resource_template'] = ResourceTemplate.objects.get(pk=booking_info['templateID'])
- data['lab'] = data['resource_template'].lab
- data['owner'] = user
-
- if 'global_cloud_config' in data.keys():
- data['global_cloud_config'] = CloudInitFile.objects.get(id=data['global_cloud_config'])
-
- return _create_booking(data)
-
-
-def create_ci_file(data: str) -> CloudInitFile:
- try:
- d = yaml.load(data)
- if not (type(d) is dict):
- raise Exception("CI file was valid yaml but was not a dict")
- except Exception:
- raise ValidationError("The provided Cloud Config is not valid yaml, please refer to the Cloud Init documentation for expected structure")
- print("about to create global cloud config")
- config = CloudInitFile.create(text=data, priority=CloudInitFile.objects.count())
- print("made global cloud config")
-
- return config
-
-
-@transaction.atomic
-def _create_booking(data):
- check_invariants(**data)
-
- # check booking privileges
- # TODO: use the canonical booking_allowed method because now template might have multiple
- # machines
- if Booking.objects.filter(owner=data['owner'], end__gt=timezone.now()).count() >= 3 and not data['owner'].userprofile.booking_privledge:
- raise PermissionError("You do not have permission to have more than 3 bookings at a time.")
-
- ResourceManager.getInstance().templateIsReservable(data['resource_template'])
-
- resource_template = update_template(data['resource_template'], data['image'], data['hostname'], data['owner'], global_cloud_config=data['global_cloud_config'])
-
- # generate resource bundle
- resource_bundle = generate_resource_bundle(resource_template)
-
- # generate booking
- booking = Booking.objects.create(
- purpose=data['purpose'],
- project=data['project'],
- lab=data['lab'],
- owner=data['owner'],
- start=timezone.now(),
- end=timezone.now() + timedelta(days=int(data['length'])),
- resource=resource_bundle,
- opnfv_config=None
- )
-
- booking.pdf = PDFTemplater.makePDF(booking)
-
- for collaborator in data['users']: # list of Users (not UserProfile)
- booking.collaborators.add(collaborator.user)
-
- booking.save()
-
- # generate job
- JobFactory.makeCompleteJob(booking)
- NotificationHandler.notify_new_booking(booking)
-
- return booking
-
-
-def drop_filter(user):
- """
- Return a dictionary that contains filters.
-
- Only certain installlers are supported on certain images, etc
- so the image filter indexed at [imageid][installerid] is truthy if
- that installer is supported on that image
- """
- installer_filter = {}
- scenario_filter = {}
-
- images = Image.objects.filter(Q(public=True) | Q(owner=user))
- image_filter = {}
- for image in images:
- image_filter[image.id] = {
- 'lab': 'lab_' + str(image.from_lab.lab_user.id),
- 'architecture': str(image.architecture),
- 'name': image.name
- }
-
- resource_filter = {}
- templates = ResourceTemplate.objects.filter(Q(public=True) | Q(owner=user))
- for rt in templates:
- profiles = [conf.profile for conf in rt.getConfigs()]
- resource_filter["resource_" + str(rt.id)] = [str(p.architecture) for p in profiles]
-
- return {
- 'installer_filter': json.dumps(installer_filter),
- 'scenario_filter': json.dumps(scenario_filter),
- 'image_filter': json.dumps(image_filter),
- 'resource_profile_map': json.dumps(resource_filter),
- }
diff --git a/src/booking/stats.py b/src/booking/stats.py
deleted file mode 100644
index 5a59d32..0000000
--- a/src/booking/stats.py
+++ /dev/null
@@ -1,109 +0,0 @@
-##############################################################################
-# Copyright (c) 2020 Parker Berberian, Sawyer Bergeron, Sean Smith 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 booking.models import Booking
-from resource_inventory.models import ResourceQuery, ResourceProfile
-from datetime import datetime, timedelta
-from collections import Counter
-import pytz
-
-
-class StatisticsManager(object):
-
- @staticmethod
- def getContinuousBookingTimeSeries(span=28):
- """
- Calculate Booking usage data points.
-
- Gathers all active bookings that fall in interval [(now - span), (now + 1 week)].
- x data points are every 12 hours
- y values are the integer number of bookings/users active at time
- """
-
- anuket_colors = [
- '#6BDAD5', # Turquoise
- '#E36386', # Pale Violet Red
- '#F5B335', # Sandy Brown
- '#007473', # Teal
- '#BCE194', # Gainsboro
- '#00CE7C', # Sea Green
- ]
-
- lfedge_colors = [
- '#0049B0',
- '#B481A5',
- '#6CAFE4',
- '#D33668',
- '#28245A'
- ]
-
- x = []
- y = []
- users = []
- projects = []
- profiles = {str(profile): [] for profile in ResourceProfile.objects.all()}
-
- now = datetime.now(pytz.utc)
- delta = timedelta(days=span)
- start = now - delta
- end = now + timedelta(weeks=1)
-
- bookings = Booking.objects.filter(
- start__lte=end,
- end__gte=start
- ).prefetch_related("collaborators")
-
- # get data
- while start <= end:
- active_users = 0
-
- books = bookings.filter(
- start__lte=start,
- end__gte=start
- ).prefetch_related("collaborators")
-
- for booking in books:
- active_users += booking.collaborators.all().count() + 1
-
- x.append(str(start.month) + '-' + str(start.day))
- y.append(books.count())
-
- step_profiles = Counter([
- str(config.profile)
- for book in books
- for config in book.resource.template.getConfigs()
- ])
-
- for profile in ResourceProfile.objects.all():
- profiles[str(profile)].append(step_profiles[str(profile)])
- users.append(active_users)
-
- start += timedelta(hours=12)
-
- in_use = len(ResourceQuery.filter(working=True, booked=True))
- not_in_use = len(ResourceQuery.filter(working=True, booked=False))
- maintenance = len(ResourceQuery.filter(working=False))
-
- projects = [x.project for x in bookings]
- proj_count = sorted(Counter(projects).items(), key=lambda x: x[1])
-
- project_keys = [proj[0] for proj in proj_count[-5:]]
- project_keys = ['None' if x is None else x for x in project_keys]
- project_counts = [proj[1] for proj in proj_count[-5:]]
-
- resources = {key: [x, value] for key, value in profiles.items()}
-
- return {
- "resources": resources,
- "booking": [x, y],
- "user": [x, users],
- "utils": [in_use, not_in_use, maintenance],
- "projects": [project_keys, project_counts],
- "colors": anuket_colors if os.environ.get('TEMPLATE_OVERRIDE_DIR') == 'laas' else lfedge_colors
- }
diff --git a/src/booking/tests/__init__.py b/src/booking/tests/__init__.py
deleted file mode 100644
index b6fef6c..0000000
--- a/src/booking/tests/__init__.py
+++ /dev/null
@@ -1,8 +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/src/booking/tests/test_models.py b/src/booking/tests/test_models.py
deleted file mode 100644
index 37eb655..0000000
--- a/src/booking/tests/test_models.py
+++ /dev/null
@@ -1,210 +0,0 @@
-##############################################################################
-# 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.test import TestCase
-from django.utils import timezone
-
-from booking.models import Booking
-from dashboard.testing_utils import make_resource_template, make_user
-
-
-class BookingModelTestCase(TestCase):
- """
- Test the Booking model.
-
- Creates all the scafolding needed and tests the Booking model
- """
-
- def setUp(self):
- """
- Prepare for Booking model tests.
-
- Creates all the needed models, such as users, resources, and configurations
- """
- self.owner = User.objects.create(username='owner')
- self.res1 = make_resource_template(name="Test template 1")
- self.res2 = make_resource_template(name="Test template 2")
- self.user1 = make_user(username='user1')
-
- def test_start_end(self):
- """
- Verify the start and end fields.
-
- if the start of a booking is greater or equal then the end,
- saving should raise a ValueException
- """
- start = timezone.now()
- end = start - timedelta(weeks=1)
- self.assertRaises(
- ValueError,
- Booking.objects.create,
- start=start,
- end=end,
- resource=self.res1,
- owner=self.user1,
- )
- end = start
- self.assertRaises(
- ValueError,
- Booking.objects.create,
- start=start,
- end=end,
- resource=self.res1,
- owner=self.user1,
- )
-
- def test_conflicts(self):
- """
- Verify conflicting dates are dealt with.
-
- saving an overlapping booking on the same resource
- should raise a ValueException
- saving for different resources should succeed
- """
- start = timezone.now()
- end = start + timedelta(weeks=1)
- self.assertTrue(
- Booking.objects.create(
- start=start,
- end=end,
- owner=self.user1,
- resource=self.res1,
- )
- )
-
- self.assertRaises(
- ValueError,
- Booking.objects.create,
- start=start,
- end=end,
- resource=self.res1,
- owner=self.user1,
- )
-
- self.assertRaises(
- ValueError,
- Booking.objects.create,
- start=start + timedelta(days=1),
- end=end - timedelta(days=1),
- resource=self.res1,
- owner=self.user1,
- )
-
- self.assertRaises(
- ValueError,
- Booking.objects.create,
- start=start - timedelta(days=1),
- end=end,
- resource=self.res1,
- owner=self.user1,
- )
-
- self.assertRaises(
- ValueError,
- Booking.objects.create,
- start=start - timedelta(days=1),
- end=end - timedelta(days=1),
- resource=self.res1,
- owner=self.user1,
- )
-
- self.assertRaises(
- ValueError,
- Booking.objects.create,
- start=start,
- end=end + timedelta(days=1),
- resource=self.res1,
- owner=self.user1,
- )
-
- self.assertRaises(
- ValueError,
- Booking.objects.create,
- start=start + timedelta(days=1),
- end=end + timedelta(days=1),
- resource=self.res1,
- owner=self.user1,
- )
-
- self.assertTrue(
- Booking.objects.create(
- start=start - timedelta(days=1),
- end=start,
- owner=self.user1,
- resource=self.res1,
- )
- )
-
- self.assertTrue(
- Booking.objects.create(
- start=end,
- end=end + timedelta(days=1),
- owner=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,
- )
- )
-
- self.assertTrue(
- Booking.objects.create(
- start=end + timedelta(days=1),
- end=end + timedelta(days=2),
- owner=self.user1,
- resource=self.res1,
- )
- )
-
- self.assertTrue(
- Booking.objects.create(
- start=start,
- end=end,
- owner=self.user1,
- resource=self.res2,
- )
- )
-
- def test_extensions(self):
- """
- Test booking extensions.
-
- 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,
- owner=self.user1,
- resource=self.res1,
- )
- )
-
- 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")
diff --git a/src/booking/tests/test_quick_booking.py b/src/booking/tests/test_quick_booking.py
deleted file mode 100644
index f405047..0000000
--- a/src/booking/tests/test_quick_booking.py
+++ /dev/null
@@ -1,180 +0,0 @@
-##############################################################################
-# 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
-##############################################################################
-
-import datetime
-import json
-
-from django.test import TestCase, Client
-
-from booking.models import Booking
-from dashboard.testing_utils import (
- make_user,
- make_user_profile,
- make_lab,
- make_image,
- make_os,
- make_opnfv_role,
- make_public_net,
- make_resource_template,
- make_server
-)
-
-
-class QuickBookingValidFormTestCase(TestCase):
- @classmethod
- def setUpTestData(cls):
- cls.user = make_user(False, username="newtestuser")
- cls.user.set_password("testpassword")
- cls.user.save()
- make_user_profile(cls.user, True)
-
- cls.lab = make_lab()
-
- cls.res_template = make_resource_template(owner=cls.user, lab=cls.lab)
- cls.res_profile = cls.res_template.getConfigs()[0].profile
- os = make_os()
- cls.image = make_image(cls.res_profile, lab=cls.lab, owner=cls.user, os=os)
- cls.server = make_server(cls.res_profile, cls.lab)
- cls.role = make_opnfv_role()
- cls.pubnet = make_public_net(10, cls.lab)
-
- cls.post_data = cls.build_post_data()
- cls.client = Client()
-
- @classmethod
- def build_post_data(cls):
- return {
- 'filter_field': json.dumps({
- "resource": {
- "resource_" + str(cls.res_profile.id): {
- "selected": True,
- "id": cls.res_template.id
- }
- },
- "lab": {
- "lab_" + str(cls.lab.lab_user.id): {
- "selected": True,
- "id": cls.lab.lab_user.id
- }
- }
- }),
- 'purpose': 'my_purpose',
- 'project': 'my_project',
- 'length': '3',
- 'ignore_this': 1,
- 'users': '',
- 'hostname': 'my_host',
- 'image': str(cls.image.id),
- }
-
- def post(self, changed_fields={}):
- payload = self.post_data.copy()
- payload.update(changed_fields)
- response = self.client.post('/booking/quick/', payload)
- return response
-
- def setUp(self):
- self.client.login(username="newtestuser", password="testpassword")
-
- def assertValidBooking(self, booking):
- self.assertEqual(booking.owner, self.user)
- self.assertEqual(booking.purpose, 'my_purpose')
- self.assertEqual(booking.project, 'my_project')
- delta = booking.end - booking.start
- delta -= datetime.timedelta(days=3)
- self.assertLess(delta, datetime.timedelta(minutes=1))
-
- resource_bundle = booking.resource
-
- host = resource_bundle.get_resources()[0]
- self.assertEqual(host.profile, self.res_profile)
- self.assertEqual(host.name, 'my_host')
-
- def test_with_too_long_length(self):
- response = self.post({'length': '22'})
-
- self.assertEqual(response.status_code, 200)
- self.assertIsNone(Booking.objects.first())
-
- def test_with_negative_length(self):
- response = self.post({'length': '-1'})
-
- self.assertEqual(response.status_code, 200)
- self.assertIsNone(Booking.objects.first())
-
- def test_with_invalid_installer(self):
- response = self.post({'installer': str(self.installer.id + 100)})
-
- self.assertEqual(response.status_code, 200)
- self.assertIsNone(Booking.objects.first())
-
- def test_with_invalid_scenario(self):
- response = self.post({'scenario': str(self.scenario.id + 100)})
-
- self.assertEqual(response.status_code, 200)
- self.assertIsNone(Booking.objects.first())
-
- def test_with_invalid_host_id(self):
- response = self.post({'filter_field': json.dumps({
- "resource": {
- "resource_" + str(self.res_profile.id + 100): {
- "selected": True,
- "id": self.res_profile.id + 100
- }
- },
- "lab": {
- "lab_" + str(self.lab.lab_user.id): {
- "selected": True,
- "id": self.lab.lab_user.id
- }
- }
- })})
-
- self.assertEqual(response.status_code, 200)
- self.assertIsNone(Booking.objects.first())
-
- def test_with_invalid_lab_id(self):
- response = self.post({'filter_field': json.dumps({
- "resource": {
- "resource_" + str(self.res_profile.id): {
- "selected": True,
- "id": self.res_profile.id
- }
- },
- "lab": {
- "lab_" + str(self.lab.lab_user.id + 100): {
- "selected": True,
- "id": self.lab.lab_user.id + 100
- }
- }
- })})
-
- self.assertEqual(response.status_code, 200)
- self.assertIsNone(Booking.objects.first())
-
- def test_with_invalid_empty_filter_field(self):
- response = self.post({'filter_field': ''})
-
- self.assertEqual(response.status_code, 200)
- self.assertIsNone(Booking.objects.first())
-
- def test_with_garbage_users_field(self): # expected behavior: treat as though field is empty if it has garbage data
- response = self.post({'users': ['X�]QP�槰DP�+m���h�U�_�yJA:.rDi��QN|.��C��n�P��F!��D�����5ȅj�9�LV��']}) # output from /dev/urandom
-
- self.assertEqual(response.status_code, 200)
- booking = Booking.objects.first()
- self.assertIsNone(booking)
-
- def test_with_valid_form(self):
- response = self.post()
-
- self.assertEqual(response.status_code, 302) # success should redirect
- booking = Booking.objects.first()
- self.assertIsNotNone(booking)
- self.assertValidBooking(booking)
diff --git a/src/booking/tests/test_stats.py b/src/booking/tests/test_stats.py
deleted file mode 100644
index 5501355..0000000
--- a/src/booking/tests/test_stats.py
+++ /dev/null
@@ -1,59 +0,0 @@
-#############################################################################
-# Copyright (c) 2018 Parker Berberian, Sawyer Bergeron, Sean Smith, 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 pytz
-from datetime import timedelta, datetime
-
-from django.test import TestCase
-
-from booking.models import Booking
-from booking.stats import StatisticsManager as sm
-from dashboard.testing_utils import make_user
-
-
-class StatsTestCases(TestCase):
-
- def test_no_booking_outside_span(self):
- now = datetime.now(pytz.utc)
-
- bad_date = now + timedelta(days=1200)
- Booking.objects.create(start=now, end=bad_date, owner=make_user(username='jj'))
-
- actual = sm.getContinuousBookingTimeSeries()
- dates = actual['booking'][0]
-
- for date in dates:
- self.assertNotEqual(date, bad_date)
-
- def check_booking_and_user_counts(self):
- now = datetime.now(pytz.utc)
-
- for i in range(20):
- Booking.objects.create(
- start=now,
- end=now + timedelta(weeks=3),
- owner=make_user(username='a'))
-
- for i in range(30):
- Booking.objects.create(
- start=now + timedelta(days=5),
- end=now + timedelta(weeks=3, days=5),
- owner=make_user(username='a'))
-
- for i in range(120):
- Booking.objects.create(
- start=now + timedelta(weeks=1),
- end=now + timedelta(weeks=4),
- owner=make_user(username='a'))
-
- dates = [[now, 20], [now + timedelta(days=5), 30], [now + timedelta(weeks=1), 120]]
- actual = sm.getContinuousBookingTimeSeries()
-
- for date in dates:
- self.assertEqual(date[1], actual['booking'][date[0]])
- self.assertEqual(date[1], actual['booking'][date[1]])
diff --git a/src/booking/urls.py b/src/booking/urls.py
deleted file mode 100644
index 0b60351..0000000
--- a/src/booking/urls.py
+++ /dev/null
@@ -1,53 +0,0 @@
-##############################################################################
-# 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
-##############################################################################
-
-
-"""
-laas_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 booking.views import (
- booking_detail_view,
- BookingDeleteView,
- bookingDelete,
- BookingListView,
- booking_stats_view,
- booking_stats_json,
- quick_create,
- booking_modify_image
-)
-
-app_name = 'booking'
-urlpatterns = [
- 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'^modify/(?P<booking_id>[0-9]+)/image/$', booking_modify_image, name='modify_booking_image'),
- 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'),
- url(r'^quick/$', quick_create, name='quick_create'),
-]
diff --git a/src/booking/views.py b/src/booking/views.py
deleted file mode 100644
index 367a18d..0000000
--- a/src/booking/views.py
+++ /dev/null
@@ -1,211 +0,0 @@
-##############################################################################
-# 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 django.contrib import messages
-from django.shortcuts import get_object_or_404
-from django.http import JsonResponse, HttpResponse
-from django.utils import timezone
-from django.views import View
-from django.views.generic import TemplateView
-from django.shortcuts import redirect, render
-from django.db.models import Q
-from django.urls import reverse
-
-from resource_inventory.models import ResourceBundle, ResourceProfile, Image, ResourceQuery
-from account.models import Downtime, Lab
-from booking.models import Booking
-from booking.stats import StatisticsManager
-from booking.forms import HostReImageForm
-from workflow.forms import FormUtils
-from api.models import JobFactory, GeneratedCloudConfig
-from workflow.views import login
-from booking.forms import QuickBookingForm
-from booking.quick_deployer import create_from_form, drop_filter
-import traceback
-
-
-def quick_create_clear_fields(request):
- request.session['quick_create_forminfo'] = None
-
-
-def quick_create(request):
- if not request.user.is_authenticated:
- return login(request)
-
- if request.method == 'GET':
- context = {}
- attrs = FormUtils.getLabData(user=request.user)
- context['form'] = QuickBookingForm(lab_data=attrs, default_user=request.user.username, user=request.user)
- context['lab_profile_map'] = {}
- context.update(drop_filter(request.user))
- context['contact_email'] = Lab.objects.filter(name="UNH_IOL").first().contact_email
- return render(request, 'booking/quick_deploy.html', context)
-
- if request.method == 'POST':
- attrs = FormUtils.getLabData(user=request.user)
- form = QuickBookingForm(request.POST, lab_data=attrs, user=request.user)
-
- context = {}
- context['lab_profile_map'] = {}
- context['form'] = form
-
- if form.is_valid():
- try:
- booking = create_from_form(form, request)
- messages.success(request, "We've processed your request. "
- "Check Account->My Bookings for the status of your new booking")
- return redirect(reverse('booking:booking_detail', kwargs={'booking_id': booking.id}))
- except Exception as e:
- print("Error occurred while handling quick deployment:")
- traceback.print_exc()
- print(str(e))
- messages.error(request, "Whoops, an error occurred: " + str(e))
- context.update(drop_filter(request.user))
- return render(request, 'booking/quick_deploy.html', context)
- else:
- messages.error(request, "Looks like the form didn't validate. Check that you entered everything correctly")
- context['status'] = 'false'
- context.update(drop_filter(request.user))
- return render(request, 'booking/quick_deploy.html', context)
-
-
-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'
- contact = Lab.objects.filter(name="UNH_IOL").first().contact_email
- downtime = Downtime.objects.filter(lab=booking.lab, start__lt=timezone.now, end__gt=timezone.now()).first()
- context = super(BookingView, self).get_context_data(**kwargs)
- context.update({
- 'title': title,
- 'booking': booking,
- 'downtime': downtime,
- 'contact_email': contact
- })
- return context
-
-
-class BookingDeleteView(TemplateView):
- template_name = "booking/booking_delete.html"
-
- def get_context_data(self, **kwargs):
- booking = get_object_or_404(Booking, id=self.kwargs['booking_id'])
- title = 'Delete Booking'
- context = super(BookingDeleteView, self).get_context_data(**kwargs)
- 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"
-
- def get_context_data(self, **kwargs):
- bookings = Booking.objects.filter(end__gte=timezone.now())
- title = 'Search Booking'
- context = super(BookingListView, self).get_context_data(**kwargs)
- context.update({'title': title, 'bookings': bookings})
- return context
-
-
-class ResourceBookingsJSON(View):
- def get(self, request, *args, **kwargs):
- resource = get_object_or_404(ResourceBundle, id=self.kwargs['resource_id'])
- bookings = resource.booking_set.get_queryset().values(
- 'id',
- 'start',
- 'end',
- 'purpose',
- 'config_bundle__name'
- )
- return JsonResponse({'bookings': list(bookings)})
-
-
-def build_image_mapping(lab, user):
- mapping = {}
- for profile in ResourceProfile.objects.filter(labs=lab):
- images = Image.objects.filter(
- from_lab=lab,
- architecture=profile.architecture
- ).filter(
- Q(public=True) | Q(owner=user)
- )
- mapping[profile.name] = [{"name": image.name, "value": image.id} for image in images]
- return mapping
-
-
-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)
- allowed_users = set(list(booking.collaborators.all()))
- allowed_users.add(booking.owner)
- if user not in allowed_users:
- return render(request, "dashboard/login.html", {'title': 'This page is private'})
-
- context = {
- 'title': 'Booking Details',
- 'booking': booking,
- 'pdf': booking.pdf,
- 'user_id': user.id,
- 'image_mapping': build_image_mapping(booking.lab, user),
- 'posix_username': GeneratedCloudConfig._normalize_username(None, user.username)
- }
-
- return render(
- request,
- "booking/booking_detail.html",
- context
- )
-
-
-def booking_modify_image(request, booking_id):
- form = HostReImageForm(request.POST)
- if form.is_valid():
- booking = Booking.objects.get(id=booking_id)
- if request.user != booking.owner:
- return HttpResponse("unauthorized")
- if timezone.now() > booking.end:
- return HttpResponse("unauthorized")
- new_image = Image.objects.get(id=form.cleaned_data['image_id'])
- host = ResourceQuery.get(id=form.cleaned_data['host_id'])
- host.config.image = new_image
- host.config.save()
- JobFactory.reimageHost(new_image, booking, host)
- return HttpResponse(new_image.name)
- return HttpResponse("error")
-
-
-def booking_stats_view(request):
- return render(
- request,
- "booking/stats.html",
- context={"data": StatisticsManager.getContinuousBookingTimeSeries(), "title": ""}
- )
-
-
-def booking_stats_json(request):
- try:
- span = int(request.GET.get("days", 14))
- except Exception:
- span = 14
- return JsonResponse(StatisticsManager.getContinuousBookingTimeSeries(span), safe=False)
diff --git a/src/dashboard/__init__.py b/src/dashboard/__init__.py
deleted file mode 100644
index b6fef6c..0000000
--- a/src/dashboard/__init__.py
+++ /dev/null
@@ -1,8 +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/src/dashboard/admin.py b/src/dashboard/admin.py
deleted file mode 100644
index bd4d96c..0000000
--- a/src/dashboard/admin.py
+++ /dev/null
@@ -1,16 +0,0 @@
-##############################################################################
-# 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
-##############################################################################
-
-
-from django.contrib import admin
-
-
-admin.site.site_header = "Laas Dashboard Administration"
-admin.site.site_title = "Laas Dashboard"
diff --git a/src/dashboard/admin_utils.py b/src/dashboard/admin_utils.py
deleted file mode 100644
index 75e4f3e..0000000
--- a/src/dashboard/admin_utils.py
+++ /dev/null
@@ -1,811 +0,0 @@
-##############################################################################
-# Copyright (c) 2021 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 resource_inventory.models import (
- ResourceTemplate,
- Image,
- Server,
- ResourceBundle,
- ResourceProfile,
- InterfaceProfile,
- PhysicalNetwork,
- ResourceConfiguration,
- NetworkConnection,
- InterfaceConfiguration,
- Network,
- DiskProfile,
- CpuProfile,
- RamProfile,
- Interface,
- CloudInitFile,
-)
-
-import json
-import yaml
-import sys
-import inspect
-import pydoc
-import csv
-
-from django.contrib.auth.models import User
-
-from account.models import (
- Lab,
- PublicNetwork
-)
-
-from resource_inventory.resource_manager import ResourceManager
-from resource_inventory.pdf_templater import PDFTemplater
-
-from booking.quick_deployer import update_template
-
-from datetime import timedelta, date, datetime, timezone
-
-from booking.models import Booking
-from notifier.manager import NotificationHandler
-from api.models import JobFactory
-
-from api.models import JobStatus, Job, GeneratedCloudConfig
-
-
-def print_div():
- """
- Utility function for printing dividers, does nothing directly useful as a utility
- """
- print("=" * 68)
-
-
-def book_host(owner_username, host_labid, lab_username, hostname, image_id, template_name, length_days=21, collaborator_usernames=[], purpose="internal", project="LaaS"):
- """
- creates a quick booking using the given host
-
- @owner_username is the simple username for the user who will own the resulting booking.
- Do not set this to a lab username!
-
- @image_id is the django id of the image in question, NOT the labid of the image.
- Query Image objects by their public status and compatible host types
-
- @host_labid is usually of the form `hpe3` or similar, is the labid of the Server (subtype of Resource) object
-
- @lab_username for iol is `unh_iol`, other labs will be documented here
-
- @hostname the hostname that the resulting host should have set
-
- @template_name the name of the (public, or user accessible) template to use for this booking
-
- @length_days how long the booking should be, no hard limit currently
-
- @collaborator_usernames a list of usernames for collaborators to the booking
-
- @purpose what this booking will be used for
-
- @project what project/group this booking is on behalf of or the owner represents
- """
- lab = Lab.objects.get(lab_user__username=lab_username)
- host = Server.objects.filter(lab=lab).get(labid=host_labid)
- if host.booked:
- print("Can't book host, already marked as booked")
- return
- else:
- host.booked = True
- host.save()
-
- template = ResourceTemplate.objects.filter(public=True).get(name=template_name)
- image = Image.objects.get(id=image_id)
-
- owner = User.objects.get(username=owner_username)
-
- new_template = update_template(template, image, hostname, owner)
-
- rmanager = ResourceManager.getInstance()
-
- vlan_map = rmanager.get_vlans(new_template)
-
- # only a single host so can reuse var for iter here
- resource_bundle = ResourceBundle.objects.create(template=new_template)
- res_configs = new_template.getConfigs()
-
- for config in res_configs:
- try:
- host.bundle = resource_bundle
- host.config = config
- rmanager.configureNetworking(resource_bundle, host, vlan_map)
- host.save()
- except Exception:
- host.booked = False
- host.save()
- print("Failed to book host due to error configuring it")
- return
-
- new_template.save()
-
- booking = Booking.objects.create(
- purpose=purpose,
- project=project,
- lab=lab,
- owner=owner,
- start=timezone.now(),
- end=timezone.now() + timedelta(days=int(length_days)),
- resource=resource_bundle,
- opnfv_config=None
- )
-
- booking.pdf = PDFTemplater.makePDF(booking)
-
- booking.save()
-
- for collaborator_username in collaborator_usernames:
- try:
- user = User.objects.get(username=collaborator_username)
- booking.collaborators.add(user)
- except Exception:
- print("couldn't add user with username ", collaborator_username)
-
- booking.save()
-
- JobFactory.makeCompleteJob(booking)
- NotificationHandler.notify_new_booking(booking)
-
-
-def mark_working(host_labid, lab_username, working=True):
- """
- Mark a host working/not working so that it is either bookable or hidden in the dashboard.
-
- @host_labid is usually of the form `hpe3` or similar, is the labid of the Server (subtype of Resource) object
-
- @lab_username: param of the form `unh_iol` or similar
-
- @working: bool, whether by the end of execution the host should be considered working or not working
- """
-
- lab = Lab.objects.get(lab_user__username=lab_username)
- server = Server.objects.filter(lab=lab).get(labid=host_labid)
- print("changing server working status from ", server.working, "to", working)
- server.working = working
- server.save()
-
-
-def mark_booked(host_labid, lab_username, booked=True):
- """
- Mark a host as booked/unbooked
-
- @host_labid is usually of the form `hpe3` or similar, is the labid of the Server (subtype of Resource) object
-
- @lab_username: param of the form `unh_iol` or similar
-
- @working: bool, whether by the end of execution the host should be considered booked or not booked
- """
-
- lab = Lab.objects.get(lab_user__username=lab_username)
- server = Server.objects.filter(lab=lab).get(labid=host_labid)
- print("changing server booked status from ", server.booked, "to", booked)
- server.booked = booked
- server.save()
-
-
-def get_host(host_labid, lab_username):
- """
- Returns host filtered by lab and then unique id within lab
-
- @host_labid is usually of the form `hpe3` or similar, is the labid of the Server (subtype of Resource) object
-
- @lab_username: param of the form `unh_iol` or similar
- """
- lab = Lab.objects.get(lab_user__username=lab_username)
- return Server.objects.filter(lab=lab).get(labid=host_labid)
-
-
-def get_info(host_labid, lab_username):
- """
- Returns various information on the host queried by the given parameters
-
- @host_labid is usually of the form `hpe3` or similar, is the labid of the Server (subtype of Resource) object
-
- @lab_username: param of the form `unh_iol` or similar
- """
- info = {}
- host = get_host(host_labid, lab_username)
- info['host_labid'] = host_labid
- info['booked'] = host.booked
- info['working'] = host.working
- info['profile'] = str(host.profile)
- if host.bundle:
- binfo = {}
- info['bundle'] = binfo
- if host.config:
- cinfo = {}
- info['config'] = cinfo
-
- return info
-
-
-class CumulativeData:
- use_days = 0
- count_bookings = 0
- count_extensions = 0
-
- def __init__(self, file_writer):
- self.file_writer = file_writer
-
- def account(self, booking, usage_days):
- self.count_bookings += 1
- self.count_extensions += booking.ext_count
- self.use_days += usage_days
-
- def write_cumulative(self):
- self.file_writer.writerow([])
- self.file_writer.writerow([])
- self.file_writer.writerow(['Lab Use Days', 'Count of Bookings', 'Total Extensions Used'])
- self.file_writer.writerow([self.use_days, self.count_bookings, (self.count_bookings * 2) - self.count_extensions])
-
-
-def get_years_booking_data(start_year=None, end_year=None):
- """
- Outputs yearly booking information from the past 'start_year' years (default: current year)
- until the last day of the end year (default current year) as a csv file.
- """
- if start_year is None and end_year is None:
- start = datetime.combine(date(datetime.now().year, 1, 1), datetime.min.time()).replace(tzinfo=timezone.utc)
- end = datetime.combine(date(start.year + 1, 1, 1), datetime.min.time()).replace(tzinfo=timezone.utc)
- elif end_year is None:
- start = datetime.combine(date(start_year, 1, 1), datetime.min.time()).replace(tzinfo=timezone.utc)
- end = datetime.combine(date(datetime.now().year, 1, 1), datetime.min.time()).replace(tzinfo=timezone.utc)
- else:
- start = datetime.combine(date(start_year, 1, 1), datetime.min.time()).replace(tzinfo=timezone.utc)
- end = datetime.combine(date(end_year + 1, 1, 1), datetime.min.time()).replace(tzinfo=timezone.utc)
-
- if (start.year == end.year - 1):
- file_name = "yearly_booking_data_" + str(start.year) + ".csv"
- else:
- file_name = "yearly_booking_data_" + str(start.year) + "-" + str(end.year - 1) + ".csv"
-
- with open(file_name, "w", newline="") as file:
- file_writer = csv.writer(file)
- cumulative_data = CumulativeData(file_writer)
- file_writer.writerow(
- [
- 'ID',
- 'Project',
- 'Purpose',
- 'User',
- 'Collaborators',
- 'Extensions Left',
- 'Usage Days',
- 'Start',
- 'End'
- ]
- )
-
- for booking in Booking.objects.filter(start__gte=start, start__lte=end):
- filtered = False
- booking_filter = [279]
- user_filter = ["ParkerBerberian", "ssmith", "ahassick", "sbergeron", "jhodgdon", "rhodgdon", "aburch", "jspewock"]
- user = booking.owner.username if booking.owner.username is not None else "None"
-
- for b in booking_filter:
- if b == booking.id:
- filtered = True
-
- for u in user_filter:
- if u == user:
- filtered = True
- # trims time delta to the the specified year(s) if between years
- usage_days = ((end if booking.end > end else booking.end) - (start if booking.start < start else booking.start)).days
- collaborators = []
-
- for c in booking.collaborators.all():
- collaborators.append(c.username)
-
- if (not filtered):
- cumulative_data.account(booking, usage_days)
- file_writer.writerow([
- str(booking.id),
- str(booking.project),
- str(booking.purpose),
- str(booking.owner.username),
- ','.join(collaborators),
- str(booking.ext_count),
- str(usage_days),
- str(booking.start),
- str(booking.end)
- ])
- cumulative_data.write_cumulative()
-
-
-def map_cntt_interfaces(labid: str):
- """
- Use this during cntt migrations, call it with a host labid and it will change profiles for this host
- as well as mapping its interfaces across. interface ens1f2 should have the mac address of interface eno50
- as an invariant before calling this function
- """
- host = get_host(labid, "unh_iol")
- host.profile = ResourceProfile.objects.get(name="HPE x86 CNTT")
- host.save()
- host = get_host(labid, "unh_iol")
-
- for iface in host.interfaces.all():
- new_ifprofile = None
- if iface.profile.name == "ens1f2":
- new_ifprofile = InterfaceProfile.objects.get(host=host.profile, name="eno50")
- else:
- new_ifprofile = InterfaceProfile.objects.get(host=host.profile, name=iface.profile.name)
-
- iface.profile = new_ifprofile
-
- iface.save()
-
-
-def detect_leaked_hosts(labid="unh_iol"):
- """
- Use this to try to detect leaked hosts.
- These hosts may still be in the process of unprovisioning,
- but if they are not (or unprovisioning is frozen) then
- these hosts are instead leaked
- """
- working_servers = Server.objects.filter(working=True, lab__lab_user__username=labid)
- booked = working_servers.filter(booked=True)
- filtered = booked
- print_div()
- print("In use now:")
- for booking in Booking.objects.filter(end__gte=timezone.now()):
- res_for_booking = booking.resource.get_resources()
- print(res_for_booking)
- for resource in res_for_booking:
- filtered = filtered.exclude(id=resource.id)
- print_div()
- print("Possibly leaked:")
- for host in filtered:
- print(host)
- print_div()
- return filtered
-
-
-def booking_for_host(host_labid: str, lab_username="unh_iol"):
- """
- Returns the booking that this server is a part of, if any.
- Fails with an exception if no such booking exists
-
- @host_labid is usually of the form `hpe3` or similar, is the labid of the Server (subtype of Resource) object
-
- @lab_username: param of the form `unh_iol` or similar
- """
- server = Server.objects.get(lab__lab_user__username=lab_username, labid=host_labid)
- booking = server.bundle.booking_set.first()
- print_div()
- print(booking)
- print("id:", booking.id)
- print("owner:", booking.owner)
- print("job (id):", booking.job, "(" + str(booking.job.id) + ")")
- print_div()
- return booking
-
-
-def force_release_booking(booking_id: int):
- """
- Takes a booking id and forces the booking to end whether or not the tasks have
- completed normally.
-
- Use with caution! Hosts may or may not be released depending on other underlying issues
-
- @booking_id: the id of the Booking object to be released
- """
- booking = Booking.objects.get(id=booking_id)
- job = booking.job
- tasks = job.get_tasklist()
- for task in tasks:
- task.status = JobStatus.DONE
- task.save()
-
-
-def free_leaked_public_vlans(safety_buffer_days=2):
- for lab in Lab.objects.all():
- current_booking_set = Booking.objects.filter(end__gte=timezone.now() + timedelta(days=safety_buffer_days))
-
- marked_nets = set()
-
- for booking in current_booking_set:
- for network in get_network_metadata(booking.id):
- marked_nets.add(network["vlan_id"])
-
- for net in PublicNetwork.objects.filter(lab=lab).filter(in_use=True):
- if net.vlan not in marked_nets:
- lab.vlan_manager.release_public_vlan(net.vlan)
-
-
-def get_network_metadata(booking_id: int):
- """
- Takes a booking id and prints all (known) networks that are owned by it.
- Returns an object of the form {<network name>: {"vlan_id": int, "netname": str <network name>, "public": bool <whether network is public/routable}}
-
- @booking_id: the id of the Booking object to be queried
- """
- booking = Booking.objects.get(id=booking_id)
- bundle = booking.resource
- pnets = PhysicalNetwork.objects.filter(bundle=bundle).all()
- metadata = {}
- for pnet in pnets:
- net = pnet.generic_network
- mdata = {"vlan_id": pnet.vlan_id, "netname": net.name, "public": net.is_public}
- metadata[net.name] = mdata
- return metadata
-
-
-def print_dict_pretty(a_dict):
- """
- admin_utils internal function
- """
-
- print(json.dumps(a_dict, sort_keys=True, indent=4))
-
-
-def import_host(filenames):
- """
- Imports host from an array of converted inspection files and if needed creates a new profile for the host.
- NOTE: CONVERT INSPECTION FILES USING convert_inspect_results(["file", "file"])
- (original file names not including "-import.yaml" i.e. hpe44) AND FILL IN <NEEDED FIELDS> BEFORE THIS
- @filenames: array of host import file names to import
- """
-
- for filename in filenames:
-
- # open import file
- file = open("dashboard/" + filename + "-import.yaml", "r")
- data = yaml.safe_load(file)
-
- # if a new profile is needed create one and a matching template
- if (data["new_profile"]):
- add_profile(data)
- print("Profile: " + data["name"] + " created!")
- make_default_template(
- ResourceProfile.objects.get(name=data["name"]),
- Image.objects.get(lab_id=data["image"]).id,
- None,
- None,
- False,
- False,
- data["owner"],
- "unh_iol",
- True,
- False,
- data["temp_desc"]
- )
-
- print(" Template: " + data["temp_name"] + " created!")
-
- # add the server
- add_server(
- ResourceProfile.objects.get(name=data["name"]),
- data["hostname"],
- data["interfaces"],
- data["lab"],
- data["vendor"],
- data["model"]
- )
-
- print(data["hostname"] + " imported!")
-
-
-def convert_inspect_results(files):
- """
- Converts an array of inspection result files into templates (filename-import.yaml) to be filled out for importing the servers into the dashboard
- @files an array of file names (not including the file type. i.e hpe44). Default: []
- """
- for filename in files:
- # open host inspect file
- file = open("dashboard/" + filename + ".yaml")
- output = open("dashboard/" + filename + "-import.yaml", "w")
- data = json.load(file)
-
- # gather data about disks
- disk_data = {}
- for i in data["disk"]:
-
- # don't include loops in disks
- if "loop" not in i:
- disk_data[i["name"]] = {
- "capacity": i["size"][:-3],
- "media_type": "<\"SSD\" or \"HDD\">",
- "interface": "<\"sata\", \"sas\", \"ssd\", \"nvme\", \"scsi\", or \"iscsi\">",
- }
-
- # gather interface data
- interface_data = {}
- for i in data["interfaces"]:
- interface_data[data["interfaces"][i]["name"]] = {
- "speed": data["interfaces"][i]["speed"],
- "nic_type": "<\"onboard\" or \"pcie\">",
- "order": "<order in switch>",
- "mac_address": data["interfaces"][i]["mac"],
- "bus_addr": data["interfaces"][i]["busaddr"],
- }
-
- # gather cpu data
- cpu_data = {
- "cores": data["cpu"]["cores"],
- "architecture": data["cpu"]["arch"],
- "cpus": data["cpu"]["cpus"],
- "cflags": "<cflags string>",
- }
-
- # gather ram data
- ram_data = {
- "amount": data["memory"][:-1],
- "channels": "<int of ram channels used>",
- }
-
- # assemble data for host import file
- import_data = {
- "new_profile": "<True or False> (Set to True to create a new profile for the host's type)",
- "name": "<profile name> (Used to set the profile of a host and for creating a new profile)",
- "description": "<profile description>",
- "labs": "<labs using profile>",
- "temp_name": "<Template name>",
- "temp_desc": "<template description>",
- "image": "<image lab_id>",
- "owner": "<template owner>",
- "hostname": data["hostname"],
- "lab": "<lab server is in> (i.e. \"unh_iol\")",
- "disks": disk_data,
- "interfaces": interface_data,
- "cpus": cpu_data,
- "ram": ram_data,
- "vendor": "<host vendor>",
- "model": "<host model>",
- }
-
- # export data as yaml
- yaml.dump(import_data, output)
-
-
-def add_profile(data):
- """
- Used for adding a host profile to the dashboard
-
- schema (of dict passed as "data" param):
- {
- "name": str
- "description": str
- "labs": [
- str (lab username)
- ]
- "disks": {
- <diskname> : {
- capacity: int (GiB)
- media_type: str ("SSD" or "HDD")
- interface: str ("sata", "sas", "ssd", "nvme", "scsi", or "iscsi")
- }
- }
- interfaces: {
- <intname>: {
- "speed": int (mbit)
- "nic_type": str ("onboard" or "pcie")
- "order": int (compared to the other interfaces, indicates the "order" that the ports are laid out)
- }
- }
- cpus: {
- cores: int (hardware threads count)
- architecture: str (x86_64" or "aarch64")
- cpus: int (number of sockets)
- cflags: str
- }
- ram: {
- amount: int (GiB)
- channels: int
- }
- }
- """
- base_profile = ResourceProfile.objects.create(name=data['name'], description=data['description'])
- base_profile.save()
-
- for lab_username in data['labs']:
- lab = Lab.objects.get(lab_user__username=lab_username)
-
- base_profile.labs.add(lab)
- base_profile.save()
-
- for diskname in data['disks'].keys():
- disk = data['disks'][diskname]
-
- disk_profile = DiskProfile.objects.create(name=diskname, size=disk['capacity'], media_type=disk['media_type'], interface=disk['interface'], host=base_profile)
- disk_profile.save()
-
- for ifacename in data['interfaces'].keys():
- iface = data['interfaces'][ifacename]
-
- iface_profile = InterfaceProfile.objects.create(name=ifacename, speed=iface['speed'], nic_type=iface['nic_type'], order=iface['order'], host=base_profile)
- iface_profile.save()
-
- cpu = data['cpus']
- cpu_prof = CpuProfile.objects.create(cores=cpu['cores'], architecture=cpu['architecture'], cpus=cpu['cpus'], cflags=cpu['cflags'], host=base_profile)
- cpu_prof.save()
-
- ram_prof = RamProfile.objects.create(amount=data['ram']['amount'], channels=data['ram']['channels'], host=base_profile)
- ram_prof.save()
-
-
-def make_default_template(resource_profile, image_id=None, template_name=None, connected_interface_names=None, interfaces_tagged=False, connected_interface_tagged=False, owner_username="root", lab_username="unh_iol", public=True, temporary=False, description=""):
- """
- Do not call this function without reading the related source code, it may have unintended effects.
-
- Used for creating a default template from some host profile
- """
-
- if not resource_profile:
- raise Exception("No viable continuation from none resource_profile")
-
- if not template_name:
- template_name = resource_profile.name
-
- if not connected_interface_names:
- connected_interface_names = [InterfaceProfile.objects.filter(host=resource_profile).first().name]
- print("setting connected interface names to", connected_interface_names)
-
- if not image_id:
- image_id = Image.objects.filter(host_type=resource_profile).first().id
-
- image = Image.objects.get(id=image_id)
-
- base = ResourceTemplate.objects.create(
- name=template_name,
- xml="",
- owner=User.objects.get(username=owner_username),
- lab=Lab.objects.get(lab_user__username=lab_username), description=description,
- public=public, temporary=temporary, copy_of=None)
-
- rconf = ResourceConfiguration.objects.create(profile=resource_profile, image=image, template=base, is_head_node=True, name="opnfv_host")
- rconf.save()
-
- connected_interfaces = []
-
- for iface_prof in InterfaceProfile.objects.filter(host=resource_profile).all():
- iface_conf = InterfaceConfiguration.objects.create(profile=iface_prof, resource_config=rconf)
-
- if iface_prof.name in connected_interface_names:
- connected_interfaces.append(iface_conf)
-
- network = Network.objects.create(name="public", bundle=base, is_public=True)
-
- for iface in connected_interfaces:
- connection = NetworkConnection.objects.create(network=network, vlan_is_tagged=interfaces_tagged)
- connection.save()
-
- iface.connections.add(connection)
- print("adding connection to iface ", iface)
- iface.save()
- connection.save()
-
-
-def add_server(profile, name, interfaces, lab_username="unh_iol", vendor="unknown", model="unknown"):
- """
- Used to enroll a new host of some profile
-
- @profile: the ResourceProfile in question (by reference to a model object)
-
- @name: the unique name of the server, currently indistinct from labid
-
- @interfaces: interfaces should be dict from interface name (eg ens1f0) to dict of schema:
- {
- mac_address: <mac addr>,
- bus_addr: <bus addr>, //this field is optional, "" is default
- }
-
- @lab_username: username of the lab to be added to
-
- @vendor: vendor name of the host, such as "HPE" or "Gigabyte"
-
- @model: specific model of the host, such as "DL380 Gen 9"
-
- """
- server = Server.objects.create(
- bundle=None,
- profile=profile,
- config=None,
- working=True,
- vendor=vendor,
- model=model,
- labid=name,
- lab=Lab.objects.get(lab_user__username=lab_username),
- name=name,
- booked=False)
-
- for iface_prof in InterfaceProfile.objects.filter(host=profile).all():
- mac_addr = interfaces[iface_prof.name]["mac_address"]
- bus_addr = "unknown"
- if "bus_addr" in interfaces[iface_prof.name].keys():
- bus_addr = interfaces[iface_prof.name]["bus_addr"]
-
- iface = Interface.objects.create(acts_as=None, profile=iface_prof, mac_address=mac_addr, bus_address=bus_addr)
- iface.save()
-
- server.interfaces.add(iface)
- server.save()
-
-
-def extend_booking(booking_id, days=0, hours=0, minutes=0, weeks=0):
- """
- Extend a booking by n <days, hours, minutes, weeks>
-
- @booking_id: id of the booking
-
- @days/@hours/@minutes/@weeks: the cumulative amount of delta to add to the length of the booking
- """
-
- booking = Booking.objects.get(id=booking_id)
- booking.end = booking.end + timedelta(days=days, hours=hours, minutes=minutes, weeks=weeks)
- booking.save()
-
-
-def regenerate_cloud_configs(booking_id):
- b = Booking.objects.get(id=booking_id)
- for res in b.resource.get_resources():
- res.config.cloud_init_files.set(res.config.cloud_init_files.filter(generated=False)) # careful!
- res.config.save()
- cif = GeneratedCloudConfig.objects.create(resource_id=res.labid, booking=b, rconfig=res.config)
- cif.save()
- cif = CloudInitFile.create(priority=0, text=cif.serialize())
- cif.save()
- res.config.cloud_init_files.add(cif)
- res.config.save()
-
-
-def set_job_new(job_id):
- j = Job.objects.get(id=job_id)
- b = j.booking
- regenerate_cloud_configs(b.id)
- for task in j.get_tasklist():
- task.status = JobStatus.NEW
- task.save()
- j.status = JobStatus.NEW
- j.save()
-
-
-def docs(function=None, fulltext=False):
- """
- Print documentation for a given function in admin_utils.
- Call without arguments for more information
- """
-
- fn = None
-
- if isinstance(function, str):
- try:
- fn = globals()[function]
- except KeyError:
- print("Couldn't find a function by the given name")
- return
- elif callable(function):
- fn = function
- else:
- print("docs(function: callable | str, fulltext: bool) was called with a 'function' that was neither callable nor a string name of a function")
- print("usage: docs('some_function_in_admin_utils', fulltext=True)")
- print("The 'fulltext' argument is used to choose if you want the complete source of the function printed. If this argument is false then you will only see the pydoc rendered documentation for the function")
- return
-
- if not fn:
- print("couldn't find a function by that name")
-
- if not fulltext:
- print("Pydoc documents the function as such:")
- print(pydoc.render_doc(fn))
- else:
- print("The full source of the function is this:")
- print(inspect.getsource(fn))
-
-
-def admin_functions():
- """
- List functions available to call within admin_utils
- """
-
- return [name for name, func in inspect.getmembers(sys.modules[__name__]) if (inspect.isfunction(func) and func.__module__ == __name__)]
-
-
-print("Hint: call `docs(<function name>)` or `admin_functions()` for help on using the admin utils")
-print("docs(<function name>) displays documentation on a given function")
-print("admin_functions() lists all functions available to call within this module")
diff --git a/src/dashboard/apps.py b/src/dashboard/apps.py
deleted file mode 100644
index e0c4f44..0000000
--- a/src/dashboard/apps.py
+++ /dev/null
@@ -1,15 +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.apps import AppConfig
-
-
-class DashboardConfig(AppConfig):
- name = 'dashboard'
diff --git a/src/dashboard/context_processors.py b/src/dashboard/context_processors.py
deleted file mode 100644
index 338f609..0000000
--- a/src/dashboard/context_processors.py
+++ /dev/null
@@ -1,13 +0,0 @@
-##############################################################################
-# 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/src/dashboard/exceptions.py b/src/dashboard/exceptions.py
deleted file mode 100644
index 61df145..0000000
--- a/src/dashboard/exceptions.py
+++ /dev/null
@@ -1,52 +0,0 @@
-##############################################################################
-# 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
-
-
-class BookingLengthException(Exception):
- pass
diff --git a/src/dashboard/fixtures/dashboard.json b/src/dashboard/fixtures/dashboard.json
deleted file mode 100644
index f0ac3b2..0000000
--- a/src/dashboard/fixtures/dashboard.json
+++ /dev/null
@@ -1,164 +0,0 @@
-[
-{
- "model": "dashboard.resource",
- "pk": 1,
- "fields": {
- "name": "Linux Foundation POD 1",
- "description": "Some description",
- "url": "https://wiki.opnfv.org/display/pharos/Lf+Lab"
- }
-},
-{
- "model": "dashboard.resource",
- "pk": 2,
- "fields": {
- "name": "Linux Foundation POD 2",
- "description": "Some description",
- "url": "https://wiki.opnfv.org/display/pharos/Lf+Lab"
- }
-},
-{
- "model": "dashboard.resource",
- "pk": 3,
- "fields": {
- "name": "Ericsson POD 2",
- "description": "Some description",
- "url": "https://wiki.opnfv.org/display/pharos/Ericsson+Hosting+and+Request+Process"
- }
-},
-{
- "model": "dashboard.resource",
- "pk": 4,
- "fields": {
- "name": "Intel POD 2",
- "description": "Some description",
- "url": "https://wiki.opnfv.org/display/pharos/Intel+Pod2"
- }
-},
-{
- "model": "dashboard.resource",
- "pk": 5,
- "fields": {
- "name": "Intel POD 5",
- "description": "Some description",
- "url": "https://wiki.opnfv.org/display/pharos/Intel+Pod5"
- }
-},
-{
- "model": "dashboard.resource",
- "pk": 6,
- "fields": {
- "name": "Intel POD 6",
- "description": "Some description",
- "url": "https://wiki.opnfv.org/display/pharos/Intel+Pod6"
- }
-},
-{
- "model": "dashboard.resource",
- "pk": 7,
- "fields": {
- "name": "Intel POD 8",
- "description": "Some description",
- "url": "https://wiki.opnfv.org/display/pharos/Intel+Pod8"
- }
-},
-{
- "model": "dashboard.resource",
- "pk": 8,
- "fields": {
- "name": "Huawei POD 1",
- "description": "Some description",
- "url": "https://wiki.opnfv.org/display/pharos/Huawei+Hosting"
- }
-},
-{
- "model": "dashboard.resource",
- "pk": 9,
- "fields": {
- "name": "Intel POD 3",
- "description": "Some description",
- "url": "https://wiki.opnfv.org/display/pharos/Intel+Pod3"
- }
-},
-{
- "model": "dashboard.resource",
- "pk": 10,
- "fields": {
- "name": "Dell POD 1",
- "description": "Some description",
- "url": "https://wiki.opnfv.org/display/pharos/Dell+Hosting"
- }
-},
-{
- "model": "dashboard.resource",
- "pk": 11,
- "fields": {
- "name": "Dell POD 2",
- "description": "Some description",
- "url": "https://wiki.opnfv.org/display/pharos/Dell+Hosting"
- }
-},
-{
- "model": "dashboard.resource",
- "pk": 12,
- "fields": {
- "name": "Orange POD 2",
- "description": "Some description",
- "url": "https://wiki.opnfv.org/display/pharos/Opnfv-orange-pod2"
- }
-},
-{
- "model": "dashboard.resource",
- "pk": 13,
- "fields": {
- "name": "Arm POD 1",
- "description": "Some description",
- "url": "https://wiki.opnfv.org/display/pharos/Enea-pharos-lab"
- }
-},
-{
- "model": "dashboard.resource",
- "pk": 14,
- "fields": {
- "name": "Ericsson POD 1",
- "description": "Some description",
- "url": "https://wiki.opnfv.org/display/pharos/Ericsson+Hosting+and+Request+Process"
- }
-},
-{
- "model": "dashboard.resource",
- "pk": 15,
- "fields": {
- "name": "Huawei POD 2",
- "description": "Some description",
- "url": "https://wiki.opnfv.org/display/pharos/Huawei+Hosting"
- }
-},
-{
- "model": "dashboard.resource",
- "pk": 16,
- "fields": {
- "name": "Huawei POD 3",
- "description": "Some description",
- "url": "https://wiki.opnfv.org/display/pharos/Huawei+Hosting"
- }
-},
-{
- "model": "dashboard.resource",
- "pk": 17,
- "fields": {
- "name": "Huawei POD 4",
- "description": "Some description",
- "url": "https://wiki.opnfv.org/display/pharos/Huawei+Hosting"
- }
-},
-{
- "model": "dashboard.resource",
- "pk": 18,
- "fields": {
- "name": "Intel POD 9",
- "description": "Some description",
- "url": "https://wiki.opnfv.org/display/pharos/Intel+Pod9"
- }
-}
-]
diff --git a/src/dashboard/models.py b/src/dashboard/models.py
deleted file mode 100644
index f9bd07e..0000000
--- a/src/dashboard/models.py
+++ /dev/null
@@ -1,9 +0,0 @@
-##############################################################################
-# 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/src/dashboard/populate_db_iol.py b/src/dashboard/populate_db_iol.py
deleted file mode 100644
index d8df03f..0000000
--- a/src/dashboard/populate_db_iol.py
+++ /dev/null
@@ -1,352 +0,0 @@
-##############################################################################
-# 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
-##############################################################################
-
-import json
-import yaml
-
-from account.models import Lab, UserProfile
-from django.contrib.auth.models import User
-from resource_inventory.models import (
- HostProfile,
- InterfaceProfile,
- DiskProfile,
- CpuProfile,
- RamProfile,
- VlanManager,
- Scenario,
- Installer,
- Opsys,
- OPNFVRole,
- Image,
- Interface,
- Host
-)
-
-
-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)
- 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
- OPNFVRole.objects.create(name="Compute", description="Does the heavy lifting")
- OPNFVRole.objects.create(name="Controller", description="Controls everything")
- OPNFVRole.objects.create(name="Jumphost", description="Entry Point")
-
- lab = Lab.objects.first()
- user = UserProfile.objects.first().user
- Image.objects.create(
- lab_id=23,
- name="hpe centos",
- from_lab=lab,
- owner=user,
- host_type=HostProfile.objects.get(name="hpe")
- )
- Image.objects.create(
- lab_id=25,
- name="hpe ubuntu",
- from_lab=lab,
- owner=user,
- host_type=HostProfile.objects.get(name="hpe")
- )
-
- Image.objects.create(
- lab_id=26,
- name="hpe suse",
- from_lab=lab,
- owner=user,
- host_type=HostProfile.objects.get(name="hpe")
- )
-
- 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):
- """
- Create Profile Data.
-
- 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 = "/laas_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 Exception:
- 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/src/dashboard/tasks.py b/src/dashboard/tasks.py
deleted file mode 100644
index 93e6a22..0000000
--- a/src/dashboard/tasks.py
+++ /dev/null
@@ -1,98 +0,0 @@
-##############################################################################
-# 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 celery import shared_task
-from django.utils import timezone
-from booking.models import Booking
-from notifier.manager import NotificationHandler
-from api.models import (
- Job,
- JobStatus,
- SoftwareRelation,
- HostHardwareRelation,
- HostNetworkRelation,
- AccessRelation,
- JobFactory
-)
-
-from resource_inventory.resource_manager import ResourceManager
-from resource_inventory.models import ConfigState
-
-
-@shared_task
-def booking_poll():
- def cleanup_resource_task(qs):
- for hostrelation in qs:
- hostrelation.config.state = ConfigState.CLEAN
- hostrelation.config.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:
- if "vpn" in relation.config.access_type.lower():
- relation.config.set_revoke(True)
- relation.config.save()
- relation.status = JobStatus.NEW
- relation.save()
-
- 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_resource_task(HostHardwareRelation.objects.filter(job=job))
- cleanup_resource_task(HostNetworkRelation.objects.filter(job=job))
- cleanup_access(AccessRelation.objects.filter(job=job))
- job.complete = True
- job.save()
- NotificationHandler.notify_booking_end(booking)
-
-
-@shared_task
-def free_hosts():
- """Free all hosts that should be freed."""
- undone_statuses = [JobStatus.NEW, JobStatus.CURRENT, JobStatus.ERROR]
- undone_jobs = Job.objects.filter(
- hostnetworkrelation__status__in=undone_statuses,
- hosthardwarerelation__status__in=undone_statuses
- )
-
- bookings = Booking.objects.exclude(
- job__in=undone_jobs
- ).filter(
- end__lt=timezone.now(),
- job__complete=True,
- complete=False,
- resource__isnull=False,
- )
-
- for booking in bookings:
- ResourceManager.getInstance().releaseResourceBundle(booking.resource)
- booking.complete = True
- print("Booking", booking.id, "is now completed")
- booking.save()
-
-
-@shared_task
-def query_vpn_users():
- """ get active vpn users """
- JobFactory.makeActiveUsersTask()
diff --git a/src/dashboard/templatetags/__init__.py b/src/dashboard/templatetags/__init__.py
deleted file mode 100644
index b6fef6c..0000000
--- a/src/dashboard/templatetags/__init__.py
+++ /dev/null
@@ -1,8 +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/src/dashboard/testing_utils.py b/src/dashboard/testing_utils.py
deleted file mode 100644
index 5be6379..0000000
--- a/src/dashboard/testing_utils.py
+++ /dev/null
@@ -1,315 +0,0 @@
-##############################################################################
-# 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.contrib.auth.models import User
-from django.core.files.base import ContentFile
-from django.utils import timezone
-
-import json
-from datetime import timedelta
-
-from booking.models import Booking
-from account.models import UserProfile, Lab, LabStatus, VlanManager, PublicNetwork
-from resource_inventory.models import (
- ResourceTemplate,
- ResourceProfile,
- ResourceConfiguration,
- InterfaceProfile,
- InterfaceConfiguration,
- Server,
- DiskProfile,
- CpuProfile,
- Opsys,
- Image,
- Scenario,
- Installer,
- OPNFVRole,
- RamProfile,
- Network,
-)
-from resource_inventory.resource_manager import ResourceManager
-
-"""
-Info for make_booking() function:
-[topology] argument structure:
- the [topology] argument should describe the structure of the pod
- the top level should be a dictionary, with each key being a hostname
- each value in the top level should be a dictionary with two keys:
- "type" should map to a host profile instance
- "nets" should map to a list of interfaces each with a list of
- dictionaries each defining a network in the format
- { "name": "netname", "tagged": True|False, "public": True|False }
- each network is defined if a matching name is not found
-
- sample argument structure:
- topology={
- "host1": {
- "type": instanceOf HostProfile,
- "role": instanceOf OPNFVRole
- "image": instanceOf Image
- "nets": [
- 0: [
- 0: { "name": "public", "tagged": True, "public": True },
- 1: { "name": "private", "tagged": False, "public": False },
- ]
- 1: []
- ]
- }
- }
-"""
-
-
-def make_booking(owner=None, start=timezone.now(),
- end=timezone.now() + timedelta(days=1),
- lab=None, purpose="my_purpose",
- project="my_project", collaborators=[],
- topology={}, installer=None, scenario=None):
-
- resource_template = make_resource_template()
- resource = ResourceManager.getInstance().convertResourceBundle(resource_template)
- if not resource:
- raise Exception("Resource not created")
-
- return Booking.objects.create(
- resource=resource,
- start=start,
- end=end,
- owner=owner,
- purpose=purpose,
- project=project,
- lab=lab,
- )
-
-
-def make_network(name, lab, grb, public):
- network = Network(name=name, bundle=grb, is_public=public)
- if public:
- public_net = lab.vlan_manager.get_public_vlan()
- if not public_net:
- raise Exception("No more public networks available")
- lab.vlan_manager.reserve_public_vlan(public_net.vlan)
- network.vlan_id = public_net.vlan
- else:
- private_nets = lab.vlan_manager.get_vlans(count=1)
- if not private_nets:
- raise Exception("No more generic vlans are available")
- lab.vlan_manager.reserve_vlans(private_nets)
- network.vlan_id = private_nets[0]
-
- network.save()
- return network
-
-
-def make_resource_template(owner=None, lab=None, name="Test Template"):
- if owner is None:
- owner = make_user(username="template_owner")
- if lab is None:
- lab = make_lab(name="template_lab")
- rt = ResourceTemplate.objects.create(name=name, owner=owner, lab=lab, public=True)
- config = make_resource_config(rt)
- make_interface_config(config)
- return rt
-
-
-def make_resource_config(template, profile=None, image=None):
- if profile is None:
- profile = make_resource_profile(lab=template.lab)
-
- if image is None:
- image = make_image(profile, lab=template.lab)
-
- return ResourceConfiguration.objects.create(profile=profile, image=image, template=template)
-
-
-def make_interface_config(resource_config):
- # lets just grab one of the iface profiles from the related host
- iface_profile = resource_config.profile.interfaceprofile.all()[0]
-
- # not adding any connections here
- return InterfaceConfiguration.objects.create(profile=iface_profile, resource_config=resource_config)
-
-
-def make_user(is_superuser=False, username="testuser",
- password="testpassword", email="default_email@user.com"):
- user = User.objects.get_or_create(username=username, email=email, password=password)[0]
-
- user.is_superuser = is_superuser
- user.save()
-
- return user
-
-
-def make_user_profile(user=None, email_addr="email@email.com",
- company="company", full_name="John Doe",
- booking_privledge=True, ssh_file=None):
- user = user or make_user()
- profile = UserProfile.objects.get_or_create(
- email_addr=email_addr,
- company=company,
- full_name=full_name,
- booking_privledge=booking_privledge,
- user=user
- )[0]
- profile.ssh_public_key.save("user_ssh_key", ssh_file if ssh_file else ContentFile("public key content string"))
-
- return profile
-
-
-def make_vlan_manager(vlans=None, block_size=20, allow_overlapping=False, reserved_vlans=None):
- if not vlans:
- vlans = [vlan % 2 for vlan in range(4095)]
- if not reserved_vlans:
- reserved_vlans = [0 for i in range(4095)]
-
- return VlanManager.objects.create(
- vlans=json.dumps(vlans),
- reserved_vlans=json.dumps(vlans),
- block_size=block_size,
- allow_overlapping=allow_overlapping
- )
-
-
-def make_lab(user=None, name="Test_Lab_Instance",
- status=LabStatus.UP, vlan_manager=None,
- pub_net_count=5):
- if Lab.objects.filter(name=name).exists():
- return Lab.objects.get(name=name)
-
- if not vlan_manager:
- vlan_manager = make_vlan_manager()
-
- if not user:
- user = make_user(username=name + " user")
-
- lab = Lab.objects.create(
- lab_user=user,
- name=name,
- contact_email='test_lab@test_site.org',
- contact_phone='603 123 4567',
- status=status,
- vlan_manager=vlan_manager,
- description='test lab instantiation',
- api_token='12345678'
- )
-
- for i in range(pub_net_count):
- make_public_net(vlan=i * 2 + 1, lab=lab)
-
- return lab
-
-
-"""
-resource_inventory instantiation section for permanent resources
-"""
-
-
-def make_resource_profile(lab, name="test_hostprofile"):
- if ResourceProfile.objects.filter(name=name).exists():
- return ResourceProfile.objects.get(name=name)
-
- resource_profile = ResourceProfile.objects.create(
- name=name,
- description='test resourceprofile instance'
- )
- resource_profile.labs.add(lab)
-
- RamProfile.objects.create(host=resource_profile, amount=8, channels=2)
- CpuProfile.objects.create(cores=4, architecture="x86_64", cpus=1, host=resource_profile)
- DiskProfile.objects.create(
- name="test disk profile",
- size=256,
- media_type="SSD",
- host=resource_profile
- )
-
- InterfaceProfile.objects.create(
- host=resource_profile,
- name="test interface profile",
- speed=1000,
- nic_type="pcie"
- )
-
- return resource_profile
-
-
-def make_image(resource_profile, lab=None, lab_id="4", owner=None, os=None,
- public=True, name="default image", description="default image"):
- if lab is None:
- lab = make_lab()
-
- if owner is None:
- owner = make_user()
-
- if os is None:
- os = make_os()
-
- return Image.objects.create(
- from_lab=lab,
- lab_id=lab_id,
- os=os,
- host_type=resource_profile,
- public=public,
- name=name,
- description=description
- )
-
-
-def make_scenario(name="test scenario"):
- return Scenario.objects.create(name=name)
-
-
-def make_installer(scenarios, name="test installer"):
- installer = Installer.objects.create(name=name)
- for scenario in scenarios:
- installer.sup_scenarios.add(scenario)
-
- return installer
-
-
-def make_os(installers=None, name="test OS"):
- if not installers:
- installers = [make_installer([make_scenario()])]
- os = Opsys.objects.create(name=name)
- for installer in installers:
- os.sup_installers.add(installer)
-
- return os
-
-
-def make_server(host_profile, lab, labid="test_host", name="test_host",
- booked=False, working=True, config=None,
- bundle=None, model="Model 1", vendor="ACME"):
- return Server.objects.create(
- lab=lab,
- profile=host_profile,
- name=name,
- booked=booked,
- working=working,
- config=config,
- bundle=bundle,
- model=model,
- vendor=vendor
- )
-
-
-def make_opnfv_role(name="Jumphost", description="test opnfvrole"):
- return OPNFVRole.objects.create(
- name=name,
- description=description
- )
-
-
-def make_public_net(vlan, lab, in_use=False,
- cidr="0.0.0.0/0", gateway="0.0.0.0"):
- return PublicNetwork.objects.create(
- lab=lab,
- vlan=vlan,
- cidr=cidr,
- gateway=gateway
- )
diff --git a/src/dashboard/tests/__init__.py b/src/dashboard/tests/__init__.py
deleted file mode 100644
index b6fef6c..0000000
--- a/src/dashboard/tests/__init__.py
+++ /dev/null
@@ -1,8 +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/src/dashboard/tests/test_views.py b/src/dashboard/tests/test_views.py
deleted file mode 100644
index f2d5490..0000000
--- a/src/dashboard/tests/test_views.py
+++ /dev/null
@@ -1,30 +0,0 @@
-##############################################################################
-# Copyright (c) 2020 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, Client
-from dashboard.testing_utils import make_lab
-
-
-class DashboardViewTestCase(TestCase):
- @classmethod
- def setUpTestData(cls):
- make_lab(name="TestLab")
- cls.client = Client()
-
- def test_landing_view_anon(self):
- response = self.client.get('/')
- self.assertEqual(response.status_code, 200)
-
- def test_lab_list_view(self):
- response = self.client.get('/lab/')
- self.assertEqual(response.status_code, 200)
-
- def test_lab_detail_view(self):
- response = self.client.get('/lab/TestLab/')
- self.assertEqual(response.status_code, 200)
diff --git a/src/dashboard/urls.py b/src/dashboard/urls.py
deleted file mode 100644
index c87dacc..0000000
--- a/src/dashboard/urls.py
+++ /dev/null
@@ -1,42 +0,0 @@
-##############################################################################
-# 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
-##############################################################################
-
-
-"""
-laas_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 dashboard.views import (
- landing_view,
- lab_list_view,
- lab_detail_view,
- host_profile_detail_view
-)
-
-app_name = 'dashboard'
-urlpatterns = [
- 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/src/dashboard/utils.py b/src/dashboard/utils.py
deleted file mode 100644
index 97c9ac7..0000000
--- a/src/dashboard/utils.py
+++ /dev/null
@@ -1,52 +0,0 @@
-##############################################################################
-# Copyright (c) 2020 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.core.exceptions import (ObjectDoesNotExist, MultipleObjectsReturned)
-
-
-class AbstractModelQuery():
- """
- This is a class made for querying abstract models.
-
- This class is itself abstract. create subclasses to
- query your own abstract models.
- """
-
- model_list = []
-
- @classmethod
- def filter(cls, *args, **kwargs):
- """
- Query all concrete model classes.
-
- Iterates over the model list and returns a list of all
- matching models from the classes given.
- Filter queries are given here as normal and are passed into the Django ORM
- for each concrete model
- """
- result = []
- for model in cls.model_list:
- result += list(model.objects.filter(*args, **kwargs))
-
- return result
-
- @classmethod
- def get(cls, *args, **kwargs):
- """
- Gets a single matching resource
- Throws ObjectDoesNotExist if none found matching, or MultipleObjectsReturned if
- the query does not narrow to a single object
- """
- try:
- ls = cls.filter(*args, **kwargs)
- if len(ls) > 1:
- raise MultipleObjectsReturned()
- return cls.filter(*args, **kwargs)[0]
- except IndexError:
- raise ObjectDoesNotExist()
diff --git a/src/dashboard/views.py b/src/dashboard/views.py
deleted file mode 100644
index ff26c64..0000000
--- a/src/dashboard/views.py
+++ /dev/null
@@ -1,119 +0,0 @@
-##############################################################################
-# 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 django.shortcuts import get_object_or_404
-from django.views.generic import TemplateView
-from django.shortcuts import render
-from django.db.models import Q
-from datetime import datetime
-import pytz
-
-from account.models import Lab
-from booking.models import Booking
-
-from resource_inventory.models import Image, ResourceProfile, ResourceQuery
-from workflow.workflow_manager import ManagerTracker
-
-from laas_dashboard import settings
-
-
-def lab_list_view(request):
- labs = Lab.objects.all()
- context = {"labs": labs, 'title': 'Labs'}
-
- return render(request, "dashboard/lab_list.html", 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)
-
- images = Image.objects.filter(from_lab=lab).filter(public=True)
- if user:
- images = images | Image.objects.filter(from_lab=lab).filter(owner=user)
-
- hosts = ResourceQuery.filter(lab=lab)
-
- return render(
- request,
- "dashboard/lab_detail.html",
- {
- 'title': "Lab Overview",
- 'lab': lab,
- 'hostprofiles': ResourceProfile.objects.filter(labs=lab),
- 'images': images,
- 'hosts': hosts
- }
- )
-
-
-def host_profile_detail_view(request):
-
- return render(
- request,
- "dashboard/host_profile_detail.html",
- {
- 'title': "Host Types",
- }
- )
-
-
-def landing_view(request):
- manager = ManagerTracker.managers.get(request.session.get('manager_session'))
- user = request.user
- if not user.is_anonymous:
- bookings = Booking.objects.filter(
- Q(owner=user) | Q(collaborators=user),
- end__gte=datetime.now(pytz.utc)
- )
- else:
- bookings = None
-
- LFID = True if settings.AUTH_SETTING == 'LFID' else False
- return render(
- request,
- 'dashboard/landing.html',
- {
- 'manager': manager is not None,
- 'title': "Welcome to the Lab as a Service Dashboard",
- 'bookings': bookings,
- 'LFID': LFID
- }
- )
-
-
-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 ResourceProfile.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/src/laas_dashboard/__init__.py b/src/laas_dashboard/__init__.py
deleted file mode 100644
index f104c4d..0000000
--- a/src/laas_dashboard/__init__.py
+++ /dev/null
@@ -1,13 +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
-##############################################################################
-
-
-# This will make sure the app is always imported when
-# Django starts so that shared_task will use this app.
-from .celery import app as celery_app # noqa
diff --git a/src/laas_dashboard/celery.py b/src/laas_dashboard/celery.py
deleted file mode 100644
index 362bbdb..0000000
--- a/src/laas_dashboard/celery.py
+++ /dev/null
@@ -1,31 +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 os
-
-from celery import Celery
-
-# set the default Django settings module for the 'celery' program.
-os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'laas_dashboard.settings')
-
-from django.conf import settings # noqa
-
-app = Celery('laas_dashboard')
-
-# Using a string here means the worker will not have to
-# pickle the object when using Windows.
-app.config_from_object('django.conf:settings')
-app.autodiscover_tasks(lambda: settings.INSTALLED_APPS)
-app.config_from_object('django.conf:settings', namespace='CELERY')
-
-
-@app.task(bind=True)
-def debug_task(self):
- print('Request: {0!r}'.format(self.request))
diff --git a/src/laas_dashboard/model_test.py b/src/laas_dashboard/model_test.py
deleted file mode 100644
index ba3ef35..0000000
--- a/src/laas_dashboard/model_test.py
+++ /dev/null
@@ -1,110 +0,0 @@
-##############################################################################
-# Copyright (c) 2020 Sawyer Bergeron, Parker Berberian, Sean Smith, 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 resource_inventory.models import (
- ResourceProfile,
- ResourceQuery,
- Image,
- DiskProfile,
- CpuProfile,
- RamProfile,
- InterfaceProfile,
-)
-
-
-def rp_has_all_components():
- """
- Check that every ResourceProfile has an InterfaceProfile,
- DiskProfile, CpuProfile, and RamProfile.
- """
-
- result = True
-
- for rp in ResourceProfile.objects.all():
- ip = InterfaceProfile.objects.filter(host=rp).exists()
- dp = DiskProfile.objects.filter(host=rp).exists()
- cp = CpuProfile.objects.filter(host=rp).exists()
- ram = RamProfile.objects.filter(host=rp).exists()
-
- if not ip:
- print("No InterfaceProfiles for host", rp.name)
- result = False
-
- if not dp:
- print("No DiskProfile for host", rp.name)
- result = False
-
- if not cp:
- print("No CpuProfile for host", rp.name)
- result = False
-
- if not ram:
- print("No RamProfile for host", rp.name)
- result = False
-
- return result
-
-
-def ip_for_all_ifaces():
- """
- Check that every InterfaceProfile for a Resource has
- an Interface.
- """
-
- result = True
-
- for res in ResourceQuery.filter():
- iface_set = res.get_interfaces()
- iface_profile_set = InterfaceProfile.objects.filter(host=res.profile)
-
- # find out what profiles we have
- curr_profiles = [iface.profile for iface in iface_set]
- missing_profiles = set(iface_profile_set) - set(curr_profiles)
-
- if missing_profiles:
- print('No interface for profiles', missing_profiles, 'for host', res.name)
- result = False
-
- return result
-
-
-def rp_has_image():
- """
- Make sure every ResourceProfile has an Image.
- """
-
- result = True
-
- rp_set = ResourceProfile.objects.all()
- image_set = Image.objects.all()
- image_profiles = set([image.host_type for image in image_set])
-
- for rp in rp_set:
- if rp not in image_profiles:
- print("ResourceProfile", rp.name, "has no image associated with it.")
- result = False
- return result
-
-
-def run_test(test):
- print('RUNNING TEST', test)
- result = test()
- if result:
- print(test, 'WAS A SUCCESS!')
- else:
- print(test, 'FAILED')
- print('============================================')
-
-
-def run_tests():
- tests = [rp_has_all_components, ip_for_all_ifaces, rp_has_image]
-
- for test in tests:
- run_test(test)
diff --git a/src/laas_dashboard/settings.py b/src/laas_dashboard/settings.py
deleted file mode 100644
index 7e27c8d..0000000
--- a/src/laas_dashboard/settings.py
+++ /dev/null
@@ -1,254 +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
-##############################################################################
-import os
-from datetime import timedelta
-
-# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
-BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
-
-# SECURITY WARNING: don't run with debug turned on in production!
-# NOTE: os.environ only returns strings, so making a comparison to
-# 'True' here will convert it to the correct Boolean value.
-DEBUG = os.environ.get('DEBUG') == 'True'
-TESTING = os.environ.get('TEST') == 'True'
-
-# Application definition
-
-INSTALLED_APPS = [
- 'dashboard',
- 'resource_inventory',
- 'booking',
- 'account',
- 'notifier',
- 'workflow',
- 'api',
- 'analytics',
- 'django.contrib.admin',
- 'django.contrib.auth',
- 'mozilla_django_oidc', # needs to be defined after auth
- 'django.contrib.contenttypes',
- 'django.contrib.sessions',
- 'django.contrib.messages',
- 'django.contrib.staticfiles',
- 'django.contrib.humanize',
- 'bootstrap4',
- 'rest_framework',
- 'rest_framework.authtoken',
-]
-
-MIDDLEWARE = [
- 'django.middleware.security.SecurityMiddleware',
- 'django.contrib.sessions.middleware.SessionMiddleware',
- 'django.middleware.common.CommonMiddleware',
- 'django.middleware.csrf.CsrfViewMiddleware',
- 'django.contrib.auth.middleware.AuthenticationMiddleware',
- 'django.contrib.messages.middleware.MessageMiddleware',
- 'django.middleware.clickjacking.XFrameOptionsMiddleware',
- 'account.middleware.TimezoneMiddleware',
-]
-
-
-# AUTHENTICATION_BACKENDS = ['django.contrib.auth.backends.ModelBackend', 'account.views.MyOIDCAB']
-AUTHENTICATION_BACKENDS = ['django.contrib.auth.backends.ModelBackend']
-
-AUTH_SETTING = os.environ.get('AUTH_SETTING')
-
-if AUTH_SETTING == 'LFID':
- # OpenID Authentications
- AUTHENTICATION_BACKENDS.append('account.views.MyOIDCAB')
- OIDC_RP_CLIENT_ID = os.environ.get('OIDC_CLIENT_ID')
- OIDC_RP_CLIENT_SECRET = os.environ.get('OIDC_CLIENT_SECRET')
-
- OIDC_OP_AUTHORIZATION_ENDPOINT = os.environ.get('OIDC_AUTHORIZATION_ENDPOINT')
- OIDC_OP_TOKEN_ENDPOINT = os.environ.get('OIDC_TOKEN_ENDPOINT')
- OIDC_OP_USER_ENDPOINT = os.environ.get('OIDC_USER_ENDPOINT')
-
- LOGIN_REDIRECT_URL = os.environ.get('DASHBOARD_URL')
- LOGOUT_REDIRECT_URL = os.environ.get('DASHBOARD_URL')
-
- OIDC_RP_SIGN_ALGO = os.environ.get("OIDC_RP_SIGN_ALGO")
-
- if OIDC_RP_SIGN_ALGO == "RS256":
- OIDC_OP_JWKS_ENDPOINT = os.environ.get("OIDC_OP_JWKS_ENDPOINT")
-else:
- raise Exception('AUTH_SETTING set to invalid value')
-
-# This is for LFID auth setups w/ an HTTPS proxy
-if os.environ.get('EXPECT_HOST_FORWARDING') == 'True':
- SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', "https")
- USE_X_FORWARDED_HOST = True
-
-ROOT_URLCONF = 'laas_dashboard.urls'
-
-TEMPLATE_OVERRIDE = os.environ.get("TEMPLATE_OVERRIDE_DIR", "") # the user's custom template dir
-TEMPLATE_DIRS = ["base"] # where all the base templates are
-
-# If the user has a custom template directory,
-# We should search that first. Then we search the
-# root template directory so that we can extend the base
-# templates within the custom template dir.
-if TEMPLATE_OVERRIDE:
- TEMPLATE_DIRS = [TEMPLATE_OVERRIDE, ""] + TEMPLATE_DIRS
-
-# all template dirs are relative to /project_root/templates/
-dirs = [os.path.join(BASE_DIR, "templates", d) for d in TEMPLATE_DIRS]
-
-TEMPLATES = [
- {
- 'BACKEND': 'django.template.backends.django.DjangoTemplates',
- 'DIRS': dirs,
- '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',
- 'django.contrib.messages.context_processors.messages',
- ],
- },
- },
-]
-
-TEMPLATE_CONTEXT_PROCESSORS = [
- 'dashboard.context_processors.debug',
-]
-
-WSGI_APPLICATION = 'laas_dashboard.wsgi.application'
-
-# Password validation
-# https://docs.djangoproject.com/en/1.10/ref/settings/#auth-password-validators
-
-AUTH_PASSWORD_VALIDATORS = [
- {
- 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
- },
- {
- 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
- },
- {
- 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
- },
- {
- 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
- },
-]
-
-# Internationalization
-# https://docs.djangoproject.com/en/1.10/topics/i18n/
-
-LANGUAGE_CODE = 'en-us'
-
-TIME_ZONE = 'UTC'
-
-USE_I18N = True
-
-USE_L10N = True
-
-USE_TZ = True
-
-# Static files (CSS, JavaScript, Images)
-# https://docs.djangoproject.com/en/1.10/howto/static-files/
-MEDIA_URL = '/media/'
-STATIC_URL = '/static/'
-
-# Static files (CSS, JavaScript, Images)
-# https://docs.djangoproject.com/en/1.10/howto/static-files/
-STATICFILES_DIRS = [
- os.path.join(BASE_DIR, "static"),
-]
-
-LOGIN_REDIRECT_URL = '/'
-
-# SECURITY WARNING: keep the secret key used in production secret!
-SECRET_KEY = os.environ.get('SECRET_KEY')
-
-BOOTSTRAP3 = {
- 'set_placeholder': False,
-}
-
-ALLOWED_HOSTS = ['*']
-
-# Database
-# https://docs.djangoproject.com/en/1.10/ref/settings/#databases
-DATABASES = {
- 'default': {
- 'ENGINE': 'django.db.backends.postgresql',
- 'NAME': os.environ.get('DB_NAME'),
- 'USER': os.environ.get('DB_USER'),
- 'PASSWORD': os.environ.get('DB_PASS'),
- 'HOST': os.environ.get('DB_SERVICE'),
- 'PORT': os.environ.get('DB_PORT')
- }
-}
-
-# Rest API Settings
-REST_FRAMEWORK = {
- 'DEFAULT_PERMISSION_CLASSES': [
- 'rest_framework.permissions.DjangoModelPermissionsOrAnonReadOnly'
- ],
- 'DEFAULT_FILTER_BACKENDS': ('django_filters.rest_framework.FilterSet',),
- 'DEFAULT_AUTHENTICATION_CLASSES': (
- 'rest_framework.authentication.SessionAuthentication',
- 'rest_framework.authentication.TokenAuthentication',
- )
-}
-
-MEDIA_ROOT = '/media'
-STATIC_ROOT = '/static'
-
-OAUTH_CONSUMER_KEY = os.environ.get('OAUTH_CONSUMER_KEY')
-OAUTH_CONSUMER_SECRET = os.environ.get('OAUTH_CONSUMER_SECRET')
-
-OAUTH_CALLBACK_URL = os.environ.get('DASHBOARD_URL') + '/accounts/authenticated'
-
-# Celery Settings
-CELERY_TIMEZONE = 'UTC'
-
-RABBITMQ_URL = 'rabbitmq'
-# RABBITMQ_DEFAULT_USER = os.environ['DEFAULT_USER']
-# RABBITMQ_DEFAULT_PASS = os.environ['DEFAULT_PASS']
-RABBITMQ_DEFAULT_USER = os.environ['RABBITMQ_DEFAULT_USER']
-RABBITMQ_DEFAULT_PASS = os.environ['RABBITMQ_DEFAULT_PASS']
-
-CELERY_BROKER_URL = 'amqp://' + RABBITMQ_DEFAULT_USER + ':' + RABBITMQ_DEFAULT_PASS + '@rabbitmq:5672//'
-
-CELERY_BEAT_SCHEDULE = {
- 'booking_poll': {
- 'task': 'dashboard.tasks.booking_poll',
- 'schedule': timedelta(minutes=1)
- },
- 'free_hosts': {
- 'task': 'dashboard.tasks.free_hosts',
- 'schedule': timedelta(minutes=1)
- },
- 'notify_expiring': {
- 'task': 'notifier.tasks.notify_expiring',
- 'schedule': timedelta(hours=1)
- },
- 'query_vpn_users': {
- 'task': 'dashboard.tasks.query_vpn_users',
- 'schedule': timedelta(hours=1)
- },
- 'dispatch_emails': {
- 'task': 'notifier.tasks.dispatch_emails',
- 'schedule': timedelta(minutes=10)
- }
-}
-
-# Notifier Settings
-EMAIL_HOST = os.environ.get('EMAIL_HOST')
-EMAIL_PORT = os.environ.get('EMAIL_PORT')
-EMAIL_HOST_USER = os.environ.get('EMAIL_HOST_USER')
-EMAIL_HOST_PASSWORD = os.environ.get('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"
-EXPIRE_LIFETIME = 12 # Minimum lifetime of booking to send notification
-EXPIRE_HOURS = 48 # Notify when booking is expiring within this many hours
diff --git a/src/laas_dashboard/urls.py b/src/laas_dashboard/urls.py
deleted file mode 100644
index 7a37d7e..0000000
--- a/src/laas_dashboard/urls.py
+++ /dev/null
@@ -1,49 +0,0 @@
-##############################################################################
-# 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
-##############################################################################
-
-
-"""
-laas_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 import settings
-from django.conf.urls import url, include
-from django.conf.urls.static import static
-from django.contrib import admin
-
-
-urlpatterns = [
-
- url(r'^workflow/', 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'^messages/', include('notifier.urls', namespace='notifier')),
- url(r'^oidc/', include('mozilla_django_oidc.urls')),
-]
-
-if settings.DEBUG is True:
- urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
diff --git a/src/laas_dashboard/wsgi.py b/src/laas_dashboard/wsgi.py
deleted file mode 100644
index 6858d3c..0000000
--- a/src/laas_dashboard/wsgi.py
+++ /dev/null
@@ -1,26 +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
-##############################################################################
-
-
-"""
-WSGI config for laas_dashboard project.
-
-It exposes the WSGI callable as a module-level variable named ``application``.
-
-For more information on this file, see
-https://docs.djangoproject.com/en/1.10/howto/deployment/wsgi/
-"""
-
-import os
-
-from django.core.wsgi import get_wsgi_application
-
-os.environ.setdefault("DJANGO_SETTINGS_MODULE", "laas_dashboard.settings")
-
-application = get_wsgi_application()
diff --git a/src/manage.py b/src/manage.py
deleted file mode 100755
index c412c6b..0000000
--- a/src/manage.py
+++ /dev/null
@@ -1,33 +0,0 @@
-#!/usr/bin/env python
-
-##############################################################################
-# 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 os
-import sys
-
-if __name__ == "__main__":
- os.environ.setdefault("DJANGO_SETTINGS_MODULE", "laas_dashboard.settings")
- try:
- from django.core.management import execute_from_command_line
- except ImportError:
- # The above import may fail for some other reason. Ensure that the
- # issue is really that Django is missing to avoid masking other
- # exceptions on Python 2.
- try:
- import django
- except ImportError:
- raise ImportError(
- "Couldn't import Django. Are you sure it's installed and "
- "available on your PYTHONPATH environment variable? Did you "
- "forget to activate a virtual environment?"
- )
- raise
- execute_from_command_line(sys.argv)
diff --git a/src/notifier/__init__.py b/src/notifier/__init__.py
deleted file mode 100644
index d65b13a..0000000
--- a/src/notifier/__init__.py
+++ /dev/null
@@ -1,8 +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
-##############################################################################
diff --git a/src/notifier/admin.py b/src/notifier/admin.py
deleted file mode 100644
index f6dbfd1..0000000
--- a/src/notifier/admin.py
+++ /dev/null
@@ -1,15 +0,0 @@
-##############################################################################
-# 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 import admin
-
-from notifier.models import Notification, Emailed
-
-admin.site.register(Notification)
-admin.site.register(Emailed)
diff --git a/src/notifier/apps.py b/src/notifier/apps.py
deleted file mode 100644
index 52902da..0000000
--- a/src/notifier/apps.py
+++ /dev/null
@@ -1,15 +0,0 @@
-##############################################################################
-# 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.apps import AppConfig
-
-
-class NotifierConfig(AppConfig):
- name = 'notifier'
diff --git a/src/notifier/manager.py b/src/notifier/manager.py
deleted file mode 100644
index e2afdec..0000000
--- a/src/notifier/manager.py
+++ /dev/null
@@ -1,162 +0,0 @@
-##############################################################################
-# Copyright (c) 2018 Parker Berberian, Sawyer Bergeron and others.
-# Copyright (c) 2020 Sawyer Bergeron, Sean Smith, 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 notifier.models import Notification, Emailed, Email
-
-from django.template.loader import render_to_string
-from django.utils import timezone
-
-
-class NotificationHandler(object):
-
- @classmethod
- def notify_new_booking(cls, booking):
- template = "notifier/new_booking.html"
- titles = ["You have a new booking (" + str(booking.id) + ")", "You have been added to a booking (" + str(booking.id) + ")"]
- cls.booking_notify(booking, template, titles)
-
- @classmethod
- def notify_booking_end(cls, booking):
- template = "notifier/end_booking.html"
- titles = ["Your booking (" + str(booking.id) + ") has ended", "A booking (" + str(booking.id) + ") that you collaborate on has ended"]
- cls.booking_notify(booking, template, titles)
-
- @classmethod
- def notify_booking_expiring(cls, booking):
- template = "notifier/expiring_booking.html"
- titles = ["Your booking (" + str(booking.id) + ") is about to expire", "A booking (" + str(booking.id) + ") that you collaborate on is about to expire"]
- cls.booking_notify(booking, template, titles)
- cls.email_booking_expiring(booking)
-
- @classmethod
- def booking_notify(cls, booking, template, titles):
- """
- Create a notification for a booking owner and collaborators using the template.
-
- titles is a list - the first is the title for the owner's notification,
- the last is the title for the collaborators'
- """
- owner_notif = Notification.objects.create(
- title=titles[0],
- content=render_to_string(
- template,
- context={
- "booking": booking,
- "owner": True
- }
- )
- )
- owner_notif.recipients.add(booking.owner.userprofile)
- if not booking.collaborators.all().exists():
- return # no collaborators - were done
-
- collab_notif = Notification.objects.create(
- title=titles[-1],
- content=render_to_string(
- template,
- context={
- "booking": booking,
- "owner": False
- }
- )
- )
- for c in booking.collaborators.all():
- collab_notif.recipients.add(c.userprofile)
-
- @classmethod
- def email_job_fulfilled(cls, job):
- template_name = "notifier/email_fulfilled.txt"
- all_tasks = job.get_tasklist()
- users = list(job.booking.collaborators.all())
- users.append(job.booking.owner)
- for user in users:
- user_tasklist = []
- # gather up all the relevant messages from the lab
- for task in all_tasks:
- if (not hasattr(task.config, "user")) or task.config.user == user:
- user_tasklist.append(
- {
- "title": task.type_str() + " Message: ",
- "content": task.message
- }
- )
- # gather up all the other needed info
- context = {
- "owner": user == job.booking.owner,
- "user_name": user.userprofile.full_name,
- "messages": user_tasklist,
- "booking_url": os.environ.get("DASHBOARD_URL", "<Dashboard url>") + "/booking/detail/" + str(job.booking.id) + "/"
- }
-
- # render email template
- message = render_to_string(template_name, context)
-
- # finally, queue email for sending
- Email.objects.create(title="Your Booking is Ready", message=message, recipient=user.userprofile.email_addr)
-
- @classmethod
- def email_booking_over(cls, booking):
- template_name = "notifier/email_ended.txt"
- hostnames = [host.name for host in booking.resource.get_resources()]
- users = list(booking.collaborators.all())
- users.append(booking.owner)
- for user in users:
- context = {
- "user_name": user.userprofile.full_name,
- "booking": booking,
- "hosts": hostnames,
- "booking_url": os.environ.get("DASHBOARD_URL", "<Dashboard url>") + "/booking/detail/" + str(booking.id) + "/"
- }
-
- message = render_to_string(template_name, context)
-
- Email.objects.create(title="Your Booking has Expired", message=message, recipient=user.userprofile.email_addr)
-
- @classmethod
- def email_booking_expiring(cls, booking):
- template_name = "notifier/email_expiring.txt"
- hostnames = [host.name for host in booking.resource.get_resources()]
- users = list(booking.collaborators.all())
- users.append(booking.owner)
- for user in users:
- context = {
- "user_name": user.userprofile.full_name,
- "booking": booking,
- "hosts": hostnames,
- "booking_url": os.environ.get("DASHBOARD_URL", "<Dashboard url>") + "/booking/detail/" + str(booking.id) + "/"
- }
-
- message = render_to_string(template_name, context)
-
- Email.objects.create(title="Your Booking is Expiring", message=message, recipient=user.userprofile.email_addr)
-
- @classmethod
- def task_updated(cls, task):
- """
- Notification of task changing.
-
- called every time a lab updated info about a task.
- sends an email when 'task' changing state means a booking has
- just been fulfilled (all tasks done, servers ready to use)
- or is over.
- """
- if task.job is None or task.job.booking is None:
- return
- if task.job.is_fulfilled():
- if task.job.booking.end < timezone.now():
- if Emailed.objects.filter(end_booking=task.job.booking).exists():
- return
- Emailed.objects.create(end_booking=task.job.booking)
- cls.email_booking_over(task.job.booking)
- if task.job.booking.end > timezone.now() and task.job.booking.start < timezone.now():
- if Emailed.objects.filter(begin_booking=task.job.booking).exists():
- return
- Emailed.objects.create(begin_booking=task.job.booking)
- cls.email_job_fulfilled(task.job)
diff --git a/src/notifier/migrations/0001_initial.py b/src/notifier/migrations/0001_initial.py
deleted file mode 100644
index e5d0009..0000000
--- a/src/notifier/migrations/0001_initial.py
+++ /dev/null
@@ -1,47 +0,0 @@
-# Generated by Django 2.1 on 2018-09-14 14:48
-
-from django.db import migrations, models
-import django.db.models.deletion
-import fernet_fields.fields
-
-
-class Migration(migrations.Migration):
-
- initial = True
-
- dependencies = [
- ('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)),
- ('title', models.CharField(max_length=240)),
- ('content', fernet_fields.fields.EncryptedTextField()),
- ('sender', models.CharField(default='unknown', max_length=240)),
- ('message_type', models.CharField(choices=[('email', 'Email'), ('webnotification', 'Web Notification')], default='email', max_length=240)),
- ('user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='account.UserProfile')),
- ],
- ),
- ]
diff --git a/src/notifier/migrations/0002_auto_20181102_1631.py b/src/notifier/migrations/0002_auto_20181102_1631.py
deleted file mode 100644
index e5fef89..0000000
--- a/src/notifier/migrations/0002_auto_20181102_1631.py
+++ /dev/null
@@ -1,44 +0,0 @@
-# Generated by Django 2.1 on 2018-11-02 16:31
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('account', '0003_publicnetwork'),
- ('notifier', '0001_initial'),
- ]
-
- operations = [
- migrations.CreateModel(
- name='Notification',
- fields=[
- ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('title', models.CharField(max_length=150)),
- ('content', models.TextField()),
- ('recipients', models.ManyToManyField(to='account.UserProfile')),
- ],
- ),
- migrations.RemoveField(
- model_name='labmessage',
- name='lab',
- ),
- migrations.RemoveField(
- model_name='metabooking',
- name='booking',
- ),
- migrations.RemoveField(
- model_name='notifier',
- name='user',
- ),
- migrations.DeleteModel(
- name='LabMessage',
- ),
- migrations.DeleteModel(
- name='MetaBooking',
- ),
- migrations.DeleteModel(
- name='Notifier',
- ),
- ]
diff --git a/src/notifier/migrations/0003_auto_20190123_1741.py b/src/notifier/migrations/0003_auto_20190123_1741.py
deleted file mode 100644
index f491993..0000000
--- a/src/notifier/migrations/0003_auto_20190123_1741.py
+++ /dev/null
@@ -1,23 +0,0 @@
-# Generated by Django 2.1 on 2019-01-23 17:41
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('notifier', '0002_auto_20181102_1631'),
- ]
-
- operations = [
- migrations.AddField(
- model_name='notification',
- name='is_html',
- field=models.BooleanField(default=True),
- ),
- migrations.AddField(
- model_name='notification',
- name='is_read',
- field=models.BooleanField(default=True),
- ),
- ]
diff --git a/src/notifier/migrations/0004_auto_20190124_2115.py b/src/notifier/migrations/0004_auto_20190124_2115.py
deleted file mode 100644
index 306ec7b..0000000
--- a/src/notifier/migrations/0004_auto_20190124_2115.py
+++ /dev/null
@@ -1,23 +0,0 @@
-# Generated by Django 2.1 on 2019-01-24 21:15
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('account', '0003_publicnetwork'),
- ('notifier', '0003_auto_20190123_1741'),
- ]
-
- operations = [
- migrations.RemoveField(
- model_name='notification',
- name='is_read',
- ),
- migrations.AddField(
- model_name='notification',
- name='read_by',
- field=models.ManyToManyField(related_name='read_notifications', to='account.UserProfile'),
- ),
- ]
diff --git a/src/notifier/migrations/0005_auto_20190306_1616.py b/src/notifier/migrations/0005_auto_20190306_1616.py
deleted file mode 100644
index d92c988..0000000
--- a/src/notifier/migrations/0005_auto_20190306_1616.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# Generated by Django 2.1 on 2019-03-06 16:16
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('notifier', '0004_auto_20190124_2115'),
- ]
-
- operations = [
- migrations.AlterField(
- model_name='notification',
- name='recipients',
- field=models.ManyToManyField(related_name='notifications', to='account.UserProfile'),
- ),
- ]
diff --git a/src/notifier/migrations/0006_emailed.py b/src/notifier/migrations/0006_emailed.py
deleted file mode 100644
index 22ba9c5..0000000
--- a/src/notifier/migrations/0006_emailed.py
+++ /dev/null
@@ -1,24 +0,0 @@
-# Generated by Django 2.2 on 2019-11-21 18:55
-
-from django.db import migrations, models
-import django.db.models.deletion
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('booking', '0006_booking_opnfv_config'),
- ('notifier', '0005_auto_20190306_1616'),
- ]
-
- operations = [
- migrations.CreateModel(
- name='Emailed',
- fields=[
- ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('almost_end_booking', models.OneToOneField(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='warning_mail', to='booking.Booking')),
- ('begin_booking', models.OneToOneField(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='begin_mail', to='booking.Booking')),
- ('end_booking', models.OneToOneField(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='over_mail', to='booking.Booking')),
- ],
- ),
- ]
diff --git a/src/notifier/migrations/0007_email.py b/src/notifier/migrations/0007_email.py
deleted file mode 100644
index aaac048..0000000
--- a/src/notifier/migrations/0007_email.py
+++ /dev/null
@@ -1,23 +0,0 @@
-# Generated by Django 2.2 on 2020-12-09 20:02
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('notifier', '0006_emailed'),
- ]
-
- operations = [
- migrations.CreateModel(
- name='Email',
- fields=[
- ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('sent', models.BooleanField(default=False)),
- ('title', models.CharField(max_length=150)),
- ('message', models.TextField()),
- ('recipient', models.CharField(max_length=150)),
- ],
- ),
- ]
diff --git a/src/notifier/migrations/__init__.py b/src/notifier/migrations/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/src/notifier/migrations/__init__.py
+++ /dev/null
diff --git a/src/notifier/models.py b/src/notifier/models.py
deleted file mode 100644
index 03e23b3..0000000
--- a/src/notifier/models.py
+++ /dev/null
@@ -1,56 +0,0 @@
-##############################################################################
-# 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 account.models import UserProfile
-from booking.models import Booking
-
-
-class Notification(models.Model):
- title = models.CharField(max_length=150)
- content = models.TextField()
- recipients = models.ManyToManyField(UserProfile, related_name='notifications')
- is_html = models.BooleanField(default=True)
- read_by = models.ManyToManyField(UserProfile, related_name='read_notifications')
-
- def __str__(self):
- return self.title
-
- def to_preview_html(self):
- return "<h3>" + self.title + "</h3>" # TODO - template?
-
-
-class Emailed(models.Model):
- """A simple record to remember who has already gotten an email to avoid resending."""
-
- begin_booking = models.OneToOneField(
- Booking,
- null=True,
- on_delete=models.CASCADE,
- related_name="begin_mail"
- )
- almost_end_booking = models.OneToOneField(
- Booking,
- null=True,
- on_delete=models.CASCADE,
- related_name="warning_mail"
- )
- end_booking = models.OneToOneField(
- Booking,
- null=True,
- on_delete=models.CASCADE,
- related_name="over_mail"
- )
-
-
-class Email(models.Model):
- sent = models.BooleanField(default=False)
- title = models.CharField(max_length=150)
- message = models.TextField()
- recipient = models.CharField(max_length=150)
diff --git a/src/notifier/tasks.py b/src/notifier/tasks.py
deleted file mode 100644
index 64d7574..0000000
--- a/src/notifier/tasks.py
+++ /dev/null
@@ -1,51 +0,0 @@
-##############################################################################
-# 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 celery import shared_task
-from django.utils import timezone
-from django.conf import settings
-from booking.models import Booking
-from notifier.models import Emailed, Email
-from notifier.manager import NotificationHandler
-from django.core.mail import send_mail
-
-import os
-
-
-@shared_task
-def notify_expiring():
- """Notify users if their booking is within 48 hours of expiring."""
- expire_time = timezone.now() + timezone.timedelta(hours=settings.EXPIRE_HOURS)
- # Don't email people about bookings that have started recently
- start_time = timezone.now() - timezone.timedelta(hours=settings.EXPIRE_LIFETIME)
- bookings = Booking.objects.filter(
- end__lte=expire_time,
- end__gte=timezone.now(),
- start__lte=start_time
- )
- for booking in bookings:
- if Emailed.objects.filter(almost_end_booking=booking).exists():
- continue
- NotificationHandler.notify_booking_expiring(booking)
- Emailed.objects.create(almost_end_booking=booking)
-
-
-@shared_task
-def dispatch_emails():
- for email in Email.objects.filter(sent=False):
- email.sent = True
- email.save()
- send_mail(
- email.title,
- email.message,
- os.environ.get("DEFAULT_FROM_EMAIL", "opnfv@laas-dashboard"),
- [email.recipient],
- fail_silently=False)
diff --git a/src/notifier/tests/test_dispatcher.py b/src/notifier/tests/test_dispatcher.py
deleted file mode 100644
index 086f621..0000000
--- a/src/notifier/tests/test_dispatcher.py
+++ /dev/null
@@ -1,15 +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 django.test import TestCase
-
-
-class DispatchTestCase(TestCase):
- # This is a stub, it will be filled out as this feature is remade with saner practices.
- pass
diff --git a/src/notifier/tests/test_models.py b/src/notifier/tests/test_models.py
deleted file mode 100644
index d332254..0000000
--- a/src/notifier/tests/test_models.py
+++ /dev/null
@@ -1,30 +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 django.test import TestCase
-from notifier.models import Notifier
-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/src/notifier/urls.py b/src/notifier/urls.py
deleted file mode 100644
index 923cc33..0000000
--- a/src/notifier/urls.py
+++ /dev/null
@@ -1,19 +0,0 @@
-##############################################################################
-# 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 InboxView, NotificationView
-
-app_name = 'notifier'
-urlpatterns = [
- url(r'^$', InboxView, name='messages'),
- url(r'^notification/(?P<notification_id>[0-9]+)/$', NotificationView, name='notifier_single')
-]
diff --git a/src/notifier/views.py b/src/notifier/views.py
deleted file mode 100644
index 3a85eda..0000000
--- a/src/notifier/views.py
+++ /dev/null
@@ -1,58 +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 django.shortcuts import render
-from notifier.models import Notification
-from django.db.models import Q
-
-
-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",
- {'unread_notifications': Notification.objects.filter(recipients=user.userprofile).order_by('-id').filter(~Q(read_by=user.userprofile)),
- 'read_notifications': Notification.objects.filter(recipients=user.userprofile).order_by('-id').filter(read_by=user.userprofile)})
-
-
-def NotificationView(request, notification_id):
-
- if request.user.is_authenticated:
- user = request.user
- else:
- return render(request,
- "dashboard/login.html",
- {'title': 'Authentication Required'})
-
- notification = Notification.objects.get(id=notification_id)
- if user.userprofile not in notification.recipients.all():
- return render(request,
- "dashboard/login.html", {'title': 'Access Denied'})
-
- notification.read_by.add(user.userprofile)
- notification.save()
- if request.method == 'POST':
- if 'delete' in request.POST:
- # handle deleting
- notification.recipients.remove(user.userprofile)
- if not notification.recipients.exists():
- notification.delete()
- else:
- notification.save()
-
- if 'unread' in request.POST:
- notification.read_by.remove(user.userprofile)
- notification.save()
-
- return render(request,
- "notifier/notification.html", {'notification': notification})
diff --git a/src/resource_inventory/__init__.py b/src/resource_inventory/__init__.py
deleted file mode 100644
index f903394..0000000
--- a/src/resource_inventory/__init__.py
+++ /dev/null
@@ -1,8 +0,0 @@
-##############################################################################
-# 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/src/resource_inventory/admin.py b/src/resource_inventory/admin.py
deleted file mode 100644
index 2444a98..0000000
--- a/src/resource_inventory/admin.py
+++ /dev/null
@@ -1,70 +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.contrib import admin
-
-from resource_inventory.forms import InterfaceConfigurationForm
-
-from resource_inventory.models import (
- ResourceProfile,
- InterfaceProfile,
- DiskProfile,
- CpuProfile,
- RamProfile,
- ResourceTemplate,
- ResourceConfiguration,
- InterfaceConfiguration,
- Server,
- Interface,
- Network,
- Vlan,
- ResourceBundle,
- Scenario,
- Installer,
- Opsys,
- OPNFVConfig,
- OPNFVRole,
- Image,
- RemoteInfo,
- PhysicalNetwork,
- NetworkConnection,
-)
-
-
-admin.site.register([
- ResourceProfile,
- InterfaceProfile,
- DiskProfile,
- CpuProfile,
- RamProfile,
- ResourceTemplate,
- ResourceConfiguration,
- Server,
- Interface,
- Network,
- Vlan,
- ResourceBundle,
- Scenario,
- Installer,
- Opsys,
- OPNFVConfig,
- OPNFVRole,
- Image,
- PhysicalNetwork,
- NetworkConnection,
- RemoteInfo]
-)
-
-
-class InterfaceConfigurationAdmin(admin.ModelAdmin):
- form = InterfaceConfigurationForm
-
-
-admin.site.register(InterfaceConfiguration, InterfaceConfigurationAdmin)
diff --git a/src/resource_inventory/apps.py b/src/resource_inventory/apps.py
deleted file mode 100644
index 79768a7..0000000
--- a/src/resource_inventory/apps.py
+++ /dev/null
@@ -1,14 +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.apps import AppConfig
-
-
-class ResourcesConfig(AppConfig):
- name = 'hwresource'
diff --git a/src/resource_inventory/forms.py b/src/resource_inventory/forms.py
deleted file mode 100644
index fb8c102..0000000
--- a/src/resource_inventory/forms.py
+++ /dev/null
@@ -1,31 +0,0 @@
-##############################################################################
-# Copyright (c) 2020 Sawyer Bergeron, Sean Smith, 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 ValidationError
-from django import forms
-
-from resource_inventory.models import Network, InterfaceConfiguration
-
-
-class InterfaceConfigurationForm(forms.ModelForm):
- class Meta:
- model = InterfaceConfiguration
- fields = ['profile', 'resource_config', 'connections']
-
- def clean(self):
- connections = self.cleaned_data.get('connections')
- resource_config = self.cleaned_data.get('resource_config')
-
- valid_nets = set(Network.objects.filter(bundle=resource_config.template))
- curr_nets = set([conn.network for conn in connections])
-
- if not curr_nets.issubset(valid_nets):
- raise ValidationError("Cannot have network connection to network outside pod")
-
- return self.cleaned_data
diff --git a/src/resource_inventory/idf_templater.py b/src/resource_inventory/idf_templater.py
deleted file mode 100644
index 8f0f924..0000000
--- a/src/resource_inventory/idf_templater.py
+++ /dev/null
@@ -1,148 +0,0 @@
-##############################################################################
-# Copyright (c) 2019 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.template.loader import render_to_string
-
-from account.models import PublicNetwork
-
-from resource_inventory.models import Vlan
-
-
-class IDFTemplater:
- """Utility class to create a full Installer Descriptor File (IDF) yaml file."""
-
- net_names = ["admin", "mgmt", "private", "public"]
- bridge_names = {
- "admin": "br-admin",
- "mgmt": "br-mgmt",
- "private": "br-private",
- "public": "br-public"
- }
-
- def __init__(self):
- self.networks = {}
- for i, name in enumerate(self.net_names):
- self.networks[name] = {
- "name": name,
- "vlan": -1,
- "interface": i,
- "ip": "10.250." + str(i) + ".0",
- "netmask": 24
- }
-
- def makeIDF(self, booking):
- """Fill the IDF template with info about the resource."""
- template = "dashboard/idf.yaml"
- info = {}
- info['version'] = "0.1"
- info['net_config'] = self.get_net_config(booking)
- info['fuel'] = self.get_fuel_config(booking)
-
- return render_to_string(template, context=info)
-
- def get_net_config(self, booking):
- net_config = {}
- try:
- net_config['oob'] = self.get_oob_net(booking)
- except Exception:
- net_config['oob'] = {}
- try:
- net_config['public'] = self.get_public_net(booking)
- except Exception:
- net_config['public'] = {}
-
- for net in [net for net in self.net_names if net != "public"]:
- try:
- net_config[net] = self.get_single_net_config(booking, net)
- except Exception:
- net_config[net] = {}
-
- return net_config
-
- def get_public_net(self, booking):
- public = {}
- config = booking.opnfv_config
- public_role = config.networks.get(name="public")
- public_vlan = Vlan.objects.filter(network=public_role.network).first()
- public_network = PublicNetwork.objects.get(vlan=public_vlan.vlan_id, lab=booking.lab)
- self.networks['public']['vlan'] = public_vlan.vlan_id
- public['interface'] = self.networks['public']['interface']
- public['vlan'] = public_network.vlan # untagged??
- public['network'] = public_network.cidr.split("/")[0]
- public['mask'] = public_network.cidr.split("/")[1]
- # public['ip_range'] = 4 # necesary?
- public['gateway'] = public_network.gateway
- public['dns'] = ["1.1.1.1", "8.8.8.8"]
-
- return public
-
- def get_oob_net(self, booking):
- net = {}
- hosts = booking.resource.hosts.all()
- addrs = [host.remote_management.address for host in hosts]
- net['ip_range'] = ",".join(addrs)
- net['vlan'] = "native"
- return net
-
- def get_single_net_config(self, booking, net_name):
- config = booking.opnfv_config
- role = config.networks.get(name=net_name)
- vlan = Vlan.objects.filter(network=role.network).first()
- self.networks[net_name]['vlan'] = vlan.vlan_id
- net = {}
- net['interface'] = self.networks[net_name]['interface']
- net['vlan'] = vlan.vlan_id
- net['network'] = self.networks[net_name]['ip']
- net['mask'] = self.networks[net_name]['netmask']
-
- return net
-
- def get_fuel_config(self, booking):
- fuel = {}
- fuel['jumphost'] = {}
- try:
- fuel['jumphost']['bridges'] = self.get_fuel_bridges()
- except Exception:
- fuel['jumphost']['bridges'] = {}
-
- fuel['network'] = {}
- try:
- fuel['network']['nodes'] = self.get_fuel_nodes(booking)
- except Exception:
- fuel['network']['nodes'] = {}
-
- return fuel
-
- def get_fuel_bridges(self):
- return self.bridge_names
-
- def get_fuel_nodes(self, booking):
- jumphost = booking.opnfv_config.host_opnfv_config.get(
- role__name__iexact="jumphost"
- )
- hosts = booking.resource.hosts.exclude(pk=jumphost.pk)
- nodes = []
- for host in hosts:
- node = {}
- ordered_interfaces = self.get_node_interfaces(host)
- node['interfaces'] = [iface['name'] for iface in ordered_interfaces]
- node['bus_addrs'] = [iface['bus'] for iface in ordered_interfaces]
- nodes.append(node)
-
- return nodes
-
- def get_node_interfaces(self, node):
- # TODO: this should sync with pdf ordering
- interfaces = []
-
- for iface in node.interfaces.all():
- interfaces.append({"name": iface.name, "bus": iface.bus_address})
-
- return interfaces
diff --git a/src/resource_inventory/migrations/0001_initial.py b/src/resource_inventory/migrations/0001_initial.py
deleted file mode 100644
index d01e8e7..0000000
--- a/src/resource_inventory/migrations/0001_initial.py
+++ /dev/null
@@ -1,328 +0,0 @@
-# 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/src/resource_inventory/migrations/0002_auto_20180919_1459.py b/src/resource_inventory/migrations/0002_auto_20180919_1459.py
deleted file mode 100644
index 80c9e6f..0000000
--- a/src/resource_inventory/migrations/0002_auto_20180919_1459.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# 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/src/resource_inventory/migrations/0003_vlan_public.py b/src/resource_inventory/migrations/0003_vlan_public.py
deleted file mode 100644
index 07dc647..0000000
--- a/src/resource_inventory/migrations/0003_vlan_public.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# 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/src/resource_inventory/migrations/0004_auto_20181017_1532.py b/src/resource_inventory/migrations/0004_auto_20181017_1532.py
deleted file mode 100644
index 3a7475c..0000000
--- a/src/resource_inventory/migrations/0004_auto_20181017_1532.py
+++ /dev/null
@@ -1,28 +0,0 @@
-# Generated by Django 2.1 on 2018-10-17 15:32
-
-from django.db import migrations
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('resource_inventory', '0003_vlan_public'),
- ]
-
- operations = [
- migrations.RemoveField(
- model_name='genericpod',
- name='genericresource_ptr',
- ),
- migrations.RemoveField(
- model_name='genericpod',
- name='hosts',
- ),
- migrations.RemoveField(
- model_name='genericpod',
- name='networks',
- ),
- migrations.DeleteModel(
- name='GenericPod',
- ),
- ]
diff --git a/src/resource_inventory/migrations/0005_image_os.py b/src/resource_inventory/migrations/0005_image_os.py
deleted file mode 100644
index ede008e..0000000
--- a/src/resource_inventory/migrations/0005_image_os.py
+++ /dev/null
@@ -1,19 +0,0 @@
-# Generated by Django 2.1 on 2019-01-10 16:18
-
-from django.db import migrations, models
-import django.db.models.deletion
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('resource_inventory', '0004_auto_20181017_1532'),
- ]
-
- operations = [
- migrations.AddField(
- model_name='image',
- name='os',
- field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='resource_inventory.Opsys'),
- ),
- ]
diff --git a/src/resource_inventory/migrations/0006_auto_20190124_1700.py b/src/resource_inventory/migrations/0006_auto_20190124_1700.py
deleted file mode 100644
index a5a972f..0000000
--- a/src/resource_inventory/migrations/0006_auto_20190124_1700.py
+++ /dev/null
@@ -1,76 +0,0 @@
-# Generated by Django 2.1 on 2019-01-24 17:00
-
-from django.conf import settings
-from django.db import migrations, models
-import django.db.models.deletion
-import resource_inventory.models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('resource_inventory', '0005_image_os'),
- ]
-
- operations = [
- migrations.AlterField(
- model_name='cpuprofile',
- name='host',
- field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='cpuprofile', to='resource_inventory.HostProfile'),
- ),
- migrations.AlterField(
- model_name='diskprofile',
- name='host',
- field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='storageprofile', to='resource_inventory.HostProfile'),
- ),
- migrations.AlterField(
- model_name='generichost',
- name='profile',
- field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='resource_inventory.HostProfile'),
- ),
- migrations.AlterField(
- model_name='generichost',
- name='resource',
- field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='generic_host', to='resource_inventory.GenericResource'),
- ),
- migrations.AlterField(
- model_name='genericinterface',
- name='host',
- field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='generic_interfaces', to='resource_inventory.GenericHost'),
- ),
- migrations.AlterField(
- model_name='genericresource',
- name='bundle',
- field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='generic_resources', to='resource_inventory.GenericResourceBundle'),
- ),
- migrations.AlterField(
- model_name='genericresourcebundle',
- name='lab',
- field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='account.Lab'),
- ),
- migrations.AlterField(
- model_name='genericresourcebundle',
- name='owner',
- field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL),
- ),
- migrations.AlterField(
- model_name='hostconfiguration',
- name='opnfvRole',
- field=models.ForeignKey(on_delete=models.SET(resource_inventory.models.get_sentinal_opnfv_role), to='resource_inventory.OPNFVRole'),
- ),
- migrations.AlterField(
- model_name='interfaceprofile',
- name='host',
- field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='interfaceprofile', to='resource_inventory.HostProfile'),
- ),
- migrations.AlterField(
- model_name='ramprofile',
- name='host',
- field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='ramprofile', to='resource_inventory.HostProfile'),
- ),
- migrations.AlterField(
- model_name='resourcebundle',
- name='template',
- field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='resource_inventory.GenericResourceBundle'),
- ),
- ]
diff --git a/src/resource_inventory/migrations/0007_auto_20190306_1616.py b/src/resource_inventory/migrations/0007_auto_20190306_1616.py
deleted file mode 100644
index 19a49c5..0000000
--- a/src/resource_inventory/migrations/0007_auto_20190306_1616.py
+++ /dev/null
@@ -1,31 +0,0 @@
-# Generated by Django 2.1 on 2019-03-06 16:16
-
-from django.db import migrations, models
-import django.db.models.deletion
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('resource_inventory', '0006_auto_20190124_1700'),
- ]
-
- operations = [
- migrations.CreateModel(
- name='RemoteInfo',
- fields=[
- ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('address', models.CharField(max_length=15)),
- ('mac_address', models.CharField(max_length=17)),
- ('password', models.CharField(max_length=100)),
- ('user', models.CharField(max_length=100)),
- ('management_type', models.CharField(default='ipmi', max_length=50)),
- ('versions', models.CharField(max_length=100)),
- ],
- ),
- migrations.AlterField(
- model_name='genericinterface',
- name='profile',
- field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='resource_inventory.InterfaceProfile'),
- ),
- ]
diff --git a/src/resource_inventory/migrations/0008_host_remote_management.py b/src/resource_inventory/migrations/0008_host_remote_management.py
deleted file mode 100644
index f74a535..0000000
--- a/src/resource_inventory/migrations/0008_host_remote_management.py
+++ /dev/null
@@ -1,19 +0,0 @@
-# Generated by Django 2.1 on 2019-03-06 16:42
-
-from django.db import migrations, models
-import resource_inventory.models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('resource_inventory', '0007_auto_20190306_1616'),
- ]
-
- operations = [
- migrations.AddField(
- model_name='host',
- name='remote_management',
- field=models.ForeignKey(default=resource_inventory.models.get_default_remote_info, on_delete=models.SET(resource_inventory.models.get_default_remote_info), to='resource_inventory.RemoteInfo'),
- ),
- ]
diff --git a/src/resource_inventory/migrations/0009_auto_20190315_1757.py b/src/resource_inventory/migrations/0009_auto_20190315_1757.py
deleted file mode 100644
index 92ed0e9..0000000
--- a/src/resource_inventory/migrations/0009_auto_20190315_1757.py
+++ /dev/null
@@ -1,73 +0,0 @@
-# Generated by Django 2.1 on 2019-03-15 17:57
-
-from django.db import migrations, models
-import django.db.models.deletion
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('resource_inventory', '0008_host_remote_management'),
- ]
-
- operations = [
- migrations.CreateModel(
- name='NetworkConnection',
- fields=[
- ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('vlan_is_tagged', models.BooleanField()),
- ],
- ),
- migrations.CreateModel(
- name='NetworkRole',
- fields=[
- ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('name', models.CharField(max_length=100)),
- ],
- ),
- migrations.RemoveField(
- model_name='genericinterface',
- name='vlans',
- ),
- migrations.RemoveField(
- model_name='network',
- name='vlan_id',
- ),
- migrations.AddField(
- model_name='network',
- name='bundle',
- field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, related_name='networks', to='resource_inventory.GenericResourceBundle'),
- preserve_default=False,
- ),
- migrations.AddField(
- model_name='network',
- name='is_public',
- field=models.BooleanField(default=False),
- preserve_default=False,
- ),
- migrations.AddField(
- model_name='vlan',
- name='network',
- field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.DO_NOTHING, to='resource_inventory.Network'),
- ),
- migrations.AddField(
- model_name='networkrole',
- name='network',
- field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='resource_inventory.Network'),
- ),
- migrations.AddField(
- model_name='networkconnection',
- name='network',
- field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='resource_inventory.Network'),
- ),
- migrations.AddField(
- model_name='genericinterface',
- name='connections',
- field=models.ManyToManyField(to='resource_inventory.NetworkConnection'),
- ),
- migrations.AddField(
- model_name='opnfvconfig',
- name='networks',
- field=models.ManyToManyField(to='resource_inventory.NetworkRole'),
- ),
- ]
diff --git a/src/resource_inventory/migrations/0010_auto_20190430_1405.py b/src/resource_inventory/migrations/0010_auto_20190430_1405.py
deleted file mode 100644
index 3823eaf..0000000
--- a/src/resource_inventory/migrations/0010_auto_20190430_1405.py
+++ /dev/null
@@ -1,54 +0,0 @@
-# Generated by Django 2.1 on 2019-04-30 14:05
-
-from django.db import migrations, models
-import django.db.models.deletion
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('resource_inventory', '0009_auto_20190315_1757'),
- ]
-
- operations = [
- migrations.CreateModel(
- name='HostOPNFVConfig',
- fields=[
- ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ],
- ),
- migrations.RemoveField(
- model_name='hostconfiguration',
- name='opnfvRole',
- ),
- migrations.AddField(
- model_name='hostconfiguration',
- name='is_head_node',
- field=models.BooleanField(default=False),
- ),
- migrations.AddField(
- model_name='opnfvconfig',
- name='description',
- field=models.CharField(blank=True, default='', max_length=600),
- ),
- migrations.AddField(
- model_name='opnfvconfig',
- name='name',
- field=models.CharField(blank=True, default='', max_length=300),
- ),
- migrations.AddField(
- model_name='hostopnfvconfig',
- name='host_config',
- field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='host_opnfv_config', to='resource_inventory.HostConfiguration'),
- ),
- migrations.AddField(
- model_name='hostopnfvconfig',
- name='opnfv_config',
- field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='host_opnfv_config', to='resource_inventory.OPNFVConfig'),
- ),
- migrations.AddField(
- model_name='hostopnfvconfig',
- name='role',
- field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='host_opnfv_configs', to='resource_inventory.OPNFVRole'),
- ),
- ]
diff --git a/src/resource_inventory/migrations/0011_auto_20191106_2024.py b/src/resource_inventory/migrations/0011_auto_20191106_2024.py
deleted file mode 100644
index bde9f9d..0000000
--- a/src/resource_inventory/migrations/0011_auto_20191106_2024.py
+++ /dev/null
@@ -1,33 +0,0 @@
-# Generated by Django 2.2 on 2019-11-06 20:24
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('resource_inventory', '0010_auto_20190430_1405'),
- ]
-
- operations = [
- migrations.AddField(
- model_name='configbundle',
- name='hidden',
- field=models.BooleanField(default=False),
- ),
- migrations.AddField(
- model_name='configbundle',
- name='public',
- field=models.BooleanField(default=False),
- ),
- migrations.AddField(
- model_name='genericresourcebundle',
- name='hidden',
- field=models.BooleanField(default=False),
- ),
- migrations.AddField(
- model_name='genericresourcebundle',
- name='public',
- field=models.BooleanField(default=False),
- ),
- ]
diff --git a/src/resource_inventory/migrations/0012_auto_20200103_1850.py b/src/resource_inventory/migrations/0012_auto_20200103_1850.py
deleted file mode 100644
index 65d8f85..0000000
--- a/src/resource_inventory/migrations/0012_auto_20200103_1850.py
+++ /dev/null
@@ -1,59 +0,0 @@
-# Generated by Django 2.2 on 2020-01-03 18:50
-
-from django.db import migrations, models
-import django.db.models.deletion
-
-
-def pairVlanPhysicalNetworks(apps, editor):
- PhysicalNetwork = apps.get_model("resource_inventory", "PhysicalNetwork")
- Vlan = apps.get_model("resource_inventory", "Vlan")
- for vlan in Vlan.objects.filter(network__isnull=False):
- if PhysicalNetwork.objects.filter(id=vlan.network.id).exists():
- continue
- PhysicalNetwork.objects.create(id=vlan.network.id, vlan_id=vlan.vlan_id, generic_network=vlan.network)
-
-
-def deletePhysicalNetworks(apps, editor):
- Vlan = apps.get_model("resource_inventory", "Vlan")
- for vlan in Vlan.objects.all():
- vlan.network = None
- PhysicalNetwork = apps.get_model("resource_inventory", "PhysicalNetwork")
- PhysicalNetwork.objects.all().delete()
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('resource_inventory', '0011_auto_20191106_2024'),
- ]
-
- operations = [
- migrations.CreateModel(
- name='PhysicalNetwork',
- fields=[
- ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('vlan_id', models.IntegerField()),
- ('generic_network', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='resource_inventory.Network')),
- ],
- options={
- 'abstract': False,
- },
- ),
- migrations.AlterField(
- model_name='host',
- name='id',
- field=models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
- ),
- migrations.AlterField(
- model_name='resourcebundle',
- name='id',
- field=models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
- ),
- migrations.RunPython(pairVlanPhysicalNetworks, deletePhysicalNetworks),
- migrations.AlterField(
- model_name='vlan',
- name='network',
- field=models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING,
- to='resource_inventory.PhysicalNetwork', null=True),
- ),
- ]
diff --git a/src/resource_inventory/migrations/0012_manual_20200218_1536.py b/src/resource_inventory/migrations/0012_manual_20200218_1536.py
deleted file mode 100644
index 378bdc3..0000000
--- a/src/resource_inventory/migrations/0012_manual_20200218_1536.py
+++ /dev/null
@@ -1,25 +0,0 @@
-# Generated by Django 2.2 on 2020-02-18 15:36
-
-from django.conf import settings
-from django.db import migrations
-
-
-def clear_networks(apps, schema_editor):
- Network = apps.get_model('resource_inventory', 'Network')
- Vlan = apps.get_model('resource_inventory', 'Vlan')
- for vlan in Vlan.objects.all():
- vlan.delete()
- for net in Network.objects.all():
- net.delete()
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- migrations.swappable_dependency(settings.AUTH_USER_MODEL),
- ('resource_inventory', '0012_auto_20200103_1850'),
- ]
-
- operations = [
- migrations.RunPython(clear_networks)
- ]
diff --git a/src/resource_inventory/migrations/0013_auto_20200218_1536.py b/src/resource_inventory/migrations/0013_auto_20200218_1536.py
deleted file mode 100644
index 053453b..0000000
--- a/src/resource_inventory/migrations/0013_auto_20200218_1536.py
+++ /dev/null
@@ -1,407 +0,0 @@
-# Generated by Django 2.2 on 2020-02-18 15:36
-
-from django.conf import settings
-from django.db import migrations, models
-import django.db.models.deletion
-import resource_inventory.models
-
-
-def clear_resource_bundles(apps, schema_editor):
- ResourceBundle = apps.get_model('resource_inventory', 'ResourceBundle')
- for rb in ResourceBundle.objects.all():
- rb.template = None
- rb.save()
-
-
-def create_default_template(apps, schema_editor):
- ResourceTemplate = apps.get_model('resource_inventory', 'ResourceTemplate')
- ResourceTemplate.objects.create(name="Default Template", hidden=True)
-
-
-def populate_servers(apps, schema_editor):
- """Convert old Host models to Server Resources."""
- Host = apps.get_model('resource_inventory', 'Host')
- Server = apps.get_model('resource_inventory', 'Server')
- ResourceProfile = apps.get_model('resource_inventory', 'ResourceProfile')
- for h in Host.objects.all():
- rp = ResourceProfile.objects.get(id=h.profile.id)
- server = Server.objects.create(
- working=h.working,
- vendor=h.vendor,
- labid=h.labid,
- booked=h.booked,
- name=h.labid,
- lab=h.lab,
- profile=rp
- )
-
- for iface in h.interfaces.all():
- server.interfaces.add(iface)
-
-
-def populate_resource_templates(apps, schema_editor):
- """
- Convert old GenericResourceBundles to ResourceTemplate.
-
- This will be kept blank for now. If, during testing, we realize
- we want to implement this, we will. For now, it seems
- fine to let the old models just die and create
- new ones as needed.
- """
- pass
-
-
-def populate_resource_profiles(apps, schema_editor):
- """
- Convert old HostProfile models to ResourceProfiles.
-
- Also updates all the foreign keys pointed to the old
- host profile. This change was basically only a name change.
- """
- HostProfile = apps.get_model('resource_inventory', 'HostProfile')
- ResourceProfile = apps.get_model('resource_inventory', 'ResourceProfile')
- for hp in HostProfile.objects.all():
- rp = ResourceProfile.objects.create(id=hp.id, name=hp.name, description=hp.description)
- rp.labs.add(*list(hp.labs.all()))
- """
- TODO: link these models together
- rp.interfaceprofile = hp.interfaceprofile
- rp.storageprofile = hp.storageprofile
- rp.cpuprofile = hp.cpuprofile
- rp.ramprofile = hp.ramprofile
- rp.save()
- hp.interfaceprofile.host = rp
- rp.storageprofile.host = rp
- rp.cpuprofile.host = rp
- rp.ramprofile.host = rp
- rp.interfaceprofile.save()
- rp.storageprofile.save()
- rp.cpuprofile.save()
- rp.ramprofile.save()
- """
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- migrations.swappable_dependency(settings.AUTH_USER_MODEL),
- ('booking', '0007_remove_booking_config_bundle'),
- ('account', '0004_downtime'),
- ('api', '0013_manual_20200218_1536'),
- ('resource_inventory', '0012_manual_20200218_1536'),
- ]
-
- operations = [
- migrations.CreateModel(
- name='InterfaceConfiguration',
- fields=[
- ('id', models.AutoField(primary_key=True, serialize=False)),
- ('connections', models.ManyToManyField(to='resource_inventory.NetworkConnection')),
- ],
- ),
- migrations.CreateModel(
- name='ResourceConfiguration',
- fields=[
- ('id', models.AutoField(primary_key=True, serialize=False)),
- ('is_head_node', models.BooleanField(default=False)),
- ],
- ),
- migrations.CreateModel(
- name='ResourceOPNFVConfig',
- fields=[
- ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ],
- ),
- migrations.CreateModel(
- name='ResourceProfile',
- fields=[
- ('id', models.AutoField(primary_key=True, serialize=False)),
- ('name', models.CharField(max_length=200, unique=True)),
- ('description', models.TextField()),
- ('labs', models.ManyToManyField(related_name='resourceprofiles', to='account.Lab')),
- ],
- ),
- migrations.RunPython(populate_resource_profiles),
- migrations.CreateModel(
- name='ResourceTemplate',
- 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)),
- ('public', models.BooleanField(default=False)),
- ('hidden', models.BooleanField(default=False)),
- ('lab', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='resourcetemplates', to='account.Lab')),
- ('owner', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL)),
- ],
- ),
- migrations.RunPython(populate_resource_templates),
- migrations.CreateModel(
- name='Server',
- fields=[
- ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('working', models.BooleanField(default=True)),
- ('vendor', models.CharField(default='unknown', max_length=100)),
- ('model', models.CharField(default='unknown', max_length=150)),
- ('labid', models.CharField(default='default_id', max_length=200, unique=True)),
- ('booked', models.BooleanField(default=False)),
- ('name', models.CharField(max_length=200, unique=True)),
- ],
- options={
- 'abstract': False,
- },
- ),
- migrations.AddField(
- model_name='server',
- name='bundle',
- field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='resource_inventory.ResourceBundle'),
- ),
- migrations.AddField(
- model_name='server',
- name='config',
- field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='resource_inventory.ResourceConfiguration'),
- ),
- migrations.AddField(
- model_name='server',
- name='interfaces',
- field=models.ManyToManyField(to='resource_inventory.Interface'),
- ),
- migrations.AddField(
- model_name='server',
- name='lab',
- field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='account.Lab'),
- ),
- migrations.AddField(
- model_name='server',
- name='profile',
- field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='resource_inventory.ResourceProfile'),
- ),
- migrations.AddField(
- model_name='server',
- name='remote_management',
- field=models.ForeignKey(default=resource_inventory.models.get_default_remote_info, on_delete=models.SET(resource_inventory.models.get_default_remote_info), to='resource_inventory.RemoteInfo'),
- ),
- migrations.RunPython(populate_servers),
- migrations.RemoveField(
- model_name='generichost',
- name='profile',
- ),
- migrations.RemoveField(
- model_name='generichost',
- name='resource',
- ),
- migrations.RemoveField(
- model_name='genericinterface',
- name='connections',
- ),
- migrations.RemoveField(
- model_name='genericinterface',
- name='host',
- ),
- migrations.RemoveField(
- model_name='genericinterface',
- name='profile',
- ),
- migrations.RemoveField(
- model_name='genericresource',
- name='bundle',
- ),
- migrations.RemoveField(
- model_name='genericresourcebundle',
- name='lab',
- ),
- migrations.RemoveField(
- model_name='genericresourcebundle',
- name='owner',
- ),
- migrations.RemoveField(
- model_name='host',
- name='bundle',
- ),
- migrations.RemoveField(
- model_name='host',
- name='config',
- ),
- migrations.RemoveField(
- model_name='host',
- name='lab',
- ),
- migrations.RemoveField(
- model_name='host',
- name='profile',
- ),
- migrations.RemoveField(
- model_name='host',
- name='remote_management',
- ),
- migrations.RemoveField(
- model_name='host',
- name='template',
- ),
- migrations.RemoveField(
- model_name='hostconfiguration',
- name='bundle',
- ),
- migrations.RemoveField(
- model_name='hostconfiguration',
- name='host',
- ),
- migrations.RemoveField(
- model_name='hostconfiguration',
- name='image',
- ),
- migrations.RemoveField(
- model_name='hostopnfvconfig',
- name='host_config',
- ),
- migrations.RemoveField(
- model_name='hostopnfvconfig',
- name='opnfv_config',
- ),
- migrations.RemoveField(
- model_name='hostopnfvconfig',
- name='role',
- ),
- migrations.RemoveField(
- model_name='hostprofile',
- name='labs',
- ),
- migrations.RemoveField(
- model_name='interface',
- name='host',
- ),
- migrations.RemoveField(
- model_name='interface',
- name='name',
- ),
- migrations.RemoveField(
- model_name='opnfvconfig',
- name='bundle',
- ),
- migrations.AddField(
- model_name='interface',
- name='profile',
- field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, to='resource_inventory.InterfaceProfile'),
- preserve_default=False,
- ),
- migrations.AddField(
- model_name='interfaceprofile',
- name='order',
- field=models.IntegerField(default=-1),
- ),
- migrations.AlterField(
- model_name='cpuprofile',
- name='host',
- field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='cpuprofile', to='resource_inventory.ResourceProfile'),
- ),
- migrations.AlterField(
- model_name='diskprofile',
- name='host',
- field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='storageprofile', to='resource_inventory.ResourceProfile'),
- ),
- migrations.AlterField(
- model_name='image',
- name='host_type',
- field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='resource_inventory.ResourceProfile'),
- ),
- migrations.AlterField(
- model_name='interfaceprofile',
- name='host',
- field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='interfaceprofile', to='resource_inventory.ResourceProfile'),
- ),
- migrations.AlterField(
- model_name='network',
- name='bundle',
- field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='networks', to='resource_inventory.ResourceTemplate'),
- ),
- migrations.AlterField(
- model_name='ramprofile',
- name='host',
- field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='ramprofile', to='resource_inventory.ResourceProfile'),
- ),
- migrations.RunPython(clear_resource_bundles),
- migrations.AlterField(
- model_name='resourcebundle',
- name='template',
- field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='resource_inventory.ResourceTemplate'),
- ),
- migrations.DeleteModel(
- name='ConfigBundle',
- ),
- migrations.DeleteModel(
- name='GenericHost',
- ),
- migrations.DeleteModel(
- name='GenericInterface',
- ),
- migrations.DeleteModel(
- name='GenericResource',
- ),
- migrations.DeleteModel(
- name='GenericResourceBundle',
- ),
- migrations.DeleteModel(
- name='HostConfiguration',
- ),
- migrations.DeleteModel(
- name='HostOPNFVConfig',
- ),
- migrations.DeleteModel(
- name='HostProfile',
- ),
- migrations.DeleteModel(
- name='Host',
- ),
- migrations.AddField(
- model_name='resourceopnfvconfig',
- name='opnfv_config',
- field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='resource_opnfv_config', to='resource_inventory.OPNFVConfig'),
- ),
- migrations.AddField(
- model_name='resourceopnfvconfig',
- name='resource_config',
- field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='resource_opnfv_config', to='resource_inventory.ResourceConfiguration'),
- ),
- migrations.AddField(
- model_name='resourceopnfvconfig',
- name='role',
- field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='resource_opnfv_configs', to='resource_inventory.OPNFVRole'),
- ),
- migrations.AddField(
- model_name='resourceconfiguration',
- name='image',
- field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='resource_inventory.Image'),
- ),
- migrations.AddField(
- model_name='resourceconfiguration',
- name='profile',
- field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='resource_inventory.ResourceProfile'),
- ),
- migrations.AddField(
- model_name='resourceconfiguration',
- name='template',
- field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='resourceConfigurations', to='resource_inventory.ResourceTemplate'),
- ),
- migrations.AddField(
- model_name='interfaceconfiguration',
- name='profile',
- field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='resource_inventory.InterfaceProfile'),
- ),
- migrations.AddField(
- model_name='interfaceconfiguration',
- name='resource_config',
- field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='interface_configs', to='resource_inventory.ResourceConfiguration'),
- ),
- migrations.AddField(
- model_name='interface',
- name='acts_as',
- field=models.OneToOneField(null=True, on_delete=django.db.models.deletion.SET_NULL, to='resource_inventory.InterfaceConfiguration'),
- ),
- migrations.RunPython(create_default_template),
- migrations.AddField(
- model_name='opnfvconfig',
- name='template',
- field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, related_name='opnfv_config', to='resource_inventory.ResourceTemplate'),
- preserve_default=False,
- ),
- ]
diff --git a/src/resource_inventory/migrations/0014_auto_20200305_1415.py b/src/resource_inventory/migrations/0014_auto_20200305_1415.py
deleted file mode 100644
index 6fcf4a6..0000000
--- a/src/resource_inventory/migrations/0014_auto_20200305_1415.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# Generated by Django 2.2 on 2020-03-05 14:15
-
-from django.db import migrations
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('resource_inventory', '0013_auto_20200218_1536'),
- ]
-
- operations = [
- migrations.RenameField(
- model_name='resourcetemplate',
- old_name='hidden',
- new_name='temporary',
- ),
- ]
diff --git a/src/resource_inventory/migrations/0015_resourcetemplate_copy_of.py b/src/resource_inventory/migrations/0015_resourcetemplate_copy_of.py
deleted file mode 100644
index 322dc00..0000000
--- a/src/resource_inventory/migrations/0015_resourcetemplate_copy_of.py
+++ /dev/null
@@ -1,19 +0,0 @@
-# Generated by Django 2.2 on 2020-04-13 13:56
-
-from django.db import migrations, models
-import django.db.models.deletion
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('resource_inventory', '0014_auto_20200305_1415'),
- ]
-
- operations = [
- migrations.AddField(
- model_name='resourcetemplate',
- name='copy_of',
- field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='resource_inventory.ResourceTemplate'),
- ),
- ]
diff --git a/src/resource_inventory/migrations/0016_auto_20201109_1947.py b/src/resource_inventory/migrations/0016_auto_20201109_1947.py
deleted file mode 100644
index d145f06..0000000
--- a/src/resource_inventory/migrations/0016_auto_20201109_1947.py
+++ /dev/null
@@ -1,59 +0,0 @@
-# Generated by Django 2.2 on 2020-11-09 19:47
-
-from django.db import migrations, models
-import django.db.models.deletion
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('resource_inventory', '0015_resourcetemplate_copy_of'),
- ]
-
- operations = [
- migrations.AddField(
- model_name='physicalnetwork',
- name='bundle',
- field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='resource_inventory.ResourceBundle'),
- ),
- migrations.AddField(
- model_name='resourceconfiguration',
- name='name',
- field=models.CharField(default='<Hostname>', max_length=3000),
- ),
- migrations.AlterField(
- model_name='cpuprofile',
- name='cflags',
- field=models.TextField(blank=True, null=True),
- ),
- migrations.AlterField(
- model_name='interface',
- name='acts_as',
- field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='resource_inventory.InterfaceConfiguration'),
- ),
- migrations.AlterField(
- model_name='interfaceconfiguration',
- name='connections',
- field=models.ManyToManyField(blank=True, to='resource_inventory.NetworkConnection'),
- ),
- migrations.AlterField(
- model_name='resourcetemplate',
- name='copy_of',
- field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='resource_inventory.ResourceTemplate'),
- ),
- migrations.AlterField(
- model_name='resourcetemplate',
- name='name',
- field=models.CharField(max_length=300),
- ),
- migrations.AlterField(
- model_name='server',
- name='bundle',
- field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='resource_inventory.ResourceBundle'),
- ),
- migrations.AlterField(
- model_name='server',
- name='config',
- field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='resource_inventory.ResourceConfiguration'),
- ),
- ]
diff --git a/src/resource_inventory/migrations/0017_auto_20201218_1516.py b/src/resource_inventory/migrations/0017_auto_20201218_1516.py
deleted file mode 100644
index d4884de..0000000
--- a/src/resource_inventory/migrations/0017_auto_20201218_1516.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# Generated by Django 2.2 on 2020-12-18 15:16
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('resource_inventory', '0016_auto_20201109_1947'),
- ]
-
- operations = [
- migrations.AlterField(
- model_name='resourceconfiguration',
- name='name',
- field=models.CharField(default='opnfv_host', max_length=3000),
- ),
- ]
diff --git a/src/resource_inventory/migrations/0018_auto_20210630_1629.py b/src/resource_inventory/migrations/0018_auto_20210630_1629.py
deleted file mode 100644
index 19e53e4..0000000
--- a/src/resource_inventory/migrations/0018_auto_20210630_1629.py
+++ /dev/null
@@ -1,101 +0,0 @@
-# Generated by Django 2.2 on 2021-06-30 16:29
-
-from django.db import migrations, models
-import django.db.models.deletion
-from account.models import Lab
-
-
-def set_availability(apps, schema_editor):
- models = [apps.get_model('resource_inventory', 'Image'), apps.get_model('resource_inventory', 'Opsys')]
-
- for model in models:
- for obj in model.objects.all():
- obj.available = False
- obj.obsolete = True
- obj.save()
-
-
-def set_rconfig_arch(apps, schema_editor):
- rprofs = apps.get_model('resource_inventory', 'ResourceProfile')
-
- for rprof in rprofs.objects.all():
- rprof.architecture = rprof.cpuprofile.first().architecture
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('account', '0009_auto_20210324_2107'),
- ('resource_inventory', '0017_auto_20201218_1516'),
- ]
-
- operations = [
- migrations.RemoveField(
- model_name='image',
- name='host_type',
- ),
- migrations.AlterField(
- model_name='image',
- name='lab_id',
- field=models.CharField(default='none (retired)', max_length=100),
- preserve_default=True,
- ),
- migrations.RemoveField(
- model_name='opsys',
- name='sup_installers',
- ),
-
- migrations.AddField(
- model_name='image',
- name='architecture',
- field=models.CharField(choices=[('x86_64', 'x86_64'), ('aarch64', 'aarch64'), ('unknown', 'unknown')], default='unknown', max_length=50),
- preserve_default=False,
- ),
-
- migrations.AddField(
- model_name='image',
- name='available',
- field=models.BooleanField(default=True),
- ),
- migrations.AddField(
- model_name='image',
- name='obsolete',
- field=models.BooleanField(default=False),
- ),
-
- migrations.AddField(
- model_name='opsys',
- name='available',
- field=models.BooleanField(default=False),
- ),
- migrations.AddField(
- model_name='opsys',
- name='obsolete',
- field=models.BooleanField(default=True),
- ),
-
- migrations.RunPython(set_availability),
-
- migrations.AddField(
- model_name='opsys',
- name='lab_id',
- field=models.CharField(default="none (retired)", max_length=100),
- preserve_default=False,
- ),
-
- migrations.AddField(
- model_name='opsys',
- name='from_lab',
- field=models.ForeignKey(default=Lab.objects.first, on_delete=django.db.models.deletion.CASCADE, to='account.Lab'),
- preserve_default=False,
- ),
-
- migrations.AddField(
- model_name='resourceprofile',
- name='architecture',
- field=models.CharField(choices=[('x86_64', 'x86_64'), ('aarch64', 'aarch64'), ('unknown', 'unknown')], default='unknown', max_length=50),
- preserve_default=False,
- ),
-
- migrations.RunPython(set_rconfig_arch),
- ]
diff --git a/src/resource_inventory/migrations/0019_auto_20210701_1947.py b/src/resource_inventory/migrations/0019_auto_20210701_1947.py
deleted file mode 100644
index e64d174..0000000
--- a/src/resource_inventory/migrations/0019_auto_20210701_1947.py
+++ /dev/null
@@ -1,43 +0,0 @@
-# Generated by Django 2.2 on 2021-07-01 19:47
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('resource_inventory', '0018_auto_20210630_1629'),
- ]
-
- operations = [
- migrations.AlterField(
- model_name='image',
- name='lab_id',
- field=models.CharField(max_length=100),
- ),
- migrations.AlterField(
- model_name='image',
- name='name',
- field=models.CharField(max_length=100),
- ),
- migrations.AlterField(
- model_name='network',
- name='name',
- field=models.CharField(max_length=200),
- ),
- migrations.AlterField(
- model_name='opsys',
- name='available',
- field=models.BooleanField(default=True),
- ),
- migrations.AlterField(
- model_name='opsys',
- name='obsolete',
- field=models.BooleanField(default=False),
- ),
- migrations.AlterField(
- model_name='resourceprofile',
- name='architecture',
- field=models.CharField(choices=[('x86_64', 'x86_64'), ('aarch64', 'aarch64')], max_length=50),
- ),
- ]
diff --git a/src/resource_inventory/migrations/0020_cloudinitfile.py b/src/resource_inventory/migrations/0020_cloudinitfile.py
deleted file mode 100644
index 198181c..0000000
--- a/src/resource_inventory/migrations/0020_cloudinitfile.py
+++ /dev/null
@@ -1,21 +0,0 @@
-# Generated by Django 2.2 on 2021-09-07 14:48
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('resource_inventory', '0019_auto_20210701_1947'),
- ]
-
- operations = [
- migrations.CreateModel(
- name='CloudInitFile',
- fields=[
- ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('text', models.TextField()),
- ('priority', models.IntegerField()),
- ],
- ),
- ]
diff --git a/src/resource_inventory/migrations/0021_resourceconfiguration_cloud_init_files.py b/src/resource_inventory/migrations/0021_resourceconfiguration_cloud_init_files.py
deleted file mode 100644
index 6b0befc..0000000
--- a/src/resource_inventory/migrations/0021_resourceconfiguration_cloud_init_files.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# Generated by Django 2.2 on 2021-09-10 18:10
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('resource_inventory', '0020_cloudinitfile'),
- ]
-
- operations = [
- migrations.AddField(
- model_name='resourceconfiguration',
- name='cloud_init_files',
- field=models.ManyToManyField(blank=True, to='resource_inventory.CloudInitFile'),
- ),
- ]
diff --git a/src/resource_inventory/migrations/0022_auto_20210925_2028.py b/src/resource_inventory/migrations/0022_auto_20210925_2028.py
deleted file mode 100644
index 2b0b902..0000000
--- a/src/resource_inventory/migrations/0022_auto_20210925_2028.py
+++ /dev/null
@@ -1,23 +0,0 @@
-# Generated by Django 2.2 on 2021-09-25 20:28
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('resource_inventory', '0021_resourceconfiguration_cloud_init_files'),
- ]
-
- operations = [
- migrations.AddField(
- model_name='resourcetemplate',
- name='private_vlan_pool',
- field=models.TextField(default=''),
- ),
- migrations.AddField(
- model_name='resourcetemplate',
- name='public_vlan_pool',
- field=models.TextField(default=''),
- ),
- ]
diff --git a/src/resource_inventory/migrations/0023_cloudinitfile_generated.py b/src/resource_inventory/migrations/0023_cloudinitfile_generated.py
deleted file mode 100644
index b309753..0000000
--- a/src/resource_inventory/migrations/0023_cloudinitfile_generated.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# Generated by Django 2.2 on 2021-12-17 18:54
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('resource_inventory', '0022_auto_20210925_2028'),
- ]
-
- operations = [
- migrations.AddField(
- model_name='cloudinitfile',
- name='generated',
- field=models.BooleanField(default=False),
- ),
- ]
diff --git a/src/resource_inventory/migrations/__init__.py b/src/resource_inventory/migrations/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/src/resource_inventory/migrations/__init__.py
+++ /dev/null
diff --git a/src/resource_inventory/models.py b/src/resource_inventory/models.py
deleted file mode 100644
index 5d87430..0000000
--- a/src/resource_inventory/models.py
+++ /dev/null
@@ -1,705 +0,0 @@
-##############################################################################
-# Copyright (c) 2018 Sawyer Bergeron, Parker Berberian, and others.
-# Copyright (c) 2020 Sawyer Bergeron, Sean Smith, 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.core.exceptions import ValidationError
-from django.db import models
-from django.db.models import Q
-import traceback
-import json
-
-import re
-from collections import Counter
-
-from account.models import Lab
-from dashboard.utils import AbstractModelQuery
-
-"""
-Profiles of resources hosted by labs.
-
-These describe hardware attributes of the different Resources a lab hosts.
-A single Resource subclass (e.g. Server) may have instances that point to different
-Profile models (e.g. an x86 server profile and armv8 server profile.
-"""
-
-
-class ResourceProfile(models.Model):
- id = models.AutoField(primary_key=True)
- name = models.CharField(max_length=200, unique=True)
- architecture = models.CharField(max_length=50, choices=[
- ("x86_64", "x86_64"),
- ("aarch64", "aarch64")
- ])
- description = models.TextField()
- labs = models.ManyToManyField(Lab, related_name="resourceprofiles")
-
- def validate(self):
- validname = re.compile(r"^[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
-
- def get_resources(self, lab=None, working=True, unreserved=False):
- """
- Return a list of Resource objects which have this profile.
-
- If lab is provided, only resources at that lab will be returned.
- If working=True, will only return working hosts
- """
- resources = []
- query = Q(profile=self)
- if lab:
- query = query & Q(lab=lab)
- if working:
- query = query & Q(working=True)
-
- resources = ResourceQuery.filter(query)
-
- if unreserved:
- resources = [r for r in resources if not r.is_reserved()]
-
- return resources
-
-
-class InterfaceProfile(models.Model):
- id = models.AutoField(primary_key=True)
- speed = models.IntegerField()
- name = models.CharField(max_length=100)
- host = models.ForeignKey(ResourceProfile, on_delete=models.CASCADE, related_name='interfaceprofile')
- nic_type = models.CharField(
- max_length=50,
- choices=[
- ("onboard", "onboard"),
- ("pcie", "pcie")
- ],
- default="onboard"
- )
- order = models.IntegerField(default=-1)
-
- 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(ResourceProfile, on_delete=models.CASCADE, 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(ResourceProfile, on_delete=models.CASCADE, related_name='cpuprofile')
- cflags = models.TextField(null=True, blank=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(ResourceProfile, on_delete=models.CASCADE, related_name='ramprofile')
-
- def __str__(self):
- return str(self.amount) + "G for " + str(self.host)
-
-
-"""
-Resource Models
-
-These models represent actual hardware resources
-with varying degrees of abstraction.
-"""
-
-
-class CloudInitFile(models.Model):
- text = models.TextField()
-
- # higher priority is applied later, so "on top" of existing files
- priority = models.IntegerField()
- generated = models.BooleanField(default=False)
-
- @classmethod
- def merge_strategy(cls):
- return [
- {'name': 'list', 'settings': ['append']},
- {'name': 'dict', 'settings': ['recurse_list', 'replace']},
- ]
-
- @classmethod
- def create(cls, text="", priority=0):
- return CloudInitFile.objects.create(priority=priority, text=text)
-
-
-class ResourceTemplate(models.Model):
- """
- Models a "template" of a complete, configured collection of resources that can be booked.
-
- For example, this may represent a Pharos POD. This model is a template of the actual
- resources that will be booked. This model can be "instantiated" into real resource models
- across multiple different bookings.
- """
-
- # TODO: template might not be a good name because this is a collection of lots of configured resources
- id = models.AutoField(primary_key=True)
- name = models.CharField(max_length=300)
- xml = models.TextField()
- owner = models.ForeignKey(User, null=True, on_delete=models.SET_NULL)
- lab = models.ForeignKey(Lab, null=True, on_delete=models.SET_NULL, related_name="resourcetemplates")
- description = models.CharField(max_length=1000, default="")
- public = models.BooleanField(default=False)
- temporary = models.BooleanField(default=False)
- copy_of = models.ForeignKey("ResourceTemplate", blank=True, null=True, on_delete=models.SET_NULL)
-
- # if these fields are empty ("") then they are implicitly "every vlan",
- # otherwise we filter any allocations we try to instantiate against this list
- # they should be represented as a json list of integers
- private_vlan_pool = models.TextField(default="")
- public_vlan_pool = models.TextField(default="")
-
- def private_vlan_pool_set(self):
- if self.private_vlan_pool != "":
- return set(json.loads(self.private_vlan_pool))
- else:
- return None
-
- def public_vlan_pool_set(self):
- if self.private_vlan_pool != "":
- return set(json.loads(self.public_vlan_pool))
- else:
- return None
-
- def getConfigs(self):
- configs = self.resourceConfigurations.all()
- return list(configs)
-
- def get_required_resources(self):
- profiles = Counter([str(config.profile) for config in self.getConfigs()])
- return dict(profiles)
-
- def __str__(self):
- return self.name
-
-
-class ResourceBundle(models.Model):
- """
- Collection of Resource objects.
-
- This is just a way of aggregating all the resources in a booking into a single model.
- """
-
- template = models.ForeignKey(ResourceTemplate, on_delete=models.SET_NULL, null=True)
-
- def __str__(self):
- if self.template is None:
- return "Resource bundle " + str(self.id) + " with no template"
- return "instance of " + str(self.template)
-
- def get_resources(self):
- return ResourceQuery.filter(bundle=self)
-
- def get_resource_with_role(self, role):
- # TODO
- pass
-
- def release(self):
- for pn in PhysicalNetwork.objects.filter(bundle=self).all():
- try:
- pn.release()
- except Exception as e:
- print("Exception occurred while trying to release resource ", pn.vlan_id)
- print(e)
- traceback.print_exc()
-
- for resource in self.get_resources():
- try:
- resource.release()
- except Exception as e:
- print("Exception occurred while trying to release resource ", resource)
- print(e)
- traceback.print_exc()
-
- def get_template_name(self):
- if not self.template:
- return ""
- if not self.template.temporary:
- return self.template.name
- return self.template.copy_of.name
-
-
-class ResourceConfiguration(models.Model):
- """Model to represent a complete configuration for a single physical Resource."""
-
- id = models.AutoField(primary_key=True)
- profile = models.ForeignKey(ResourceProfile, on_delete=models.CASCADE)
- image = models.ForeignKey("Image", on_delete=models.PROTECT)
- template = models.ForeignKey(ResourceTemplate, related_name="resourceConfigurations", null=True, on_delete=models.CASCADE)
- is_head_node = models.BooleanField(default=False)
- name = models.CharField(max_length=3000, default="opnfv_host")
-
- cloud_init_files = models.ManyToManyField(CloudInitFile, blank=True)
-
- def __str__(self):
- return str(self.name)
-
- def ci_file_list(self):
- return list(self.cloud_init_files.order_by("priority").all())
-
-
-def get_default_remote_info():
- return RemoteInfo.objects.get_or_create(
- address="default",
- mac_address="default",
- password="default",
- user="default",
- management_type="default",
- versions="[default]"
- )[0].pk
-
-
-class Resource(models.Model):
- """
- Super class for all hardware resource models.
-
- Defines methods that must be implemented and common database fields.
- Any new kind of Resource a lab wants to host (White box switch, traffic generator, etc)
- should inherit from this class and fulfill the functional interface
- """
-
- class Meta:
- abstract = True
-
- bundle = models.ForeignKey(ResourceBundle, on_delete=models.SET_NULL, blank=True, null=True)
- profile = models.ForeignKey(ResourceProfile, on_delete=models.CASCADE)
- config = models.ForeignKey(ResourceConfiguration, on_delete=models.SET_NULL, blank=True, null=True)
- working = models.BooleanField(default=True)
- vendor = models.CharField(max_length=100, default="unknown")
- model = models.CharField(max_length=150, default="unknown")
- interfaces = models.ManyToManyField("Interface")
- remote_management = models.ForeignKey("RemoteInfo", default=get_default_remote_info, on_delete=models.SET(get_default_remote_info))
- labid = models.CharField(max_length=200, default="default_id", unique=True)
- lab = models.ForeignKey(Lab, on_delete=models.CASCADE)
-
- def get_configuration(self, state):
- """
- Get configuration of Resource.
-
- Returns the desired configuration for this host as a
- JSON object as defined in the rest api spec.
- state is a ConfigState
- """
- raise NotImplementedError("Must implement in concrete Resource classes")
-
- def reserve(self):
- """Reserve this resource for its currently assigned booking."""
- raise NotImplementedError("Must implement in concrete Resource classes")
-
- def release(self):
- """Make this resource available again for new boookings."""
- raise NotImplementedError("Must implement in concrete Resource classes")
-
- def get_interfaces(self):
- """
- Return a list of interfaces on this resource.
-
- The ordering of interfaces should be consistent.
- """
- raise NotImplementedError("Must implement in concrete Resource classes")
-
- def is_reserved(self):
- """Return True if this Resource is reserved."""
- raise NotImplementedError("Must implement in concrete Resource classes")
-
- def same_instance(self, other):
- """Return True if this Resource is the same instance as other."""
- raise NotImplementedError("Must implement in concrete Resource classes")
-
- def save(self, *args, **kwargs):
- """Assert that labid is unique across all Resource models."""
- res = ResourceQuery.filter(labid=self.labid)
- if len(res) > 1:
- raise ValidationError("Too many resources with labid " + str(self.labid))
-
- if len(res) == 1:
- if not self.same_instance(res[0]):
- raise ValidationError("Too many resources with labid " + str(self.labid))
- super().save(*args, **kwargs)
-
-
-class RemoteInfo(models.Model):
- address = models.CharField(max_length=15)
- mac_address = models.CharField(max_length=17)
- password = models.CharField(max_length=100)
- user = models.CharField(max_length=100)
- management_type = models.CharField(max_length=50, default="ipmi")
- versions = models.CharField(max_length=100) # json serialized list of floats
-
-
-class Server(Resource):
- """Resource subclass - a basic baremetal server."""
-
- booked = models.BooleanField(default=False)
- name = models.CharField(max_length=200, unique=True)
-
- def __str__(self):
- return self.name
-
- def get_configuration(self, state):
- ipmi = state == ConfigState.NEW
- power = "off" if state == ConfigState.CLEAN else "on"
- image = self.config.image.lab_id if self.config else "unknown"
-
- return {
- "id": self.labid,
- "image": image,
- "hostname": self.config.name,
- "power": power,
- "ipmi_create": str(ipmi)
- }
-
- def get_interfaces(self):
- return list(self.interfaces.all().order_by('bus_address'))
-
- def release(self):
- self.bundle = None
- self.booked = False
- self.save()
-
- def reserve(self):
- self.booked = True
- self.save()
-
- def is_reserved(self):
- return self.booked
-
- def same_instance(self, other):
- return isinstance(other, Server) and other.name == self.name
-
-
-def is_serializable(data):
- try:
- json.dumps(data)
- return True
- except Exception:
- return False
-
-
-class Opsys(models.Model):
- id = models.AutoField(primary_key=True)
- name = models.CharField(max_length=100)
- lab_id = models.CharField(max_length=100)
- obsolete = models.BooleanField(default=False)
- available = models.BooleanField(default=True) # marked true by Cobbler if it exists there
- from_lab = models.ForeignKey(Lab, on_delete=models.CASCADE)
-
- indexes = [
- models.Index(fields=['cobbler_id'])
- ]
-
- def new_from_data(data):
- opsys = Opsys()
- opsys.update(data)
- return opsys
-
- def serialize(self):
- d = {}
- for field in vars(self):
- attr = getattr(self, field)
- if is_serializable(attr):
- d[field] = attr
- return d
-
- def update(self, data):
- for field in vars(self):
- if field in data:
- setattr(self, field, data[field] if data[field] else getattr(self, field))
-
- def __str__(self):
- return self.name
-
-
-class Image(models.Model):
- """Model for representing OS images / snapshots of hosts."""
-
- id = models.AutoField(primary_key=True)
- from_lab = models.ForeignKey(Lab, on_delete=models.CASCADE)
- architecture = models.CharField(max_length=50, choices=[
- ("x86_64", "x86_64"),
- ("aarch64", "aarch64"),
- ("unknown", "unknown"),
- ])
- lab_id = models.CharField(max_length=100)
- name = models.CharField(max_length=100)
- owner = models.ForeignKey(User, null=True, on_delete=models.SET_NULL)
- public = models.BooleanField(default=True)
- description = models.TextField()
- os = models.ForeignKey(Opsys, null=True, on_delete=models.CASCADE)
-
- available = models.BooleanField(default=True) # marked True by cobbler if it exists there
- obsolete = models.BooleanField(default=False)
-
- indexes = [
- models.Index(fields=['architecture']),
- models.Index(fields=['cobbler_id'])
- ]
-
- def __str__(self):
- return self.name
-
- def is_obsolete(self):
- return self.obsolete or self.os.obsolete
-
- def serialize(self):
- d = {}
- for field in vars(self):
- attr = getattr(self, field)
- if is_serializable(attr):
- d[field] = attr
- return d
-
- def update(self, data):
- for field in vars(self):
- if field in data:
- setattr(self, field, data[field] if data[field] else getattr(self, field))
-
- def new_from_data(data):
- img = Image()
- img.update(data)
- return img
-
- def in_use(self):
- for resource in ResourceQuery.filter(config__image=self):
- if resource.is_reserved():
- return True
-
- return False
-
-
-"""
-Networking configuration models
-"""
-
-
-class Network(models.Model):
- id = models.AutoField(primary_key=True)
- name = models.CharField(max_length=200)
- bundle = models.ForeignKey(ResourceTemplate, on_delete=models.CASCADE, related_name="networks")
- is_public = models.BooleanField()
-
- def __str__(self):
- return self.name
-
-
-class PhysicalNetwork(models.Model):
- vlan_id = models.IntegerField()
- generic_network = models.ForeignKey(Network, on_delete=models.CASCADE)
- bundle = models.ForeignKey(ResourceBundle, null=True, blank=True, on_delete=models.CASCADE)
-
- def get_configuration(self, state):
- """
- Get the network configuration.
-
- Collects info about each attached network interface and vlan, etc
- """
- return {}
-
- def reserve(self):
- """Reserve vlan(s) associated with this network."""
- return False
-
- def release(self):
- from booking.models import Booking
-
- booking = Booking.objects.get(resource=self.bundle)
- lab = booking.lab
- vlan_manager = lab.vlan_manager
-
- if self.generic_network.is_public:
- vlan_manager.release_public_vlan(self.vlan_id)
- else:
- vlan_manager.release_vlans([self.vlan_id])
- return False
-
- def __str__(self):
- return 'Physical Network for ' + self.generic_network.name
-
-
-class NetworkConnection(models.Model):
- network = models.ForeignKey(Network, on_delete=models.CASCADE)
- vlan_is_tagged = models.BooleanField()
-
- def __str__(self):
- return 'Connection to ' + self.network.name
-
-
-class Vlan(models.Model):
- id = models.AutoField(primary_key=True)
- vlan_id = models.IntegerField()
- tagged = models.BooleanField()
- public = models.BooleanField(default=False)
- network = models.ForeignKey(PhysicalNetwork, on_delete=models.DO_NOTHING, null=True)
-
- def __str__(self):
- return str(self.vlan_id) + ("_T" if self.tagged else "")
-
-
-class InterfaceConfiguration(models.Model):
- id = models.AutoField(primary_key=True)
- profile = models.ForeignKey(InterfaceProfile, on_delete=models.CASCADE)
- resource_config = models.ForeignKey(ResourceConfiguration, on_delete=models.CASCADE, related_name='interface_configs')
- connections = models.ManyToManyField(NetworkConnection, blank=True)
-
- def __str__(self):
- return "type " + str(self.profile) + " on host " + str(self.resource_config)
-
-
-"""
-OPNFV / Software configuration models
-"""
-
-
-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 NetworkRole(models.Model):
- name = models.CharField(max_length=100)
- network = models.ForeignKey(Network, on_delete=models.CASCADE)
-
-
-def create_resource_ref_string(for_hosts: [str]) -> str:
- # need to sort the list, then do dump
- for_hosts.sort()
-
- return json.dumps(for_hosts)
-
-
-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)
- template = models.ForeignKey(ResourceTemplate, related_name="opnfv_config", on_delete=models.CASCADE)
- networks = models.ManyToManyField(NetworkRole)
- name = models.CharField(max_length=300, blank=True, default="")
- description = models.CharField(max_length=600, blank=True, default="")
-
- 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
-
-
-def get_sentinal_opnfv_role():
- return OPNFVRole.objects.get_or_create(name="deleted", description="Role was deleted.")
-
-
-class ResourceOPNFVConfig(models.Model):
- role = models.ForeignKey(OPNFVRole, related_name="resource_opnfv_configs", on_delete=models.CASCADE)
- resource_config = models.ForeignKey(ResourceConfiguration, related_name="resource_opnfv_config", on_delete=models.CASCADE)
- opnfv_config = models.ForeignKey(OPNFVConfig, related_name="resource_opnfv_config", on_delete=models.CASCADE)
-
-
-class Interface(models.Model):
- id = models.AutoField(primary_key=True)
- mac_address = models.CharField(max_length=17)
- bus_address = models.CharField(max_length=50)
- config = models.ManyToManyField(Vlan)
- acts_as = models.OneToOneField(InterfaceConfiguration, blank=True, null=True, on_delete=models.CASCADE)
- profile = models.ForeignKey(InterfaceProfile, on_delete=models.CASCADE)
-
- def __str__(self):
- return self.mac_address + " on host " + str(self.profile.host.name)
-
- def clean(self, *args, **kwargs):
- if self.acts_as and self.acts_as.profile != self.profile:
- raise ValidationError("Interface Configuration's Interface Profile does not match Interface Profile chosen for Interface.")
- super().clean(*args, **kwargs)
-
- def save(self, *args, **kwargs):
- self.full_clean()
- super().save(*args, **kwargs)
-
-
-"""
-Some Enums for dealing with global constants.
-"""
-
-
-class OPNFV_SETTINGS():
- """This is a static configuration class."""
-
- # all the required network types in PDF/IDF spec
- NETWORK_ROLES = ["public", "private", "admin", "mgmt"]
-
-
-class ConfigState:
- NEW = 0
- RESET = 100
- CLEAN = 200
-
-
-RESOURCE_TYPES = [Server]
-
-
-class ResourceQuery(AbstractModelQuery):
- model_list = [Server]
diff --git a/src/resource_inventory/pdf_templater.py b/src/resource_inventory/pdf_templater.py
deleted file mode 100644
index c4b22fe..0000000
--- a/src/resource_inventory/pdf_templater.py
+++ /dev/null
@@ -1,176 +0,0 @@
-##############################################################################
-# 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.template.loader import render_to_string
-import booking
-from resource_inventory.models import Server
-
-
-class PDFTemplater:
- """Utility class to create a full PDF yaml file."""
-
- @classmethod
- def makePDF(cls, booking):
- """Fill the pod descriptor file template with info about the resource."""
- template = "dashboard/pdf.yaml"
- info = {}
- info['details'] = cls.get_pdf_details(booking.resource)
- try:
- info['jumphost'] = cls.get_pdf_jumphost(booking)
- except Exception:
- # filling in jumphost info can be optional in some cases, this shouldn't be a hard error
- info['jumphost'] = {}
- info['nodes'] = cls.get_pdf_nodes(booking)
-
- return render_to_string(template, context=info)
-
- @classmethod
- def get_pdf_details(cls, resource):
- """Info for the "details" section."""
- details = {}
- owner = "Anon"
- email = "email@mail.com"
- resource_lab = resource.template.lab
- lab = resource_lab.name
- location = resource_lab.location
- pod_type = "development"
- link = resource_lab.lab_info_link
-
- 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:
- pass
-
- details['contact'] = email
- details['lab'] = lab
- details['link'] = link
- details['owner'] = owner
- details['location'] = location
- details['type'] = pod_type
-
- return details
-
- @classmethod
- def get_jumphost(cls, booking):
- """Return the host designated as the Jumphost for the booking."""
- jumphost = None
- if booking.opnfv_config:
- jumphost_opnfv_config = booking.opnfv_config.host_opnfv_config.get(
- role__name__iexact="jumphost"
- )
- jumphost = booking.resource.hosts.get(config=jumphost_opnfv_config.host_config)
- else: # if there is no opnfv config, use headnode
- jumphost = Server.objects.filter(
- bundle=booking.resource,
- config__is_head_node=True
- ).first()
-
- return jumphost
-
- @classmethod
- def get_pdf_jumphost(cls, booking):
- """Return a dict of all the info for the "jumphost" section."""
- jumphost = cls.get_jumphost(booking)
- jumphost_info = cls.get_pdf_host(jumphost)
- jumphost_info['os'] = jumphost.config.image.os.name
- return jumphost_info
-
- @classmethod
- def get_pdf_nodes(cls, booking):
- """Return a list of all the "nodes" (every host except jumphost)."""
- pdf_nodes = []
- nodes = set(Server.objects.filter(bundle=booking.resource))
- nodes.discard(cls.get_jumphost(booking))
-
- for node in nodes:
- pdf_nodes.append(cls.get_pdf_host(node))
-
- return pdf_nodes
-
- @classmethod
- def get_pdf_host(cls, host):
- """
- Gather all needed info about a host.
-
- returns a dictionary
- """
- host_info = {}
- host_info['name'] = host.name
- host_info['node'] = cls.get_pdf_host_node(host)
- host_info['disks'] = []
- for disk in host.profile.storageprofile.all():
- host_info['disks'].append(cls.get_pdf_host_disk(disk))
-
- host_info['interfaces'] = []
- for interface in host.interfaces.all():
- host_info['interfaces'].append(cls.get_pdf_host_iface(interface))
-
- host_info['remote'] = cls.get_pdf_host_remote_management(host)
-
- return host_info
-
- @classmethod
- def get_pdf_host_node(cls, host):
- """Return "node" info for a given host."""
- d = {}
- d['type'] = "baremetal"
- d['vendor'] = host.vendor
- d['model'] = host.model
- d['memory'] = str(host.profile.ramprofile.first().amount) + "G"
-
- cpu = host.profile.cpuprofile.first()
- d['arch'] = cpu.architecture
- d['cpus'] = cpu.cpus
- d['cores'] = cpu.cores
- cflags = cpu.cflags
- if cflags and cflags.strip():
- d['cpu_cflags'] = cflags
- else:
- d['cpu_cflags'] = "none"
-
- return d
-
- @classmethod
- def get_pdf_host_disk(cls, disk):
- """Return a dict describing the given disk."""
- 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
- return disk_info
-
- @classmethod
- def get_pdf_host_iface(cls, interface):
- """Return a dict describing given interface."""
- iface_info = {}
- iface_info['features'] = "none"
- iface_info['mac_address'] = interface.mac_address
- iface_info['name'] = interface.profile.name
- speed = str(int(interface.profile.speed / 1000)) + "gb"
- iface_info['speed'] = speed
- return iface_info
-
- @classmethod
- def get_pdf_host_remote_management(cls, host):
- """Get the remote params of the host."""
- man = host.remote_management
- mgmt = {}
- mgmt['address'] = man.address
- mgmt['mac_address'] = man.mac_address
- mgmt['pass'] = man.password
- mgmt['type'] = man.management_type
- mgmt['user'] = man.user
- mgmt['versions'] = [man.versions]
- return mgmt
diff --git a/src/resource_inventory/resource_manager.py b/src/resource_inventory/resource_manager.py
deleted file mode 100644
index 16c106e..0000000
--- a/src/resource_inventory/resource_manager.py
+++ /dev/null
@@ -1,197 +0,0 @@
-##############################################################################
-# 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 __future__ import annotations # noqa: F407
-
-import re
-from typing import Optional
-from django.db.models import Q
-
-from dashboard.exceptions import ResourceAvailabilityException
-
-from resource_inventory.models import (
- Resource,
- ResourceBundle,
- ResourceTemplate,
- ResourceConfiguration,
- Network,
- Vlan,
- PhysicalNetwork,
- InterfaceConfiguration,
-)
-
-from account.models import Lab
-from django.contrib.auth.models import User
-
-
-class ResourceManager:
-
- instance = None
-
- def __init__(self):
- pass
-
- @staticmethod
- def getInstance() -> ResourceManager:
- if ResourceManager.instance is None:
- ResourceManager.instance = ResourceManager()
- return ResourceManager.instance
-
- def getAvailableResourceTemplates(self, lab: Lab, user: Optional[User] = None) -> list[ResourceTemplate]:
- filter = Q(public=True)
- if user:
- filter = filter | Q(owner=user)
- filter = filter & Q(temporary=False) & Q(lab=lab)
- return ResourceTemplate.objects.filter(filter)
-
- def templateIsReservable(self, resource_template: ResourceTemplate):
- """
- Check if the required resources to reserve this template is available.
-
- No changes to the database
- """
- # count up hosts
- profile_count = {}
- for config in resource_template.getConfigs():
- if config.profile not in profile_count:
- profile_count[config.profile] = 0
- profile_count[config.profile] += 1
-
- # check that all required hosts are available
- for profile in profile_count.keys():
- available = len(profile.get_resources(lab=resource_template.lab, unreserved=True))
- needed = profile_count[profile]
- if available < needed:
- return False
- return True
-
- # public interface
- def deleteResourceBundle(self, resourceBundle: ResourceBundle):
- raise NotImplementedError("Resource Bundle Deletion Not Implemented")
-
- def releaseResourceBundle(self, resourceBundle: ResourceBundle):
- resourceBundle.release()
-
- def get_vlans(self, resourceTemplate: ResourceTemplate) -> dict[str, int]:
- """
- returns: dict from network name to the associated vlan number (backend vlan id)
- """
- networks = {}
- vlan_manager = resourceTemplate.lab.vlan_manager
- for network in resourceTemplate.networks.all():
- if network.is_public:
- # already throws if can't get requested count, so can always expect public_net to be Some
- public_net = vlan_manager.get_public_vlan(within=resourceTemplate.public_vlan_pool_set())
- vlan_manager.reserve_public_vlan(public_net.vlan)
- networks[network.name] = public_net.vlan
- else:
- # already throws if can't get requested count, so can always index in @ 0
- vlans = vlan_manager.get_vlans(count=1, within=resourceTemplate.private_vlan_pool_set())
- vlan_manager.reserve_vlans(vlans[0])
- networks[network.name] = vlans[0]
- return networks
-
- def instantiateTemplate(self, resource_template: ResourceTemplate):
- """
- Convert a ResourceTemplate into a ResourceBundle.
-
- Takes in a ResourceTemplate and reserves all the
- Resources needed and returns a completed ResourceBundle.
- """
- resource_bundle = ResourceBundle.objects.create(template=resource_template)
- res_configs = resource_template.getConfigs()
- resources = []
-
- vlan_map = self.get_vlans(resource_template)
-
- for config in res_configs:
- try:
- phys_res = self.acquireHost(config)
- phys_res.bundle = resource_bundle
- phys_res.config = config
- resources.append(phys_res)
-
- self.configureNetworking(resource_bundle, phys_res, vlan_map)
- phys_res.save()
-
- except Exception as e:
- self.fail_acquire(resources, vlan_map, resource_template)
- raise e
-
- return resource_bundle
-
- def configureNetworking(self, resource_bundle: ResourceBundle, resource: Resource, vlan_map: dict[str, int]):
- """
- @vlan_map: dict from network name to the associated vlan number (backend vlan id)
- """
- for physical_interface in resource.interfaces.all():
-
- # assign interface configs
- iface_config = InterfaceConfiguration.objects.get(
- profile=physical_interface.profile,
- resource_config=resource.config
- )
-
- physical_interface.acts_as = iface_config
- physical_interface.acts_as.save()
-
- physical_interface.config.clear()
- for connection in iface_config.connections.all():
- physicalNetwork = PhysicalNetwork.objects.create(
- vlan_id=vlan_map[connection.network.name],
- generic_network=connection.network,
- bundle=resource_bundle,
- )
- physical_interface.config.add(
- Vlan.objects.create(
- vlan_id=vlan_map[connection.network.name],
- tagged=connection.vlan_is_tagged,
- public=connection.network.is_public,
- network=physicalNetwork
- )
- )
-
- # private interface
- def acquireHost(self, resource_config: ResourceConfiguration) -> Resource:
- resources = resource_config.profile.get_resources(
- lab=resource_config.template.lab,
- unreserved=True
- )
-
- try:
- resource = resources[0] # TODO: should we randomize and 'load balance' the servers?
- resource.config = resource_config
- resource.reserve()
- return resource
- except IndexError:
- raise ResourceAvailabilityException("No available resources of requested type")
-
- def releaseNetworks(self, template, vlans):
- vlan_manager = template.lab.vlan_manager
- for net_name, vlan_id in vlans.items():
- net = Network.objects.get(name=net_name, bundle=template)
- if (net.is_public):
- vlan_manager.release_public_vlan(vlan_id)
- else:
- vlan_manager.release_vlans(vlan_id)
-
- def fail_acquire(self, hosts, vlans, template):
- self.releaseNetworks(template, vlans)
- for host in hosts:
- host.release()
-
-
-class HostNameValidator(object):
- regex = r'^[A-Za-z0-9][A-Za-z0-9-]*$'
- message = "Hostnames can only contain alphanumeric characters and hyphens (-). Hostnames must start with a letter"
- pattern = re.compile(regex)
-
- @classmethod
- def is_valid_hostname(cls, hostname):
- return len(hostname) < 65 and cls.pattern.fullmatch(hostname) is not None
diff --git a/src/resource_inventory/tests/test_managers.py b/src/resource_inventory/tests/test_managers.py
deleted file mode 100644
index 46cee5a..0000000
--- a/src/resource_inventory/tests/test_managers.py
+++ /dev/null
@@ -1,301 +0,0 @@
-##############################################################################
-# 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, HostNameValidator
-from account.models import Lab
-from resource.models import (
- Host,
- Vlan,
- Interface,
- ResourceBundle,
- GenericHost,
- GenericResourceBundle,
- CpuProfile,
- RamProfile,
- DiskProfile,
- HostProfile,
- InterfaceProfile
-)
-
-
-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.objects.create(
- speed=1000,
- name='eno3',
- host=hostProfile
- )
- DiskProfile.objects.create(
- size=1000,
- media_type="SSD",
- name='/dev/sda',
- host=hostProfile
- )
- CpuProfile.objects.create(
- cores=96,
- architecture="x86_64",
- cpus=2,
- host=hostProfile
- )
- 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)
-
- 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
- )
- 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.objects.create(
- speed=1000,
- name='eno3',
- host=hostProfile
- )
- DiskProfile.objects.create(
- size=1000,
- media_type="SSD",
- name='/dev/sda',
- host=hostProfile
- )
- CpuProfile.objects.create(
- cores=96,
- architecture="x86_64",
- cpus=2,
- host=hostProfile
- )
- 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)
-
- 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
- )
- 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):
- ResourceManager.getInstance().convertResoureBundle(self.genericBundle, self.lab.name)
- # verify bundle configuration
-
-
-class HostNameValidatorTestCase(TestCase):
-
- def test_valid_hostnames(self):
- self.assertTrue(HostNameValidator.is_valid_hostname("localhost"))
- self.assertTrue(HostNameValidator.is_valid_hostname("Localhost"))
- self.assertTrue(HostNameValidator.is_valid_hostname("localHost"))
- self.assertTrue(HostNameValidator.is_valid_hostname("LOCALHOST"))
- self.assertTrue(HostNameValidator.is_valid_hostname("f"))
- self.assertTrue(HostNameValidator.is_valid_hostname("abc123doreyme"))
- self.assertTrue(HostNameValidator.is_valid_hostname("F9999999"))
- self.assertTrue(HostNameValidator.is_valid_hostname("my-host"))
- self.assertTrue(HostNameValidator.is_valid_hostname("My-Host"))
- self.assertTrue(HostNameValidator.is_valid_hostname("MY-HOST"))
- self.assertTrue(HostNameValidator.is_valid_hostname("a-long-name-for-my-host"))
-
- def test_invalid_hostnames(self):
- self.assertFalse(HostNameValidator.is_valid_hostname("-long-name-for-my-host"))
- self.assertFalse(HostNameValidator.is_valid_hostname("546"))
- self.assertFalse(HostNameValidator.is_valid_hostname("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"))
-
- def test_invalid_chars(self):
- self.assertFalse(HostNameValidator.is_valid_hostname("contains!char"))
- self.assertFalse(HostNameValidator.is_valid_hostname("contains@char"))
- self.assertFalse(HostNameValidator.is_valid_hostname("contains#char"))
- self.assertFalse(HostNameValidator.is_valid_hostname("contains$char"))
- self.assertFalse(HostNameValidator.is_valid_hostname("contains%char"))
- self.assertFalse(HostNameValidator.is_valid_hostname("contains^char"))
- self.assertFalse(HostNameValidator.is_valid_hostname("contains&char"))
- self.assertFalse(HostNameValidator.is_valid_hostname("contains*char"))
- self.assertFalse(HostNameValidator.is_valid_hostname("contains(char"))
- self.assertFalse(HostNameValidator.is_valid_hostname("contains)char"))
- self.assertFalse(HostNameValidator.is_valid_hostname("contains_char"))
- self.assertFalse(HostNameValidator.is_valid_hostname("contains=char"))
- self.assertFalse(HostNameValidator.is_valid_hostname("contains+char"))
- self.assertFalse(HostNameValidator.is_valid_hostname("contains|char"))
- self.assertFalse(HostNameValidator.is_valid_hostname("contains\\char"))
- self.assertFalse(HostNameValidator.is_valid_hostname("contains[char"))
- self.assertFalse(HostNameValidator.is_valid_hostname("contains]char"))
- self.assertFalse(HostNameValidator.is_valid_hostname("contains;char"))
- self.assertFalse(HostNameValidator.is_valid_hostname("contains:char"))
- self.assertFalse(HostNameValidator.is_valid_hostname("contains'char"))
- self.assertFalse(HostNameValidator.is_valid_hostname('contains"char'))
- self.assertFalse(HostNameValidator.is_valid_hostname("contains'char"))
- self.assertFalse(HostNameValidator.is_valid_hostname("contains<char"))
- self.assertFalse(HostNameValidator.is_valid_hostname("contains>char"))
- self.assertFalse(HostNameValidator.is_valid_hostname("contains,char"))
- self.assertFalse(HostNameValidator.is_valid_hostname("contains?char"))
- self.assertFalse(HostNameValidator.is_valid_hostname("contains/char"))
- self.assertFalse(HostNameValidator.is_valid_hostname("contains`char"))
- self.assertFalse(HostNameValidator.is_valid_hostname("contains~char"))
diff --git a/src/resource_inventory/tests/test_models.py b/src/resource_inventory/tests/test_models.py
deleted file mode 100644
index 3f2d1d8..0000000
--- a/src/resource_inventory/tests/test_models.py
+++ /dev/null
@@ -1,173 +0,0 @@
-##############################################################################
-# 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 (
- Scenario,
- Installer,
- Opsys,
- ConfigBundle,
- OPNFVConfig,
- OPNFVRole,
- Image,
- HostProfile,
- GenericResourceBundle,
- GenericResource,
- GenericHost,
- HostConfiguration
-)
-
-
-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(
- cobbler_id="profile1",
- 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/src/resource_inventory/urls.py b/src/resource_inventory/urls.py
deleted file mode 100644
index a9a4d43..0000000
--- a/src/resource_inventory/urls.py
+++ /dev/null
@@ -1,36 +0,0 @@
-##############################################################################
-# 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
-##############################################################################
-
-
-"""
-laas_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, hostprofile_detail_view
-
-
-app_name = 'resource'
-urlpatterns = [
- url(r'^hosts$', HostView.as_view(), name='hosts'),
- url(r'^profiles/(?P<hostprofile_id>.+)/$', hostprofile_detail_view, name='host_detail'),
-]
diff --git a/src/resource_inventory/views.py b/src/resource_inventory/views.py
deleted file mode 100644
index 52f8c75..0000000
--- a/src/resource_inventory/views.py
+++ /dev/null
@@ -1,38 +0,0 @@
-##############################################################################
-# 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.views.generic import TemplateView
-from django.shortcuts import get_object_or_404
-from django.shortcuts import render
-
-from resource_inventory.models import ResourceProfile, ResourceQuery
-
-
-class HostView(TemplateView):
- template_name = "resource/hosts.html"
-
- def get_context_data(self, **kwargs):
- context = super(HostView, self).get_context_data(**kwargs)
- hosts = ResourceQuery.filter(working=True)
- context.update({'hosts': hosts, 'title': "Hardware Resources"})
- return context
-
-
-def hostprofile_detail_view(request, hostprofile_id):
- hostprofile = get_object_or_404(ResourceProfile, id=hostprofile_id)
-
- return render(
- request,
- "resource/hostprofile_detail.html",
- {
- 'title': "Host Type: " + str(hostprofile.name),
- 'hostprofile': hostprofile
- }
- )
diff --git a/src/static/bower.json b/src/static/bower.json
deleted file mode 100644
index c9c53e7..0000000
--- a/src/static/bower.json
+++ /dev/null
@@ -1,29 +0,0 @@
-{
- "name": "laas-dashboard-dependencies",
- "authors": [
- "maxbr <maxbr@mi.fu-berlin.de>"
- ],
- "description": "This package contains all the Js/CSS dependencies needed to run the Laas Dashboard.",
- "main": "",
- "license": "Apache2",
- "homepage": "",
- "private": true,
- "ignore": [
- "**/.*",
- "node_modules",
- "bower_components",
- "test",
- "tests"
- ],
- "dependencies": {
- "fullcalendar": "^2.9.0",
- "jquery-migrate": "^3.0.0",
- "bootstrap": "4.3.1",
- "popper.js": "1.14.3",
- "Font-Awesome": "5.9.0",
- "datatables.net": "1.10.19",
- "datatables.net-bs4": "1.10.19",
- "datatables.net-responsive": "2.1.1",
- "datatables.net-responsive-bs4": "2.2.3"
- }
-}
diff --git a/src/static/css/anuket.css b/src/static/css/anuket.css
deleted file mode 100644
index 6bbdb3f..0000000
--- a/src/static/css/anuket.css
+++ /dev/null
@@ -1,115 +0,0 @@
-nav ,body{
- background-color:#fff !important;
- color:#343a40 !important;
- }
-
- header{
- background-color:#f8f9fa !important;
- color:#343a40 !important;
- }
-
- p, h1, h2, h3, h4, h5{
- color:#343a40 !important;
- }
-
- a, .page-link {
- color: #007473 !important;
- }
-
- .page-item.active .page-link{
- color: #f8f9fa !important;
- background-color: #007473 !important;
- }
-
- .topcrumb.active > span {
- background: #007473 !important;
- }
-
- .nav-bg{
- background-color:#fff !important;
- color:#343a40 !important;
- }
-
- .nav-bg:hover{
- background-color:#f8f9fa !important;
- transition-duration:0.2s;
- }
-
- .dropDown-bg{
- background-color:#d6d8db !important;
- color:#343a40 !important;
- }
-
- .btn-primary{
- color: #f8f9fa !important;
- background-color: #007473 !important;
- border:0px !important;
- transition-duration:0.2s !important;
- }
-
- .btn-primary:hover{
- color: #343a40 !important;
- background-color: #6BDAD5 !important;
- border:0px !important;
- }
-
- .btn-primary:focus{
- color: #343a40 !important;
- background-color: #6BDAD5 !important;
- border:0px !important;
- }
-
- .btn-success{
- color: #f8f9fa;
- background-color: #008852;
- border:0px !important;
- }
-
- .btn-success:hover{
- color: #343a40;
- background-color: #00CE7C;
- border:0px !important;
- }
-
- .btn-success:focus{
- color: #343a40;
- background-color: #00CE7C;
- border:0px !important;
- }
-
- .btn-danger {
- color: #f8f9fa;
- background-color: #af2b38;
- border:0px !important;
- }
-
- .btn-danger:hover {
- color: #f8f9fa;
- background-color: #dc3545;
- border:0px !important;
- }
-
- .alert-danger{
- background-color: #e6b3c1 !important;
- color:#820c2c !important;
- border:0px !important;
- }
-
- .Anuket-Text{
- color:#343a40 !important;
- }
-
- .selected_node {
- border-color: #008852;
- box-shadow: 0 1px 1px rgb(0 0 0 / 8%), 0 0 8px rgb(109 243 76 / 60%);
- transition: border-color ease-in-out .1s,box-shadow ease-in-out .1s;
- }
-
- ::selection {
- background: #BCE194;
- color:#343a40;
- }
- ::-moz-selection {
- background: #BCE194;
- color:#343a40;
- } \ No newline at end of file
diff --git a/src/static/css/base.css b/src/static/css/base.css
deleted file mode 100644
index 12364bd..0000000
--- a/src/static/css/base.css
+++ /dev/null
@@ -1,93 +0,0 @@
-/* Sizing */
-#wrapper {
- height: 100vh;
-}
-
-/* Used for turning divs into square */
-.square-20 {
- height: 20px;
- width: 20px;
-}
-
-/* Make links stay the same color with no underline */
-.discrete-a {
- text-decoration: none;
- color: inherit;
-}
-
-.discrete-a:hover {
- text-decoration: none;
- color: inherit;
-}
-
-/* Make sure pre elements wrap to not break box sizing */
-/* Note: the pre element or parent may need to use the text-break class as well */
-.pre-wrap {
- white-space: pre-wrap;
-}
-
-/* Allow for sidebar to be small, but also resize on small screens */
-.sidebar {
- min-width: 200px;
-}
-
-/* Rotating arrows when dropdown happens */
-i.fas.rotate {
- transition: transform 0.3s ease-in-out;
-}
-
-a[aria-expanded="true"] > i.rotate {
- transform: rotate(180deg);
-}
-/* End rotating arrows */
-
-/* Start breadcrumbs for workflow */
-#topPagination .topcrumb {
- flex: 1 1 0;
- display: flex;
- align-content: center;
- justify-content: center;
- border: 1px solid #dee2e6;
- border-left: none;
-}
-
-.topcrumb > span {
- color: #343a40;
- cursor: default;
-}
-
-.topcrumb.active > span {
- background: #007bff;
- color: white;
-}
-
-.topcrumb.disabled > span {
- color: #6c757d;
- background: #f8f9fa;
-}
-
-/* Booking Node Styles */
-.selected_node {
- border-color: #40c640;
- box-shadow: 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(109, 243, 76, 0.6);
- transition: border-color ease-in-out .1s,box-shadow ease-in-out .1s;
-}
-
-/* Cursor effects */
-.not-allowed {
- cursor: not-allowed;
-}
-
-.z-2 {
- z-index: 2;
-}
-
-.mh-30vh {
- max-height: 30vh;
-}
-
-.overflow-ellipsis {
- text-overflow: ellipsis;
- white-space: nowrap;
- overflow: hidden;
-}
diff --git a/src/static/css/lfedge.css b/src/static/css/lfedge.css
deleted file mode 100644
index 328d71a..0000000
--- a/src/static/css/lfedge.css
+++ /dev/null
@@ -1,14 +0,0 @@
-.LFEdge {
- background: #0049b0;
- margin-left: -25px;
-}
-
-.wtext {
- font-size: 18px;
- color: #FFFFFF;
-}
-
-.wtext:hover {
- color: #FFFFFF;
- text-decoration: none;
-} \ No newline at end of file
diff --git a/src/static/css/theme.css b/src/static/css/theme.css
deleted file mode 100644
index fb350e2..0000000
--- a/src/static/css/theme.css
+++ /dev/null
@@ -1,19 +0,0 @@
-.blink_me {
- animation: blinker 1.5s linear infinite;
-}
-
-@keyframes blinker {
- 20% {
- opacity: 0.4;
- }
-}
-
-.modal p {
- word-wrap: break-word;
-}
-
-.create_drop {
- display: none;
- width: 100%;
-
-} \ No newline at end of file
diff --git a/src/static/img/Anuket-logo-reverse.png b/src/static/img/Anuket-logo-reverse.png
deleted file mode 100644
index fe256a6..0000000
--- a/src/static/img/Anuket-logo-reverse.png
+++ /dev/null
Binary files differ
diff --git a/src/static/img/Anuket-logo.svg b/src/static/img/Anuket-logo.svg
deleted file mode 100644
index 6db4c9b..0000000
--- a/src/static/img/Anuket-logo.svg
+++ /dev/null
@@ -1 +0,0 @@
-<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 575.79 214.62"><defs><style>.cls-1{fill:#001e61;}.cls-2{fill:#007473;}.cls-3{fill:#f5b335;}.cls-4{fill:#e36386;}.cls-5{fill:#6bdad5;}</style></defs><path class="cls-1" d="M190.64,348.73h25.05l36.15,89.34H224.2l-6.29-18.27h-30.6l-6.54,18.27H154.49Zm12.09,27.15c-1.72,5.67-3.45,10.86-5.55,16.66l-3.08,8.51h17.27l-3-8.51c-2.1-6.05-3.7-11.11-5.43-16.66Z" transform="translate(-18.6 -289.19)"/><path class="cls-1" d="M258.63,377.36h23.94v4.69l.25.12A29.79,29.79,0,0,1,300.46,376,22.52,22.52,0,0,1,316,381.8c3.95,3.82,5.93,9.25,5.93,18.14v38.13H297.5V403.64c0-2.71-.61-4.69-1.72-5.8a6.36,6.36,0,0,0-4.69-1.73,12.2,12.2,0,0,0-8,3.21v38.75H258.63Z" transform="translate(-18.6 -289.19)"/><path class="cls-1" d="M373.51,433a27.31,27.31,0,0,1-17.27,6.41,23.37,23.37,0,0,1-15.55-5.55c-4.32-3.95-6.29-9.62-6.29-18.75V377.36h24.43v34.55c0,3.21.62,4.69,1.73,5.8a6.23,6.23,0,0,0,4.56,1.72,12.4,12.4,0,0,0,8.15-3.45V377.36H397.7v60.71H373.76v-4.94Z" transform="translate(-18.6 -289.19)"/><path class="cls-1" d="M410.66,343.79h24.43v58.49l.25.13,17-25.05h26.9L459,404.5l21.1,33.57H452.24l-16.9-29.86-.25.12v29.74H410.66Z" transform="translate(-18.6 -289.19)"/><path class="cls-1" d="M538.62,434.74c-6.42,3-15.42,4.68-23.69,4.68-21.47,0-36.16-11.84-36.16-31.09,0-20,15.68-32.33,33-32.33,14.93,0,30.35,9.26,30.35,31.1a48.27,48.27,0,0,1-.37,6.17H503.58c2.34,6.41,8.14,8.63,14.68,8.63a44.37,44.37,0,0,0,20.36-5.67Zm-18.51-34.06c-1.11-5.55-4.94-7.4-8.51-7.4-4.32,0-7.28,2.71-8.39,7.4Z" transform="translate(-18.6 -289.19)"/><path class="cls-1" d="M594.4,436.46a39.34,39.34,0,0,1-15.18,3c-7.53,0-14-2.34-17.89-6.29-3.46-3.45-5.93-8.64-5.93-19.12v-17.4h-9V377.36h9V362.18l24.43-4.2v19.38H594.4v19.25H579.83v15.67c0,4.81,1.49,7.52,6,7.52a25.89,25.89,0,0,0,8.52-2Z" transform="translate(-18.6 -289.19)"/><path class="cls-2" d="M109.6,503.76s-25.39-24.16-8.15-74.86,47.05-99-13.75-138.22c0,0,26.09,21.81,19.8,62.19S51.36,468.1,109.6,503.76Z" transform="translate(-18.6 -289.19)"/><path class="cls-3" d="M109.65,503.81s-16.13-21.22-7.18-49.08,47.77-82.15,29.12-126.24c0,0,22.15,31,5.31,71.12S93.23,470.47,109.65,503.81Z" transform="translate(-18.6 -289.19)"/><path class="cls-4" d="M96.37,343.86a37.21,37.21,0,1,0-74.41,0c0,.74,0,1.48.07,2.21a40.56,40.56,0,1,1,74.28,0C96.35,345.34,96.37,344.6,96.37,343.86Z" transform="translate(-18.6 -289.19)"/><path class="cls-5" d="M117.6,460.73c-5.4,10.4-8,20.81-7.9,29.84-1.9-10.41.38-23.67,7.25-36.91,10.67-20.54,29.55-39.72,28.44-69C150.15,418,129.1,438.58,117.6,460.73Z" transform="translate(-18.6 -289.19)"/></svg> \ No newline at end of file
diff --git a/src/static/img/akraino_logo.logo b/src/static/img/akraino_logo.logo
deleted file mode 100644
index ac85b38..0000000
--- a/src/static/img/akraino_logo.logo
+++ /dev/null
Binary files differ
diff --git a/src/static/img/lfedge-logo.png b/src/static/img/lfedge-logo.png
deleted file mode 100644
index 689f09a..0000000
--- a/src/static/img/lfedge-logo.png
+++ /dev/null
Binary files differ
diff --git a/src/static/js/dashboard.js b/src/static/js/dashboard.js
deleted file mode 100644
index a63c71b..0000000
--- a/src/static/js/dashboard.js
+++ /dev/null
@@ -1,1664 +0,0 @@
-///////////////////
-// Global Variables
-///////////////////
-
-form_submission_callbacks = []; //all runnables will be executed before form submission
-
-///////////////////
-// Global Functions
-///////////////////
-
-// Taken from https://docs.djangoproject.com/en/3.0/ref/csrf/
-function getCookie(name) {
- var cookieValue = null;
- if (document.cookie && document.cookie !== '') {
- var cookies = document.cookie.split(';');
- for (var i = 0; i < cookies.length; i++) {
- var cookie = cookies[i].trim();
- // Does this cookie string begin with the name we want?
- if (cookie.substring(0, name.length + 1) === (name + '=')) {
- cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
- break;
- }
- }
- }
- return cookieValue;
-}
-
-function update_page(response) {
- if( response.redirect )
- {
- window.location.replace(response.redirect);
- return;
- }
- draw_breadcrumbs(response.meta);
- update_exit_button(response.meta);
- update_side_buttons(response.meta);
- $("#formContainer").html(response.content);
-}
-
-function update_side_buttons(meta) {
- const step = meta.active;
- const page_count = meta.steps.length;
-
- const back_button = document.getElementById("workflow-nav-back");
- if (step == 0) {
- back_button.classList.add("disabled");
- back_button.disabled = true;
- } else {
- back_button.classList.remove("disabled");
- back_button.disabled = false;
- }
-
- const forward_btn = document.getElementById("workflow-nav-next");
- if (step == page_count - 1) {
- forward_btn.classList.add("disabled");
- forward_btn.disabled = true;
- } else {
- forward_btn.classList.remove("disabled");
- forward_btn.disabled = false;
- }
-}
-
-function update_exit_button(meta) {
- if (meta.workflow_count == 1) {
- document.getElementById("cancel_btn").innerText = "Exit Workflow";
- } else {
- document.getElementById("cancel_btn").innerText = "Return to Parent";
- }
-}
-
-function draw_breadcrumbs(meta) {
- $("#topPagination").children().not(".page-control").remove();
-
- for (const i in meta.steps) {
- const step_btn = create_step(meta.steps[i], i == meta["active"]);
- $("#topPagination li:last-child").before(step_btn);
- }
-}
-
-function create_step(step_json, active) {
- const step_dom = document.createElement("li");
- // First create the dom object depending on active or not
- step_dom.className = "topcrumb";
- if (active) {
- step_dom.classList.add("active");
- }
- $(step_dom).html(`<span class="d-flex align-items-center justify-content-center text-capitalize w-100">${step_json['title']}</span>`)
-
- const code = step_json.valid;
-
- let stat = "";
- let msg = "";
- if (code < 100) {
- $(step_dom).children().first().append("<i class='ml-2 far fa-square'></i>")
- stat = "";
- msg = "";
- } else if (code < 200) {
- $(step_dom).children().first().append("<i class='ml-2 fas fa-minus-square'></i>")
- stat = "invalid";
- msg = step_json.message;
- } else if (code < 300) {
- $(step_dom).children().first().append("<i class='ml-2 far fa-check-square'></i>")
- stat = "valid";
- msg = step_json.message;
- }
-
- if (step_json.enabled == false) {
- step_dom.classList.add("disabled");
- }
- if (active) {
- update_message(msg, stat);
- }
-
- return step_dom;
-}
-
-function update_description(title, desc) {
- document.getElementById("view_title").innerText = title;
- document.getElementById("view_desc").innerText = desc;
-}
-
-function update_message(message, stepstatus) {
- let color_code;
- if (stepstatus == 'valid') {
- color_code = 'text-success';
- } else if (stepstatus == 'invalid') {
- color_code = 'text-danger';
- } else {
- color_code = 'none';
- }
- document.getElementById("view_message").innerText = message;
- document.getElementById("view_message").className = "step_message";
- document.getElementById("view_message").classList.add("message_" + stepstatus);
- document.getElementById("view_message").classList.add(color_code);
-}
-
-function submitStepForm(next_step = "current"){
- run_form_callbacks();
- const step_form_data = $("#step_form").serialize();
- const form_data = $.param({
- "step": next_step,
- "step_form": step_form_data,
- "csrfmiddlewaretoken": $("[name=csrfmiddlewaretoken]").val()
- });
- $.post(
- '/workflow/manager/',
- form_data,
- (data) => update_page(data),
- 'json'
- ).fail(() => alert("failure"));
-}
-
-function run_form_callbacks(){
- for(f of form_submission_callbacks)
- f();
- form_submission_callbacks = [];
-}
-
-function create_workflow(type) {
- $.ajax({
- type: "POST",
- url: "/workflow/create/",
- data: {
- "workflow_type": type
- },
- headers: {
- "X-CSRFToken": getCookie('csrftoken')
- }
- }).done(function (data, textStatus, jqXHR) {
- window.location = "/workflow/";
- }).fail(function (jqxHR, textstatus) {
- alert("Something went wrong...");
- });
-}
-
-function add_workflow(type) {
- data = $.ajax({
- type: "POST",
- url: "/workflow/add/",
- data: {
- "workflow_type": type
- },
- headers: {
- "X-CSRFToken": getCookie('csrftoken')
- }
- }).done(function (data, textStatus, jqXHR) {
- update_page(data);
- }).fail(function (jqxHR, textstatus) {
- alert("Something went wrong...");
- });
-}
-
-function pop_workflow() {
- data = $.ajax({
- type: "POST",
- url: "/workflow/pop/",
- headers: {
- "X-CSRFToken": getCookie('csrftoken')
- }
- }).done(function (data, textStatus, jqXHR) {
- update_page(data);
- }).fail(function (jqxHR, textstatus) {
- alert("Something went wrong...");
- });
-}
-
-function continue_workflow() {
- window.location.replace("/workflow/");
-}
-
-///////////////////
-//Class Definitions
-///////////////////
-
-class MultipleSelectFilterWidget {
-
- constructor(neighbors, items, initial) {
- this.inputs = [];
- this.graph_neighbors = neighbors;
- this.filter_items = items;
- this.currentLab = null;
- this.available_resources = {};
- this.result = {};
- this.dropdown_count = 0;
-
- for(let nodeId in this.filter_items) {
- const node = this.filter_items[nodeId];
- this.result[node.class] = {}
- }
-
- this.make_selection(initial);
- }
-
- make_selection(initial_data){
- if(!initial_data || jQuery.isEmptyObject(initial_data))
- return;
-
- // Need to sort through labs first
- let initial_lab = initial_data['lab'];
- let initial_resources = initial_data['resource'];
-
- for( let node_id in initial_lab) { // This should only be length one
- const node = this.filter_items[node_id];
- const selection_data = initial_lab[node_id];
- if( selection_data.selected ) {
- this.select(node);
- this.markAndSweep(node);
- this.updateResult(node);
- }
- if(node['multiple']){
- this.make_multiple_selection(node, selection_data);
- }
- this.currentLab = node;
- this.available_resources = JSON.parse(node['available_resources']);
- }
-
- for( let node_id in initial_resources){
- const node = this.filter_items[node_id];
- const selection_data = initial_resources[node_id];
- if( selection_data.selected ) {
- this.select(node);
- this.markAndSweep(node);
- this.updateResult(node);
- }
- if(node['multiple']){
- this.make_multiple_selection(node, selection_data);
- }
- }
- this.updateAvailibility();
- }
-
- make_multiple_selection(node, selection_data){
- const prepop_data = selection_data.values;
- for(let k in prepop_data){
- const div = this.add_item_prepopulate(node, prepop_data[k]);
- this.updateObjectResult(node, div.id, prepop_data[k]);
- }
- }
-
- markAndSweep(root){
- for(let i in this.filter_items) {
- const node = this.filter_items[i];
- node['marked'] = true; //mark all nodes
- }
-
- const toCheck = [root];
- while(toCheck.length > 0){
- const node = toCheck.pop();
-
- if(!node['marked']) {
- continue; //already visited, just continue
- }
-
- node['marked'] = false; //mark as visited
- if(node['follow'] || node == root){ //add neighbors if we want to follow this node
- const neighbors = this.graph_neighbors[node.id];
- for(let neighId of neighbors) {
- const neighbor = this.filter_items[neighId];
- toCheck.push(neighbor);
- }
- }
- }
-
- //now remove all nodes still marked
- for(let i in this.filter_items){
- const node = this.filter_items[i];
- if(node['marked']){
- this.disable_node(node);
- }
- }
- }
-
- process(node) {
- if(node['selected']) {
- this.markAndSweep(node);
- }
- else { //TODO: make this not dumb
- const selected = []
- //remember the currently selected, then reset everything and reselect one at a time
- for(let nodeId in this.filter_items) {
- node = this.filter_items[nodeId];
- if(node['selected']) {
- selected.push(node);
- }
- this.clear(node);
- }
- for(let node of selected) {
- this.select(node);
- this.markAndSweep(node);
- }
- }
- }
-
- select(node) {
- const elem = document.getElementById(node['id']);
- node['selected'] = true;
- elem.classList.remove('bg-white', 'not-allowed', 'bg-light');
- elem.classList.add('selected_node');
-
- if(node['class'] == 'resource')
- this.reserveResource(node);
-
- }
-
- clear(node) {
- const elem = document.getElementById(node['id']);
- node['selected'] = false;
- node['selectable'] = true;
- elem.classList.add('bg-white')
- elem.classList.remove('not-allowed', 'bg-light', 'selected_node');
- }
-
- disable_node(node) {
- const elem = document.getElementById(node['id']);
- node['selected'] = false;
- node['selectable'] = false;
- elem.classList.remove('bg-white', 'selected_node');
- elem.classList.add('not-allowed', 'bg-light');
- }
-
- labCheck(node){
- // if lab is not already selected update available resources
- if(!node['selected']) {
- this.currentLab = node;
- this.available_resources = JSON.parse(node['available_resources']);
- this.updateAvailibility();
- } else {
- // a lab is already selected, clear already selected resources
- if(confirm('Unselecting a lab will reset all selected resources, are you sure?')) {
- location.reload();
- return false;
- }
- }
- return true;
- }
-
- updateAvailibility() {
- const lab_resources = this.graph_neighbors[this.currentLab.id];
-
- // need to loop through and update all quantities
- for(let i in lab_resources) {
- const resource_node = this.filter_items[lab_resources[i]];
- const required_resources = JSON.parse(resource_node['required_resources']);
- let elem = document.getElementById(resource_node.id).getElementsByClassName("grid-item-description")[0];
- let leastAvailable = 100;
- let currCount;
- let quantityDescription;
- let quantityNode;
-
- for(let resource in required_resources) {
- currCount = Math.floor(this.available_resources[resource] / required_resources[resource]);
- if(currCount < leastAvailable)
- leastAvailable = currCount;
-
- if(!currCount || currCount < 0) {
- leastAvailable = 0
- break;
- }
- }
-
- if (elem.children[0]){
- elem.removeChild(elem.children[0]);
- }
-
- quantityDescription = '<br> Quantity Currently Available: ' + leastAvailable;
- quantityNode = document.createElement('P');
- if (leastAvailable > 0) {
- quantityDescription = quantityDescription.fontcolor('green');
- } else {
- quantityDescription = quantityDescription.fontcolor('red');
- }
-
- quantityNode.innerHTML = quantityDescription;
- elem.appendChild(quantityNode)
- }
- }
-
- reserveResource(node){
- const required_resources = JSON.parse(node['required_resources']);
- let hostname = document.getElementById('id_hostname');
- let image = document.getElementById('id_image');
- let cnt = 0
-
-
- for(let resource in required_resources){
- this.available_resources[resource] -= required_resources[resource];
- cnt += required_resources[resource];
- }
-
- if (cnt > 1 && hostname) {
- hostname.readOnly = true;
- // we only disable hostname modification because there is no sane case where you want all hosts to have the same hostname
- // image is still allowed to be set across all hosts, but is filtered to the set of images that are commonly applicable still
- // if no images exist that would apply to all hosts in a pod, then the user is restricted to not setting an image
- // and the default image for each host is used
- }
-
- this.updateAvailibility();
- }
-
- releaseResource(node){
- const required_resources = JSON.parse(node['required_resources']);
- let hostname = document.getElementById('id_hostname');
- let image = document.getElementById('id_image');
-
- for(let resource in required_resources){
- this.available_resources[resource] += required_resources[resource];
- }
-
- if (hostname && image) {
- hostname.readOnly = false;
- image.disabled = false;
- }
-
- this.updateAvailibility();
- }
-
- processClick(id){
- let lab_check;
- const node = this.filter_items[id];
- if(!node['selectable'])
- return;
-
- // If they are selecting a lab, update accordingly
- if (node['class'] == 'lab') {
- lab_check = this.labCheck(node);
- if (!lab_check)
- return;
- }
-
- // Can only select a resource if a lab is selected
- if (!this.currentLab) {
- alert('You must select a lab before selecting a resource');
- return;
- }
-
- if(node['multiple']){
- return this.processClickMultiple(node);
- } else {
- return this.processClickSingle(node);
- }
- }
-
- processClickSingle(node){
- node['selected'] = !node['selected']; //toggle on click
- if(node['selected']) {
- this.select(node);
- } else {
- this.clear(node);
- this.releaseResource(node); // can't do this in clear since clear removes border
- }
- this.process(node);
- this.updateResult(node);
- }
-
- processClickMultiple(node){
- this.select(node);
- const div = this.add_item_prepopulate(node, false);
- this.process(node);
- this.updateObjectResult(node, div.id, "");
- }
-
- restrictchars(input){
- if( input.validity.patternMismatch ){
- input.setCustomValidity("Only alphanumeric characters (a-z, A-Z, 0-9), underscore(_), and hyphen (-) are allowed");
- input.reportValidity();
- }
- input.value = input.value.replace(/([^A-Za-z0-9-_.])+/g, "");
- this.checkunique(input);
- }
-
- checkunique(tocheck){ //TODO: use set
- const val = tocheck.value;
- for( let input of this.inputs ){
- if( input.value == val && input != tocheck){
- tocheck.setCustomValidity("All hostnames must be unique");
- tocheck.reportValidity();
- return;
- }
- }
- tocheck.setCustomValidity("");
- }
-
- make_remove_button(div, node){
- const button = document.createElement("BUTTON");
- button.type = "button";
- button.appendChild(document.createTextNode("Remove"));
- button.classList.add("btn", "btn-danger", "d-inline-block");
- const that = this;
- button.onclick = function(){ that.remove_dropdown(div.id, node.id); }
- return button;
- }
-
- make_input(div, node, prepopulate){
- const input = document.createElement("INPUT");
- input.type = node.form.type;
- input.name = node.id + node.form.name
- input.classList.add("form-control", "w-auto", "d-inline-block");
- input.pattern = "(?=^.{1,253}$)(^([A-Za-z0-9-_]{1,62}\.)*[A-Za-z0-9-_]{1,63})";
- input.title = "Only alphanumeric characters (a-z, A-Z, 0-9), underscore(_), and hyphen (-) are allowed"
- input.placeholder = node.form.placeholder;
- this.inputs.push(input);
- const that = this;
- input.onchange = function() { that.updateObjectResult(node, div.id, input.value); that.restrictchars(this); };
- input.oninput = function() { that.restrictchars(this); };
- if(prepopulate)
- input.value = prepopulate;
- return input;
- }
-
- add_item_prepopulate(node, prepopulate){
- const div = document.createElement("DIV");
- div.id = "dropdown_" + this.dropdown_count;
- div.classList.add("card", "flex-row", "d-flex", "mb-2");
- this.dropdown_count++;
- const label = document.createElement("H5")
- label.appendChild(document.createTextNode(node['name']))
- label.classList.add("p-1", "m-1", "flex-grow-1");
- div.appendChild(label);
- let remove_btn = this.make_remove_button(div, node);
- remove_btn.classList.add("p-1", "m-1");
- div.appendChild(remove_btn);
- document.getElementById("dropdown_wrapper").appendChild(div);
- return div;
- }
-
- remove_dropdown(div_id, node_id){
- const div = document.getElementById(div_id);
- const node = this.filter_items[node_id]
- const parent = div.parentNode;
- div.parentNode.removeChild(div);
- this.result[node.class][node.id]['count']--;
- this.releaseResource(node); // This can't be done on clear b/c clear removes border
-
- //checks if we have removed last item in class
- if(this.result[node.class][node.id]['count'] == 0){
- delete this.result[node.class][node.id];
- this.clear(node);
- }
- }
-
- updateResult(node){
- if(!node['multiple']){
- this.result[node.class][node.id] = {selected: node.selected, id: node.model_id}
- if(!node.selected)
- delete this.result[node.class][node.id];
- }
- }
-
- updateObjectResult(node, childKey, childValue){
- if(!this.result[node.class][node.id])
- this.result[node.class][node.id] = {selected: true, id: node.model_id, count: 0}
-
- this.result[node.class][node.id]['count']++;
- }
-
- finish(){
- document.getElementById("filter_field").value = JSON.stringify(this.result);
- }
-}
-
-class NetworkStep {
- // expects:
- //
- // debug: bool
- // resources: {
- // id: {
- // id: int,
- // value: {
- // description: string,
- // },
- // interfaces: [
- // id: int,
- // name: str,
- // description: str,
- // connections: [
- // {
- // network: int, [networks.id]
- // tagged: bool
- // }
- // ],
- // ],
- // }
- // }
- // networks: {
- // id: {
- // id: int,
- // name: str,
- // public: bool,
- // }
- // }
- //
- constructor(debug, resources, networks, graphContainer, overviewContainer, toolbarContainer){
- if(!this.check_support()) {
- console.log("Aborting, browser is not supported");
- return;
- }
-
- this.currentWindow = null;
- this.netCount = 0;
- this.netColors = ['red', 'blue', 'purple', 'green', 'orange', '#8CCDF5', '#1E9BAC'];
- this.hostCount = 0;
- this.lastHostBottom = 100;
- this.networks = new Set();
- this.has_public_net = false;
- this.debug = debug;
- this.editor = new mxEditor();
- this.graph = this.editor.graph;
-
- window.global_graph = this.graph;
- window.network_rr_index = 5;
-
- this.editor.setGraphContainer(graphContainer);
- this.doGlobalConfig();
-
- let mx_networks = {}
-
- for(const network_id in networks) {
- let network = networks[network_id];
-
- mx_networks[network_id] = this.populateNetwork(network);
- }
-
- this.prefillHosts(resources, mx_networks);
-
- //this.addToolbarButton(this.editor, toolbarContainer, 'zoomIn', '', "/static/img/mxgraph/zoom_in.png", true);
- //this.addToolbarButton(this.editor, toolbarContainer, 'zoomOut', '', "/static/img/mxgraph/zoom_out.png", true);
- this.addToolbarButton(this.editor, toolbarContainer, 'zoomIn', 'fa-search-plus');
- this.addToolbarButton(this.editor, toolbarContainer, 'zoomOut', 'fa-search-minus');
-
- if(this.debug){
- this.editor.addAction('printXML', function(editor, cell) {
- mxLog.write(this.encodeGraph());
- mxLog.show();
- }.bind(this));
- this.addToolbarButton(this.editor, toolbarContainer, 'printXML', 'fa-file-code');
- }
-
- new mxOutline(this.graph, overviewContainer);
- //sets the edge color to be the same as the network
- this.graph.addListener(mxEvent.CELL_CONNECTED, function(sender, event) {this.cellConnectionHandler(sender, event)}.bind(this));
- //hooks up double click functionality
- this.graph.dblClick = function(evt, cell) {this.doubleClickHandler(evt, cell);}.bind(this);
- }
-
- check_support(){
- if (!mxClient.isBrowserSupported()) {
- mxUtils.error('Browser is not supported', 200, false);
- return false;
- }
- return true;
- }
-
- /**
- * Expects
- * mx_interface: mxCell for the interface itself
- * network: mxCell for the outer network
- * tagged: bool
- */
- connectNetwork(mx_interface, network, tagged) {
- var cell = new mxCell(
- "connection from " + network + " to " + mx_interface,
- new mxGeometry(0, 0, 50, 50));
- cell.edge = true;
- cell.geometry.relative = true;
- cell.setValue(JSON.stringify({tagged: tagged}));
-
- let terminal = this.getClosestNetworkCell(mx_interface.geometry.y, network);
- let edge = this.graph.addEdge(cell, null, mx_interface, terminal);
- this.colorEdge(edge, terminal, true);
- this.graph.refresh(edge);
- }
-
- /**
- * Expects:
- *
- * to: desired y axis position of the matching cell
- * within: graph cell for a full network, with all child cells
- *
- * Returns:
- * an mx cell, the one vertically closest to the desired value
- *
- * Side effect:
- * modifies the <rr_index> on the <within> parameter
- */
- getClosestNetworkCell(to, within) {
- if(window.network_rr_index === undefined) {
- window.network_rr_index = 5;
- }
-
- let child_keys = within.children.keys();
- let children = Array.from(within.children);
- let index = (window.network_rr_index++) % children.length;
-
- let child = within.children[child_keys[index]];
-
- return children[index];
- }
-
- /** Expects
- *
- * hosts: {
- * id: {
- * id: int,
- * value: {
- * description: string,
- * },
- * interfaces: [
- * id: int,
- * name: str,
- * description: str,
- * connections: [
- * {
- * network: int, [networks.id]
- * tagged: bool
- * }
- * ],
- * ],
- * }
- * }
- *
- * network_mappings: {
- * <django network id>: <mxnetwork id>
- * }
- *
- * draws given hosts into the mxgraph
- */
- prefillHosts(hosts, network_mappings){
- for(const host_id in hosts) {
- this.makeHost(hosts[host_id], network_mappings);
- }
- }
-
- cellConnectionHandler(sender, event){
- const edge = event.getProperty('edge');
- const terminal = event.getProperty('terminal')
- const source = event.getProperty('source');
- if(this.checkAllowed(edge, terminal, source)) {
- this.colorEdge(edge, terminal, source);
- this.alertVlan(edge, terminal, source);
- }
- }
-
- doubleClickHandler(evt, cell) {
- if( cell != null ){
- if( cell.getParent() != null && cell.getParent().getId().indexOf("network") > -1) {
- cell = cell.getParent();
- }
- if( cell.isEdge() || cell.getId().indexOf("network") > -1 ) {
- this.createDeleteDialog(cell.getId());
- }
- else {
- this.showDetailWindow(cell);
- }
- }
- }
-
- alertVlan(edge, terminal, source) {
- if( terminal == null || edge.getTerminal(!source) == null) {
- return;
- }
- const form = document.createElement("form");
- const tagged = document.createElement("input");
- tagged.type = "radio";
- tagged.name = "tagged";
- tagged.value = "True";
- tagged.checked = "True";
- form.appendChild(tagged);
- form.appendChild(document.createTextNode(" Tagged"));
- form.appendChild(document.createElement("br"));
-
- const untagged = document.createElement("input");
- untagged.type = "radio";
- untagged.name = "tagged";
- untagged.value = "False";
- form.appendChild(untagged);
- form.appendChild(document.createTextNode(" Untagged"));
- form.appendChild(document.createElement("br"));
-
- const yes_button = document.createElement("button");
- yes_button.onclick = function() {this.parseVlanWindow(edge.getId());}.bind(this);
- yes_button.appendChild(document.createTextNode("Okay"));
-
- const cancel_button = document.createElement("button");
- cancel_button.onclick = function() {this.deleteVlanWindow(edge.getId());}.bind(this);
- cancel_button.appendChild(document.createTextNode("Cancel"));
-
- const error_div = document.createElement("div");
- error_div.id = "current_window_errors";
- form.appendChild(error_div);
-
- const content = document.createElement('div');
- content.appendChild(form);
- content.appendChild(yes_button);
- content.appendChild(cancel_button);
- this.showWindow("Vlan Selection", content, 200, 200);
- }
-
- createDeleteDialog(id) {
- const content = document.createElement('div');
- const remove_button = document.createElement("button");
- remove_button.style.width = '46%';
- remove_button.onclick = function() { this.deleteCell(id);}.bind(this);
- remove_button.appendChild(document.createTextNode("Remove"));
- const cancel_button = document.createElement("button");
- cancel_button.style.width = '46%';
- cancel_button.onclick = function() { this.closeWindow();}.bind(this);
- cancel_button.appendChild(document.createTextNode("Cancel"));
-
- content.appendChild(remove_button);
- content.appendChild(cancel_button);
- this.showWindow('Do you want to delete this network?', content, 200, 62);
- }
-
- checkAllowed(edge, terminal, source) {
- //check if other terminal is null, and that they are different
- const otherTerminal = edge.getTerminal(!source);
- if(terminal != null && otherTerminal != null) {
- if( terminal.getParent().getId().split('_')[0] == //'host' or 'network'
- otherTerminal.getParent().getId().split('_')[0] ) {
- //not allowed
- this.graph.removeCells([edge]);
- return false;
- }
- }
- return true;
- }
-
- colorEdge(edge, terminal, source) {
- if(terminal.getParent().getId().indexOf('network') >= 0) {
- const styles = terminal.getParent().getStyle().split(';');
- let color = 'black';
- for(let style of styles){
- const kvp = style.split('=');
- if(kvp[0] == "fillColor"){
- color = kvp[1];
- }
- }
-
- edge.setStyle('strokeColor=' + color);
- } else {
- console.log("Failed to color " + edge + ", " + terminal + ", " + source);
- }
- }
-
- showDetailWindow(cell) {
- const info = JSON.parse(cell.getValue());
- const content = document.createElement("div");
- const pre_tag = document.createElement("pre");
- pre_tag.appendChild(document.createTextNode("Name: " + info.name + "\nDescription:\n" + info.description));
- const ok_button = document.createElement("button");
- ok_button.onclick = function() { this.closeWindow();};
- content.appendChild(pre_tag);
- content.appendChild(ok_button);
- this.showWindow('Details', content, 400, 400);
- }
-
- restoreFromXml(xml, editor) {
- const doc = mxUtils.parseXml(xml);
- const node = doc.documentElement;
- editor.readGraphModel(node);
-
- //Iterate over all children, and parse the networks to add them to the sidebar
- for( const cell of this.graph.getModel().getChildren(this.graph.getDefaultParent())) {
- if(cell.getId().indexOf("network") > -1) {
- const info = JSON.parse(cell.getValue());
- const name = info['name'];
- this.networks.add(name);
- const styles = cell.getStyle().split(";");
- let color = null;
- for(const style of styles){
- const kvp = style.split('=');
- if(kvp[0] == "fillColor") {
- color = kvp[1];
- break;
- }
- }
- if(info.public){
- this.has_public_net = true;
- }
- this.netCount++;
- this.makeSidebarNetwork(name, color, cell.getId());
- }
- }
- }
-
- deleteCell(cellId) {
- var cell = this.graph.getModel().getCell(cellId);
- if( cellId.indexOf("network") > -1 ) {
- let elem = document.getElementById(cellId);
- elem.parentElement.removeChild(elem);
- }
- this.graph.removeCells([cell]);
- this.currentWindow.destroy();
- }
-
- newNetworkWindow() {
- const input = document.createElement("input");
- input.type = "text";
- input.name = "net_name";
- input.maxlength = 100;
- input.id = "net_name_input";
- input.style.margin = "5px";
-
- const yes_button = document.createElement("button");
- yes_button.onclick = function() {this.parseNetworkWindow();}.bind(this);
- yes_button.appendChild(document.createTextNode("Okay"));
-
- const cancel_button = document.createElement("button");
- cancel_button.onclick = function() {this.closeWindow();}.bind(this);
- cancel_button.appendChild(document.createTextNode("Cancel"));
-
- const error_div = document.createElement("div");
- error_div.id = "current_window_errors";
-
- const content = document.createElement("div");
- content.appendChild(document.createTextNode("Name: "));
- content.appendChild(input);
- content.appendChild(document.createElement("br"));
- content.appendChild(yes_button);
- content.appendChild(cancel_button);
- content.appendChild(document.createElement("br"));
- content.appendChild(error_div);
-
- this.showWindow("Network Creation", content, 300, 300);
- }
-
- parseNetworkWindow() {
- const net_name = document.getElementById("net_name_input").value
- const error_div = document.getElementById("current_window_errors");
- if( this.networks.has(net_name) ){
- error_div.innerHTML = "All network names must be unique";
- return;
- }
- this.addNetwork(net_name);
- this.currentWindow.destroy();
- }
-
- addToolbarButton(editor, toolbar, action, image) {
- const button = document.createElement('button');
- button.setAttribute('class', 'btn btn-sm m-1');
- if (image != null) {
- const icon = document.createElement('i');
- icon.setAttribute('class', 'fas ' + image);
- button.appendChild(icon);
- }
- mxEvent.addListener(button, 'click', function(evt) {
- editor.execute(action);
- });
- mxUtils.write(button, '');
- toolbar.appendChild(button);
- };
-
- encodeGraph() {
- const encoder = new mxCodec();
- const xml = encoder.encode(this.graph.getModel());
- return mxUtils.getXml(xml);
- }
-
- doGlobalConfig() {
- //general graph stuff
- this.graph.setMultigraph(false);
- this.graph.setCellsSelectable(false);
- this.graph.setCellsMovable(false);
-
- //testing
- this.graph.vertexLabelIsMovable = true;
-
- //edge behavior
- this.graph.setConnectable(true);
- this.graph.setAllowDanglingEdges(false);
- mxEdgeHandler.prototype.snapToTerminals = true;
- mxConstants.MIN_HOTSPOT_SIZE = 16;
- mxConstants.DEFAULT_HOTSPOT = 1;
- //edge 'style' (still affects behavior greatly)
- const style = this.graph.getStylesheet().getDefaultEdgeStyle();
- style[mxConstants.STYLE_EDGE] = mxConstants.EDGESTYLE_ELBOW;
- style[mxConstants.STYLE_ENDARROW] = mxConstants.NONE;
- style[mxConstants.STYLE_ROUNDED] = true;
- style[mxConstants.STYLE_FONTCOLOR] = 'black';
- style[mxConstants.STYLE_STROKECOLOR] = 'red';
- style[mxConstants.STYLE_LABEL_BACKGROUNDCOLOR] = '#FFFFFF';
- style[mxConstants.STYLE_STROKEWIDTH] = '3';
- style[mxConstants.STYLE_ROUNDED] = true;
- style[mxConstants.STYLE_EDGE] = mxEdgeStyle.EntityRelation;
-
- const hostStyle = this.graph.getStylesheet().getDefaultVertexStyle();
- hostStyle[mxConstants.STYLE_ROUNDED] = 1;
-
- this.graph.convertValueToString = function(cell) {
- try{
- //changes value for edges with xml value
- if(cell.isEdge()) {
- if(JSON.parse(cell.getValue())["tagged"]) {
- return "tagged";
- }
- return "untagged";
- }
- else{
- return JSON.parse(cell.getValue())['name'];
- }
- }
- catch(e){
- return cell.getValue();
- }
- };
- }
-
- showWindow(title, content, width, height) {
- //create transparent black background
- const background = document.createElement('div');
- background.style.position = 'absolute';
- background.style.left = '0px';
- background.style.top = '0px';
- background.style.right = '0px';
- background.style.bottom = '0px';
- background.style.background = 'black';
- mxUtils.setOpacity(background, 50);
- document.body.appendChild(background);
-
- const x = Math.max(0, document.body.scrollWidth/2-width/2);
- const y = Math.max(10, (document.body.scrollHeight ||
- document.documentElement.scrollHeight)/2-height*2/3);
-
- const wnd = new mxWindow(title, content, x, y, width, height, false, true);
- wnd.setClosable(false);
-
- wnd.addListener(mxEvent.DESTROY, function(evt) {
- this.graph.setEnabled(true);
- mxEffects.fadeOut(background, 50, true, 10, 30, true);
- }.bind(this));
- this.currentWindow = wnd;
-
- this.graph.setEnabled(false);
- this.currentWindow.setVisible(true);
- };
-
- closeWindow() {
- //allows the current window to be destroyed
- this.currentWindow.destroy();
- };
-
- othersUntagged(edgeID) {
- const edge = this.graph.getModel().getCell(edgeID);
- const end1 = edge.getTerminal(true);
- const end2 = edge.getTerminal(false);
-
- if( end1.getParent().getId().split('_')[0] == 'host' ){
- var netint = end1;
- } else {
- var netint = end2;
- }
-
- var edges = netint.edges;
- for( let edge of edges) {
- if( edge.getValue() ) {
- var tagged = JSON.parse(edge.getValue()).tagged;
- } else {
- var tagged = true;
- }
- if( !tagged ) {
- return true;
- }
- }
-
- return false;
- };
-
-
- deleteVlanWindow(edgeID) {
- const cell = this.graph.getModel().getCell(edgeID);
- this.graph.removeCells([cell]);
- this.currentWindow.destroy();
- }
-
- parseVlanWindow(edgeID) {
- //do parsing and data manipulation
- const radios = document.getElementsByName("tagged");
- const edge = this.graph.getModel().getCell(edgeID);
-
- for(let radio of radios){
- if(radio.checked) {
- //set edge to be tagged or untagged
- if( radio.value == "False") {
- if( this.othersUntagged(edgeID) ) {
- document.getElementById("current_window_errors").innerHTML = "Only one untagged vlan per interface is allowed.";
- return;
- }
- }
- const edgeVal = {tagged: radio.value == "True"};
- edge.setValue(JSON.stringify(edgeVal));
- break;
- }
- }
- this.graph.refresh(edge);
- this.closeWindow();
- }
-
- makeMxNetwork(net_name, is_public = false) {
- const model = this.graph.getModel();
- const width = 10;
- const height = 1700;
- const xoff = 400 + (30 * this.netCount);
- const yoff = -10;
- let color = this.netColors[this.netCount];
- if( this.netCount > (this.netColors.length - 1)) {
- color = Math.floor(Math.random() * 16777215); //int in possible color space
- color = '#' + color.toString(16).toUpperCase(); //convert to hex
- }
- const net_val = { name: net_name, public: is_public};
- const net = this.graph.insertVertex(
- this.graph.getDefaultParent(),
- 'network_' + this.netCount,
- JSON.stringify(net_val),
- xoff,
- yoff,
- width,
- height,
- 'fillColor=' + color,
- false
- );
- const num_ports = 45;
- for(var i=0; i<num_ports; i++){
- let port = this.graph.insertVertex(
- net,
- null,
- '',
- 0,
- (1/num_ports) * i,
- 10,
- height / num_ports,
- 'fillColor=black;opacity=0',
- true
- );
- }
-
- const ret_val = { color: color, element_id: "network_" + this.netCount };
-
- this.networks.add(net_name);
- this.netCount++;
- return ret_val;
- }
-
- // expects:
- //
- // {
- // id: int,
- // name: str,
- // public: bool,
- // }
- //
- // returns:
- // mxgraph id of network
- populateNetwork(network) {
- let mxNet = this.makeMxNetwork(network.name, network.public);
- this.makeSidebarNetwork(network.name, mxNet.color, mxNet.element_id);
-
- if( network.public ) {
- this.has_public_net = true;
- }
-
- return mxNet.element_id;
- }
-
- addPublicNetwork() {
- const net = this.makeMxNetwork("public", true);
- this.makeSidebarNetwork("public", net['color'], net['element_id']);
- this.has_public_net = true;
- }
-
- addNetwork(net_name) {
- const ret = this.makeMxNetwork(net_name);
- this.makeSidebarNetwork(net_name, ret.color, ret.element_id);
- }
-
- updateHosts(removed) {
- const cells = []
- for(const hostID of removed) {
- cells.push(this.graph.getModel().getCell("host_" + hostID));
- }
- this.graph.removeCells(cells);
-
- const hosts = this.graph.getChildVertices(this.graph.getDefaultParent());
- let topdist = 100;
- for(const i in hosts) {
- const host = hosts[i];
- if(host.id.startsWith("host_")){
- const geometry = host.getGeometry();
- geometry.y = topdist + 50;
- topdist = geometry.y + geometry.height;
- host.setGeometry(geometry);
- }
- }
- }
-
- makeSidebarNetwork(net_name, color, net_id){
- const colorBlob = document.createElement("div");
- colorBlob.className = "square-20 rounded-circle";
- colorBlob.style['background'] = color;
-
- const textContainer = document.createElement("span");
- textContainer.className = "ml-2";
- textContainer.appendChild(document.createTextNode(net_name));
-
- const timesIcon = document.createElement("i");
- timesIcon.classList.add("fas", "fa-times");
-
- const deletebutton = document.createElement("button");
- deletebutton.className = "btn btn-danger ml-auto square-20 p-0 d-flex justify-content-center";
- deletebutton.appendChild(timesIcon);
- deletebutton.addEventListener("click", function() { this.createDeleteDialog(net_id); }.bind(this), false);
-
- const newNet = document.createElement("li");
- newNet.classList.add("list-group-item", "d-flex", "bg-light");
- newNet.id = net_id;
- newNet.appendChild(colorBlob);
- newNet.appendChild(textContainer);
-
- if( net_name != "public" ) {
- newNet.appendChild(deletebutton);
- }
- document.getElementById("network_list").appendChild(newNet);
- }
-
- /**
- * Expects format:
- * {
- * 'id': int,
- * 'value': {
- * 'description': string,
- * },
- * 'interfaces': [
- * {
- * id: int,
- * name: str,
- * description: str,
- * connections: [
- * {
- * network: int, <django network id>,
- * tagged: bool
- * }
- * ]
- * }
- * ]
- * }
- *
- * network_mappings: {
- * <django network id>: <mxnetwork id>
- * }
- */
- makeHost(hostInfo, network_mappings) {
- const value = JSON.stringify(hostInfo['value']);
- const interfaces = hostInfo['interfaces'];
- const width = 100;
- const height = (25 * interfaces.length) + 25;
- const xoff = 75;
- const yoff = this.lastHostBottom + 50;
- this.lastHostBottom = yoff + height;
- const host = this.graph.insertVertex(
- this.graph.getDefaultParent(),
- 'host_' + hostInfo['id'],
- value,
- xoff,
- yoff,
- width,
- height,
- 'editable=0',
- false
- );
- host.getGeometry().offset = new mxPoint(-50,0);
- host.setConnectable(false);
- this.hostCount++;
-
- for(var i=0; i<interfaces.length; i++) {
- const port = this.graph.insertVertex(
- host,
- null,
- JSON.stringify(interfaces[i]),
- 90,
- (i * 25) + 12,
- 20,
- 20,
- 'fillColor=blue;editable=0',
- false
- );
- port.getGeometry().offset = new mxPoint(-4*interfaces[i].name.length -2,0);
- const iface = interfaces[i];
- for( const connection of iface.connections ) {
- const network = this
- .graph
- .getModel()
- .getCell(network_mappings[connection.network]);
-
- this.connectNetwork(port, network, connection.tagged);
- }
- this.graph.refresh(port);
- }
- this.graph.refresh(host);
- }
-
- prepareForm() {
- const input_elem = document.getElementById("hidden_xml_input");
- input_elem.value = this.encodeGraph(this.graph);
- }
-}
-
-class SearchableSelectMultipleWidget {
- constructor(format_vars, field_dataset, field_initial) {
- this.format_vars = format_vars;
- this.items = field_dataset;
- this.initial = field_initial;
-
- this.expanded_name_trie = {"isComplete": false};
- this.small_name_trie = {"isComplete": false};
- this.string_trie = {"isComplete": false};
-
- this.added_items = new Set();
-
- for( let e of ["show_from_noentry", "show_x_results", "results_scrollable", "selectable_limit", "placeholder"] )
- {
- this[e] = format_vars[e];
- }
-
- this.search_field_init();
-
- if( this.show_from_noentry )
- {
- this.search("");
- }
- }
-
- disable() {
- const textfield = document.getElementById("user_field");
- const drop = document.getElementById("drop_results");
-
- textfield.disabled = "True";
- drop.style.display = "none";
-
- const btns = document.getElementsByClassName("btn-remove");
- for( const btn of btns )
- {
- btn.classList.add("disabled");
- btn.onclick = "";
- }
- }
-
- search_field_init() {
- this.build_all_tries(this.items);
-
- for( const elem of this.initial )
- {
- this.select_item(elem);
- }
- if(this.initial.length == 1)
- {
- this.search(this.items[this.initial[0]]["small_name"]);
- document.getElementById("user_field").value = this.items[this.initial[0]]["small_name"];
- }
- }
-
- build_all_tries(dict)
- {
- for( const key in dict )
- {
- this.add_item(dict[key]);
- }
- }
-
- add_item(item)
- {
- const id = item['id'];
- this.add_to_tree(item['expanded_name'], id, this.expanded_name_trie);
- this.add_to_tree(item['small_name'], id, this.small_name_trie);
- this.add_to_tree(item['string'], id, this.string_trie);
- }
-
- add_to_tree(str, id, trie)
- {
- let inner_trie = trie;
- while( str )
- {
- if( !inner_trie[str.charAt(0)] )
- {
- var new_trie = {};
- inner_trie[str.charAt(0)] = new_trie;
- }
- else
- {
- var new_trie = inner_trie[str.charAt(0)];
- }
-
- if( str.length == 1 )
- {
- new_trie.isComplete = true;
- if( !new_trie.ids )
- {
- new_trie.ids = [];
- }
- new_trie.ids.push(id);
- }
- inner_trie = new_trie;
- str = str.substring(1);
- }
- }
-
- search(input)
- {
- if( input.length == 0 && !this.show_from_noentry){
- this.dropdown([]);
- return;
- }
- else if( input.length == 0 && this.show_from_noentry)
- {
- this.dropdown(this.items); //show all items
- }
- else
- {
- const trees = []
- const tr1 = this.getSubtree(input, this.expanded_name_trie);
- trees.push(tr1);
- const tr2 = this.getSubtree(input, this.small_name_trie);
- trees.push(tr2);
- const tr3 = this.getSubtree(input, this.string_trie);
- trees.push(tr3);
- const results = this.collate(trees);
- this.dropdown(results);
- }
- }
-
- getSubtree(input, given_trie)
- {
- /*
- recursive function to return the trie accessed at input
- */
-
- if( input.length == 0 ){
- return given_trie;
- }
-
- else{
- const substr = input.substring(0, input.length - 1);
- const last_char = input.charAt(input.length-1);
- const subtrie = this.getSubtree(substr, given_trie);
-
- if( !subtrie ) //substr not in the trie
- {
- return {};
- }
-
- const indexed_trie = subtrie[last_char];
- return indexed_trie;
- }
- }
-
- serialize(trie)
- {
- /*
- takes in a trie and returns a list of its item id's
- */
- let itemIDs = [];
- if ( !trie )
- {
- return itemIDs; //empty, base case
- }
- for( const key in trie )
- {
- if(key.length > 1)
- {
- continue;
- }
- itemIDs = itemIDs.concat(this.serialize(trie[key]));
- }
- if ( trie.isComplete )
- {
- itemIDs.push(...trie.ids);
- }
-
- return itemIDs;
- }
-
- collate(trees)
- {
- /*
- takes a list of tries
- returns a list of ids of objects that are available
- */
- const results = [];
- for( const tree of trees )
- {
- const available_IDs = this.serialize(tree);
-
- for( const itemID of available_IDs ) {
- results[itemID] = this.items[itemID];
- }
- }
- return results;
- }
-
- generate_element_text(obj)
- {
- const content_strings = [obj.expanded_name, obj.small_name, obj.string].filter(x => Boolean(x));
- const result = content_strings.shift();
- if( result == null || content_strings.length < 1) {
- return result;
- } else {
- return result + " (" + content_strings.join(", ") + ")";
- }
- }
-
- dropdown(ids)
- {
- /*
- takes in a mapping of ids to objects in items
- and displays them in the dropdown
- */
- const drop = document.getElementById("drop_results");
- while(drop.firstChild)
- {
- drop.removeChild(drop.firstChild);
- }
-
- for( const id in ids )
- {
- const obj = this.items[id];
- const result_text = this.generate_element_text(obj);
- const result_entry = document.createElement("a");
- result_entry.href = "#";
- result_entry.innerText = result_text;
- result_entry.title = result_text;
- result_entry.classList.add("list-group-item", "list-group-item-action", "overflow-ellipsis", "flex-shrink-0");
- result_entry.onclick = function() { searchable_select_multiple_widget.select_item(obj.id); };
- const tooltip = document.createElement("span");
- const tooltiptext = document.createTextNode(result_text);
- tooltip.appendChild(tooltiptext);
- tooltip.classList.add("d-none");
- result_entry.appendChild(tooltip);
- drop.appendChild(result_entry);
- }
-
- const scroll_restrictor = document.getElementById("scroll_restrictor");
-
- if( !drop.firstChild )
- {
- scroll_restrictor.style.visibility = 'hidden';
- }
- else
- {
- scroll_restrictor.style.visibility = 'inherit';
- }
- }
-
- select_item(item_id)
- {
- if( (this.selectable_limit > -1 && this.added_items.size < this.selectable_limit) || this.selectable_limit < 0 )
- {
- this.added_items.add(item_id);
- }
- this.update_selected_list();
- // clear search bar contents
- document.getElementById("user_field").value = "";
- document.getElementById("user_field").focus();
- this.search("");
- }
-
- remove_item(item_id)
- {
- this.added_items.delete(item_id);
-
- this.update_selected_list()
- document.getElementById("user_field").focus();
- }
-
- update_selected_list()
- {
- document.getElementById("added_number").innerText = this.added_items.size;
- const selector = document.getElementById('selector');
- selector.value = JSON.stringify([...this.added_items]);
- const added_list = document.getElementById('added_list');
-
- while(selector.firstChild)
- {
- selector.removeChild(selector.firstChild);
- }
- while(added_list.firstChild)
- {
- added_list.removeChild(added_list.firstChild);
- }
-
- const list_html = document.createElement("div");
- list_html.classList.add("list-group");
-
- for( const item_id of this.added_items )
- {
- const times = document.createElement("li");
- times.classList.add("fas", "fa-times");
-
- const deleteButton = document.createElement("a");
- deleteButton.href = "#";
- deleteButton.innerHTML = "<i class='fas fa-times'></i>"
- // Setting .onclick/.addEventListener does not work,
- // which is why I took the setAttribute approach
- // If anyone knows why, please let me know :]
- deleteButton.setAttribute("onclick", `searchable_select_multiple_widget.remove_item(${item_id});`);
- deleteButton.classList.add("btn");
- const deleteColumn = document.createElement("div");
- deleteColumn.classList.add("col-auto");
- deleteColumn.append(deleteButton);
-
- const item = this.items[item_id];
- const element_entry_text = this.generate_element_text(item);
- const textColumn = document.createElement("div");
- textColumn.classList.add("col", "overflow-ellipsis");
- textColumn.innerText = element_entry_text;
- textColumn.title = element_entry_text;
-
- const itemRow = document.createElement("div");
- itemRow.classList.add("list-group-item", "d-flex", "p-0", "align-items-center");
- itemRow.append(textColumn, deleteColumn);
-
- list_html.append(itemRow);
- }
- added_list.innerHTML = list_html.innerHTML;
- }
-}
diff --git a/src/static/js/flot-pie-chart.js b/src/static/js/flot-pie-chart.js
deleted file mode 100644
index 3b80b2a..0000000
--- a/src/static/js/flot-pie-chart.js
+++ /dev/null
@@ -1,30 +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
-*****************************************************************************/
-
-
-function loadChartData(chart_id, url) {
- $.ajax({
- url: url,
- type: 'get',
- success: function (data) {
- var data = data['data'];
- var plotObj = $.plot($("#" + chart_id), data, {
- series: {
- pie: {
- show: true
- }
- }
- });
- },
- failure: function (data) {
- alert('Error loading data');
- }
- });
-
-} \ No newline at end of file
diff --git a/src/static/package-lock.json b/src/static/package-lock.json
deleted file mode 100644
index 89a26db..0000000
--- a/src/static/package-lock.json
+++ /dev/null
@@ -1,163 +0,0 @@
-{
- "name": "laas",
- "version": "1.0.0",
- "lockfileVersion": 2,
- "requires": true,
- "packages": {
- "": {
- "name": "laas",
- "version": "1.0.0",
- "license": "Apache-2.0",
- "dependencies": {
- "@fortawesome/fontawesome-free": "^5.12.0",
- "bootstrap": "^4.4.1",
- "datatables.net-bs4": "^1.10.20",
- "datatables.net-responsive-bs4": "^2.2.3",
- "jquery": "^3.4.1",
- "mxgraph": "^4.0.6",
- "plotly.js-dist": "^1.51.3",
- "popper.js": "^1.16.0"
- }
- },
- "node_modules/@fortawesome/fontawesome-free": {
- "version": "5.12.0",
- "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-5.12.0.tgz",
- "integrity": "sha512-vKDJUuE2GAdBERaQWmmtsciAMzjwNrROXA5KTGSZvayAsmuTGjam5z6QNqNPCwDfVljLWuov1nEC3mEQf/n6fQ==",
- "engines": {
- "node": ">=6"
- }
- },
- "node_modules/bootstrap": {
- "version": "4.4.1",
- "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-4.4.1.tgz",
- "integrity": "sha512-tbx5cHubwE6e2ZG7nqM3g/FZ5PQEDMWmMGNrCUBVRPHXTJaH7CBDdsLeu3eCh3B1tzAxTnAbtmrzvWEvT2NNEA==",
- "engines": {
- "node": ">=6"
- }
- },
- "node_modules/datatables.net": {
- "version": "1.10.20",
- "resolved": "https://registry.npmjs.org/datatables.net/-/datatables.net-1.10.20.tgz",
- "integrity": "sha512-4E4S7tTU607N3h0fZPkGmAtr9mwy462u+VJ6gxYZ8MxcRIjZqHy3Dv1GNry7i3zQCktTdWbULVKBbkAJkuHEnQ==",
- "dependencies": {
- "jquery": "3.4.1"
- }
- },
- "node_modules/datatables.net-bs4": {
- "version": "1.10.20",
- "resolved": "https://registry.npmjs.org/datatables.net-bs4/-/datatables.net-bs4-1.10.20.tgz",
- "integrity": "sha512-kQmMUMsHMOlAW96ztdoFqjSbLnlGZQ63iIM82kHbmldsfYdzuyhbb4hTx6YNBi481WCO3iPSvI6YodNec46ZAw==",
- "dependencies": {
- "datatables.net": "1.10.20",
- "jquery": "3.4.1"
- }
- },
- "node_modules/datatables.net-responsive": {
- "version": "2.2.3",
- "resolved": "https://registry.npmjs.org/datatables.net-responsive/-/datatables.net-responsive-2.2.3.tgz",
- "integrity": "sha512-8D6VtZcyuH3FG0Hn5A4LPZQEOX3+HrRFM7HjpmsQc/nQDBbdeBLkJX4Sh/o1nzFTSneuT1Wh/lYZHVPpjcN+Sw==",
- "dependencies": {
- "datatables.net": "1.10.20",
- "jquery": "3.4.1"
- }
- },
- "node_modules/datatables.net-responsive-bs4": {
- "version": "2.2.3",
- "resolved": "https://registry.npmjs.org/datatables.net-responsive-bs4/-/datatables.net-responsive-bs4-2.2.3.tgz",
- "integrity": "sha512-SQaWI0uLuPcaiBBin9zX+MuQfTSIkK1bYxbXqUV6NLkHCVa6PMQK7Rvftj0ywG4R7uOtjbzY8nSVqxEKvQI0Vg==",
- "dependencies": {
- "datatables.net-bs4": "1.10.20",
- "datatables.net-responsive": "2.2.3",
- "jquery": "3.4.1"
- }
- },
- "node_modules/jquery": {
- "version": "3.4.1",
- "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.4.1.tgz",
- "integrity": "sha512-36+AdBzCL+y6qjw5Tx7HgzeGCzC81MDDgaUP8ld2zhx58HdqXGoBd+tHdrBMiyjGQs0Hxs/MLZTu/eHNJJuWPw=="
- },
- "node_modules/mxgraph": {
- "version": "4.0.6",
- "resolved": "https://registry.npmjs.org/mxgraph/-/mxgraph-4.0.6.tgz",
- "integrity": "sha512-5XZXeAkA4k6n4BS05Fxd2cNhMw+3dnlRqAaLtsuXdT0g8BvvEa1VT4jjuGtUW4QTt38Q+I2Dr/3EWiAaGRfAXw=="
- },
- "node_modules/plotly.js-dist": {
- "version": "1.51.3",
- "resolved": "https://registry.npmjs.org/plotly.js-dist/-/plotly.js-dist-1.51.3.tgz",
- "integrity": "sha512-Bxz0XBg963gpnbt7FVPEhYvT33JsaKa0hEozXBnQZkiKtsiM2M1lZN6tkEHmq6o1N2K6qJXFtdzCXbZ/hLGV0Q=="
- },
- "node_modules/popper.js": {
- "version": "1.16.0",
- "resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.0.tgz",
- "integrity": "sha512-+G+EkOPoE5S/zChTpmBSSDYmhXJ5PsW8eMhH8cP/CQHMFPBG/kC9Y5IIw6qNYgdJ+/COf0ddY2li28iHaZRSjw=="
- }
- },
- "dependencies": {
- "@fortawesome/fontawesome-free": {
- "version": "5.12.0",
- "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-5.12.0.tgz",
- "integrity": "sha512-vKDJUuE2GAdBERaQWmmtsciAMzjwNrROXA5KTGSZvayAsmuTGjam5z6QNqNPCwDfVljLWuov1nEC3mEQf/n6fQ=="
- },
- "bootstrap": {
- "version": "4.4.1",
- "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-4.4.1.tgz",
- "integrity": "sha512-tbx5cHubwE6e2ZG7nqM3g/FZ5PQEDMWmMGNrCUBVRPHXTJaH7CBDdsLeu3eCh3B1tzAxTnAbtmrzvWEvT2NNEA=="
- },
- "datatables.net": {
- "version": "1.10.20",
- "resolved": "https://registry.npmjs.org/datatables.net/-/datatables.net-1.10.20.tgz",
- "integrity": "sha512-4E4S7tTU607N3h0fZPkGmAtr9mwy462u+VJ6gxYZ8MxcRIjZqHy3Dv1GNry7i3zQCktTdWbULVKBbkAJkuHEnQ==",
- "requires": {
- "jquery": "3.4.1"
- }
- },
- "datatables.net-bs4": {
- "version": "1.10.20",
- "resolved": "https://registry.npmjs.org/datatables.net-bs4/-/datatables.net-bs4-1.10.20.tgz",
- "integrity": "sha512-kQmMUMsHMOlAW96ztdoFqjSbLnlGZQ63iIM82kHbmldsfYdzuyhbb4hTx6YNBi481WCO3iPSvI6YodNec46ZAw==",
- "requires": {
- "datatables.net": "1.10.20",
- "jquery": "3.4.1"
- }
- },
- "datatables.net-responsive": {
- "version": "2.2.3",
- "resolved": "https://registry.npmjs.org/datatables.net-responsive/-/datatables.net-responsive-2.2.3.tgz",
- "integrity": "sha512-8D6VtZcyuH3FG0Hn5A4LPZQEOX3+HrRFM7HjpmsQc/nQDBbdeBLkJX4Sh/o1nzFTSneuT1Wh/lYZHVPpjcN+Sw==",
- "requires": {
- "datatables.net": "1.10.20",
- "jquery": "3.4.1"
- }
- },
- "datatables.net-responsive-bs4": {
- "version": "2.2.3",
- "resolved": "https://registry.npmjs.org/datatables.net-responsive-bs4/-/datatables.net-responsive-bs4-2.2.3.tgz",
- "integrity": "sha512-SQaWI0uLuPcaiBBin9zX+MuQfTSIkK1bYxbXqUV6NLkHCVa6PMQK7Rvftj0ywG4R7uOtjbzY8nSVqxEKvQI0Vg==",
- "requires": {
- "datatables.net-bs4": "1.10.20",
- "datatables.net-responsive": "2.2.3",
- "jquery": "3.4.1"
- }
- },
- "jquery": {
- "version": "3.4.1",
- "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.4.1.tgz",
- "integrity": "sha512-36+AdBzCL+y6qjw5Tx7HgzeGCzC81MDDgaUP8ld2zhx58HdqXGoBd+tHdrBMiyjGQs0Hxs/MLZTu/eHNJJuWPw=="
- },
- "mxgraph": {
- "version": "4.0.6",
- "resolved": "https://registry.npmjs.org/mxgraph/-/mxgraph-4.0.6.tgz",
- "integrity": "sha512-5XZXeAkA4k6n4BS05Fxd2cNhMw+3dnlRqAaLtsuXdT0g8BvvEa1VT4jjuGtUW4QTt38Q+I2Dr/3EWiAaGRfAXw=="
- },
- "plotly.js-dist": {
- "version": "1.51.3",
- "resolved": "https://registry.npmjs.org/plotly.js-dist/-/plotly.js-dist-1.51.3.tgz",
- "integrity": "sha512-Bxz0XBg963gpnbt7FVPEhYvT33JsaKa0hEozXBnQZkiKtsiM2M1lZN6tkEHmq6o1N2K6qJXFtdzCXbZ/hLGV0Q=="
- },
- "popper.js": {
- "version": "1.16.0",
- "resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.0.tgz",
- "integrity": "sha512-+G+EkOPoE5S/zChTpmBSSDYmhXJ5PsW8eMhH8cP/CQHMFPBG/kC9Y5IIw6qNYgdJ+/COf0ddY2li28iHaZRSjw=="
- }
- }
-}
diff --git a/src/static/package.json b/src/static/package.json
deleted file mode 100644
index c2c4e41..0000000
--- a/src/static/package.json
+++ /dev/null
@@ -1,26 +0,0 @@
-{
- "name": "laas",
- "version": "1.0.0",
- "description": "",
- "main": "index.js",
- "scripts": {
- "test": "echo \"Error: no test specified\" && exit 1"
- },
- "author": "",
- "repository": {
- "type": "git",
- "url": "https://git.opnfv.org/laas"
- },
- "license": "Apache-2.0",
- "private": true,
- "dependencies": {
- "@fortawesome/fontawesome-free": "^5.12.0",
- "bootstrap": "^4.4.1",
- "datatables.net-bs4": "^1.10.20",
- "datatables.net-responsive-bs4": "^2.2.3",
- "jquery": "^3.4.1",
- "mxgraph": "^4.0.6",
- "plotly.js-dist": "^1.51.3",
- "popper.js": "^1.16.0"
- }
-}
diff --git a/src/templates/README b/src/templates/README
deleted file mode 100644
index d058036..0000000
--- a/src/templates/README
+++ /dev/null
@@ -1,25 +0,0 @@
-We use a special directory structure here that allows you to define
-your own set of templates than inherit from a common base.
-
-To create your own templates:
-
-Create your new directory as a sibling of base/
- `mkdir my_templates`
-
-Now you can override any template file in base/ by creating a file of the same name in my_templates.
-
-For example, to replace base/booking/booking_table.html,
-you would create my_templates/booking/booking_table.html. Your template will be loaded instead of the original.
-
-
-You can also inherit from the base templates. For example, if you
-wanted to add to base/dashboard/landing.html, you can create
-a template at my_templates/dashboard/landing.html and add this line to
-the top of the file:
- {% extends base/dashboard/landing.html %}
-
-This way you can add in new {% block %} tags to the parents and define only
-the new content you need without affecting the default behavior.
-
-
-When your template directory is ready, you must add it to your config.env
diff --git a/src/templates/base/account/booking_list.html b/src/templates/base/account/booking_list.html
deleted file mode 100644
index f9234bc..0000000
--- a/src/templates/base/account/booking_list.html
+++ /dev/null
@@ -1,146 +0,0 @@
-{% extends "base.html" %}
-{% block content %}
-<h2>Bookings I Own</h2>
-<div class="row">
- {% for booking in bookings %}
- <div class="col-12 col-md-6 col-lg-4 col-xl-3 mb-3">
- <div class="card h-100">
- <div class="card-header">
- <h3>Booking {{booking.id}}</h3>
- </div>
- <ul class="list-group list-group-flush h-100">
- <li class="list-group-item">id: {{booking.id}}</li>
- <li class="list-group-item">lab: {{booking.lab}}</li>
- <li class="list-group-item">resource: {{booking.resource.template.name}}</li>
- <li class="list-group-item">start: {{booking.start}}</li>
- <li class="list-group-item">end: {{booking.end}}</li>
- <li class="list-group-item">purpose: {{booking.purpose}}</li>
- </ul>
- <div class="card-footer d-flex">
- <a class="btn btn-primary ml-auto mr-2" href="/booking/detail/{{booking.id}}/">Details</a>
- <button
- class="btn btn-danger"
- onclick='cancel_booking({{booking.id}});'
- data-toggle="modal"
- data-target="#resModal"
- >Cancel</button>
- </div>
- </div>
- </div>
- {% empty %}
- <div class="col">
- <p>You don't have any bookings. You can create a booking by booking a pod.</p>
- </div>
- {% endfor %}
-</div>
-
-<h2>Bookings I Collaborate On</h2>
-<div class="row">
-
- {% for booking in collab_bookings %}
- <div class="col-12 col-md-6 col-lg-4 col-xl-3 mb-3">
- <div class="card h-100">
- <div class="card-header">
- <h3>Booking {{booking.id}}</h3>
- </div>
- <ul class="list-group list-group-flush h-100">
- <li class="list-group-item">id: {{booking.id}}</li>
- <li class="list-group-item">lab: {{booking.lab}}</li>
- <li class="list-group-item">resource: {{booking.resource.template.name}}</li>
- <li class="list-group-item">start: {{booking.start}}</li>
- <li class="list-group-item">end: {{booking.end}}</li>
- <li class="list-group-item">purpose: {{booking.purpose}}</li>
- </ul>
- <div class="card-footer d-flex">
- <a class="btn btn-primary ml-auto" href="/booking/detail/{{booking.id}}/">Details</a>
- </div>
- </div>
- </div>
- {% empty %}
- <div class="col">
- <p>There are no collaborative bookings.</p>
- </div>
- {% endfor %}
-</div>
-
-<a href="#expired_bookings" data-toggle="collapse" class="h2 discrete-a">
- Expired Bookings
- <i class="fas fa-angle-down rotate"></i>
-</a>
-<div id="expired_bookings" class="row collapse">
- {% for booking in expired_bookings %}
- <div class="col-12 col-md-6 col-lg-4 col-xl-3 mb-3">
- <div class="card h-100">
- <div class="card-header">
- <h3>Booking {{booking.id}}</h3>
- </div>
- <ul class="list-group list-group-flush h-100">
- <li class="list-group-item">id: {{booking.id}}</li>
- <li class="list-group-item">lab: {{booking.lab}}</li>
- <li class="list-group-item">resource: {{booking.resource.template.name}}</li>
- <li class="list-group-item">start: {{booking.start}}</li>
- <li class="list-group-item">end: {{booking.end}}</li>
- <li class="list-group-item">purpose: {{booking.purpose}}</li>
- <li class="list-group-item">owner: {{booking.owner.userprofile.email_addr}}</li>
- </ul>
- <div class="card-footer d-flex">
- <a class="btn btn-primary ml-auto" href="/booking/detail/{{booking.id}}/">Details</a>
- </div>
- </div>
- </div>
- {% empty %}
- <div class="col">
- <p>There are no expired bookings.</p>
- </div>
- {% endfor %}
-</div>
-
-<script>
- var current_booking_id = -1;
- function cancel_booking(booking_id) {
- current_booking_id = booking_id;
- }
-
- function submit_cancel_form() {
- var ajaxForm = $("#booking_cancel_form");
- var formData = ajaxForm.serialize();
- req = new XMLHttpRequest();
- var url = "cancel/" + current_booking_id;
- req.open("POST", url, true);
- req.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
- req.onerror = function() { alert("problem submitting form"); }
- req.send(formData);
- }
-</script>
-
-<div class="modal fade" id="resModal" tabindex="-1" role="dialog" aria-hidden="true">
- <div class="modal-dialog" role="document">
- <div class="modal-content">
- <div class="modal-header">
- <h4 class="modal-title d-inline float-left">Cancel Booking?</h4>
- <button type="button" class="close" data-dismiss="modal" aria-label="Close">
- <span aria-hidden="true">&times;</span>
- </button>
- </div>
- <form id="booking_cancel_form">
- {% csrf_token %}
- </form>
- <div class="modal-footer d-flex flex-column">
- <p>Everything on your machine(s) will be lost</p>
- <div class="mb-2">
- <button type="button" class="btn btn-outline-secondary" data-dismiss="modal">Close</button>
- <button type="button" class="btn btn-danger" data-toggle="collapse" data-target="#warning">Cancel Booking</button>
- </div>
- <div class="collapse w-100 text-center border-top" id="warning">
- <div class="p-4">
- <h3>Are You Sure?</h3>
- <p>This cannot be undone</p>
- <button class="btn btn-outline-secondary" data-dismiss="modal">Nevermind</button>
- <button class="btn btn-danger" id="confirm_cancel_button" data-dismiss="modal" onclick="submit_cancel_form();">I'm Sure</button>
- </div>
- </div>
- </div>
- </div>
- </div>
-</div>
-{% endblock %}
diff --git a/src/templates/base/account/details.html b/src/templates/base/account/details.html
deleted file mode 100644
index ad59c9a..0000000
--- a/src/templates/base/account/details.html
+++ /dev/null
@@ -1,8 +0,0 @@
-{% extends "base.html" %}
-{% load staticfiles %}
-{% block content %}
-<h1>Account Details</h1>
-<a class="btn btn-primary" href="{% url 'account:my-resources' %}">My Resources</a>
-<a class="btn btn-primary" href="{% url 'account:my-bookings' %}">My Bookings</a>
-<a class="btn btn-primary" href="{% url 'account:my-images' %}">My Snapshots</a>
-{% endblock content %}
diff --git a/src/templates/base/account/image_list.html b/src/templates/base/account/image_list.html
deleted file mode 100644
index 6263016..0000000
--- a/src/templates/base/account/image_list.html
+++ /dev/null
@@ -1,132 +0,0 @@
-{% extends "base.html" %}
-{% block content %}
-<h2>Images I Own</h2>
-<div class="row">
-{% for image in images %}
- <div class="col-12 col-md-6 col-lg-4 col-xl-3 mb-3">
- <div class="card h-100">
- <div class="card-header">
- <h3>Image {{image.id}}</h3>
- </div>
- <ul class="list-group list-group-flush h-100">
- <li class="list-group-item">id: {{image.id}}</li>
- <li class="list-group-item">lab: {{image.from_lab.name}}</li>
- <li class="list-group-item">name: {{image.name}}</li>
- <li class="list-group-item">description: {{image.description}}</li>
- <li class="list-group-item">host profile: {{image.host_type.name}}</li>
- </ul>
- <div class="card-footer">
- <button class="btn btn-danger w-100" onclick='delete_image({{image.id}});'
- data-toggle="modal" data-target="#imageModal">
- Delete
- </button>
- </div>
- </div>
- </div>
-{% empty %}
- <div class="col">
- <p>You don't have any images. You can create an image by creating a snapshot.</p>
- </div>
-{% endfor %}
-</div>
-
-<h2>Public Images</h2>
-<div class="row">
- {% for image in public_images %}
- <div class="col-12 col-md-6 col-lg-4 col-xl-3 mb-3">
- <div class="card h-100">
- <div class="card-header">
- <h3>Image {{image.id}}</h3>
- </div>
- <ul class="list-group list-group-flush h-100">
- <li class="list-group-item">id: {{image.id}}</li>
- <li class="list-group-item">lab: {{image.from_lab.name}}</li>
- <li class="list-group-item">name: {{image.name}}</li>
- <li class="list-group-item">description: {{image.description}}</li>
- <li class="list-group-item">host profile: {{image.host_type.name}}</li>
- </ul>
- </div>
- </div>
- {% empty %}
- <div class="col">
- <p>There are no public images.</p>
- </div>
- {% endfor %}
-</div>
-
-<script>
- var current_image_id = -1;
- var used_images = {{used_images|safe|default:"{}"}};
- function delete_image(image_id) {
- current_image_id = image_id;
- var warning_header = document.getElementById("warning_header");
- var warning_text = document.getElementById("warning_text");
- var delete_image_button = document.getElementById("final_delete_b");
- clear(warning_header);
- clear(warning_text);
- if(used_images[image_id]) {
- warning_header.appendChild(
- document.createTextNode("Cannot Delete")
- );
- warning_text.appendChild(
- document.createTextNode("This snapshot is being used in a booking.")
- );
- delete_image_button.disabled = true;
- } else {
- warning_header.appendChild(
- document.createTextNode("Are You Sure?")
- );
- warning_text.appendChild(
- document.createTextNode("This cannot be undone")
- );
- delete_image_button.removeAttribute("disabled");
- }
- }
-
- function submit_delete_form() {
- var ajaxForm = $("#image_delete_form");
- var formData = ajaxForm.serialize();
- req = new XMLHttpRequest();
- var url = "delete/" + current_image_id;
- req.open("POST", url, true);
- req.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
- req.onerror = function() { alert("problem submitting form"); }
- req.send(formData);
- }
-
- function clear(node) {
- while(node.lastChild) {
- node.removeChild(node.lastChild);
- }
- }
-</script>
-<div class="modal fade" id="imageModal" tabindex="-1" role="dialog" aria-hidden="true">
- <div class="modal-dialog" role="document">
- <div class="modal-content">
- <div class="modal-header">
- <h4 class="modal-title d-inline float-left">Delete Configuration?</h4>
- <button type="button" class="close" data-dismiss="modal" aria-label="Close">
- <span aria-hidden="true">&times;</span>
- </button>
- </div>
- <form id="image_delete_form">
- {% csrf_token %}
- </form>
- <div class="modal-footer d-flex flex-column">
- <div class="mb-2">
- <button type="button" class="btn btn-outline-secondary" data-dismiss="modal">Close</button>
- <button type="button" class="btn btn-danger" data-toggle="collapse" data-target="#warning">Delete</button>
- </div>
- <div class="collapse w-100 text-center border-top" id="warning">
- <div class="p-4">
- <h3 id="warning_header">Are You Sure?</h3>
- <p id="warning_text">This cannot be undone</p>
- <button class="btn btn-outline-secondary" data-dismiss="modal">Nevermind</button>
- <button id="final_delete_b" class="btn btn-danger" data-dismiss="modal" onclick="submit_delete_form();">I'm Sure</button>
- </div>
- </div>
- </div>
- </div>
- </div>
-</div>
-{% endblock %}
diff --git a/src/templates/base/account/resource_list.html b/src/templates/base/account/resource_list.html
deleted file mode 100644
index 33ccaff..0000000
--- a/src/templates/base/account/resource_list.html
+++ /dev/null
@@ -1,126 +0,0 @@
-{% extends "base.html" %}
-{% block content %}
-<div class="row">
-{% for resource in resources %}
- <div class="col-12 col-md-6 col-lg-4 col-xl-3 mb-3">
- <div class="card h-100">
- <div class="card-header">
- <h3>Resource {{resource.id}}</h3>
- </div>
- <ul class="list-group list-group-flush h-100">
- <li class="list-group-item">id: {{resource.id}}</li>
- <li class="list-group-item">name: {{resource.name}}</li>
- <li class="list-group-item">description: {{resource.description}}</li>
- </ul>
- <div class="card-footer">
- <button
- class="btn btn-danger w-100"
- onclick='delete_resource({{resource.id}});'
- data-toggle="modal"
- data-target="#resModal"
- >Delete</button>
- </div>
- </div>
- </div>
-{% empty %}
- <div class="col">
- <p>You don't have any resources. You can create a resource by designing a pod.</p>
- </div>
-{% endfor %}
-</div>
-<script>
- var active_resources = {{active_resources|safe|default:"{}"}}
- var current_resource_id = -1;
- function delete_resource(resource_id) {
- document.getElementById("confirm_delete_button").removeAttribute("disabled");
- var warning = document.createTextNode("Are You Sure?");
- var warning_subtext = document.createTextNode("This cannot be undone");
- if(active_resources[resource_id]){
- var warning = document.createTextNode("This resource is being used or is scheduled to be used. It cannot be deleted.");
- var warning_subtext = document.createTextNode("If your booking just ended, you may need to give us a few minutes to clean it up before this can be removed.");
-
- document.getElementById("confirm_delete_button").disabled = true;
- }
- else {
- warning_text = "Are You Sure?";
- warning = document.createTextNode(warning_text);
- }
-
- current_resource_id = resource_id;
- set_modal_text(warning, warning_subtext);
- }
-
- function set_modal_text(title, text) {
- var clear = function(node) {
- while(node.lastChild) {
- node.removeChild(node.lastChild);
- }
- }
- var warning_title = document.getElementById("config_warning");
- var warning_text = document.getElementById("warning_subtext");
-
- clear(warning_title);
- clear(warning_text);
-
- warning_title.appendChild(title);
- warning_text.appendChild(text);
- }
-
- function list_configs(configs) {
- var list = document.getElementById("config_list");
- for(var i=0; i<configs.length; i++){
- var str = configs[i].name;
- var list_item = document.createElement("LI");
- list_item.appendChild(document.createTextNode(str));
- list.appendChild(list_item);
- }
- }
-
- function submit_delete_form() {
- var ajaxForm = $("#res_delete_form");
- var formData = ajaxForm.serialize();
- req = new XMLHttpRequest();
- var url = "delete/" + current_resource_id;
- req.onreadystatechange = function() {
- if (this.readyState == 4 && this.status == 200) {
- location.reload();
- }
- };
- req.open("POST", url, true);
- req.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
- req.onerror = function() { alert("problem submitting form"); }
- req.send(formData);
- }
-</script>
-<div class="modal fade" id="resModal" tabindex="-1" role="dialog" aria-hidden="true">
- <div class="modal-dialog" role="document">
- <div class="modal-content">
- <div class="modal-header">
- <h4 class="modal-title d-inline float-left">Delete Resource?</h4>
- <button type="button" class="close" data-dismiss="modal" aria-label="Close">
- <span aria-hidden="true">&times;</span>
- </button>
- </div>
- <form id="res_delete_form">
- {% csrf_token %}
- </form>
- <div class="modal-footer d-flex flex-column">
- <div class="mb-2">
- <button type="button" class="btn btn-outline-secondary" data-dismiss="modal">Close</button>
- <button type="button" class="btn btn-danger" data-toggle="collapse" data-target="#warning">Delete</button>
- </div>
- <div class="collapse w-100 text-center border-top" id="warning">
- <div class="p-4">
- <h3 id="config_warning">Are You Sure?</h3>
- <p id="warning_subtext">This cannot be undone</p>
- <ul id="config_list"></ul>
- <button class="btn btn-outline-secondary" data-dismiss="modal">Nevermind</button>
- <button class="btn btn-danger" id="confirm_delete_button" data-dismiss="modal" onclick="submit_delete_form();">I'm Sure</button>
- </div>
- </div>
- </div>
- </div>
- </div>
-</div>
-
-{% endblock %}
diff --git a/src/templates/base/account/user_list.html b/src/templates/base/account/user_list.html
deleted file mode 100644
index e564524..0000000
--- a/src/templates/base/account/user_list.html
+++ /dev/null
@@ -1,55 +0,0 @@
-{% extends "dashboard/table.html" %}
-{% load staticfiles %}
-
-{% block table %}
- <thead>
- <tr>
- <th>Username</th>
- <th>Full Name</th>
- <th>Email</th>
- <th>Company</th>
- <th>SSH Key</th>
- <th>GPG Key</th>
- </tr>
- </thead>
- <tbody>
- {% for user in users %}
- <tr>
- <td>
- {{ user.username }}
- </td>
- <td>
- {{ user.userprofile.full_name }}
- </td>
- <td>
- {{ user.userprofile.email_addr }}
- </td>
- <td>
- {{ user.userprofile.company }}
- </td>
- <td>
- {% if user.userprofile.ssh_public_key %}
- <a href={{ user.userprofile.ssh_public_key.url }}>SSH</a>
- {% endif %}
- </td>
- <td>
- {% if user.userprofile.pgp_public_key %}
- <a href={{ user.userprofile.pgp_public_key.url }}>GPG</a>
- {% endif %}
- </td>
- </tr>
- {% endfor %}
- </tbody>
-{% endblock table %}
-
-
-{% block tablejs %}
- <script type="text/javascript">
- $(document).ready(function () {
- $('#table').DataTable({
- scrollX: true,
- "order": [[0, "asc"]]
- });
- });
- </script>
-{% endblock tablejs %}
diff --git a/src/templates/base/account/userprofile_update_form.html b/src/templates/base/account/userprofile_update_form.html
deleted file mode 100644
index 16a8270..0000000
--- a/src/templates/base/account/userprofile_update_form.html
+++ /dev/null
@@ -1,31 +0,0 @@
-{% extends "base.html" %}
-{% load bootstrap4 %}
-
-{% block content %}
-<div class="row">
- <div class="col-12 col-xl-6">
- {% bootstrap_messages %}
- <div class="login-panel panel panel-default">
- <div class="panel-body">
- <form enctype="multipart/form-data" method="post">
- {% csrf_token %}
- {% bootstrap_form form %}
- <p><b>API Token</b>
- <a href="{% url 'generate_token' %}" class="btn btn-primary">
- Generate
- </a>
- </p>
- <p class="text-break">{{ token.key }}</p>
-
- <p></p>
- {% buttons %}
- <button type="submit" class="btn btn btn-success">
- Save Profile
- </button>
- {% endbuttons %}
- </form>
- </div>
- </div>
- </div>
-</div>
-{% endblock content %}
diff --git a/src/templates/base/base.html b/src/templates/base/base.html
deleted file mode 100644
index 351bd9a..0000000
--- a/src/templates/base/base.html
+++ /dev/null
@@ -1,191 +0,0 @@
-{% extends "layout.html" %}
-{% load bootstrap4 %}
-{% load staticfiles %}
-{% block extrahead %}
-
-
-<!-- Custom CSS -->
-<link href="{% static "css/base.css" %}" rel="stylesheet">
-<script src="/static/js/dashboard.js"></script>
-
-{% endblock %}
-{% block basecontent %}
-<div id="wrapper" class="d-flex flex-column">
- <!-- Navigation -->
- {% block bgColor %}
- <nav class="navbar navbar-light bg-light navbar-fixed-top border-bottom py-0 mb-0" role="navigation">
- {% endblock bgColor %}
- <div class="container-fluid pb-2 pb-sm-0">
- <!-- Logo -->
- {% block logo %}
- <div class="col-12 col-sm order-1 order-sm-2 text-center text-lg-left">
- <h2>Logo Here</h2>
- <a class="navbar-brand d-none d-lg-inline Anuket-Text" href={% url 'dashboard:index' %}>
- Laas Dashboard
- </a>
- </div>
- {% endblock logo %}
- <!-- Sidebar button -->
- <div class="col-6 col-sm-2 d-flex order-1 order-lg-3 d-lg-none">
- <button class="btn border mx-auto" type="button" data-toggle="collapse" data-target="#sidebar"
- aria-expanded="false" aria-controls="sidebar">
- <i class="fas fa-bars d-inline"></i>
- </button>
- </div>
- <!-- Login dropdown -->
- <div class="col-6 col-sm-2 order-3 d-flex">
- <ul class="nav mx-auto mr-sm-0">
- <li class="dropdown ml-auto">
- {% block userDropDownText %}
- <a class="nav-link p-0 p-2 Anuket-Text" data-toggle="dropdown" href="#">
- {% if request.user.username %}
- {{request.user.username}}
- {% else %}
- <i class="fas fa-user"></i>
- {% endif %}
- <i class="fas fa-caret-down rotate"></i>
- </a>
- {% endblock userDropDownText %}
- <div class="dropdown-menu dropdown-menu-right">
- {% if LFID %}
- {% if user.is_authenticated %}
- <a href="{% url 'account:settings' %}" class="dropdown-item Anuket-Text">
- <i class="fas fa-cog"></i>
- Settings
- </a>
- <a id="logout_btn" href="#" method="post" class="dropdown-item Anuket-Text">
- <i class="fas fa-sign-out-alt"></i>
- Logout
- </a>
- <form id="logout_form" action="{% url 'oidc_logout' %}" method="post" style="visibility: hidden;">
- {% csrf_token %}
- <input type="submit" value="logout">
- </form>
- <script>
- const logout_btn = document.getElementById("logout_btn");
-
- const logout_form = document.getElementById("logout_form");
-
- logout_btn.onclick = function() { logout_form.submit(); };
- </script>
- {% else %}
- <a href="{% url 'oidc_authentication_init' %}" class="dropdown-item Anuket-Text">
- <i class="fas fa-sign-in-alt"></i>
- Login with LFID
- </a>
- {% endif %}
- {% else %}
- {% if user.is_authenticated %}
- <a href="{% url 'account:settings' %}" class="dropdown-item Anuket-Text">
- <i class="fas fa-cog"></i>
- Settings
- </a>
- <a href="{% url 'account:logout' %}?next={{ request.path }}" class="dropdown-item Anuket-Text">
- <i class="fas fa-sign-out-alt"></i>
- Logout
- </a>
- {% else %}
- <a href="{% url 'account:login' %}" class="dropdown-item Anuket-Text">
- <i class="fas fa-sign-in-alt"></i>
- Login
- </a>
- {% endif %}
- {% endif %}
- </div>
- </li>
- </ul>
- </div>
- </div>
- </nav>
-
- <!-- Page Content -->
- <div class="container-fluid d-flex flex-grow-1 px-0 align-items-start flex-column">
- <div class="row flex-grow-1 w-100 mx-0 align-content-start flex-lg-grow-1">
- <div class="col-12 col-lg-auto px-0 border-right border-left bg-light" role="navigation">
- <nav class="navbar navbar-expand-lg border-bottom p-0 w-100 sidebar">
- <div class="collapse navbar-collapse" id="sidebar">
- <div class="list-group list-group-flush w-100 ">
- <a href="/" class="list-group-item list-group-item-action nav-bg">
- Home
- </a>
- {% block dropDown %}
- <a class="list-group-item list-group-item-action nav-bg" data-toggle="collapse"
- href="#createList" role="button">
- Create <i class="fas fa-angle-down rotate"></i>
- </a>
- <div class="collapse" id="createList">
- <a href="/booking/quick/" class="list-group-item list-group-item-action list-group-item-secondary dropDown-bg">
- Express Booking
- </a>
- <a href="#" onclick="create_workflow(0)" class="list-group-item list-group-item-action list-group-item-secondary dropDown-bg">
- Book a Pod
- </a>
- <a href="#" onclick="create_workflow(1)" class="list-group-item list-group-item-action list-group-item-secondary dropDown-bg">
- Design a Pod
- </a>
- <a href="#" onclick="create_workflow(2)" class="list-group-item list-group-item-action list-group-item-secondary dropDown-bg">
- Create a Snapshot
- </a>
- <a href="#" onclick="create_workflow(3)" class="list-group-item list-group-item-action list-group-item-secondary dropDown-bg">
- Configure Anuket
- </a>
- </div>
- {% endblock dropDown %}
- <a href="{% url 'resource:hosts' %}" class="list-group-item list-group-item-action nav-bg">
- Hosts
- </a>
- {% if user.is_authenticated %}
- <a href="{% url 'account:users' %}" class="list-group-item list-group-item-action nav-bg">
- User List
- </a>
- {% endif %}
- <a href="{% url 'booking:list' %}" class="list-group-item list-group-item-action nav-bg">
- Booking List
- </a>
- <a href="{% url 'booking:stats' %}" class="list-group-item list-group-item-action nav-bg">
- Booking Statistics
- </a>
- <a class="list-group-item list-group-item-action nav-bg" data-toggle="collapse"
- href="#accountList" role="button">
- Account <i class="fas fa-angle-down rotate"></i>
- </a>
- <div class="collapse" id="accountList">
- <a href="{% url 'account:my-resources' %}" class="list-group-item list-group-item-action list-group-item-secondary dropDown-bg">
- My Resources
- </a>
- <a href="{% url 'account:my-bookings' %}" class="list-group-item list-group-item-action list-group-item-secondary dropDown-bg">
- My Bookings
- </a>
- <a href="{% url 'account:my-images' %}" class="list-group-item list-group-item-action list-group-item-secondary dropDown-bg">
- My Snapshots
- </a>
- </div>
- <a href="{% url 'dashboard:all_labs' %}" class="list-group-item list-group-item-action nav-bg">
- Lab Info
- </a>
- <a href="{% url 'notifier:messages' %}" class="list-group-item list-group-item-action nav-bg">
- Inbox
- </a>
- </div>
- </div>
- </nav>
- </div>
- <div class="col overflow-auto flex-grow-1 d-flex flex-column h-100">
- {% if title %}
- <div class="row flex-shrink-1">
- <div class="col-lg-12">
- <h1 class="page-header">{{ title }}</h1>
- </div>
- </div>
- {% endif %}
- {% if messages %}
- <div id="bsm" class="mt-4">{% bootstrap_messages %}</div>
- {% endif %}
- <!-- Content block placed here -->
- {% block content %}
- {% endblock content %}
- </div>
- </div>
- </div>
-</div>
-{% endblock basecontent %}
diff --git a/src/templates/base/booking/booking_calendar.html b/src/templates/base/booking/booking_calendar.html
deleted file mode 100644
index 450c97f..0000000
--- a/src/templates/base/booking/booking_calendar.html
+++ /dev/null
@@ -1,207 +0,0 @@
-{% extends "base.html" %}
-{% load staticfiles %}
-
-{% load bootstrap4 %}
-
-{% block extrahead %}
- {{ block.super }}
-
- <link href="{% static "bower_components/fullcalendar/dist/fullcalendar.css" %}"
- rel='stylesheet'/>
- <link href="{% static "bower_components/eonasdan-bootstrap-datetimepicker/build/css/bootstrap-datetimepicker.min.css" %}"
- rel='stylesheet'/>
-{% endblock extrahead %}
-
-{% block content %}
- <div class="col-lg-8">
- <div class="container-fluid">
- <div class="panel panel-default">
- <div class="panel-heading">
- <i class="fa fa-calendar fa-fw"></i>Calendar
- </div>
- <div class="panel-body">
- <div id='calendar'>
- </div>
- </div>
- </div>
- </div>
- </div>
-
- <div class="col-lg-4">
- <div class="panel panel-default">
- <div class="panel-heading">
- <i class="fa fa-edit fa-fw"></i>Booking
- </div>
- <div class="panel-body">
- {% if user.is_authenticated %}
- <div id="booking_form_div">
- {% bootstrap_form_errors form type='non_fields' %}
- <form method="post" action="" class="form" id="bookingform">
- {% csrf_token %}
-
- <div class='input-group' id='starttimepicker'>
- {% bootstrap_field form.start addon_after='<span class="glyphicon glyphicon-calendar"></span>' %}
- </div>
- <div class='input-group' id='endtimepicker'>
- {% bootstrap_field form.end addon_after='<span class="glyphicon glyphicon-calendar"></span>' %}
- </div>
- {% bootstrap_field form.opsys %}
- {% bootstrap_field form.purpose %}
- {% bootstrap_field form.installer %}
- {% bootstrap_field form.scenario %}
- {% buttons %}
- <button type="submit" class="btn btn btn-success">
- Book
- </button>
- <p>
- <br>By continuing, you agree to our
- <a href="https://wiki.opnfv.org/display/INF/Lab-as-a-Service+at+the+UNH-IOL#Lab-as-a-ServiceattheUNH-IOL-AcceptableUsagePolicy">Acceptable Usage Policy</a>
- </p>
- {% endbuttons %}
- </form>
- </div>
- <div id="booking_edit_form_div">
- {% bootstrap_form_errors form type='non_fields' %}
- <form method="post" action="" class="form" id="bookingeditform">
- {% csrf_token %}
- <div id='hide_if_noext'>
- <div class='input-group' id='starttimeeditpicker'>
- {% bootstrap_field form.start addon_after='<span class="glyphicon glyphicon-calendar"></span>' %}
- </div>
- <div class='input-group' id='endtimeeditpicker'>
- {% bootstrap_field form.end addon_after='<span class="glyphicon glyphicon-calendar"></span>' %}
- </div>
- </div>
- <script type="text/javascript">
- if( {{ booking.ext_count }} <= 0 ){
- var hidediv = document.getElementById('hide_if_noext');
- hidediv.style.display = 'none';
- var par = document.createElement("div");
- var text = document.createTextNode("No Extensions Remaining");
- par.appendChild(text);
- par.style.fontWeight = 'bold';
- par.style.paddingBottom = '15pt';
- hidediv.parentNode.insertBefore(par,hidediv.nextSibling);
- }
- </script>
- {% bootstrap_field form.opsys %}
- {% bootstrap_field form.purpose %}
- {% bootstrap_field form.installer %}
- {% bootstrap_field form.scenario %}
- {% bootstrap_field form.reset %}
- {% buttons %}
- <button type="submit" class="btn btn btn-success">
- Confirm Edit
- </button>
- {% endbuttons %}
- </form>
- </div>
- <script type="text/javascript">
- //Check if current view is an edit or a standard booking view
- if(window.location.href.includes('edit')) {
- var element = document.getElementById("bookingform");
- element.parentNode.removeChild(element); }
- else {
- var element = document.getElementById("bookingeditform");
- element.parentNode.removeChild(element); }
- </script>
- {% else %}
- <p>Please
- <a href="{% url 'account:login' %}">
- login with Jira</a>
- to book this Pod</p>
- {% endif %}
- </div>
- </div>
- </div>
-
- <div id="booking_detail_modal" class="modal fade" role="dialog">
- <div class="modal-dialog">
-
- <!-- Modal content-->
- <div class="modal-content">
- <div class="modal-header">
- <button type="button" class="close" data-dismiss="modal">&times;</button>
- <h4 class="modal-title">Booking Detail</h4>
- </div>
- <div class="modal-body" id="booking_detail_content">
- </div>
- <div class="modal-footer">
- <button type="button" class="btn btn-primary" data-dismiss="modal">Close
- </button>
- </div>
- </div>
-
- </div>
- </div>
-{% endblock content %}
-
-{% block extrajs %}
- <script type="text/javascript">
- var bookings_url = "{% url 'booking:bookings_json' resource_id=resource.id %}";
- var booking_detail_prefix = "{% url 'booking:detail_prefix' %}";
- var booking_delete_prefix = "{% url 'booking:delete_prefix' %}";
- var user_timezone = "{{ request.user.userprofile.timezone }}"
- {% autoescape off %}
- var sup_installer_dict = {{ installer_filter }}
- var sup_scenario_dict = {{ scenario_filter }}
- {% endautoescape %}
-
- </script>
- <script type="text/javascript">
- $(document).ready(function () {
- $("#id_installer").children().hide();
- $("#id_scenario").children().hide();
- });
-
- function installerHider() {
- dropFilter("id_installer", sup_installer_dict, "id_opsys");
- scenarioHider();
- }
- document.getElementById('id_opsys').addEventListener('change', installerHider);
-
- function scenarioHider() {
- dropFilter("id_scenario", sup_scenario_dict, "id_installer");
- }
- document.getElementById('id_installer').addEventListener('change', scenarioHider);
-
- function dropFilter(target, target_filter, master) {
- ob = document.getElementById(target);
-
- for(var i=0; i<ob.options.length; i++) {
- if ( ob.options[i].text == '---------' ) {
- ob.selectedIndex = i;
- }
- }
-
- targ_id = "#" + target;
- $(targ_id).children().hide();
- var drop = document.getElementById(master);
- var opts = target_filter[drop.options[drop.selectedIndex].value];
- if (!opts) {
- opts = {};
- }
- var emptyMap = {}
-
- var map = Object.create(null);
- for (var i = 0; i < opts.length; i++) {
- var j = opts[i];
- map[j] = true;
- }
-
- for (var i = 0; i < document.getElementById(target).childNodes.length; i++) {
- if (document.getElementById(target).childNodes[i].value in opts && !(document.getElementById(target).childNodes[i].value in emptyMap) ) {
- document.getElementById(target).childNodes[i].style.display = "inherit";
- }
- }
- }
-
- </script>
- <script src={% static "bower_components/moment/moment.js" %}></script>
- <script src={% static "bower_components/fullcalendar/dist/fullcalendar.js" %}></script>
- <script type="text/javascript"
- src={% static "bower_components/eonasdan-bootstrap-datetimepicker/build/js/bootstrap-datetimepicker.min.js" %}></script>
- <script src={% static "js/fullcalendar-options.js" %}></script>
- <script src={% static "js/datetimepicker-options.js" %}></script>
- <script src={% static "js/booking-calendar.js" %}></script>
-{% endblock extrajs %}
diff --git a/src/templates/base/booking/booking_delete.html b/src/templates/base/booking/booking_delete.html
deleted file mode 100644
index 4afa370..0000000
--- a/src/templates/base/booking/booking_delete.html
+++ /dev/null
@@ -1,10 +0,0 @@
-{% load bootstrap4 %}
-
-<p>
- Really delete Booking from {{ booking.start}} to {{ booking.end }}?
-</p>
-<p>
- <div id="booking_delete_form_div">
- <a href="{% url 'booking:delete_booking' booking_id=booking.id %}" class="btn btn btn-danger">Delete Booking</a>
- </div>
-</p> \ No newline at end of file
diff --git a/src/templates/base/booking/booking_detail.html b/src/templates/base/booking/booking_detail.html
deleted file mode 100644
index 70958f6..0000000
--- a/src/templates/base/booking/booking_detail.html
+++ /dev/null
@@ -1,373 +0,0 @@
-{% extends "base.html" %}
-{% load staticfiles %}
-{% load bootstrap4 %}
-
-{% block extrahead %}
- {{block.super}}
- <script src="https://cdn.rawgit.com/google/code-prettify/master/loader/run_prettify.js?lang=yaml"></script>
-{% endblock %}
-
-<style>
-code {
- overflow: scroll;
-}
-</style>
-
-{% block content %}
-<div class="row">
- <div class="col-12 col-lg-5">
- <div class="card mb-3">
- <div class="card-header d-flex">
- <h4 class="d-inline">Overview</h4>
- <button data-toggle="collapse" data-target="#panel_overview" class="btn btn-outline-secondary ml-auto">Expand</button>
- </div>
- <div class="collapse show" id="panel_overview">
- <table class="table m-0">
- <tr>
- <td>Username</td>
- <td>{{ posix_username }}</td>
- </tr>
- <tr>
- <td>Purpose</td>
- <td>{{ booking.purpose }}</td>
- </tr>
- <tr>
- <td>Project</td>
- <td>{{ booking.project }}</td>
- </tr>
- <tr>
- <td>Start Time</td>
- <td>{{ booking.start }}</td>
- </tr>
- <tr>
- <td>End Time</td>
- <td>{{ booking.end }}</td>
- </tr>
- <tr>
- <td>Pod Definition</td>
- <td>{{ booking.resource.template }}</td>
- </tr>
- <tr>
- <td>Lab Deployed At</td>
- <td>{{ booking.lab }}</td>
- </tr>
- </table>
- </div>
- </div>
- <div class="card my-3">
- <div class="card-header d-flex">
- <h4 class="d-inline">Pod</h4>
- <button data-toggle="collapse" data-target="#pod_panel" class="btn btn-outline-secondary ml-auto">Expand</button>
- </div>
- <div class="collapse show" id="pod_panel">
- <div class="card-body">
- <h4>{{host.bundle.template.copy_of.name}}</h4>
- {% for host in booking.resource.get_resources %}
- <h4>{{host.config.name}}</h4>
- <div class="overflow-auto">
- <table class="table m-0">
- <tr>
- <td>Hostname:</td>
- <td>{{host.config.name}}</td>
- </tr>
- <tr>
- <td>Machine:</td>
- <td>{{host.name}}</td>
- </tr>
- <tr>
- <td>Role:</td>
- <td>{{host.template.opnfvRole}}</td>
- </tr>
- <tr>
- <td>Is Headnode:</td>
- <td>{{host.config.is_head_node}}</td>
- <tr>
- <td>Image:</td>
- <td id="host_image_{{host.id}}">
- {{host.config.image}}
- <button
- class="btn btn-primary ml-4"
- data-toggle="modal"
- data-target="#imageModal"
- onclick="set_image_dropdown('{{host.profile.name}}', {{host.id}});"
- >Change/Reset</button></td>
- </tr>
- <tr>
- <td>RAM:</td>
- <td>{{host.profile.ramprofile.first.amount}}G,
- {{host.profile.ramprofile.first.channels}} channels</td>
- </tr>
- <tr>
- <td>CPU:</td>
- <td>
- <table class="table m-0">
- <tr>
- <td>Arch:</td>
- <td>{{host.profile.cpuprofile.first.architecture}}</td>
- </tr>
- <tr>
- <td>Cores:</td>
- <td>{{host.profile.cpuprofile.first.cores}}</td>
- </tr>
- <tr>
- <td>Sockets:</td>
- <td>{{host.profile.cpuprofile.first.cpus}}</td>
- </tr>
- </table>
- </td>
- </tr>
- <tr>
- <td>DISK:</td>
- <td>
- <table class="table m-0">
- <tr>
- <td>Size:</td>
- <td>{{host.profile.storageprofile.first.size}} GiB</td>
- </tr>
- <tr>
- <td>Type:</td>
- <td>{{host.profile.storageprofile.first.media_type}}</td>
- </tr>
- <tr>
- <td>Mount Point:</td>
- <td>{{host.profile.storageprofile.first.name}}</td>
- </tr>
- </table>
- </td>
- </tr>
- <tr>
- <td>Interfaces:</td>
- <td>
- <table class="table m-0">
- {% for intprof in host.profile.interfaceprofile.all %}
- <tr>
- <td>
- <table class="table table-sm table-borderless m-0">
- <tr>
- <td>Name:</td>
- <td>{{intprof.name}}</td>
- </tr>
- <tr>
- <td>Speed:</td>
- <td>{{intprof.speed}}</td>
- </tr>
- </table>
- </td>
- </tr>
- {% endfor %}
- </table>
- </td>
- </tr>
- </table>
- </div>
- {% endfor %}
- </div>
- </div>
- </div>
- <div class="card my-3">
- <div class="card-header d-flex">
- <h4 class="d-inline">Diagnostic Information</h4>
- <button data-toggle="collapse" data-target="#diagnostics_panel" class="btn btn-outline-secondary ml-auto">Expand</button>
- </div>
- <div class="collapse" id="diagnostics_panel">
- <div class="card-body">
- <table class="table m-0">
- <tr>
- <th>Job ID: </th>
- <td>{{booking.job.id}}</td>
- </tr>
- <tr>
- <th>CI Files</th>
- </tr>
- {% for host in booking.resource.get_resources %}
- <tr>
- <td>
- <table class="table m-0">
- <tr>
- <th>Host:</th>
- <td>{{host.name}}</td>
- </tr>
- <tr>
- <th>Configs:</th>
- </tr>
- {% for ci_file in host.config.cloud_init_files.all %}
- <tr>
- <td>{{ci_file.id}}</td>
- <td>
- <div class="modal fade" id="ci_file_modal_{{ci_file.id}}" tabindex="-1" role="dialog" aria-hidden="true">
- <div class="modal-dialog modal-xl" role="document">
- <div class="modal-content">
- <div class="modal-header">
- <h4 class="modal-title d-inline float-left">Cloud Config Content</h4>
- <button type="button" class="close" data-dismiss="modal" aria-label="Close">
- <span aria-hidden="true">&times;</span>
- </button>
- </div>
- <div class="card-body">
- <pre class="prettyprint lang-yaml m-0 border-0 text-break pre-wrap">
-{{ci_file.text}}
- </pre>
- </div>
- </div>
- </div>
- </div>
- <button class="btn btn-primary" data-toggle="modal" data-target="#ci_file_modal_{{ci_file.id}}">Show File Content</button>
- </td>
- </tr>
- {% endfor %}
- </table>
- </td>
- </tr>
- {% endfor %}
- </table>
- </div>
- </div>
- </div>
- </div>
- <div class="col">
- <div class="card mb-3">
- <div class="card-header d-flex">
- <h4 class="d-inline">Deployment Progress</h4>
- <p>These are the different tasks that have to be completed before your deployment is ready.
- If this is taking a really long time, let us know <a href="mailto:{{contact_email}}">here!</a></p>
- <button data-toggle="collapse" data-target="#panel_tasks" class="btn btn-outline-secondary ml-auto">Expand</button>
- </div>
- <div class="collapse show" id="panel_tasks">
- <table class="table m-0">
- <tr>
- <th></th>
- <th>Status</th>
- <th>Lab Response</th>
- <th>Type</th>
- </tr>
- {% for task in booking.job.get_tasklist %}
- <tr>
- <td>
- {% if task.status < 100 %}
- <div class="rounded-circle bg-secondary square-20"></div>
- {% elif task.status < 200 %}
- <div class="spinner-border text-primary square-20"></div>
- {% else %}
- <div class="rounded-circle bg-success square-20"></div>
- {% endif %}
- </td>
- <td>
- {% if task.status < 100 %}
- PENDING
- {% elif task.status < 200 %}
- IN PROGRESS
- {% else %}
- DONE
- {% endif %}
- </td>
- <td>
- {% if task.message %}
- {% if task.type_str == "Access Task" and user_id != task.config.user.id %}
- Message from Lab: <pre>--secret--</pre>
- {% else %}
- Message from Lab: <pre class="text-break pre-wrap">{{ task.message }}</pre>
- {% endif %}
- {% else %}
- No response provided (yet)
- {% endif %}
- </td>
- <td>
- {{ task.type_str }}
- </td>
- </tr>
- {% endfor %}
- </table>
- </div>
- </div>
- <div class="card my-3">
- <div class="card-header d-flex">
- <h4 class="d-inline">PDF</h4>
- <button data-toggle="collapse" data-target="#pdf_panel" class="btn btn-outline-secondary ml-auto">Expand</button>
- </div>
- <div class="collapse show" id="pdf_panel">
- <div class="card-body">
- <pre class="prettyprint lang-yaml m-0 border-0 text-break pre-wrap">
-{{pdf}}
- </pre>
- </div>
- </div>
- </div>
- </div>
-</div>
-
-
-<div class="modal fade" id="imageModal" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true">
- <div class="modal-dialog" role="document">
- <div class="modal-content">
- <div class="modal-header">
- <h4 class="modal-title d-inline float-left" id="exampleModalLabel">Host Image</h4>
- <button type="button" class="close" data-dismiss="modal" aria-label="Close">
- <span aria-hidden="true">&times;</span>
- </button>
- </div>
- <div class="modal-body">
- <form id="image_host_form">
- {% csrf_token %}
- <select class="form-control" id="image_select" name="image_id">
- </select>
- <input id="host_id_input" type="hidden" name="host_id">
- </form>
- </div>
- <div class="modal-footer d-flex flex-column">
- <div>
- <button type="button" class="btn btn-outline-secondary" data-dismiss="modal">Close</button>
- <button type="button" class="btn btn-danger" data-toggle="collapse" data-target="#modal_warning" aria-expanded="false">Reset Host</button>
- </div>
- <div class="border-top collapse mt-3 py-2 text-center w-100" id="modal_warning">
- <h3>Are You Sure?</h3>
- <p>This will wipe the disk and reimage the host</p>
- <button class="btn btn-outline-secondary" data-dismiss="modal">Nevermind</button>
- <button class="btn btn-danger" data-dismiss="modal" onclick="submit_image_form();">I'm Sure</button>
- </div>
- </div>
- </div>
- </div>
-</div>
-
-<script>
- var image_mapping = {{image_mapping|safe}};
- var current_host_id = 0;
- function set_image_dropdown(profile_name, host_id) {
- document.getElementById("host_id_input").value = host_id;
- current_host_id = host_id;
- var dropdown = document.getElementById("image_select");
- var length = dropdown.length;
- //clear dropdown
- for(i=length-1; i>=0; i--){
- dropdown.options.remove(i);
- }
- var images = image_mapping[profile_name];
- var image_length = images.length;
- for(i=0; i<image_length; i++){
- var opt = document.createElement("OPTION");
- opt.value = images[i].value;
- opt.appendChild(document.createTextNode(images[i].name));
- dropdown.options.add(opt);
- }
-
- document.getElementById("modal_warning").classList.add("collapse");
- }
-
- function submit_image_form() {
- var ajaxForm = $("#image_host_form");
- var formData = ajaxForm.serialize();
- req = new XMLHttpRequest();
- req.open("POST", "/booking/modify/{{booking.id}}/image/", true);
- req.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
- req.onerror = function() { alert("problem submitting form"); }
- req.onreadystatechange = function() {
- if(req.readyState === 4) {
- node = document.getElementById("host_image_" + current_host_id);
- text = document.createTextNode(req.responseText);
- node.replaceChild(text, node.firstChild);
- }
- }
- req.send(formData);
- }
-</script>
-{% endblock content %}
diff --git a/src/templates/base/booking/booking_grid_item.html b/src/templates/base/booking/booking_grid_item.html
deleted file mode 100644
index 3c72fd2..0000000
--- a/src/templates/base/booking/booking_grid_item.html
+++ /dev/null
@@ -1,11 +0,0 @@
-<p>{{ id|default:"id" }}</p>
-<p>{{ lab|default:"lab" }}</p>
-<p>{{ resource_name|default:"resource name" }}</p>
-<p>{{ start|default:"start" }}</p>
-<p>{{ end|default:"end" }}<p>
-<p>Collaborators:</p>
-<ul>
- {% for collaborator in collaborators %}
- <li>{{ collaborator }}</li>
- {% endfor %}
-</ul>
diff --git a/src/templates/base/booking/booking_list.html b/src/templates/base/booking/booking_list.html
deleted file mode 100644
index 523c84b..0000000
--- a/src/templates/base/booking/booking_list.html
+++ /dev/null
@@ -1,44 +0,0 @@
-{% extends "base.html" %}
-{% load staticfiles %}
-{% load bootstrap4 %}
-
-{% block extrahead %}
- {{ block.super }}
- <!-- DataTables CSS -->
- <link href="{% static "node_modules/datatables.net-bs4/css/dataTables.bootstrap4.min.css" %}"
- rel="stylesheet">
-
- <!-- DataTables Responsive CSS -->
- <link href="{% static "node_modules/datatables.net-responsive-bs4/css/responsive.bootstrap4.min.css" %}"
- rel="stylesheet">
-{% endblock extrahead %}
-
-{% block content %}
- <div class="row">
- <div class="col">
- <div class="panel-body">
- <div class="dataTables_wrapper">
- <table class="table table-striped table-bordered table-hover" id="table"
- cellspacing="0"
- width="100%">
- {% include "booking/booking_table.html" %}
- </table>
- </div>
- </div>
- </div>
- </div>
-{% endblock content %}
-
-{% block extrajs %}
- <!-- DataTables JavaScript -->
- <script src={% static "node_modules/datatables.net/js/jquery.dataTables.min.js" %}></script>
- <script src={% static "node_modules/datatables.net-bs4/js/dataTables.bootstrap4.min.js" %}></script>
-
- <script type="text/javascript">
- $(document).ready(function () {
- $('#table').DataTable({
- scrollX: true,
- });
- });
- </script>
-{% endblock extrajs %}
diff --git a/src/templates/base/booking/booking_table.html b/src/templates/base/booking/booking_table.html
deleted file mode 100644
index b4a713a..0000000
--- a/src/templates/base/booking/booking_table.html
+++ /dev/null
@@ -1,37 +0,0 @@
-
-
-
-<thead>
-<tr class="Anuket-Text">
- <th>Owner</th>
- <th>Purpose</th>
- <th>Project</th>
- <th>Start</th>
- <th>End</th>
- <th>Operating System</th>
-</tr>
-</thead>
-<tbody>
-{% for booking in bookings %}
- <tr class="Anuket-Text">
- <td>
- {{ booking.owner.username }}
- </td>
- <td>
- {{ booking.purpose }}
- </td>
- <td>
- {{ booking.project }}
- </td>
- <td>
- {{ booking.start }}
- </td>
- <td>
- {{ booking.end }}
- </td>
- <td>
- {{ booking.resource.get_head_node.config.image.os.name }}
- </td>
- </tr>
-{% endfor %}
-</tbody>
diff --git a/src/templates/base/booking/quick_deploy.html b/src/templates/base/booking/quick_deploy.html
deleted file mode 100644
index c51e234..0000000
--- a/src/templates/base/booking/quick_deploy.html
+++ /dev/null
@@ -1,157 +0,0 @@
-{% extends "base.html" %}
-{% load staticfiles %}
-{% load bootstrap4 %}
-{% block content %}
-
-<style>
-/* hides images not in use. Not applied globally since doesn't make sense in all cases */
-select option:disabled {
- display:none;
-}
-</style>
-
-{% bootstrap_form_errors form type='non_fields' %}
-<form id="quick_booking_form" action="/booking/quick/" method="POST" class="form class="Anuket-Text"">
- {% csrf_token %}
- <div class="row mx-0 px-0">
- <div class="col-12 mx-0 px-0 mt-2">
- {% block form-text %}
- <p class="my-0">
- Please select a host type you wish to book. Only available types are shown.
- If something isn't working right, let us know <a href="mailto:{{contact_email}}"> here! </a>
- </p>
- {% endblock form-text %}
- {% bootstrap_field form.filter_field show_label=False %}
- </div>
- </div>
- <div class="row justify-content-center">
- <div class="col-12 col-lg-6 my-2">
- <div class="col border rounded py-2 h-100">
- {% bootstrap_field form.purpose %}
- {% bootstrap_field form.project %}
- {% bootstrap_field form.length %}
- <span>Days: </span><output id="daysout">0</output>
- <script>
- document.getElementById("id_length").setAttribute("oninput", "daysout.value=this.value");
- document.getElementById("daysout").value = document.getElementById("id_length").value;
- </script>
- </div>
- </div>
- {% block collab %}
- <div class="col-12 col-lg-6 my-2">
- <div class="col border rounded py-2 h-100">
- <label>Collaborators</label>
- {{ form.users }}
- </div>
- </div>
- {% endblock collab %}
- </div>
- <div class="row justify-content-center">
- <div class="col-12 col-lg-6 my-2">
- <div class="col border rounded py-2 h-100">
- {% bootstrap_field form.hostname %}
- {% bootstrap_field form.image %}
- </div>
- </div>
- <div class="col-12 col-lg-6 my-2">
- <div class="col border rounded py-2 h-100">
- {% bootstrap_field form.global_cloud_config %}
- </div>
- </div>
- <div class="col-12 d-flex mt-2 justify-content-end">
- <button id="quick_booking_confirm" onclick="submit_form();" type="button" class="btn btn-success">Confirm</button>
- </div>
- </div>
-</form>
-{% block image_script %}
-{% endblock image_script %}
-
-<script type="text/javascript">
- function submit_form()
- {
- run_form_callbacks();
- document.getElementById("quick_booking_form").submit();
- }
-
- function hide_dropdown(drop_id) {
- var drop = document.getElementById(drop_id);
- //select 'blank' option
- for( var i=0; i < drop.length; i++ )
- {
- if ( drop.options[i].text == '---------' )
- drop.selectedIndex = i;
- }
- }
-
- function get_selected_value(key){
- for( var attr in multi_filter_widget.result[key] ){
- if(!(attr in {}) )
- return attr;
- }
- }
-
- $(document).ready(function() {
- $('.has-popover').popover({'trigger':'hover'});
- });
-
- var sup_image_dict = {{image_filter | safe}};
- var sup_installer_dict = {{installer_filter | safe}};
- var sup_scenario_dict = {{scenario_filter | safe}};
- var resource_profile_map = {{resource_profile_map | safe}};
-
- function imageFilter() {
- var drop = document.getElementById("id_image");
- var lab_pk = get_selected_value("lab");
- var profile_pk = get_selected_value("resource");
-
- for (const childNode of drop.childNodes) {
- var image_object = sup_image_dict[childNode.value];
- if (image_object) //weed out empty option
- {
- console.log("image object:");
- console.log(image_object);
- const img_at_lab = image_object.lab == lab_pk;
- const profiles = resource_profile_map[profile_pk];
- console.log("profiles are:");
- console.log(profiles);
- console.log("profile map is:");
- console.log(resource_profile_map);
- console.log("host profile is" + image_object.architecture);
- const img_in_template = profiles && profiles.indexOf(image_object.architecture) > -1
- childNode.disabled = !img_at_lab || !img_in_template;
- }
- }
- }
-
- imageFilter();
-
- Array.from(document.getElementsByClassName("grid-item-select-btn")).forEach(function (element) {
- element.addEventListener('click', imageFilter);
- });
-
- function dropFilter(target, target_filter, master) {
- var dropdown = document.getElementById(target);
-
- hide_dropdown(target);
-
- var drop = document.getElementById(master);
- var opts = target_filter[drop.options[drop.selectedIndex].value];
- if (!opts) {
- opts = {};
- }
-
- var map = Object.create(null);
- for (var i = 0; i < opts.length; i++) {
- var j = opts[i];
- map[j] = true;
- }
-
- for (var i = 0; i < dropdown.childNodes.length; i++) {
- if (dropdown.childNodes[i].value in opts && !(dropdown.childNodes[i].value in {})) {
- dropdown.childNodes[i].style.display = "inherit";
- dropdown.childNodes[i].disabled = false;
- }
- }
- }
-</script>
-{% endblock %}
diff --git a/src/templates/base/booking/stats.html b/src/templates/base/booking/stats.html
deleted file mode 100644
index 3429acf..0000000
--- a/src/templates/base/booking/stats.html
+++ /dev/null
@@ -1,288 +0,0 @@
-{% extends "base.html" %}
-{% load staticfiles %}
-
-{% block content %}
-<div class="row">
- <div class="col-lg-12">
- <div class="card">
- <div class="card-header no-border-bottom">
- <h2 class="card-title">Booking Statistics</h2>
- </div>
- <div class="card-content">
- <div class="card-body">
- <div class="row justify-content-md-center">
- <div class="col-lg-4">
- <div class="container">
- <canvas id="util-gauge"></canvas>
- </div>
- </div>
- <div class="col-4 border-left border-right">
- <div class="container">
- <canvas id="resources-time-series"></canvas>
- </div>
- </div>
- <div class="col-lg-4">
- <div class="container">
- <canvas id="project-chart"></canvas>
- </div>
- </div>
- </div>
- <div class="row border-top">
- <div class="col-6">
- <div class="container">
- <canvas id="booking-time-series"></canvas>
- </div>
- </div>
- <div class="col-6">
- <div class="container">
- <canvas id="users-time-series"></canvas>
- </div>
- </div>
- </div>
- </div>
- </div>
- </div>
- </div>
-</div>
-
-<script src="https://cdn.jsdelivr.net/npm/chart.js@2.9.3/dist/Chart.min.js"></script>
-<script>
-
- function processTimeSeriesData(data_dict, colors) {
- let output = [];
- let i = 0;
-
- for (let key in data_dict) {
- output.push({
- label: key,
- data: data_dict[key][1],
- steppedLine: true,
- fill: false,
- backgroundColor: colors[i],
- borderColor: colors[i]
- });
-
- i += 1;
- }
-
- return output
- }
-
- let booking_chart = document.getElementById('booking-time-series').getContext('2d');
- let users_chart = document.getElementById('users-time-series').getContext('2d');
- let util_chart = document.getElementById('util-gauge').getContext('2d');
- let project_chart = document.getElementById('project-chart').getContext('2d');
- let resources_chart = document.getElementById('resources-time-series').getContext('2d');
-
- let data = {{data | safe}};
- let booking = data['booking'];
- let users = data['user'];
- let projects = data['projects'];
- let colors = data['colors'];
-
- let primary_color = colors[0];
- let secondary_color = colors[1];
- let accent_color = colors[2];
-
- let booking_config = {
- type: 'line',
- data: {
- labels: booking[0],
- datasets: [{
- label: 'Bookings',
- data: booking[1],
- steppedLine: true,
- fill: false,
- backgroundColor: primary_color,
- borderColor: primary_color
- }]
- },
- options: {
- responsive: true,
- interaction: {
- intersect: false,
- axis: 'x'
- },
- title: {
- display: true,
- text: 'Active Bookings'
- },
- legend: {
- display: true
- },
- elements: {
- point: {
- radius: 0
- }
- }
- }
- };
-
- let resources_config = {
- type: 'line',
- data: {
- labels: booking[0],
- datasets: processTimeSeriesData(data['resources'], colors)
- },
- options: {
- responsive: true,
- interaction: {
- intersect: false,
- axis: 'x'
- },
- title: {
- display: true,
- text: 'Booked Resources'
- },
- legend: {
- display: true
- },
- transitions: {
- show: {
- animations: {
- x: {
- from: 100
- },
- y: {
- from: 1
- }
- }
- },
- hide: {
- animations: {
- x: {
- to: 0
- },
- y: {
- to: 100
- }
- }
- }
- },
- elements: {
- point: {
- radius: 0
- }
- }
- }
- };
-
- let users_config = {
- type: 'line',
- data: {
- labels: users[0],
- datasets: [{
- label: 'Users',
- data: users[1],
- fill: false,
- steppedLine: true,
- backgroundColor: primary_color,
- borderColor: primary_color
- }]
- },
- options: {
- responsive: true,
- interaction: {
- intersect: false,
- axis: 'x'
- },
- legend: {
- display: true
- },
- title: {
- display: true,
- text: 'Active Users'
- },
- elements: {
- point: {
- radius: 0
- }
- }
- }
- };
-
- let utilization_config = {
- type:"doughnut",
- data: {
- labels : ["In Use","Not In Use","Maitenance"],
- datasets: [{
- label: 'Lab Utilization',
- data : [data['utils'][0], data['utils'][1], data['utils'][2]],
- backgroundColor: [
- primary_color,
- secondary_color,
- accent_color,
- ]
- }]
- },
- options: {
- circumference: Math.PI,
- rotation : Math.PI,
- cutoutPercentage : 80,
- plugins: {
- datalabels: {
- backgroundColor: primary_color,
- borderColor: secondary_color,
- align: 'start',
- anchor: 'start',
- offset: 10,
- borderRadius: 4,
- borderWidth: 1,
- }
- },
- legend: {
- display: false
- },
- tooltips: {
- enabled: true
- },
- title: {
- display: true,
- text: "Lab Resources Utilization"
- }
- }
- };
-
- let project_config = {
- type: 'bar',
- data: {
- labels: projects[0],
- datasets:[{
- label: 'Projects',
- data: projects[1],
- backgroundColor: primary_color,
- borderColor: secondary_color
- }]
- },
- options: {
- scales: {
- yAxes: [{
- ticks: {
- beginAtZero: true
- }
- }]
- },
- elements: {
- bar: {
- borderWidth: 2,
- }
- },
- responsive: true,
- legend: {
- display: false,
- position: 'right'
- },
- title: {
- display: true,
- text: 'Top Represented Projects'
- }
- }
- };
-
- let bookingChart = new Chart(booking_chart, booking_config);
- let usersChart = new Chart(users_chart, users_config);
- let utilGauge = new Chart(util_chart, utilization_config);
- let projectBars = new Chart(project_chart, project_config);
- let resourceChart = new Chart(resources_chart, resources_config);
-</script>
-{% endblock content %}
diff --git a/src/templates/base/booking/steps/booking_meta.html b/src/templates/base/booking/steps/booking_meta.html
deleted file mode 100644
index f12496e..0000000
--- a/src/templates/base/booking/steps/booking_meta.html
+++ /dev/null
@@ -1,38 +0,0 @@
-{% extends "workflow/viewport-element.html" %}
-{% load staticfiles %}
-
-{% load bootstrap4 %}
-
-{% block content %}
-
-{% bootstrap_form_errors form type='non_fields' %}
-<form id="step_form" method="POST" class="form">
-{% csrf_token %}
-<div id="form_div" class="container-fluid">
- <div class="row">
- <div class="p-4 col">
- {% bootstrap_field form.purpose %}
- {% bootstrap_field form.project %}
- {% bootstrap_field form.length %}
- <span>Days: </span><output id="daysout">0</output>
- <script>
- document.getElementById("id_length").oninput = function() { daysout.value=this.value; }
- document.getElementById("daysout").value = document.getElementById("id_length").value;
- </script>
- {% bootstrap_field form.info_file %}
- <p>You must provide a url to your project's INFO.yaml file if you are a PTL and you are trying to book a POD with multiple servers in it.</p>
- {% bootstrap_field form.deploy_opnfv %}
- </div>
- <div class="p-4 col">
- <p>You may add collaborators on your booking to share resources with coworkers.</p>
- {% bootstrap_field form.users label="Collaborators" %}
- </div>
- </div>
- <div class="panel_wrap">
- {% buttons %}
- <button type="submit" class="btn btn-success d-none">Confirm</button>
- {% endbuttons %}
- </div>
-</div>
-</form>
-{% endblock content %}
diff --git a/src/templates/base/config_bundle/steps/assign_host_roles.html b/src/templates/base/config_bundle/steps/assign_host_roles.html
deleted file mode 100644
index b87a17f..0000000
--- a/src/templates/base/config_bundle/steps/assign_host_roles.html
+++ /dev/null
@@ -1,22 +0,0 @@
-{% extends "config_bundle/steps/table_formset.html" %}
-
-{% load bootstrap4 %}
-
-{% block table %}
-<thead>
- <tr>
- <th>Host</th>
- <th>Role</th>
- </tr>
-</thead>
-<tbody>
- {% for form in formset %}
- <tr>
- <td>{% bootstrap_field form.host_name show_label=False %}</td>
- <td>{% bootstrap_field form.role show_label=False %}</td>
- </tr>
- {% endfor %}
-</tbody>
-
-{{formset.management_form}}
-{% endblock table %}
diff --git a/src/templates/base/config_bundle/steps/assign_network_roles.html b/src/templates/base/config_bundle/steps/assign_network_roles.html
deleted file mode 100644
index aa1df44..0000000
--- a/src/templates/base/config_bundle/steps/assign_network_roles.html
+++ /dev/null
@@ -1,22 +0,0 @@
-{% extends "config_bundle/steps/table_formset.html" %}
-
-{% load bootstrap4 %}
-
-{% block table %}
-<thead>
- <tr>
- <th>Role</th>
- <th>Network</th>
- </tr>
-</thead>
-<tbody>
- {% for form in formset %}
- <tr>
- <td>{% bootstrap_field form.role show_label=False %}</td>
- <td>{% bootstrap_field form.network show_label=False %}</td>
- </tr>
- {% endfor %}
-</tbody>
-
-{{formset.management_form}}
-{% endblock table %}
diff --git a/src/templates/base/config_bundle/steps/config_software.html b/src/templates/base/config_bundle/steps/config_software.html
deleted file mode 100644
index 7e8b25d..0000000
--- a/src/templates/base/config_bundle/steps/config_software.html
+++ /dev/null
@@ -1,15 +0,0 @@
-{% extends "workflow/viewport-element.html" %}
-{% load staticfiles %}
-
-{% load bootstrap4 %}
-
-{% block content %}
-
-<form method="POST" id="step_form" class="form">
- {% csrf_token %}
- {% bootstrap_field form.name %}
- {% bootstrap_field form.description %}
-</form>
-
-
-{% endblock content %}
diff --git a/src/templates/base/config_bundle/steps/define_software.html b/src/templates/base/config_bundle/steps/define_software.html
deleted file mode 100644
index b61cb1c..0000000
--- a/src/templates/base/config_bundle/steps/define_software.html
+++ /dev/null
@@ -1,44 +0,0 @@
-{% extends "config_bundle/steps/table_formset.html" %}
-
-{% load bootstrap4 %}
-
-{% block table %}
- <thead>
- <tr>
- <th>Device</th>
- <th>Image</th>
- <th>HeadNode</th>
- </tr>
- </thead>
- <tbody>
-{% for form in formset %}
- <tr>
- <td>{% bootstrap_field form.host_name show_label=False %}</td>
- <td>{% bootstrap_field form.image show_label=False %}</td>
- <td class="table_hidden_input_parent">
- <input id="radio_{{forloop.counter}}" class="my_radio" type="radio" name="headnode" value="{{forloop.counter}}">
- {{ form.headnode }}
- </td>
- </tr>
-{% endfor %}
-{{formset.management_form}}
-
-{% endblock table %}
-
-{% block tablejs %}
-<script>
-
- function radio_pre_submit(){
- var parents = document.getElementsByClassName("table_hidden_input_parent");
- for(const node of parents){
- const radio = node.getElementsByClassName("my_radio")[0];
- const checkbox = radio.nextElementSibling;
- if(radio.checked){
- checkbox.value = "True";
- }
- }
- }
-
- form_submission_callbacks.push(radio_pre_submit);
-</script>
-{% endblock tablejs %}
diff --git a/src/templates/base/config_bundle/steps/pick_installer.html b/src/templates/base/config_bundle/steps/pick_installer.html
deleted file mode 100644
index c3b505d..0000000
--- a/src/templates/base/config_bundle/steps/pick_installer.html
+++ /dev/null
@@ -1,22 +0,0 @@
-{% extends "workflow/viewport-element.html" %}
-{% load staticfiles %}
-
-{% load bootstrap4 %}
-
-{% block content %}
-
-{% if unavailable %}
-<h1>Please choose a config bundle first</h1>
-{% else %}
-
-<form id="step_form" method="POST" class="form">
- {% csrf_token %}
- <p>Choose your installer:</p>
- {% bootstrap_field form.installer %}
- <p>Choose your scenario:</p>
- {% bootstrap_field form.scenario %}
-</form>
-
-{% endif %}
-
-{% endblock content %}
diff --git a/src/templates/base/config_bundle/steps/table_formset.html b/src/templates/base/config_bundle/steps/table_formset.html
deleted file mode 100644
index 3bee6af..0000000
--- a/src/templates/base/config_bundle/steps/table_formset.html
+++ /dev/null
@@ -1,53 +0,0 @@
-{% extends "workflow/viewport-element.html" %}
-{% load staticfiles %}
-
-{% load bootstrap4 %}
-
-{% block extrahead %}
- <!-- DataTables CSS -->
- <link href="{% static "node_modules/datatables.net-bs4/css/dataTables.bootstrap4.min.css" %}"
- rel="stylesheet">
-
- <!-- DataTables Responsive CSS -->
- <link href="{% static "node_modules/datatables.net-responsive-bs4/css/responsive.bootstrap4.min.css" %}"
- rel="stylesheet">
-{% endblock extrahead %}
-
-{% block content %}
-{% if error %}
- <h1 class="text-center">{{ error }}</h1>
-{% else %}
-<div class="p-2">
- <form method="post" class="form" id="step_form"> <!-- formset, requires special consideration -->
- {% csrf_token %}
-
- <div class="row">
- <div class="col-lg-12">
- <div class="dataTables_wrapper">
- <table class="table table-striped table-bordered table-hover" id="table" cellspacing="0" width="100%">
-
- {% block table %}
- {% endblock table %}
-
- </table>
- </div>
- </div>
- </div>
- </form>
-</div>
-
-{% endif %}
-{% endblock content %}
-
-{% block extrajs %}
- {{ block.super }}
- <!-- DataTables JavaScript -->
-
- <script src={% static "node_modules/datatables.net/js/jquery.dataTables.min.js" %}></script>
- <script src={% static "node_modules/datatables.net-bs4/js/dataTables.bootstrap4.min.js" %}></script>
-
- <script src={% static "js/dataTables-sort.js" %}></script>
-
- {% block tablejs %}
- {% endblock tablejs %}
-{% endblock extrajs %}
diff --git a/src/templates/base/dashboard/genericselect.html b/src/templates/base/dashboard/genericselect.html
deleted file mode 100644
index 863d33f..0000000
--- a/src/templates/base/dashboard/genericselect.html
+++ /dev/null
@@ -1,30 +0,0 @@
-{% extends "workflow/viewport-element.html" %}
-
-{% load bootstrap4 %}
-
-{% block content %}
-
-<div id="select_form_div" class="h-100 border d-flex flex-column p-4">
- <h3 id="create_section">Create a Resource
- <button class="btn btn-primary {% if disabled %} disabled {% endif %}"
- {% if not disabled %}onclick="add_workflow({{addable_type_num}})"
- {% endif %}>Here
- </button>
- </h3>
- <div class="border-top"></div>
- <h3 id="select_header_section">Or select from the list below:</h3>
- <div id="select_section" class="d-flex flex-column">
- <form id="step_form" method="post" action="" class="form d-flex flex-column">
- {% csrf_token %}
- {{ form|default:"<p>no form loaded</p>" }}
- </form>
- </div>
-</div>
-
-<script>
- {% if disabled %}
- disable();
- {% endif %}
-</script>
-
-{% endblock content %}
diff --git a/src/templates/base/dashboard/host_profile_detail.html b/src/templates/base/dashboard/host_profile_detail.html
deleted file mode 100644
index f65d4fe..0000000
--- a/src/templates/base/dashboard/host_profile_detail.html
+++ /dev/null
@@ -1,70 +0,0 @@
-{% extends "base.html" %}
-{% load staticfiles %}
-
-{% block extrahead %}
- {{block.super}}
- <script src="https://cdn.rawgit.com/google/code-prettify/master/loader/run_prettify.js?lang=yaml"></script>
-<script src="https://unpkg.com/masonry-layout@4/dist/masonry.pkgd.min.js"></script>
-
-{% endblock %}
-
-{% block content %}
-<script type="text/javascript">
- $('.grid').masonry(
- {
- itemSelector: '.grid-item',
- columnWidth: '.grid-sizer',
- percentPosition: true
- }
- );
-</script>
-<style media="screen">
-
- @media screen and (min-width: 0px) and (max-width: 767px)
- {
-
- .grid-item
- {
- width: 100%;
- }
-}
- @media screen and (min-width: 768px) and (max-width: 1200px) {
- .grid-item {
- width: 50%;
- }
-
- }
-
- @media screen and (min-width: 1201px) and (max-width: 2000px) {
- .grid-item {
- width: 33%;
- }
- }
- @media screen and (min-width: 2001px) {
- .grid-item {
- width: 25%;
- }
-
- }
- .grid-item-content {
- height: 100px;
- background: #D26;
- border: 2px solid hsla(0, 0%, 0%, 0.5);
- border-radius: 5px;
-}
-
-.grid-item-content--height2 { height: 200px; }
-</style>
-
-<div class="container-fluid">
- <!-- add extra container element for Masonry -->
- <div class="grid">
- {% for host in hosts %}
- <div class="grid-item col-xs-4">
- <p>stub for current PR</p>
- </div>
- {% endfor %}
- </div>
-</div>
-
-{% endblock content %}
diff --git a/src/templates/base/dashboard/idf.yaml b/src/templates/base/dashboard/idf.yaml
deleted file mode 100644
index 9e0cc26..0000000
--- a/src/templates/base/dashboard/idf.yaml
+++ /dev/null
@@ -1,46 +0,0 @@
----
-idf:
- version: {{version|default:"0.1"}}
- net_config:
- oob:
- ip-range: {{net_config.oob.ip_range}}
- vlan: {{net_config.oob.vlan}}
- admin:
- interface: {{net_config.admin.interface}}
- vlan: {{net_config.admin.vlan}}
- network: {{net_config.admin.network}}
- mask: {{net_config.admin.mask}}
- mgmt:
- interface: {{net_config.mgmt.interface}}
- vlan: {{net_config.mgmt.vlan}}
- network: {{net_config.mgmt.network}}
- mask: {{net_config.mgmt.mask}}
- private:
- interface: {{net_config.private.interface}}
- vlan: {{net_config.private.vlan}}
- network: {{net_config.private.network}}
- mask: {{net_config.private.mask}}
- public:
- interface: {{net_config.public.interface}}
- vlan: {{net_config.public.vlan}}
- network: {{net_config.public.network}}
- mask: {{net_config.public.mask}}
- ip-range: {{net_config.public.ip_range}}
- mask: {{net_config.public.mask}}
- gateway: {{net_config.public.gateway}}
- dns: {% for serv in net_config.public.dns %}
- - {{serv}}{% endfor %}
- fuel:
- jumphost:
- bridges:
- admin: {{fuel.jumphost.bridges.admin}}
- mgmt: {{fuel.jumphost.bridges.mgmt}}
- private: {{fuel.jumphost.bridges.private}}
- public: {{fuel.jumphost.bridges.public}}
- network: {% for node in fuel.network.nodes %}
- node:
- - interfaces: {% for iface in node.interfaces %}
- - {{ iface }}{% endfor %}
- - busaddr: {% for addr in node.bus_addrs %}
- - {{addr}}{% endfor %}
- {% endfor %}
diff --git a/src/templates/base/dashboard/lab_detail.html b/src/templates/base/dashboard/lab_detail.html
deleted file mode 100644
index 3d90a51..0000000
--- a/src/templates/base/dashboard/lab_detail.html
+++ /dev/null
@@ -1,165 +0,0 @@
-{% extends "base.html" %}
-{% load staticfiles %}
-
-{% block extrahead %}
- {{block.super}}
- <script src="https://cdn.rawgit.com/google/code-prettify/master/loader/run_prettify.js?lang=yaml"></script>
-{% endblock %}
-
-{% block content %}
-<div class="row">
- <div class="col-lg-4">
- <div class="card mb-3">
- <div class="card-header d-flex">
- <h4>Lab Profile</h4>
- <button class="btn btn-outline-secondary ml-auto" data-toggle="collapse" data-target="#panel_overview">Expand</button>
- </div>
- <div class="collapse show" id="panel_overview">
- <div class="overflow-auto">
- <table class="table m-0">
- <tr>
- <td>Lab Name: </td>
- <td>{{lab.name}}</td>
- </tr>
- <tr>
- <td>Lab Location: </td>
- <td>{{lab.location}}</td>
- </tr>
- <tr>
- <td>Lab Email: </td>
- <td>{{lab.contact_email}}</td>
- </tr>
- {% if lab.contact_phone %}
- <tr>
- <td>Lab Phone: </td>
- <td>{{lab.contact_phone}}</td>
- </tr>
- {% endif %}
- <tr>
- <td>Lab Status: </td>
- {% if lab.status < 100 %}
- <td>
- <div class="rounded-circle bg-success square-20 d-inline-block"></div>
- Up
- </td>
- {% elif lab.status < 200 %}
- <td>
- <div class="rounded-circle bg-warning square-20 d-inline-block"></div>
- Temporarily Offline
- </td>
- {% else %}
- <td>
- <div class="rounded-circle bg-danger square-20 d-inline-block"></div>
- Offline Indefinitely
- </td>
- {% endif %}
- </tr>
- </table>
- </div>
- </div>
- </div>
- <div class="card my-3">
- <div class="card-header d-flex">
- <h4 class="d-inline-block">Host Profiles</h4>
- <button data-toggle="collapse" data-target="#profile_panel" class="btn btn-outline-secondary ml-auto">Expand</button>
- </div>
- <div class="collapse show" id="profile_panel">
- <div class="overflow-auto">
- <table class="table m-0">
- {% for profile in hostprofiles %}
- <tr>
- <td>{{profile.name}}</td>
- <td>{{profile.description}}</td>
- <td><a href="/resource/profiles/{{ profile.id }}" class="btn btn-info">Profile</a></td>
- </tr>
- {% endfor %}
- </table>
- </div>
- </div>
- </div>
-
- <div class="card my-3">
- <div class="card-header d-flex">
- <h4 class="d-inline">Networking Capabilities</h4>
- <button data-toggle="collapse" data-target="#network_panel" class="btn btn-outline-secondary ml-auto">Expand</button>
- </div>
-
- <div class="collapse show" id="network_panel">
- <table class="table m-0">
- <tr>
- <td>Block Size: (number of VLANs allowed per deployment)</td><td>{{lab.vlan_manager.block_size}}</td>
- </tr>
- <tr>
- <td>Overlapping Vlans Allowed (user can pick which VLANs they wish to use): </td>
- <td>{{lab.vlan_manager.allow_overlapping|yesno:"Yes,No"}}</td>
- </tr>
- </table>
- </div>
- </div>
- <div class="card my-3">
- <div class="card-header d-flex">
- <h4>Images</h4>
- <button data-toggle="collapse" data-target="#image_panel" class="btn btn-outline-secondary ml-auto">Expand</button>
- </div>
- <div class="collapse show" id="image_panel">
- <div class="overflow-auto">
- <table class="table m-0">
- <tr>
- <th>Name</th>
- <th>Owner</th>
- <th>For Host Type</th>
- <th>Description</th>
- </tr>
- {% for image in images %}
- <tr>
- <td>{{image.name}}</td>
- <td>{{image.owner}}</td>
- <td>{{image.host_type}}</td>
- <td>{{image.description}}</td>
- </tr>
- {% endfor %}
- </table>
- </div>
- </div>
- </div>
-
- </div>
- <div class="col-lg-8">
- <div class="card mb-3">
- <div class="card-header d-flex">
- <h4>Lab Hosts</h4>
- <button data-toggle="collapse" data-target="#lab_hosts_panel" class="btn btn-outline-secondary ml-auto">Expand</button>
- </div>
-
- <div class="collapse show" id="lab_hosts_panel">
- <table class="table m-0">
- <tr>
- <th>Name</th>
- <th>Profile</th>
- <th>Booked</th>
- <th>Working</th>
- <th>Vendor</th>
- </tr>
- {% for host in hosts %}
- <tr>
- <td>{{host.name}}</td>
- <td>{{host.profile}}</td>
- <td>{{host.booked|yesno:"Yes,No"}}</td>
- {% if host.working %}
- <td class="bg-success text-white">Yes</td>
- {% else %}
- <td class="bg-danger text-white">No</td>
- {% endif %}
- <td>{{host.vendor}}</td>
- </tr>
- {% endfor %}
- </table>
- </div>
- </div>
- </div>
-
-</div>
-
-
-{% endblock content %}
-
diff --git a/src/templates/base/dashboard/lab_list.html b/src/templates/base/dashboard/lab_list.html
deleted file mode 100644
index ba627bc..0000000
--- a/src/templates/base/dashboard/lab_list.html
+++ /dev/null
@@ -1,29 +0,0 @@
-{% extends "base.html" %}
-{% block content %}
-<div class="row">
- {% for lab in labs %}
- <div class="col-12 col-md-6 col-lg-4 col-xl-3 mb-3">
- <div class="card h-100">
- <div class="card-header">
- <h3 class="mt-2">{{lab.name}}</h3>
- </div>
- <ul class="list-group list-group-flush h-100">
- <li class="list-group-item">name: {{lab.name}}</li>
- <li class="list-group-item">description: {{lab.description}}</li>
- <li class="list-group-item">location: {{lab.location}}</li>
- {% if lab.status == 0 %}
- <li class="list-group-item">status: Up</li>
- {% elif lab.status == 100 %}
- <li class="list-group-item">status: Down for Maintenance</li>
- {% elif lab.status == 200 %}
- <li class="list-group-item">status: Down</li>
- {% endif %}
- </ul>
- <div class="card-footer">
- <a class="btn btn-primary w-100" href="/lab/{{lab.name}}/">Details</a>
- </div>
- </div>
- </div>
- {% endfor %}
-</div>
-{% endblock %} \ No newline at end of file
diff --git a/src/templates/base/dashboard/landing.html b/src/templates/base/dashboard/landing.html
deleted file mode 100644
index 8d6a8f7..0000000
--- a/src/templates/base/dashboard/landing.html
+++ /dev/null
@@ -1,101 +0,0 @@
-{% extends "base.html" %}
-{% load staticfiles %}
-
-{% block content %}
-
-<div class="text-center">
- {% if not request.user.is_anonymous %}
- {% if not request.user.userprofile.ssh_public_key %}
- <div class="alert alert-danger alertAnuket" role="alert">
- <b>Warning: you need to upload an ssh key under <a href="/accounts/settings" class="inTextLink" >account settings</a> if you wish to
- log into the servers you book</b>
- </div>
- {% endif %}
- {% else %}
- {% endif %}
-</div>
-{% csrf_token %}
-
-<div class="row">
- <div class="col-12 col-lg-6 mb-4">
- <!-- About us -->
- <h2 class="border-bottom">About Us</h2>
- {% block about_us %}
- <p>Here is some information about us!</p>
- {% endblock about_us %}
- {% block welcome_back %}
- {% if user.is_authenticated %}
- <h2 class="border-bottom">Welcome Back!</h2>
- {% if bookings %}
- <h5> These are your current bookings: </h5>
- <ul style="list-style: none;">
- {% for book in bookings %}
- <li><a href="/booking/detail/{{ book.id }}/">{{ book.purpose }}</a></li>
- {% endfor %}
- </ul>
- {% else %}
- <h5> You have no current bookings <h5>
- {% endif %}
- {% endif %}
- {% endblock welcome_back %}
- </div>
-
- <!-- Get started -->
- <div class="col-12 col-lg-6 mb-4">
- <h2 class="border-bottom">Get Started</h2>
- {% if request.user.is_anonymous %}
- {% if LFID %}
- <h4 class="text-center">
- To get started, please log in with <a href="{% url 'oidc_authentication_init' %}" class="inTextLink">Linux Foundation ID</a>
- </h4>
- {% else %}
- <h4 class="text-center">
- To get started, please log in with your <a href="/accounts/login" class="inTextLink">Linux Foundation Jira account</a>
- </h4>
- {% endif %}
- {% else %}
- {% block btnGrp %}
- <p>To get started, book a server below:</p>
- <a class="btn btn-primary btn-lg d-flex flex-column justify-content-center align-content-center border p-4 btnAnuket" href="/booking/quick/" >
- Book a Resource
- </a>
- <p class="mt-4">PTLs can use our advanced options to book multi-node pods. If you are a PTL, you may use the options
- below:
- </p>
- <div class="btn-group-vertical w-100">
- <button class="btn btn-primary btnAnuket" onclick="create_workflow(0)">Book a Pod</button>
- <button class="btn btn-primary btnAnuket" onclick="create_workflow(1)">Design a Pod</button>
- </div>
- {% endblock btnGrp %}
- {% endif %}
- </div>
-
- <!-- Returning users -->
- {% if not request.user.is_anonymous %}
- {% block returningUsers %}
- <div class="col-12 col-lg-6 offset-lg-6 mb-4 mt-lg-4">
- <h2 class="ht-4 border-bottom">Returning Users</h2>
- <p>If you're a returning user, some of the following options may be of interest:</p>
- <div class="btn-group-vertical w-100">
- <button class="btn btn-primary btnAnuket" onclick="create_workflow(3)">Snapshot a Host</button>
- <a class="btn btn-primary btnAnuket" href="{% url 'account:my-bookings' %}">
- My Bookings
- </a>
- {% if manager == True %}
- <button class="btn btn-primary" onclick="continue_workflow()">
- Resume Workflow
- </button>
- {% endif %}
- </div>
- </div>
- {% endblock returningUsers %}
- {% endif %}
-</div>
-
-<div class="hidden_form d-none" id="form_div">
- <form method="post" action="" class="form" id="wf_selection_form">
- {% csrf_token %}
- </form>
-</div>
-
-{% endblock content %}
diff --git a/src/templates/base/dashboard/login.html b/src/templates/base/dashboard/login.html
deleted file mode 100644
index 5af201a..0000000
--- a/src/templates/base/dashboard/login.html
+++ /dev/null
@@ -1,7 +0,0 @@
-{% extends "base.html" %}
-
-{% block content %}
-<h3> You Must Login To Do That<h3>
-
-<a href="{% url 'oidc_authentication_init' %}"> Login Here </a>
-{% endblock %}
diff --git a/src/templates/base/dashboard/multiple_select_filter_widget.html b/src/templates/base/dashboard/multiple_select_filter_widget.html
deleted file mode 100644
index 92aa1ba..0000000
--- a/src/templates/base/dashboard/multiple_select_filter_widget.html
+++ /dev/null
@@ -1,54 +0,0 @@
-<input name="filter_field" id="filter_field" type="hidden"/>
-<div class="row">
- {% for object_class, object_list in display_objects %}
- <div class="col-12 col-lg-6 d-flex flex-column pt-2 mx-0">
- <div class="col mx-0 border rounded py-2 flex-grow-1 d-flex flex-column">
- <div class="w-100">
- <h4 class="text-capitalize">{{object_class}}</h4>
- </div>
- <div id="{{object_class}}" class="row flex-grow-1">
- {% for obj in object_list %}
- <div class="col-12 col-md-6 col-xl-4 my-2 d-flex flex-grow-1">
- <div id="{{ obj.id|default:'not_provided' }}" class="card flex-grow-1">
- <div class="card-header">
- <p class="h5 font-weight-bold mt-2">{{obj.name}}</p>
- </div>
- <div class="card-body">
- <p class="grid-item-description">{{obj.description}}</p>
- </div>
- <div class="card-footer">
- <button type="button" class="btn btn-success grid-item-select-btn w-100 stretched-link"
- onclick="multi_filter_widget.processClick('{{obj.id}}');">
- {% if obj.multiple %}
- Add
- {% else %}
- Select
- {% endif %}
- </button>
- </div>
- </div>
- </div>
- {% endfor %}
- </div>
- </div>
- </div>
- {% endfor %}
-</div>
-
-<div id="dropdown_row" class="row">
- <div id="dropdown_wrapper" class="col-12 col-lg-6 d-flex flex-column pt-2 mx-0">
- </div>
-</div>
-<script>
-function multipleSelectFilterWidgetEntry() {
- const graph_neighbors = {{ neighbors|safe }};
- const filter_items = {{ filter_items|safe }};
- const initial_value = {{ initial_value|default_if_none:"{}"|safe }};
-
- //global variables
- multi_filter_widget = new MultipleSelectFilterWidget(graph_neighbors, filter_items, initial_value);
- form_submission_callbacks.push(() => multi_filter_widget.finish());
-}
-
-multipleSelectFilterWidgetEntry();
-</script>
diff --git a/src/templates/base/dashboard/pdf.yaml b/src/templates/base/dashboard/pdf.yaml
deleted file mode 100644
index c893919..0000000
--- a/src/templates/base/dashboard/pdf.yaml
+++ /dev/null
@@ -1,92 +0,0 @@
----
-version: {{version|default:"1.0"}}
-details:
- contact: {{details.contact}}
- lab: {{details.lab}}
- link: {{details.link}}
- location: {{details.location}}
- pod_owner: {{details.owner}}
- type: {{details.type}}
-jumphost:
- disks:
- {% for disk in jumphost.disks %}
- - disk_capacity: {{disk.capacity}}
- disk_interface: {{disk.interface}}
- disk_rotation: {{disk.rotation}}
- disk_type: {{disk.type}}
- name: {{disk.name}}
- {% endfor %}
- interfaces:
- {% for interface in jumphost.interfaces %}
- - features: {{interface.features}}
- mac_address: {{interface.mac_address}}
- name: {{interface.name}}
- speed: {{interface.speed}}
- {% endfor %}
- name: {{jumphost.name}}
- node:
- arch: {{jumphost.node.arch}}
- cores: {{jumphost.node.cores}}
- cpu_cflags: {{jumphost.node.cpu_cflags}}
- cpus: {{jumphost.node.cpus}}
- memory: {{jumphost.node.memory}}
- model: {{jumphost.node.model}}
- type: {{jumphost.node.type}}
- vendor: {{jumphost.node.vendor}}
- os: {{jumphost.os}}
- remote_management:
- address: {{jumphost.remote.address}}
- mac_address: {{jumphost.remote.mac_address}}
- pass: {{jumphost.remote.pass}}
- type: {{jumphost.remote.type}}
- user: {{jumphost.remote.user}}
- versions:
- {% for version in jumphost.remote.versions %}
- - {{version}}
- {% endfor %}
- remote_params:
- pass: {{jumphost.remote.pass}}
- type: {{jumphost.remote.type}}
- user: {{jumphost.remote.user}}
- versions:
- {% for version in jumphost.remote.versions %}
- - {{version}}
- {% endfor %}
-nodes:
-{% for node in nodes %}
-- disks:
- {% for disk in node.disks %}
- - disk_capacity: {{disk.capacity}}
- disk_interface: {{disk.interface}}
- disk_rotation: {{disk.rotation}}
- disk_type: {{disk.type}}
- name: {{disk.name}}
- {% endfor %}
- interfaces:
- {% for interface in node.interfaces %}
- - features: {{interface.features}}
- mac_address: {{interface.mac_address}}
- name: {{interface.name}}
- speed: {{interface.speed}}
- {% endfor %}
- name: {{node.name}}
- node:
- arch: {{node.node.arch}}
- cores: {{node.node.cores}}
- cpu_cflags: {{node.node.cpu_cflags}}
- cpus: {{node.node.cpus}}
- memory: {{node.node.memory}}
- model: {{node.node.model}}
- type: {{node.node.type}}
- vendor: {{node.node.vendor}}
- remote_management:
- address: {{node.remote.address}}
- mac_address: {{node.remote.mac_address}}
- pass: {{node.remote.pass}}
- type: {{node.remote.type}}
- user: {{node.remote.user}}
- versions:
- {% for version in node.remote.versions %}
- - {{version}}
- {% endfor %}
-{% endfor %}
diff --git a/src/templates/base/dashboard/resource.html b/src/templates/base/dashboard/resource.html
deleted file mode 100644
index f36ee7b..0000000
--- a/src/templates/base/dashboard/resource.html
+++ /dev/null
@@ -1,57 +0,0 @@
-{% extends "base.html" %}
-{% load staticfiles %}
-
-{% block extrahead %}
- {{ block.super }}
- <!-- Morris Charts CSS -->
- <link href="{% static "bower_components/morrisjs/morris.css" %}" rel="stylesheet">
-
- <!-- DataTables CSS -->
- <link href="{% static "bower_components/datatables.net-bs4/css/dataTables.bootstrap4.min.css" %}"
- rel="stylesheet">
-
- <!-- DataTables Responsive CSS -->
- <link href="{% static "bower_components/datatables.net-responsive-bs4/css/responsive.bootstrap4.min.css" %}"
- rel="stylesheet">
-{% endblock extrahead %}
-
-
-{% block content %}
- {% include "dashboard/resource_detail.html" %}
-{% endblock content %}
-
-
-{% block extrajs %}
- <!-- DataTables JavaScript -->
- <link href="{% static "bower_components/datatables/media/css/dataTables.bootstrap.css" %}"
- rel="stylesheet">
-
- <script src={% static "bower_components/datatables.net/js/jquery.dataTables.min.js" %}></script>
- <script src={% static "bower_components/datatables.net-bs4/js/dataTables.bootstrap4.min.js" %}></script>
-
-
-
- <!-- Flot Charts JavaScript -->
- <script src="{% static "bower_components/flot/excanvas.min.js" %}"></script>
- <script src="{% static "bower_components/flot/jquery.flot.js" %}"></script>
- <script src="{% static "bower_components/flot/jquery.flot.pie.js" %}"></script>
- <script src="{% static "bower_components/flot/jquery.flot.resize.js" %}"></script>
- <script src="{% static "bower_components/flot/jquery.flot.time.js" %}"></script>
- <script src="{% static "bower_components/flot.tooltip/js/jquery.flot.tooltip.min.js" %}"></script>
-
- <script src="{% static "js/flot-pie-chart.js" %}"></script>
-
- <script type="text/javascript">
- $(document).ready(function () {
- $('#{{ resource.id }}_server_table').DataTable({});
- $('#{{ resource.id }}_bookings_table').DataTable({});
- $('#{{ resource.id }}_vpn_user_table').DataTable({});
-
- var chart_id = "{{ resource.id }}_booking_utilization";
- var utilization_url = "{% url 'dashboard:booking_utilization' resource_id=resource.id weeks=4 %}";
- loadChartData(chart_id, utilization_url);
-
- loadChartData(chart_id, utilization_url);
- });
- </script>
-{% endblock extrajs %} \ No newline at end of file
diff --git a/src/templates/base/dashboard/resource_all.html b/src/templates/base/dashboard/resource_all.html
deleted file mode 100644
index fb8cc7e..0000000
--- a/src/templates/base/dashboard/resource_all.html
+++ /dev/null
@@ -1,70 +0,0 @@
-{% extends "base.html" %}
-{% load staticfiles %}
-
-{% block extrahead %}
- {{ block.super }}
- <!-- Morris Charts CSS -->
- <link href="{% static "bower_components/morrisjs/morris.css" %}" rel="stylesheet">
-
- <!-- DataTables CSS -->
- <link href="{% static "bower_components/datatables.net-bs4/css/dataTables.bootstrap4.min.css" %}"
- rel="stylesheet">
-
- <!-- DataTables Responsive CSS -->
- <link href="{% static "bower_components/datatables.net-responsive-bs4/css/responsive.bootstrap4.min.css" %}"
- rel="stylesheet">
-{% endblock extrahead %}
-
-
-{% block content %}
- {% for resource, utilization, bookings in pods %}
- <div class="row">
- <div class="col-lg-12">
- <div class="panel panel-default">
- <div class="panel-heading">
- {{ resource.name }}
- </div>
- <div class="panel-body">
- {% include "dashboard/resource_detail.html" %}
- </div>
- </div>
- </div>
- </div>
- {% endfor %}
-{% endblock content %}
-
-
-{% block extrajs %}
- <!-- DataTables JavaScript -->
- <link href="{% static "bower_components/datatables/media/css/dataTables.bootstrap.css" %}"
- rel="stylesheet">
-
- <script src={% static "bower_components/datatables.net/js/jquery.dataTables.min.js" %}></script>
- <script src={% static "bower_components/datatables.net-bs4/js/dataTables.bootstrap4.min.js" %}></script>
-
-
-
- <!-- Flot Charts JavaScript -->
- <script src="{% static "bower_components/flot/excanvas.min.js" %}"></script>
- <script src="{% static "bower_components/flot/jquery.flot.js" %}"></script>
- <script src="{% static "bower_components/flot/jquery.flot.pie.js" %}"></script>
- <script src="{% static "bower_components/flot/jquery.flot.resize.js" %}"></script>
- <script src="{% static "bower_components/flot/jquery.flot.time.js" %}"></script>
- <script src="{% static "bower_components/flot.tooltip/js/jquery.flot.tooltip.min.js" %}"></script>
- <script src="{% static "js/flot-pie-chart.js" %}"></script><
-
- <script type="text/javascript">
- $(document).ready(function () {
- {% for resource, utilization, bookings in pods %}
-
- $('#{{ resource.id }}_server_table').DataTable({});
- $('#{{ resource.id }}_bookings_table').DataTable({});
- $('#{{ resource.id }}_vpn_user_table').DataTable({});
-
- var chart_id = "{{ resource.id }}_booking_utilization";
- var utilization_url = "{% url 'dashboard:booking_utilization' resource_id=resource.id weeks=4 %}";
- loadChartData(chart_id, utilization_url);
- {% endfor %}
- });
- </script>
-{% endblock extrajs %} \ No newline at end of file
diff --git a/src/templates/base/dashboard/resource_detail.html b/src/templates/base/dashboard/resource_detail.html
deleted file mode 100644
index cd85354..0000000
--- a/src/templates/base/dashboard/resource_detail.html
+++ /dev/null
@@ -1,151 +0,0 @@
-<div class="row">
- <div class="col-lg-9">
- <div class="panel panel-default">
- <div class="panel-heading Anuket-Text">
- Status
- </div>
- <div class="panel-body">
- <div class="list-group pre-scrollable">
- {% for status in resource.resourcestatus_set.all %}
- <a href="#" class="list-group-item">
- <i class="fa fa-info fa-fw"></i> {{ status.title }}
- <span class="pull-right text-muted small">
- <em>{{ status.timestamp }}</em>
- </span>
- </a>
- {% endfor %}
- </div>
- </div>
- </div>
- </div>
- <div class="col-lg-9">
- <div class="panel panel-default">
- <div class="panel-heading Anuket-Text">
- Servers
- </div>
- <div class="panel-body">
- <div class="dataTables_wrapper">
- <table class="table table-striped table-bordered table-hover Anuket-Text"
- id="{{ resource.id }}_server_table" cellspacing="0"
- width="100%">
- {% include "dashboard/server_table.html" %}
- </table>
- </div>
- </div>
- </div>
- </div>
-</div>
-<div class="row">
- <div class="col-lg-3">
- <div class="panel panel-default">
- <div class="panel-heading Anuket-Text">
- Booking Utilization
- <div class="pull-right">
- <div class="form-group">
- <select onchange="loadChartData('{{ resource.id }}_booking_utilization', this.value);">
- <option value="{% url 'dashboard:booking_utilization' resource_id=resource.id weeks=-4 %}" class="Anuket-Text">
- Last Month
- </option>
- <option value="{% url 'dashboard:booking_utilization' resource_id=resource.id weeks=-1 %}" class="Anuket-Text">
- Last Week
- </option>
- <option value="{% url 'dashboard:booking_utilization' resource_id=resource.id weeks=1 %}" class="Anuket-Text">
- Next Week
- </option>
- <option selected="selected"
- value="{% url 'dashboard:booking_utilization' resource_id=resource.id weeks=4 %}" class="Anuket-Text">
- Next Month
- </option>
- </select>
- </div>
- </div>
- </div>
- <div class="panel-body">
- <div class="flot-chart">
- <div class="flot-chart-content"
- id="{{ resource.id }}_booking_utilization"></div>
- </div>
- </div>
- </div>
- </div>
- <div class="col-lg-9">
- <div class="panel panel-default">
- <div class="panel-heading Anuket-Text">
- Bookings
- </div>
- <div class="panel-body">
- <div class="dataTables_wrapper">
- <table class="table table-striped table-bordered table-hover"
- id="{{ resource.id }}_bookings_table" cellspacing="0"
- width="100%" class="Anuket-Text">
- {% include "booking/booking_table.html" %}
- </table>
- </div>
- </div>
- </div>
- </div>
-</div>
-<div class="row">
- <div class="col-lg-3">
- <div class="panel panel-default">
- <div class="panel-heading Anuket-Text">
- Contact
- </div>
- <div class="panel-body Anuket-Text">
- <p>
- <b>Lab Owner: </b>
- {{ resource.owner.username }}
- </p>
- <p>
- <b>Email: </b>
- {{ resource.owner.email }}
- </p>
- <p>
- <a href="{% url 'booking:create' resource_id=resource.id %}" class="btn btn-primary btnAnuket">
- Booking
- </a>
- <a href="{{ resource.url }}" class="btn btn-primary btnAnuket">
- Anuket Wiki
- </a>
- </p>
- </div>
- </div>
- </div>
- <div class="col-lg-6">
- <div class="panel panel-default">
- <div class="panel-heading Anuket-Text">
- VPN Users
- </div>
- <div class="panel-body">
- <div class="dataTables_wrapper">
- <table class="table table-striped table-bordered table-hover Anuket-Text"
- id="{{ resource.id }}_vpn_user_table" cellspacing="0"
- width="100%">
- <thead>
- <tr>
- <th>User</th>
- <th>Email</th>
- <th>Company</th>
- </tr>
- </thead>
- <tbody>
- {% for user in resource.vpn_users.all %}
- <tr>
- <td>
- {{ user.username }}
- </td>
- <td>
- {{ user.email }}
- </td>
- <td>
- {{ user.userprofile.company }}
- </td>
- </tr>
- {% endfor %}
- </table>
- </tbody>
- </div>
- </div>
- </div>
- </div>
-</div>
diff --git a/src/templates/base/dashboard/searchable_select_multiple.html b/src/templates/base/dashboard/searchable_select_multiple.html
deleted file mode 100644
index 4d996e5..0000000
--- a/src/templates/base/dashboard/searchable_select_multiple.html
+++ /dev/null
@@ -1,73 +0,0 @@
-<div id="search_select_outer" class="d-flex flex-column">
- {% if incompatible == "true" %}
- <div class="alert alert-danger alertAnuket" role="alert">
- <h3>Warning: Incompatible Configuration</h3>
- <p>Please make a different selection, as the current config conflicts with the selected pod</p>
- </div>
- {% endif %}
- <div id="added_counter" class="text-center">
- <span id="added_number">0</span>
- <span id="addable_limit">/ {% if selectable_limit > -1 %} {{ selectable_limit }} {% else %} &infin; {% endif %}added</span>
- </div>
-
- <div id="added_list" class="pb-2">
-
- </div>
-
- <input id="user_field" name="ignore_this" class="form-control" autocomplete="off" type="text" placeholder="{{placeholder}}" value="" oninput="searchable_select_multiple_widget.search(this.value)"
- {% if disabled %} disabled {% endif %}>
-
- <input type="hidden" id="selector" name="{{ name }}" class="form-control d-none"
- {% if disabled %} disabled {% endif %}>
-
- <div id="scroll_restrictor" class="d-flex pb-4 position-relative">
- <div id="drop_results" class="list-group w-100 z-2 overflow-auto position-absolute mh-30vh"></div>
- </div>
-</div>
-
-<script type="text/javascript">
- function searchableSelectMultipleWidgetEntry() {
- let format_vars = {
- "show_from_noentry": {{show_from_noentry|yesno:"true,false"}},
- "show_x_results": {{show_x_results|default:-1}},
- "results_scrollable": {{results_scrollable|yesno:"true,false"}},
- "selectable_limit": {{selectable_limit|default:-1}},
- "placeholder": "{{placeholder|default:"begin typing"}}"
- };
-
- let field_dataset = {{items|safe}};
-
- let field_initial = {{ initial|safe }};
-
- //global
- searchable_select_multiple_widget = new SearchableSelectMultipleWidget(format_vars, field_dataset, field_initial);
- }
-
- searchableSelectMultipleWidgetEntry();
-
- /*
- var show_from_noentry = context(show_from_noentry|yesno:"true,false") // whether to show any results before user starts typing
- var show_x_results = context(show_x_results|default:-1) // how many results to show at a time, -1 shows all results
- var results_scrollable = {{results_scrollable|yesno:"true,false") // whether list should be scrollable
- var selectable_limit = {{selectable_limit|default:-1) // how many selections can be made, -1 allows infinitely many
- var placeholder = "context(placeholder|default:"begin typing")" // placeholder that goes in text box
-
- needed info
- var items = context(items|safe) // items to add to trie. Type is a dictionary of dictionaries with structure:
- {
- id# : {
- "id": any, identifiable on backend
- "small_name": string, displayed first (before separator), searchable (use for e.g. username)
- "expanded_name": string, displayed second (after separator), searchable (use for e.g. email address)
- "string": string, not displayed, still searchable
- }
- }
-
- used later:
- context(selectable_limit): changes what number displays for field
- context(name): form identifiable name, relevant for backend
- // when submitted, form will contain field data in post with name as the key
- context(placeholder): "greyed out" contents put into search field initially to guide user as to what they're searching for
- context(initial): in search_field_init(), marked safe, an array of id's each referring to an id from items
- */
-</script>
diff --git a/src/templates/base/dashboard/server_table.html b/src/templates/base/dashboard/server_table.html
deleted file mode 100644
index efe531f..0000000
--- a/src/templates/base/dashboard/server_table.html
+++ /dev/null
@@ -1,30 +0,0 @@
-<thead>
-<tr class="Anuket-Text">
- <th>Server</th>
- <th>Model</th>
- <th>CPU</th>
- <th>RAM</th>
- <th>Storage</th>
-</tr>
-</thead>
-<tbody>
-{% for server in resource.server_set.all %}
- <tr>
- <td>
- {{ server.name }}
- </td>
- <td>
- {{ server.model }}
- </td>
- <td>
- {{ server.cpu }}
- </td>
- <td>
- {{ server.ram }}
- </td>
- <td>
- {{ server.storage }}
- </td>
- </tr>
-{% endfor %}
-</tbody> \ No newline at end of file
diff --git a/src/templates/base/dashboard/table.html b/src/templates/base/dashboard/table.html
deleted file mode 100644
index bdc350e..0000000
--- a/src/templates/base/dashboard/table.html
+++ /dev/null
@@ -1,39 +0,0 @@
-{% extends "base.html" %}
-{% load staticfiles %}
-
-{% block extrahead %}
- {{ block.super }}
- <!-- DataTables CSS -->
- <link href="{% static "node_modules/datatables.net-bs4/css/dataTables.bootstrap4.min.css" %}"
- rel="stylesheet">
-
- <!-- DataTables Responsive CSS -->
- <link href="{% static "node_modules/datatables.net-responsive-bs4/css/responsive.bootstrap4.min.css" %}"
- rel="stylesheet">
-{% endblock extrahead %}
-
-{% block content %}
- <div class="row">
- <div class="col-lg-12">
- <div class="dataTables_wrapper table-responsive mw-100">
- <table class="table table-striped table-bordered table-hover Anuket-Text" id="table" cellspacing="0"
- width="100%">
- {% block table %}
- {% endblock table %}
- </table>
- </div>
- </div>
- </div>
-{% endblock content %}
-
-{% block extrajs %}
- <!-- DataTables JavaScript -->
-
- <script src={% static "node_modules/datatables.net/js/jquery.dataTables.min.js" %}></script>
- <script src={% static "node_modules/datatables.net-bs4/js/dataTables.bootstrap4.min.js" %}></script>
-
- <script src={% static "js/dataTables-sort.js" %}></script>
-
- {% block tablejs %}
- {% endblock tablejs %}
-{% endblock extrajs %}
diff --git a/src/templates/base/layout.html b/src/templates/base/layout.html
deleted file mode 100644
index bea84f3..0000000
--- a/src/templates/base/layout.html
+++ /dev/null
@@ -1,59 +0,0 @@
-{% load staticfiles %}
-<!DOCTYPE html>
-<html lang="en">
-
-<head>
-
- <meta charset="utf-8">
- <meta http-equiv="X-UA-Compatible" content="IE=edge">
- <meta name="viewport" content="width=device-width, initial-scale=1">
- <meta name="description" content="">
- <meta name="author" content="">
-
- {% block head-title %}
- <title>Anuket Laas {{ title }}</title>
- {% endblock head-title %}
-
- <!-- jQuery -->
- <script src="{% static "node_modules/jquery/dist/jquery.min.js" %}"></script>
-
- <!-- Bootstrap Core CSS -->
- <link href="{% static "node_modules/bootstrap/dist/css/bootstrap.min.css" %}"
- rel="stylesheet">
-
- <!-- Custom Fonts -->
- <link href="{% static "node_modules/@fortawesome/fontawesome-free/css/all.min.css" %}"
- rel="stylesheet" type="text/css">
-
- <link href="{% static "css/base.css" %}" rel="stylesheet">
-
- <!-- Favicon -->
- <link rel="shortcut icon" href="{% static 'favicon.ico' %}">
-
- {% block extrahead %}
- {% endblock extrahead %}
-
- <!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
- <!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
- <!--[if lt IE 9]>
- <script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
- <script src="https://oss.maxcdn.com/libs/respond.js/1.4.2/respond.min.js"></script>
- <![endif]-->
-</head>
-
-{% block extrastyle %}
-{% endblock extrastyle %}
-
-<body>
-{% block basecontent %}
-{% endblock basecontent %}
-
-<!-- Popper.js -->
-<script src="{% static "node_modules/popper.js/dist/umd/popper.min.js" %}"></script>
-<!-- Bootstrap Core JavaScript -->
-<script src="{% static "node_modules/bootstrap/dist/js/bootstrap.min.js" %}"></script>
-
-{% block extrajs %}
-{% endblock extrajs %}
-</body>
-</html>
diff --git a/src/templates/base/notifier/email_ended.txt b/src/templates/base/notifier/email_ended.txt
deleted file mode 100644
index 33a1ec5..0000000
--- a/src/templates/base/notifier/email_ended.txt
+++ /dev/null
@@ -1,25 +0,0 @@
-{{user_name|default:"Developer"}},
-
-{% if owner %}
-The booking you requested from the {{booking.lab.project}} Lab has ended.
-{% else %}
-The booking you collaborated on at the {{booking.lab.project}} Lab has ended.
-{% endif %}
-
-booking information:
- start: {{booking.start}}
- end: {{booking.end}}
- machines:
- {% for host in hosts %}
- - {{host}}
- {% endfor %}
- purpose: {{booking.purpose}}
-
-You may visit the following link for more information:
-{{booking_url}}
-
-Feel free to create another booking with us!
-
-Thank you for contributing to the {{booking.lab.project}} platform!
-
- - The Lab-as-a-Service team
diff --git a/src/templates/base/notifier/email_expiring.txt b/src/templates/base/notifier/email_expiring.txt
deleted file mode 100644
index d78e59f..0000000
--- a/src/templates/base/notifier/email_expiring.txt
+++ /dev/null
@@ -1,25 +0,0 @@
-{{user_name|default:"Developer"}},
-
-{% if owner %}
-The booking you requested from the {{booking.lab.project}} lab is about to expire.
-{% else %}
-The booking you collaborate on at the {{booking.lab.project}} lab is about to expire.
-{% endif %}
-
-booking information:
- start: {{booking.start}}
- end: {{booking.end}}
- machines:
- {% for host in hosts %}
- - {{host}}
- {% endfor %}
- purpose: {{booking.purpose}}
-
-You may visit the following link for more information:
-{{booking_url}}
-
-Please take the time to backup all data or extend the booking if needed.
-
-Thank you for contributing to the {{booking.lab.project}} platform!
-
- - The Lab-as-a-Service team
diff --git a/src/templates/base/notifier/email_fulfilled.txt b/src/templates/base/notifier/email_fulfilled.txt
deleted file mode 100644
index e3e07f9..0000000
--- a/src/templates/base/notifier/email_fulfilled.txt
+++ /dev/null
@@ -1,21 +0,0 @@
-{{user_name|default:"Developer"}},
-
-{% if owner %}
-The booking you requested of the {{booking.lab.project}} lab has finished deploying and is ready for you to use.
-{% else %}
-A booking you collaborate on is ready for you to use
-{% endif %}
-
-The lab that fulfilled your booking request has sent you the following messages:
- {% for email_message in messages %}
- {{ email_message.title }}
- {{ email_message.content }}
- --------------------
- {% endfor %}
-
-You may visit the following link for more information:
-{{booking_url}}
-
-Thank you for contributing to the {{booking.lab.project}} platform!
-
- - The Lab-as-a-Service team
diff --git a/src/templates/base/notifier/end_booking.html b/src/templates/base/notifier/end_booking.html
deleted file mode 100644
index 22fbd58..0000000
--- a/src/templates/base/notifier/end_booking.html
+++ /dev/null
@@ -1,134 +0,0 @@
-<html>
- <link rel="preconnect" href="https://fonts.googleapis.com">
- <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
- <link href="https://fonts.googleapis.com/css2?family=Source+Sans+Pro&display=swap" rel="stylesheet">
-
- <style>
- h2{
- font-family: 'Source Sans Pro';
- }
- p{
- font-family: Montserrat;
- }
- li{
- font-family: Montserrat;
- }
- a{
- color: #f8f9fa;
- text-decoration: none;
- }
- button{
- background-color:#a3c1db;
- color: #343a40;
- border: 0px;
- border-radius: 4mm;
- height: 25px;
- width: 310px;
- text-align: center;
- margin: 15px;
- text-decoration: none;
- font-family: Montserrat;
- font-size: 16;
- }
- button:focus{
- border: 0px;
- }
- .textFormatting{
- text-align: center;
- color:#343a40;
- margin: auto;
-
- }
- .content{
- background-color: #f8f9fa;
- position: center;
- }
- table{
- margin-left: auto;
- margin-right: auto;
- border: 1px solid #343a40;
- border-collapse: collapse;
- }
- tr{
- border-bottom: 1px solid #343a40;
- }
- td{
- color:#343a40;
- padding: 3px;
- font-family: Montserrat
- }
- .row1{
- background-color: #7598b6;
- }
- .row2{
- background-color: #d7e2f0;
- }
- .row3{
- background-color: #d2e5f3;
- }
- </style>
-
- <body>
- <div id="message_content_wrapper" class="textFormatting content">
- {% if owner %}
- <h2>Your Booking Has Expired.</h2>
- <p>Your booking has ended and the machines have been cleaned up.</p>
- <p>Thank you for working on {{booking.lab.project}}, and feel free to book more machines if you need them.</p>
- {% else %}
- <h2>A Booking That You Collaborated on Has Expired.</h2>
- <p>The booking owned by {{booking.owner.username}} that you worked on has ended.</p>
- <p>Thank you for contributing to {{booking.lab.project}}.</p>
- {% endif %}
- <br>
- <table>
- <tr class="row1">
- <td style="text-align: center; font-size: larger;" colspan="2">Booking Information:</td>
- </tr>
- <tr class="row2">
- <td>Owner:</td>
- <td>{{booking.owner.username}}</td>
- </tr>
- <tr class="row3">
- <td>id:</td>
- <td>{{booking.id}}</td>
- </tr>
- <tr class="row2">
- <td>lab:</td>
- <td>{{booking.resource.template.lab.lab_user.username}}</td>
- </tr>
- <tr class="row3">
- <td>resource:</td>
- <td>{{booking.resource.template.name}}</td>
- </tr>
- <tr class="row2">
- <td>start:</td>
- <td>{{booking.start}}</td>
- </tr>
- <tr class="row3">
- <td>end:</td>
- <td>{{booking.end}}</td>
- </tr>
- <tr class="row2">
- <td>purpose:</td>
- <td>{{booking.purpose}}</td>
- </tr>
- <tr class="row3">
- <td>collaborator{% booking.collaborators.all.count|pluralize:"s" %}:</td>
- <td></td>
- </tr>
- {% for user in booking.collaborators.all %}
- <tr class="{% cycle 'row2' 'row3' %}">
- <td></td>
- <td>{{user.username}}</td>
- </tr>
- {% empty %}
- <tr class="row2">
- <td></td>
- <td>No collaborators</td>
- </tr>
- {% endfor %}
- </table>
- <button type="button" onclick="window.location.href='/booking/detail/{{booking.id}}'">You can find more detailed information here</button>
- </div>
- </body>
-</html>
diff --git a/src/templates/base/notifier/expiring_booking.html b/src/templates/base/notifier/expiring_booking.html
deleted file mode 100644
index 7247081..0000000
--- a/src/templates/base/notifier/expiring_booking.html
+++ /dev/null
@@ -1,134 +0,0 @@
-<html>
- <link rel="preconnect" href="https://fonts.googleapis.com">
- <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
- <link href="https://fonts.googleapis.com/css2?family=Source+Sans+Pro&display=swap" rel="stylesheet">
-
- <style>
- h2{
- font-family: 'Source Sans Pro';
- }
- p{
- font-family: Montserrat;
- }
- li{
- font-family: Montserrat;
- }
- a{
- color: #f8f9fa;
- text-decoration: none;
- }
- button{
- background-color:#a3c1db;
- color: #343a40;
- border: 0px;
- border-radius: 4mm;
- height: 25px;
- width: 310px;
- text-align: center;
- margin: 15px;
- text-decoration: none;
- font-family: Montserrat;
- font-size: 16;
- }
- button:focus{
- border: 0px;
- }
- .textFormatting{
- text-align: center;
- color:#343a40;
- margin: auto;
-
- }
- .content{
- background-color: #f8f9fa;
- position: center;
- }
- table{
- margin-left: auto;
- margin-right: auto;
- border: 1px solid #343a40;
- border-collapse: collapse;
- }
- tr{
- border-bottom: 1px solid #343a40;
- }
- td{
- color:#343a40;
- padding: 3px;
- font-family: Montserrat
- }
- .row1{
- background-color: #7598b6;
- }
- .row2{
- background-color: #d7e2f0;
- }
- .row3{
- background-color: #d2e5f3;
- }
- </style>
-
-
- <body>
- <div id="message_content_wrapper" class="textFormatting content">
- {% if owner %}
- <h2>Your Booking Is About to Expire.</h2>
- <p>Your booking will expire within 48 hours ({{booking.end}}).</p>
- {% else %}
- <h2>A Booking That You Collaborate on Is About to Expire.</h2>
- <p>The booking owned by {{booking.owner.username}} that you work on is about to expire.</p>
- {% endif %}
- <p>Please take the time to backup all data or extend the booking if needed.</p>
- <br>
- <table>
- <tr class="row1">
- <td style="text-align: center; font-size: larger;" colspan="2">Booking Information:</td>
- </tr>
- <tr class="row2">
- <td>Owner:</td>
- <td>{{booking.owner.username}}</td>
- </tr>
- <tr class="row3">
- <td>id:</td>
- <td>{{booking.id}}</td>
- </tr>
- <tr class="row2">
- <td>lab:</td>
- <td>{{booking.resource.template.lab.lab_user.username}}</td>
- </tr>
- <tr class="row3">
- <td>resource:</td>
- <td>{{booking.resource.template.name}}</td>
- </tr>
- <tr class="row2">
- <td>start:</td>
- <td>{{booking.start}}</td>
- </tr>
- <tr class="row3">
- <td>end:</td>
- <td>{{booking.end}}</td>
- </tr>
- <tr class="row2">
- <td>purpose:</td>
- <td>{{booking.purpose}}</td>
- </tr>
- <tr class="row3">
- <td>collaborator{% booking.collaborators.all.count|pluralize:"s" %}:</td>
- <td></td>
- </tr>
- {% for user in booking.collaborators.all %}
- <tr class="{% cycle 'row2' 'row3' %}">
- <td></td>
- <td>{{user.username}}</td>
- </tr>
- {% empty %}
- <tr class="row2">
- <td></td>
- <td>No collaborators</td>
- </tr>
- {% endfor %}
- </table>
- <button type="button" onclick="window.location.href='/booking/detail/{{booking.id}}'">You can find more detailed information here</button>
- </div>
- </body>
-</html>
diff --git a/src/templates/base/notifier/inbox.html b/src/templates/base/notifier/inbox.html
deleted file mode 100644
index 26b6d32..0000000
--- a/src/templates/base/notifier/inbox.html
+++ /dev/null
@@ -1,87 +0,0 @@
-{% extends "base.html" %}
-
-
-{% load staticfiles %}
-
-{% block content %}
-
-<div class="container-fluid d-flex flex-grow-1 flex-column">
- <div class="row mt-3 mb-2">
- <div class="col-2 px-0">
- <div class="btn-group w-100" id="filterGroup">
- <button class="btn btn-secondary active" data-read="-1">All</button>
- <button class="btn btn-secondary" data-read="0">Unread</button>
- <button class="btn btn-secondary" data-read="1">Read</button>
- </div>
- </div>
- </div>
- <div class="row flex-grow-1" id="fixHeight">
- <!-- Notification list && Controls -->
- <div class="mb-2 mb-lg-0 col-lg-2 px-0 mh-100">
- <span class="text-muted d-none" id="noMessages">No messages available</span>
- <div class="list-group rounded-0 rounded-left overflow-auto mh-100 notifications" id="unreadNotifications" data-read="0">
- {% for notification in unread_notifications %}
- <a
- href="#"
- onclick="showmessage({{notification.id}}); setactive(this);"
- class="list-group-item list-group-item-action notification">
- {{ notification }}
- </a>
- {% endfor %}
- </div>
- <div class="list-group rounded-0 rounded-left overflow-auto mh-100 notifications" id="readNotifications" data-read="1">
- {% for notification in read_notifications %}
- <a
- href="#"
- onclick="showmessage({{notification.id}}); setactive(this);"
- class="list-group-item list-group-item-action list-group-item-secondary notification">
- {{ notification }}
- </a>
- {% endfor %}
- </div>
- </div>
- <!-- Content -->
- <div class="col ml-lg-2 border mh-100 p-4">
- <iframe name="messageView" class="w-100 h-100" id="inbox-iframe" frameBorder="0" scrolling="yes">Please select a notification</iframe>
- </div>
- </div>
-</div>
-
-<script type="text/javascript">
- function showmessage(msg_id) {
- window.frames["messageView"].location = "notification/" + msg_id;
- }
-
- function setactive(obj) {
- $(".notification").removeClass("active");
- $(obj).addClass("active");
- }
-
- // Shows messages in the given notification list.
- // Shows/hides the 'no messages' span after checking children amount
- // given the .notification classed element
- function showMessages(notificationList) {
- $(".notifications").addClass("d-none");
- if (notificationList.children().length < 1) {
- $("#noMessages").removeClass("d-none");
- } else {
- $("#noMessages").addClass("d-none");
- notificationList.removeClass("d-none");
- }
- }
-
- $(document).ready(function(){
- // For all / unread / read
- $("#filterGroup button").click(function(){
- let read = $(this).attr("data-read");
- $(this).siblings().removeClass("active");
- $(this).addClass("active");
- if (read === "-1") {
- return showMessages($(".notifications"));
- }
- return showMessages($(`.notifications[data-read="${read}"]`));
- });
- showMessages($(".notifications"));
- });
-</script>
-{% endblock %} \ No newline at end of file
diff --git a/src/templates/base/notifier/new_booking.html b/src/templates/base/notifier/new_booking.html
deleted file mode 100644
index d886f40..0000000
--- a/src/templates/base/notifier/new_booking.html
+++ /dev/null
@@ -1,133 +0,0 @@
-<html>
- <link rel="preconnect" href="https://fonts.googleapis.com">
- <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
- <link href="https://fonts.googleapis.com/css2?family=Source+Sans+Pro&display=swap" rel="stylesheet">
-
- <style>
- h2{
- font-family: 'Source Sans Pro';
- }
- p{
- font-family: Montserrat;
- }
- li{
- font-family: Montserrat;
- }
- a{
- color: #f8f9fa;
- text-decoration: none;
- }
- button{
- background-color:#a3c1db;
- color: #343a40;
- border: 0px;
- border-radius: 4mm;
- height: 25px;
- width: 310px;
- text-align: center;
- margin: 15px;
- text-decoration: none;
- font-family: Montserrat;
- font-size: 16;
- }
- button:focus{
- border: 0px;
- }
- .textFormatting{
- text-align: center;
- color:#343a40;
- margin: auto;
-
- }
- .content{
- background-color: #f8f9fa;
- position: center;
- }
- table{
- margin-left: auto;
- margin-right: auto;
- border: 1px solid #343a40;
- border-collapse: collapse;
- }
- tr{
- border-bottom: 1px solid #343a40;
- }
- td{
- color:#343a40;
- padding: 3px;
- font-family: Montserrat
- }
- .row1{
- background-color: #7598b6;
- font-weight: bolder;
- }
- .row2{
- background-color: #d7e2f0;
- }
- .row3{
- background-color: #d2e5f3;
- }
- </style>
- <body>
- <div id="message_content_wrapper" class="textFormatting content">
- {% if owner %}
- <h2>You Have Created a New Booking.</h2>
- <p>We have recieved your booking request and will start working on it right away.</p>
- {% else %}
- <h2>You Have Been Added as a Collaborator to a Booking.</h2>
- <p>{{booking.owner.username}} has given you access to their booking.</p>
- {% endif %}
- <br>
-
- <table>
- <tr class="row1">
- <td style="text-align: center; font-size: larger;" colspan="2">Booking Information:</td>
- </tr>
- <tr class="row2">
- <td>Owner:</td>
- <td>{{booking.owner.username}}</td>
- </tr>
- <tr class="row3">
- <td>id:</td>
- <td>{{booking.id}}</td>
- </tr>
- <tr class="row2">
- <td>lab:</td>
- <td>{{booking.resource.template.lab.lab_user.username}}</td>
- </tr>
- <tr class="row3">
- <td>resource:</td>
- <td>{{booking.resource.template.name}}</td>
- </tr>
- <tr class="row2">
- <td>start:</td>
- <td>{{booking.start}}</td>
- </tr>
- <tr class="row3">
- <td>end:</td>
- <td>{{booking.end}}</td>
- </tr>
- <tr class="row2">
- <td>purpose:</td>
- <td>{{booking.purpose}}</td>
- </tr>
- <tr class="row3">
- <td>collaborators:</td>
- <td></td>
- </tr>
- {% for user in booking.collaborators.all %}
- <tr class="{% cycle 'row2' 'row3' %}">
- <td></td>
- <td>{{user.username}}</td>
- </tr>
- {% empty %}
- <tr class="row2">
- <td></td>
- <td>No collaborators</td>
- </tr>
- {% endfor %}
- </table>
- <button type="button" onclick="window.location.href='/booking/detail/{{booking.id}}'">You can find more detailed information here</button>
- </div>
- </body>
-</html>
diff --git a/src/templates/base/notifier/notification.html b/src/templates/base/notifier/notification.html
deleted file mode 100644
index 603edea..0000000
--- a/src/templates/base/notifier/notification.html
+++ /dev/null
@@ -1,50 +0,0 @@
-{% extends "layout.html" %}
-{% block extrahead %}
-<base target="_parent">
-{% endblock %}
-
-{% block basecontent %}
-<script>
- function send_request(post_data){
- var form = $("#notification_action_form");
- var formData = form.serialize() + '&' + post_data + '=true';
- var req = new XMLHttpRequest();
- req.open("POST", ".", false);
- req.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
- req.onerror = function() { alert("problem occurred while trying to cancel current workflow"); }
- req.onreadystatechange = function() { if(req.readyState === 4){
- window.top.location.href += '';
- }};
- req.send(formData);
- }
- function delete_notification()
- {
- send_request("delete");
- }
- function mark_unread()
- {
- send_request("unread");
- }
-</script>
-
-<div class="d-flex justify-content-between border-bottom">
- <span class="h3">{{notification.title}}</span>
- <div class="btn_group">
- <button class="btn btn-primary inbox-btn" onclick="mark_unread()">Mark Unread</button>
- <button class="btn btn-danger inbox-btn" onclick="delete_notification()">Delete</button>
- </div>
-</div>
-
-<p class="content-divider"></p>
-
-{% if not notification.is_html %}
-<pre>
-{% endif %}
- {{notification.content|safe}}
-{% if not notification.is_html %}
-</pre>
-{% endif %}
-<form id="notification_action_form" action="." method="post">
- {% csrf_token %}
-</form>
-{% endblock %}
diff --git a/src/templates/base/resource/hostprofile_detail.html b/src/templates/base/resource/hostprofile_detail.html
deleted file mode 100644
index 0b3262c..0000000
--- a/src/templates/base/resource/hostprofile_detail.html
+++ /dev/null
@@ -1,104 +0,0 @@
-{% extends "base.html" %}
-{% load staticfiles %}
-
-{% block content %}
-<div class="row">
- <div class="col-lg-6">
- <div class="card mb-4">
- <div class="card-header d-flex">
- <h4 class="d-inline">Available at</h4>
- <button data-toggle="collapse" data-target="#availableAt" class="btn ml-auto btn-outline-secondary">Expand</button>
- </div>
- <div class="collapse show" id="availableAt">
- <ul class="list-group list-group-flush">
- {% for lab in hostprofile.labs.all %}
- <li class="list-group-item">{{lab.name}}</li>
- {% endfor %}
- </ul>
- </div>
- </div>
- <div class="card mb-4">
- <div class="card-header d-flex">
- <h4 class="d-inline">RAM</h4>
- <button data-toggle="collapse" data-target="#ramPanel" class="btn ml-auto btn-outline-secondary">Expand</button>
- </div>
- <div id="ramPanel" class="collapse show">
- <div class="card-body">
- {{hostprofile.ramprofile.first.amount}}G,
- {{hostprofile.ramprofile.first.channels}} channels
- </div>
- </div>
- </div>
- <div class="card mb-4">
- <div class="card-header d-flex">
- <h4 class="d-inline">CPU</h4>
- <button data-toggle="collapse" data-target="#cpuPanel" class="btn ml-auto btn-outline-secondary">Expand</button>
- </div>
- <div class="collapse show" id="cpuPanel">
- <table class="table">
- <tr>
- <td>Arch:</td>
- <td>{{hostprofile.cpuprofile.first.architecture}}</td>
- </tr>
- <tr>
- <td>Cores:</td>
- <td>{{hostprofile.cpuprofile.first.cores}}</td>
- </tr>
- <tr>
- <td>Sockets:</td>
- <td>{{hostprofile.cpuprofile.first.cpus}}</td>
- </tr>
- </table>
- </div>
- </div>
- <div class="card mb-4">
- <div class="card-header d-flex">
- <h4 class="d-inline">Disk</h4>
- <button data-toggle="collapse" data-target="#diskPanel" class="btn ml-auto btn-outline-secondary">Expand</button>
- </div>
- <div class="collapse show" id="diskPanel">
- <table class="table">
- <tr>
- <td>Size:</td>
- <td>{{hostprofile.storageprofile.first.size}} GiB</td>
- </tr>
- <tr>
- <td>Type:</td>
- <td>{{hostprofile.storageprofile.first.media_type}}</td>
- </tr>
- <tr>
- <td>Mount Point:</td>
- <td>{{hostprofile.storageprofile.first.name}}</td>
- </tr>
- </table>
- </div>
- </div>
- </div>
- <div class="col-lg-6">
- <div class="card">
- <div class="card-header d-flex">
- <h4 class="d-inline">Interfaces</h4>
- <button data-toggle="collapse" data-target="#interfacePanel" class="btn ml-auto btn-outline-secondary">Expand</button>
- </div>
- <div class="collapse show" id="interfacePanel">
- <table class="table">
- <thead>
- <tr>
- <th>Name</th>
- <th>Speed</th>
- </tr>
- </thead>
- <tbody>
- {% for intprof in hostprofile.interfaceprofile.all %}
- <tr>
- <td>{{intprof.name}}</td>
- <td>{{intprof.speed}}</td>
- </tr>
- {% endfor %}
- </tbody>
- </table>
- </div>
- </div>
- </div>
-</div>
-{% endblock content %}
diff --git a/src/templates/base/resource/hosts.html b/src/templates/base/resource/hosts.html
deleted file mode 100644
index 9fc50ce..0000000
--- a/src/templates/base/resource/hosts.html
+++ /dev/null
@@ -1,42 +0,0 @@
-{% extends "dashboard/table.html" %}
-{% load staticfiles %}
-
-{% block table %}
- <thead>
- <tr>
- <th>Name</th>
- <th>Profile</th>
- <th>Booked</th>
- <th>Working</th>
- </tr>
- </thead>
- <tbody>
- {% for host in hosts %}
- <tr>
- <td>
- {{ host.name }}
- </td>
- <td>
- <a href="profiles/{{ host.profile.id }}">{{ host.profile }}</a>
- </td>
- <td>
- {{ host.booked|yesno:"Yes,No" }}
- </td>
- <td>
- {{ host.working|yesno:"Yes,No" }}
- </td>
- </tr>
- {% endfor %}
- </tbody>
-{% endblock table %}
-
-{% block tablejs %}
- <script type="text/javascript">
- $(document).ready(function () {
- $('#table').DataTable({
- scrollX: true,
- "order": [[0, "asc"]]
- });
- });
- </script>
-{% endblock tablejs %}
diff --git a/src/templates/base/resource/mxClient.min.js b/src/templates/base/resource/mxClient.min.js
deleted file mode 100644
index 150a0ac..0000000
--- a/src/templates/base/resource/mxClient.min.js
+++ /dev/null
@@ -1,1808 +0,0 @@
-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&&(d[0].x=p.x),Math.abs(d[0].y-p.y)<k&&(d[0].y=p.y));t=g[q];null!=t&&null!=d[d.length-1]&&(Math.abs(d[d.length-1].x-t.x)<k&&(d[d.length-1].x=t.x),Math.abs(d[d.length-1].y-t.y)<k&&(d[d.length-1].y=t.y));var n=d[0],u=b,x=g[0],y=!1,A=!1,y=n;null!=x&&(x.x=Math.round(x.x),x.y=Math.round(x.y),u=null);for(r=0;2>r;r++){var z=null!=x&&x.x==y.x,v=null!=x&&x.y==y.y,B=null!=u&&y.y>=u.y&&y.y<=u.y+
-u.height,u=null!=u&&y.x>=u.x&&y.x<=u.x+u.width,y=v||null==x&&B,A=z||null==x&&u;if(0!=r||!(y&&A||z&&v)){if(null!=x&&!v&&!z&&(B||u)){m=B?!1:!0;break}if(A||y){m=y;1==r&&(m=0==d.length%2?y:A);break}}u=c;x=g[q];null!=x&&(x.x=Math.round(x.x),x.y=Math.round(x.y),u=null);y=d[d.length-1];z&&v&&(d=d.slice(1))}m&&(null!=g[0]&&g[0].y!=n.y||null==g[0]&&null!=b&&(n.y<b.y||n.y>b.y+b.height))?f(new mxPoint(p.x,n.y)):!m&&(null!=g[0]&&g[0].x!=n.x||null==g[0]&&null!=b&&(n.x<b.x||n.x>b.x+b.width))&&f(new mxPoint(n.x,
-p.y));m?p.y=n.y:p.x=n.x;for(r=0;r<d.length;r++)m=!m,n=d[r],m?p.y=n.y:p.x=n.x,f(p.clone())}else n=p,m=!0;p=g[q];null==p&&null!=c&&(p=new mxPoint(a.view.getRoutingCenterX(c),a.view.getRoutingCenterY(c)));null!=p&&(p.x=Math.round(p.x),p.y=Math.round(p.y),null!=n&&(m&&(null!=g[q]&&g[q].y!=n.y||null==g[q]&&null!=c&&(n.y<c.y||n.y>c.y+c.height))?f(new mxPoint(p.x,n.y)):!m&&(null!=g[q]&&g[q].x!=n.x||null==g[q]&&null!=c&&(n.x<c.x||n.x>c.x+c.width))&&f(new mxPoint(n.x,p.y))));if(null==g[0]&&null!=b)for(;1<
-e.length&&null!=e[1]&&mxUtils.contains(b,e[1].x,e[1].y);)e.splice(1,1);if(null==g[q]&&null!=c)for(;1<e.length&&null!=e[e.length-1]&&mxUtils.contains(c,e[e.length-1].x,e[e.length-1].y);)e.splice(e.length-1,1);null!=t&&null!=e[e.length-1]&&Math.abs(t.x-e[e.length-1].x)<k&&Math.abs(t.y-e[e.length-1].y)<k&&(e.splice(e.length-1,1),null!=e[e.length-1]&&(Math.abs(e[e.length-1].x-t.x)<k&&(e[e.length-1].x=t.x),Math.abs(e[e.length-1].y-t.y)<k&&(e[e.length-1].y=t.y)))},orthBuffer:10,orthPointsFallback:!0,dirVectors:[[-1,
-0],[0,-1],[1,0],[0,1],[-1,0],[0,-1],[1,0]],wayPoints1:[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],routePatterns:[[[513,2308,2081,2562],[513,1090,514,2184,2114,2561],[513,1090,514,2564,2184,2562],[513,2308,2561,1090,514,2568,2308]],[[514,1057,513,2308,2081,2562],[514,2184,2114,2561],[514,2184,2562,1057,513,2564,2184],[514,1057,513,2568,2308,2561]],[[1090,514,1057,513,2308,2081,2562],[2114,2561],[1090,2562,1057,513,2564,2184],[1090,514,1057,513,2308,2561,2568]],[[2081,
-2562],[1057,513,1090,514,2184,2114,2561],[1057,513,1090,514,2184,2562,2564],[1057,2561,1090,514,2568,2308]]],inlineRoutePatterns:[[null,[2114,2568],null,null],[null,[514,2081,2114,2568],null,null],[null,[2114,2561],null,null],[[2081,2562],[1057,2114,2568],[2184,2562],null]],vertexSeperations:[],limits:[[0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0]],LEFT_MASK:32,TOP_MASK:64,RIGHT_MASK:128,BOTTOM_MASK:256,LEFT:1,TOP:2,RIGHT:4,BOTTOM:8,SIDE_MASK:480,CENTER_MASK:512,SOURCE_MASK:1024,TARGET_MASK:2048,VERTEX_MASK:3072,
-getJettySize:function(a,b,c,d,e){b=mxUtils.getValue(a.style,e?mxConstants.STYLE_SOURCE_JETTY_SIZE:mxConstants.STYLE_TARGET_JETTY_SIZE,mxUtils.getValue(a.style,mxConstants.STYLE_JETTY_SIZE,mxEdgeStyle.orthBuffer));"auto"==b&&(mxUtils.getValue(a.style,e?mxConstants.STYLE_STARTARROW:mxConstants.STYLE_ENDARROW,mxConstants.NONE)!=mxConstants.NONE?(a=mxUtils.getNumber(a.style,e?mxConstants.STYLE_STARTSIZE:mxConstants.STYLE_ENDSIZE,mxConstants.DEFAULT_MARKERSIZE),b=Math.max(2,Math.ceil((a+mxEdgeStyle.orthBuffer)/
-mxEdgeStyle.orthBuffer))*mxEdgeStyle.orthBuffer):b=2*mxEdgeStyle.orthBuffer);return b},OrthConnector:function(a,b,c,d,e){var f=a.view.graph,g=null==b?!1:f.getModel().isEdge(b.cell),k=null==c?!1:f.getModel().isEdge(c.cell),f=a.absolutePoints,l=f[0],m=f[f.length-1],n=null!=b?b.x:l.x,p=null!=b?b.y:l.y,q=null!=b?b.width:0,r=null!=b?b.height:0,t=null!=c?c.x:m.x,u=null!=c?c.y:m.y,x=null!=c?c.width:0,y=null!=c?c.height:0,f=a.view.scale*mxEdgeStyle.getJettySize(a,b,c,d,!0),A=a.view.scale*mxEdgeStyle.getJettySize(a,
-b,c,d,!1);null!=b&&c==b&&(f=A=Math.max(f,A));var z=A+f,v=!1;if(null!=l&&null!=m)var v=m.x-l.x,B=m.y-l.y,v=v*v+B*B<z*z;if(v||mxEdgeStyle.orthPointsFallback&&null!=d&&0<d.length||g||k)mxEdgeStyle.SegmentConnector(a,b,c,d,e);else{d=[mxConstants.DIRECTION_MASK_ALL,mxConstants.DIRECTION_MASK_ALL];null!=b&&(d[0]=mxUtils.getPortConstraints(b,a,!0,mxConstants.DIRECTION_MASK_ALL),v=mxUtils.getValue(b.style,mxConstants.STYLE_ROTATION,0),0!=v&&(v=mxUtils.getBoundingBox(new mxRectangle(n,p,q,r),v),n=v.x,p=v.y,
-q=v.width,r=v.height));null!=c&&(d[1]=mxUtils.getPortConstraints(c,a,!1,mxConstants.DIRECTION_MASK_ALL),v=mxUtils.getValue(c.style,mxConstants.STYLE_ROTATION,0),0!=v&&(v=mxUtils.getBoundingBox(new mxRectangle(t,u,x,y),v),t=v.x,u=v.y,x=v.width,y=v.height));n=Math.round(10*n)/10;p=Math.round(10*p)/10;q=Math.round(10*q)/10;r=Math.round(10*r)/10;t=Math.round(10*t)/10;u=Math.round(10*u)/10;x=Math.round(10*x)/10;y=Math.round(10*y)/10;a=[0,0];n=[[n,p,q,r],[t,u,x,y]];A=[f,A];for(v=0;2>v;v++)mxEdgeStyle.limits[v][1]=
-n[v][0]-A[v],mxEdgeStyle.limits[v][2]=n[v][1]-A[v],mxEdgeStyle.limits[v][4]=n[v][0]+n[v][2]+A[v],mxEdgeStyle.limits[v][8]=n[v][1]+n[v][3]+A[v];A=n[0][1]+n[0][3]/2;p=n[1][1]+n[1][3]/2;v=n[0][0]+n[0][2]/2-(n[1][0]+n[1][2]/2);B=A-p;A=0;0>v?A=0>B?2:1:0>=B&&(A=3,0==v&&(A=2));p=null;null!=b&&(p=l);b=[[.5,.5],[.5,.5]];for(v=0;2>v;v++)null!=p&&(b[v][0]=(p.x-n[v][0])/n[v][2],1>=Math.abs(p.x-n[v][0])?a[v]=mxConstants.DIRECTION_MASK_WEST:1>=Math.abs(p.x-n[v][0]-n[v][2])&&(a[v]=mxConstants.DIRECTION_MASK_EAST),
-b[v][1]=(p.y-n[v][1])/n[v][3],1>=Math.abs(p.y-n[v][1])?a[v]=mxConstants.DIRECTION_MASK_NORTH:1>=Math.abs(p.y-n[v][1]-n[v][3])&&(a[v]=mxConstants.DIRECTION_MASK_SOUTH)),p=null,null!=c&&(p=m);v=n[0][1]-(n[1][1]+n[1][3]);m=n[0][0]-(n[1][0]+n[1][2]);p=n[1][1]-(n[0][1]+n[0][3]);q=n[1][0]-(n[0][0]+n[0][2]);mxEdgeStyle.vertexSeperations[1]=Math.max(m-z,0);mxEdgeStyle.vertexSeperations[2]=Math.max(v-z,0);mxEdgeStyle.vertexSeperations[4]=Math.max(p-z,0);mxEdgeStyle.vertexSeperations[3]=Math.max(q-z,0);z=[];
-c=[];l=[];c[0]=m>=q?mxConstants.DIRECTION_MASK_WEST:mxConstants.DIRECTION_MASK_EAST;l[0]=v>=p?mxConstants.DIRECTION_MASK_NORTH:mxConstants.DIRECTION_MASK_SOUTH;c[1]=mxUtils.reversePortConstraints(c[0]);l[1]=mxUtils.reversePortConstraints(l[0]);m=m>=q?m:q;p=v>=p?v:p;q=[[0,0],[0,0]];r=!1;for(v=0;2>v;v++)0==a[v]&&(0==(c[v]&d[v])&&(c[v]=mxUtils.reversePortConstraints(c[v])),0==(l[v]&d[v])&&(l[v]=mxUtils.reversePortConstraints(l[v])),q[v][0]=l[v],q[v][1]=c[v]);0<p&&0<m&&(0<(c[0]&d[0])&&0<(l[1]&d[1])?(q[0][0]=
-c[0],q[0][1]=l[0],q[1][0]=l[1],q[1][1]=c[1],r=!0):0<(l[0]&d[0])&&0<(c[1]&d[1])&&(q[0][0]=l[0],q[0][1]=c[0],q[1][0]=c[1],q[1][1]=l[1],r=!0));0<p&&!r&&(q[0][0]=l[0],q[0][1]=c[0],q[1][0]=l[1],q[1][1]=c[1],r=!0);0<m&&!r&&(q[0][0]=c[0],q[0][1]=l[0],q[1][0]=c[1],q[1][1]=l[1]);for(v=0;2>v;v++)0==a[v]&&(0==(q[v][0]&d[v])&&(q[v][0]=q[v][1]),z[v]=q[v][0]&d[v],z[v]|=(q[v][1]&d[v])<<8,z[v]|=(q[1-v][v]&d[v])<<16,z[v]|=(q[1-v][1-v]&d[v])<<24,0==(z[v]&15)&&(z[v]<<=8),0==(z[v]&3840)&&(z[v]=z[v]&15|z[v]>>8),0==(z[v]&
-983040)&&(z[v]=z[v]&65535|(z[v]&251658240)>>8),a[v]=z[v]&15,d[v]==mxConstants.DIRECTION_MASK_WEST||d[v]==mxConstants.DIRECTION_MASK_NORTH||d[v]==mxConstants.DIRECTION_MASK_EAST||d[v]==mxConstants.DIRECTION_MASK_SOUTH)&&(a[v]=d[v]);d=a[0]==mxConstants.DIRECTION_MASK_EAST?3:a[0];z=a[1]==mxConstants.DIRECTION_MASK_EAST?3:a[1];d-=A;z-=A;1>d&&(d+=4);1>z&&(z+=4);d=mxEdgeStyle.routePatterns[d-1][z-1];mxEdgeStyle.wayPoints1[0][0]=n[0][0];mxEdgeStyle.wayPoints1[0][1]=n[0][1];switch(a[0]){case mxConstants.DIRECTION_MASK_WEST:mxEdgeStyle.wayPoints1[0][0]-=
-f;mxEdgeStyle.wayPoints1[0][1]+=b[0][1]*n[0][3];break;case mxConstants.DIRECTION_MASK_SOUTH:mxEdgeStyle.wayPoints1[0][0]+=b[0][0]*n[0][2];mxEdgeStyle.wayPoints1[0][1]+=n[0][3]+f;break;case mxConstants.DIRECTION_MASK_EAST:mxEdgeStyle.wayPoints1[0][0]+=n[0][2]+f;mxEdgeStyle.wayPoints1[0][1]+=b[0][1]*n[0][3];break;case mxConstants.DIRECTION_MASK_NORTH:mxEdgeStyle.wayPoints1[0][0]+=b[0][0]*n[0][2],mxEdgeStyle.wayPoints1[0][1]-=f}f=0;c=z=0<(a[0]&(mxConstants.DIRECTION_MASK_EAST|mxConstants.DIRECTION_MASK_WEST))?
-0:1;for(v=0;v<d.length;v++)l=d[v]&15,r=l==mxConstants.DIRECTION_MASK_EAST?3:l,r+=A,4<r&&(r-=4),m=mxEdgeStyle.dirVectors[r-1],l=0<r%2?0:1,l!=z&&(f++,mxEdgeStyle.wayPoints1[f][0]=mxEdgeStyle.wayPoints1[f-1][0],mxEdgeStyle.wayPoints1[f][1]=mxEdgeStyle.wayPoints1[f-1][1]),t=0<(d[v]&mxEdgeStyle.TARGET_MASK),u=0<(d[v]&mxEdgeStyle.SOURCE_MASK),p=(d[v]&mxEdgeStyle.SIDE_MASK)>>5,p<<=A,15<p&&(p>>=4),q=0<(d[v]&mxEdgeStyle.CENTER_MASK),(u||t)&&9>p?(r=u?0:1,p=q&&0==l?n[r][0]+b[r][0]*n[r][2]:q?n[r][1]+b[r][1]*
-n[r][3]:mxEdgeStyle.limits[r][p],0==l?(p=(p-mxEdgeStyle.wayPoints1[f][0])*m[0],0<p&&(mxEdgeStyle.wayPoints1[f][0]+=m[0]*p)):(p=(p-mxEdgeStyle.wayPoints1[f][1])*m[1],0<p&&(mxEdgeStyle.wayPoints1[f][1]+=m[1]*p))):q&&(mxEdgeStyle.wayPoints1[f][0]+=m[0]*Math.abs(mxEdgeStyle.vertexSeperations[r]/2),mxEdgeStyle.wayPoints1[f][1]+=m[1]*Math.abs(mxEdgeStyle.vertexSeperations[r]/2)),0<f&&mxEdgeStyle.wayPoints1[f][l]==mxEdgeStyle.wayPoints1[f-1][l]?f--:z=l;for(v=0;v<=f&&(v!=f||((0<(a[1]&(mxConstants.DIRECTION_MASK_EAST|
-mxConstants.DIRECTION_MASK_WEST))?0:1)==c?0:1)==(f+1)%2);v++)e.push(new mxPoint(Math.round(mxEdgeStyle.wayPoints1[v][0]),Math.round(mxEdgeStyle.wayPoints1[v][1])));for(a=1;a<e.length;)null==e[a-1]||null==e[a]||e[a-1].x!=e[a].x||e[a-1].y!=e[a].y?a++:e.splice(a,1)}},getRoutePattern:function(a,b,c,d){var e=a[0]==mxConstants.DIRECTION_MASK_EAST?3:a[0];a=a[1]==mxConstants.DIRECTION_MASK_EAST?3:a[1];e-=b;a-=b;1>e&&(e+=4);1>a&&(a+=4);b=routePatterns[e-1][a-1];0!=c&&0!=d||null==inlineRoutePatterns[e-1][a-
-1]||(b=inlineRoutePatterns[e-1][a-1]);return b}},mxStyleRegistry={values:[],putValue:function(a,b){mxStyleRegistry.values[a]=b},getValue:function(a){return mxStyleRegistry.values[a]},getName:function(a){for(var b in mxStyleRegistry.values)if(mxStyleRegistry.values[b]==a)return b;return null}};mxStyleRegistry.putValue(mxConstants.EDGESTYLE_ELBOW,mxEdgeStyle.ElbowConnector);mxStyleRegistry.putValue(mxConstants.EDGESTYLE_ENTITY_RELATION,mxEdgeStyle.EntityRelation);
-mxStyleRegistry.putValue(mxConstants.EDGESTYLE_LOOP,mxEdgeStyle.Loop);mxStyleRegistry.putValue(mxConstants.EDGESTYLE_SIDETOSIDE,mxEdgeStyle.SideToSide);mxStyleRegistry.putValue(mxConstants.EDGESTYLE_TOPTOBOTTOM,mxEdgeStyle.TopToBottom);mxStyleRegistry.putValue(mxConstants.EDGESTYLE_ORTHOGONAL,mxEdgeStyle.OrthConnector);mxStyleRegistry.putValue(mxConstants.EDGESTYLE_SEGMENT,mxEdgeStyle.SegmentConnector);mxStyleRegistry.putValue(mxConstants.PERIMETER_ELLIPSE,mxPerimeter.EllipsePerimeter);
-mxStyleRegistry.putValue(mxConstants.PERIMETER_RECTANGLE,mxPerimeter.RectanglePerimeter);mxStyleRegistry.putValue(mxConstants.PERIMETER_RHOMBUS,mxPerimeter.RhombusPerimeter);mxStyleRegistry.putValue(mxConstants.PERIMETER_TRIANGLE,mxPerimeter.TrianglePerimeter);mxStyleRegistry.putValue(mxConstants.PERIMETER_HEXAGON,mxPerimeter.HexagonPerimeter);function mxGraphView(a){this.graph=a;this.translate=new mxPoint;this.graphBounds=new mxRectangle;this.states=new mxDictionary}mxGraphView.prototype=new mxEventSource;
-mxGraphView.prototype.constructor=mxGraphView;mxGraphView.prototype.EMPTY_POINT=new mxPoint;mxGraphView.prototype.doneResource="none"!=mxClient.language?"done":"";mxGraphView.prototype.updatingDocumentResource="none"!=mxClient.language?"updatingDocument":"";mxGraphView.prototype.allowEval=!1;mxGraphView.prototype.captureDocumentGesture=!0;mxGraphView.prototype.optimizeVmlReflows=!0;mxGraphView.prototype.rendering=!0;mxGraphView.prototype.graph=null;mxGraphView.prototype.currentRoot=null;
-mxGraphView.prototype.graphBounds=null;mxGraphView.prototype.scale=1;mxGraphView.prototype.translate=null;mxGraphView.prototype.states=null;mxGraphView.prototype.updateStyle=!1;mxGraphView.prototype.lastNode=null;mxGraphView.prototype.lastHtmlNode=null;mxGraphView.prototype.lastForegroundNode=null;mxGraphView.prototype.lastForegroundHtmlNode=null;mxGraphView.prototype.getGraphBounds=function(){return this.graphBounds};mxGraphView.prototype.setGraphBounds=function(a){this.graphBounds=a};
-mxGraphView.prototype.getBounds=function(a){var b=null;if(null!=a&&0<a.length)for(var c=this.graph.getModel(),d=0;d<a.length;d++)if(c.isVertex(a[d])||c.isEdge(a[d])){var e=this.getState(a[d]);null!=e&&(null==b?b=mxRectangle.fromRectangle(e):b.add(e))}return b};mxGraphView.prototype.setCurrentRoot=function(a){if(this.currentRoot!=a){var b=new mxCurrentRootChange(this,a);b.execute();var c=new mxUndoableEdit(this,!1);c.add(b);this.fireEvent(new mxEventObject(mxEvent.UNDO,"edit",c));this.graph.sizeDidChange()}return a};
-mxGraphView.prototype.scaleAndTranslate=function(a,b,c){var d=this.scale,e=new mxPoint(this.translate.x,this.translate.y);if(this.scale!=a||this.translate.x!=b||this.translate.y!=c)this.scale=a,this.translate.x=b,this.translate.y=c,this.isEventsEnabled()&&(this.revalidate(),this.graph.sizeDidChange());this.fireEvent(new mxEventObject(mxEvent.SCALE_AND_TRANSLATE,"scale",a,"previousScale",d,"translate",this.translate,"previousTranslate",e))};mxGraphView.prototype.getScale=function(){return this.scale};
-mxGraphView.prototype.setScale=function(a){var b=this.scale;this.scale!=a&&(this.scale=a,this.isEventsEnabled()&&(this.revalidate(),this.graph.sizeDidChange()));this.fireEvent(new mxEventObject(mxEvent.SCALE,"scale",a,"previousScale",b))};mxGraphView.prototype.getTranslate=function(){return this.translate};
-mxGraphView.prototype.setTranslate=function(a,b){var c=new mxPoint(this.translate.x,this.translate.y);if(this.translate.x!=a||this.translate.y!=b)this.translate.x=a,this.translate.y=b,this.isEventsEnabled()&&(this.revalidate(),this.graph.sizeDidChange());this.fireEvent(new mxEventObject(mxEvent.TRANSLATE,"translate",this.translate,"previousTranslate",c))};mxGraphView.prototype.refresh=function(){null!=this.currentRoot&&this.clear();this.revalidate()};
-mxGraphView.prototype.revalidate=function(){this.invalidate();this.validate()};mxGraphView.prototype.clear=function(a,b,c){var d=this.graph.getModel();a=a||d.getRoot();b=null!=b?b:!1;c=null!=c?c:!0;this.removeState(a);if(c&&(b||a!=this.currentRoot)){c=d.getChildCount(a);for(var e=0;e<c;e++)this.clear(d.getChildAt(a,e),b)}else this.invalidate(a)};
-mxGraphView.prototype.invalidate=function(a,b,c){var d=this.graph.getModel();a=a||d.getRoot();b=null!=b?b:!0;c=null!=c?c:!0;var e=this.getState(a);null!=e&&(e.invalid=!0);if(!a.invalidating){a.invalidating=!0;if(b)for(var f=d.getChildCount(a),e=0;e<f;e++){var g=d.getChildAt(a,e);this.invalidate(g,b,c)}if(c)for(f=d.getEdgeCount(a),e=0;e<f;e++)this.invalidate(d.getEdgeAt(a,e),b,c);delete a.invalidating}};
-mxGraphView.prototype.validate=function(a){var b=mxLog.enter("mxGraphView.validate");window.status=mxResources.get(this.updatingDocumentResource)||this.updatingDocumentResource;this.resetValidationState();var c=null;this.optimizeVmlReflows&&null!=this.canvas&&null==this.textDiv&&(8==document.documentMode&&!mxClient.IS_EM||mxClient.IS_QUIRKS)&&(this.placeholder=document.createElement("div"),this.placeholder.style.position="absolute",this.placeholder.style.width=this.canvas.clientWidth+"px",this.placeholder.style.height=
-this.canvas.clientHeight+"px",this.canvas.parentNode.appendChild(this.placeholder),c=this.drawPane.style.display,this.canvas.style.display="none",this.textDiv=document.createElement("div"),this.textDiv.style.position="absolute",this.textDiv.style.whiteSpace="nowrap",this.textDiv.style.visibility="hidden",this.textDiv.style.display=mxClient.IS_QUIRKS?"inline":"inline-block",this.textDiv.style.zoom="1",document.body.appendChild(this.textDiv));a=this.getBoundingBox(this.validateCellState(this.validateCell(a||
-(null!=this.currentRoot?this.currentRoot:this.graph.getModel().getRoot()))));this.setGraphBounds(null!=a?a:this.getEmptyBounds());this.validateBackground();null!=c&&(this.canvas.style.display=c,this.textDiv.parentNode.removeChild(this.textDiv),null!=this.placeholder&&this.placeholder.parentNode.removeChild(this.placeholder),this.textDiv=null);this.resetValidationState();window.status=mxResources.get(this.doneResource)||this.doneResource;mxLog.leave("mxGraphView.validate",b)};
-mxGraphView.prototype.getEmptyBounds=function(){return new mxRectangle(this.translate.x*this.scale,this.translate.y*this.scale)};
-mxGraphView.prototype.getBoundingBox=function(a,b){b=null!=b?b:!0;var c=null;if(null!=a&&(null!=a.shape&&null!=a.shape.boundingBox&&(c=a.shape.boundingBox.clone()),null!=a.text&&null!=a.text.boundingBox&&(null!=c?c.add(a.text.boundingBox):c=a.text.boundingBox.clone()),b))for(var d=this.graph.getModel(),e=d.getChildCount(a.cell),f=0;f<e;f++){var g=this.getBoundingBox(this.getState(d.getChildAt(a.cell,f)));null!=g&&(null==c?c=g:c.add(g))}return c};
-mxGraphView.prototype.createBackgroundPageShape=function(a){return new mxRectangleShape(a,"white","black")};mxGraphView.prototype.validateBackground=function(){this.validateBackgroundImage();this.validateBackgroundPage()};
-mxGraphView.prototype.validateBackgroundImage=function(){var a=this.graph.getBackgroundImage();if(null!=a){if(null==this.backgroundImage||this.backgroundImage.image!=a.src){null!=this.backgroundImage&&this.backgroundImage.destroy();var b=new mxRectangle(0,0,1,1);this.backgroundImage=new mxImageShape(b,a.src);this.backgroundImage.dialect=this.graph.dialect;this.backgroundImage.init(this.backgroundPane);this.backgroundImage.redraw();8!=document.documentMode||mxClient.IS_EM||mxEvent.addGestureListeners(this.backgroundImage.node,
-mxUtils.bind(this,function(a){this.graph.fireMouseEvent(mxEvent.MOUSE_DOWN,new mxMouseEvent(a))}),mxUtils.bind(this,function(a){this.graph.fireMouseEvent(mxEvent.MOUSE_MOVE,new mxMouseEvent(a))}),mxUtils.bind(this,function(a){this.graph.fireMouseEvent(mxEvent.MOUSE_UP,new mxMouseEvent(a))}))}this.redrawBackgroundImage(this.backgroundImage,a)}else null!=this.backgroundImage&&(this.backgroundImage.destroy(),this.backgroundImage=null)};
-mxGraphView.prototype.validateBackgroundPage=function(){if(this.graph.pageVisible){var a=this.getBackgroundPageBounds();null==this.backgroundPageShape?(this.backgroundPageShape=this.createBackgroundPageShape(a),this.backgroundPageShape.scale=this.scale,this.backgroundPageShape.isShadow=!0,this.backgroundPageShape.dialect=this.graph.dialect,this.backgroundPageShape.init(this.backgroundPane),this.backgroundPageShape.redraw(),this.graph.nativeDblClickEnabled&&mxEvent.addListener(this.backgroundPageShape.node,
-"dblclick",mxUtils.bind(this,function(a){this.graph.dblClick(a)})),mxEvent.addGestureListeners(this.backgroundPageShape.node,mxUtils.bind(this,function(a){this.graph.fireMouseEvent(mxEvent.MOUSE_DOWN,new mxMouseEvent(a))}),mxUtils.bind(this,function(a){null!=this.graph.tooltipHandler&&this.graph.tooltipHandler.isHideOnHover()&&this.graph.tooltipHandler.hide();this.graph.isMouseDown&&!mxEvent.isConsumed(a)&&this.graph.fireMouseEvent(mxEvent.MOUSE_MOVE,new mxMouseEvent(a))}),mxUtils.bind(this,function(a){this.graph.fireMouseEvent(mxEvent.MOUSE_UP,
-new mxMouseEvent(a))}))):(this.backgroundPageShape.scale=this.scale,this.backgroundPageShape.bounds=a,this.backgroundPageShape.redraw())}else null!=this.backgroundPageShape&&(this.backgroundPageShape.destroy(),this.backgroundPageShape=null)};mxGraphView.prototype.getBackgroundPageBounds=function(){var a=this.graph.pageFormat,b=this.scale*this.graph.pageScale;return new mxRectangle(this.scale*this.translate.x,this.scale*this.translate.y,a.width*b,a.height*b)};
-mxGraphView.prototype.redrawBackgroundImage=function(a,b){a.scale=this.scale;a.bounds.x=this.scale*this.translate.x;a.bounds.y=this.scale*this.translate.y;a.bounds.width=this.scale*b.width;a.bounds.height=this.scale*b.height;a.redraw()};
-mxGraphView.prototype.validateCell=function(a,b){if(null!=a)if(b=(null!=b?b:!0)&&this.graph.isCellVisible(a),null==this.getState(a,b)||b)for(var c=this.graph.getModel(),d=c.getChildCount(a),e=0;e<d;e++)this.validateCell(c.getChildAt(a,e),b&&(!this.isCellCollapsed(a)||a==this.currentRoot));else this.removeState(a);return a};
-mxGraphView.prototype.validateCellState=function(a,b){b=null!=b?b:!0;var c=null;if(null!=a&&(c=this.getState(a),null!=c)){var d=this.graph.getModel();c.invalid&&(c.invalid=!1,null==c.style&&(c.style=this.graph.getCellStyle(c.cell)),a!=this.currentRoot&&this.validateCellState(d.getParent(a),!1),c.setVisibleTerminalState(this.validateCellState(this.getVisibleTerminal(a,!0),!1),!0),c.setVisibleTerminalState(this.validateCellState(this.getVisibleTerminal(a,!1),!1),!1),this.updateCellState(c),a==this.currentRoot||
-c.invalid||(this.graph.cellRenderer.redraw(c,!1,this.isRendering()),c.updateCachedBounds()));if(b&&!c.invalid){null!=c.shape&&this.stateValidated(c);for(var e=d.getChildCount(a),f=0;f<e;f++)this.validateCellState(d.getChildAt(a,f))}}return c};
-mxGraphView.prototype.updateCellState=function(a){a.absoluteOffset.x=0;a.absoluteOffset.y=0;a.origin.x=0;a.origin.y=0;a.length=0;if(a.cell!=this.currentRoot){var b=this.graph.getModel(),c=this.getState(b.getParent(a.cell));null!=c&&c.cell!=this.currentRoot&&(a.origin.x+=c.origin.x,a.origin.y+=c.origin.y);var d=this.graph.getChildOffsetForCell(a.cell);null!=d&&(a.origin.x+=d.x,a.origin.y+=d.y);var e=this.graph.getCellGeometry(a.cell);null!=e&&(b.isEdge(a.cell)||(d=e.offset||this.EMPTY_POINT,e.relative&&
-null!=c?b.isEdge(c.cell)?(d=this.getPoint(c,e),null!=d&&(a.origin.x+=d.x/this.scale-c.origin.x-this.translate.x,a.origin.y+=d.y/this.scale-c.origin.y-this.translate.y)):(a.origin.x+=e.x*c.width/this.scale+d.x,a.origin.y+=e.y*c.height/this.scale+d.y):(a.absoluteOffset.x=this.scale*d.x,a.absoluteOffset.y=this.scale*d.y,a.origin.x+=e.x,a.origin.y+=e.y)),a.x=this.scale*(this.translate.x+a.origin.x),a.y=this.scale*(this.translate.y+a.origin.y),a.width=this.scale*e.width,a.unscaledWidth=e.width,a.height=
-this.scale*e.height,b.isVertex(a.cell)&&this.updateVertexState(a,e),b.isEdge(a.cell)&&this.updateEdgeState(a,e))}a.updateCachedBounds()};mxGraphView.prototype.isCellCollapsed=function(a){return this.graph.isCellCollapsed(a)};
-mxGraphView.prototype.updateVertexState=function(a,b){var c=this.graph.getModel(),d=this.getState(c.getParent(a.cell));if(b.relative&&null!=d&&!c.isEdge(d.cell)){var e=mxUtils.toRadians(d.style[mxConstants.STYLE_ROTATION]||"0");if(0!=e){var c=Math.cos(e),e=Math.sin(e),f=new mxPoint(a.getCenterX(),a.getCenterY()),d=new mxPoint(d.getCenterX(),d.getCenterY()),d=mxUtils.getRotatedPoint(f,c,e,d);a.x=d.x-a.width/2;a.y=d.y-a.height/2}}this.updateVertexLabelOffset(a)};
-mxGraphView.prototype.updateEdgeState=function(a,b){var c=a.getVisibleTerminalState(!0),d=a.getVisibleTerminalState(!1);null!=this.graph.model.getTerminal(a.cell,!0)&&null==c||null==c&&null==b.getTerminalPoint(!0)||null!=this.graph.model.getTerminal(a.cell,!1)&&null==d||null==d&&null==b.getTerminalPoint(!1)?this.clear(a.cell,!0):(this.updateFixedTerminalPoints(a,c,d),this.updatePoints(a,b.points,c,d),this.updateFloatingTerminalPoints(a,c,d),c=a.absolutePoints,a.cell!=this.currentRoot&&(null==c||2>
-c.length||null==c[0]||null==c[c.length-1])?this.clear(a.cell,!0):(this.updateEdgeBounds(a),this.updateEdgeLabelOffset(a)))};
-mxGraphView.prototype.updateVertexLabelOffset=function(a){var b=mxUtils.getValue(a.style,mxConstants.STYLE_LABEL_POSITION,mxConstants.ALIGN_CENTER);if(b==mxConstants.ALIGN_LEFT)b=mxUtils.getValue(a.style,mxConstants.STYLE_LABEL_WIDTH,null),b=null!=b?b*this.scale:a.width,a.absoluteOffset.x-=b;else if(b==mxConstants.ALIGN_RIGHT)a.absoluteOffset.x+=a.width;else if(b==mxConstants.ALIGN_CENTER&&(b=mxUtils.getValue(a.style,mxConstants.STYLE_LABEL_WIDTH,null),null!=b)){var c=mxUtils.getValue(a.style,mxConstants.STYLE_ALIGN,
-mxConstants.ALIGN_CENTER),d=0;c==mxConstants.ALIGN_CENTER?d=.5:c==mxConstants.ALIGN_RIGHT&&(d=1);0!=d&&(a.absoluteOffset.x-=(b*this.scale-a.width)*d)}b=mxUtils.getValue(a.style,mxConstants.STYLE_VERTICAL_LABEL_POSITION,mxConstants.ALIGN_MIDDLE);b==mxConstants.ALIGN_TOP?a.absoluteOffset.y-=a.height:b==mxConstants.ALIGN_BOTTOM&&(a.absoluteOffset.y+=a.height)};mxGraphView.prototype.resetValidationState=function(){this.lastForegroundHtmlNode=this.lastForegroundNode=this.lastHtmlNode=this.lastNode=null};
-mxGraphView.prototype.stateValidated=function(a){var b=this.graph.getModel().isEdge(a.cell)&&this.graph.keepEdgesInForeground||this.graph.getModel().isVertex(a.cell)&&this.graph.keepEdgesInBackground;a=this.graph.cellRenderer.insertStateAfter(a,b?this.lastForegroundNode||this.lastNode:this.lastNode,b?this.lastForegroundHtmlNode||this.lastHtmlNode:this.lastHtmlNode);b?(this.lastForegroundHtmlNode=a[1],this.lastForegroundNode=a[0]):(this.lastHtmlNode=a[1],this.lastNode=a[0])};
-mxGraphView.prototype.updateFixedTerminalPoints=function(a,b,c){this.updateFixedTerminalPoint(a,b,!0,this.graph.getConnectionConstraint(a,b,!0));this.updateFixedTerminalPoint(a,c,!1,this.graph.getConnectionConstraint(a,c,!1))};mxGraphView.prototype.updateFixedTerminalPoint=function(a,b,c,d){a.setAbsoluteTerminalPoint(this.getFixedTerminalPoint(a,b,c,d),c)};
-mxGraphView.prototype.getFixedTerminalPoint=function(a,b,c,d){var e=null;null!=d&&(e=this.graph.getConnectionPoint(b,d));if(null==e&&null==b){b=this.scale;d=this.translate;var f=a.origin,e=this.graph.getCellGeometry(a.cell).getTerminalPoint(c);null!=e&&(e=new mxPoint(b*(d.x+e.x+f.x),b*(d.y+e.y+f.y)))}return e};
-mxGraphView.prototype.updateBoundsFromStencil=function(a){var b=null;if(null!=a&&null!=a.shape&&null!=a.shape.stencil&&"fixed"==a.shape.stencil.aspect){var b=mxRectangle.fromRectangle(a),c=a.shape.stencil.computeAspect(a.style,a.x,a.y,a.width,a.height);a.setRect(c.x,c.y,a.shape.stencil.w0*c.width,a.shape.stencil.h0*c.height)}return b};
-mxGraphView.prototype.updatePoints=function(a,b,c,d){if(null!=a){var e=[];e.push(a.absolutePoints[0]);var f=this.getEdgeStyle(a,b,c,d);if(null!=f){c=this.getTerminalPort(a,c,!0);d=this.getTerminalPort(a,d,!1);var g=this.updateBoundsFromStencil(c),k=this.updateBoundsFromStencil(d);f(a,c,d,b,e);null!=g&&c.setRect(g.x,g.y,g.width,g.height);null!=k&&d.setRect(k.x,k.y,k.width,k.height)}else if(null!=b)for(f=0;f<b.length;f++)null!=b[f]&&(c=mxUtils.clone(b[f]),e.push(this.transformControlPoint(a,c)));b=
-a.absolutePoints;e.push(b[b.length-1]);a.absolutePoints=e}};mxGraphView.prototype.transformControlPoint=function(a,b){if(null!=a&&null!=b){var c=a.origin;return new mxPoint(this.scale*(b.x+this.translate.x+c.x),this.scale*(b.y+this.translate.y+c.y))}return null};
-mxGraphView.prototype.isLoopStyleEnabled=function(a,b,c,d){var e=this.graph.getConnectionConstraint(a,c,!0),f=this.graph.getConnectionConstraint(a,d,!1);return!(null==b||2>b.length)||mxUtils.getValue(a.style,mxConstants.STYLE_ORTHOGONAL_LOOP,!1)&&(null!=e&&null!=e.point||null!=f&&null!=f.point)?!1:null!=c&&c==d};
-mxGraphView.prototype.getEdgeStyle=function(a,b,c,d){a=this.isLoopStyleEnabled(a,b,c,d)?mxUtils.getValue(a.style,mxConstants.STYLE_LOOP,this.graph.defaultLoopStyle):mxUtils.getValue(a.style,mxConstants.STYLE_NOEDGESTYLE,!1)?null:a.style[mxConstants.STYLE_EDGE];"string"==typeof a&&(b=mxStyleRegistry.getValue(a),null==b&&this.isAllowEval()&&(b=mxUtils.eval(a)),a=b);return"function"==typeof a?a:null};
-mxGraphView.prototype.updateFloatingTerminalPoints=function(a,b,c){var d=a.absolutePoints,e=d[0];null==d[d.length-1]&&null!=c&&this.updateFloatingTerminalPoint(a,c,b,!1);null==e&&null!=b&&this.updateFloatingTerminalPoint(a,b,c,!0)};mxGraphView.prototype.updateFloatingTerminalPoint=function(a,b,c,d){a.setAbsoluteTerminalPoint(this.getFloatingTerminalPoint(a,b,c,d),d)};
-mxGraphView.prototype.getFloatingTerminalPoint=function(a,b,c,d){b=this.getTerminalPort(a,b,d);var e=this.getNextPoint(a,c,d),f=this.graph.isOrthogonal(a);c=mxUtils.toRadians(Number(b.style[mxConstants.STYLE_ROTATION]||"0"));var g=new mxPoint(b.getCenterX(),b.getCenterY());if(0!=c)var k=Math.cos(-c),l=Math.sin(-c),e=mxUtils.getRotatedPoint(e,k,l,g);k=parseFloat(a.style[mxConstants.STYLE_PERIMETER_SPACING]||0);k+=parseFloat(a.style[d?mxConstants.STYLE_SOURCE_PERIMETER_SPACING:mxConstants.STYLE_TARGET_PERIMETER_SPACING]||
-0);a=this.getPerimeterPoint(b,e,0==c&&f,k);0!=c&&(k=Math.cos(c),l=Math.sin(c),a=mxUtils.getRotatedPoint(a,k,l,g));return a};mxGraphView.prototype.getTerminalPort=function(a,b,c){a=mxUtils.getValue(a.style,c?mxConstants.STYLE_SOURCE_PORT:mxConstants.STYLE_TARGET_PORT);null!=a&&(a=this.getState(this.graph.getModel().getCell(a)),null!=a&&(b=a));return b};
-mxGraphView.prototype.getPerimeterPoint=function(a,b,c,d){var e=null;if(null!=a){var f=this.getPerimeterFunction(a);if(null!=f&&null!=b&&(d=this.getPerimeterBounds(a,d),0<d.width||0<d.height)){var e=new mxPoint(b.x,b.y),g=b=!1;this.graph.model.isVertex(a.cell)&&(b=1==mxUtils.getValue(a.style,mxConstants.STYLE_FLIPH,0),g=1==mxUtils.getValue(a.style,mxConstants.STYLE_FLIPV,0),null!=a.shape&&null!=a.shape.stencil&&(b=1==mxUtils.getValue(a.style,"stencilFlipH",0)||b,g=1==mxUtils.getValue(a.style,"stencilFlipV",
-0)||g),b&&(e.x=2*d.getCenterX()-e.x),g&&(e.y=2*d.getCenterY()-e.y));e=f(d,a,e,c);null!=e&&(b&&(e.x=2*d.getCenterX()-e.x),g&&(e.y=2*d.getCenterY()-e.y))}null==e&&(e=this.getPoint(a))}return e};mxGraphView.prototype.getRoutingCenterX=function(a){var b=null!=a.style?parseFloat(a.style[mxConstants.STYLE_ROUTING_CENTER_X])||0:0;return a.getCenterX()+b*a.width};
-mxGraphView.prototype.getRoutingCenterY=function(a){var b=null!=a.style?parseFloat(a.style[mxConstants.STYLE_ROUTING_CENTER_Y])||0:0;return a.getCenterY()+b*a.height};mxGraphView.prototype.getPerimeterBounds=function(a,b){b=null!=b?b:0;null!=a&&(b+=parseFloat(a.style[mxConstants.STYLE_PERIMETER_SPACING]||0));return a.getPerimeterBounds(b*this.scale)};
-mxGraphView.prototype.getPerimeterFunction=function(a){a=a.style[mxConstants.STYLE_PERIMETER];if("string"==typeof a){var b=mxStyleRegistry.getValue(a);null==b&&this.isAllowEval()&&(b=mxUtils.eval(a));a=b}return"function"==typeof a?a:null};mxGraphView.prototype.getNextPoint=function(a,b,c){a=a.absolutePoints;var d=null;null!=a&&2<=a.length&&(d=a.length,d=a[c?Math.min(1,d-1):Math.max(0,d-2)]);null==d&&null!=b&&(d=new mxPoint(b.getCenterX(),b.getCenterY()));return d};
-mxGraphView.prototype.getVisibleTerminal=function(a,b){for(var c=this.graph.getModel(),d=c.getTerminal(a,b),e=d;null!=d&&d!=this.currentRoot;){if(!this.graph.isCellVisible(e)||this.isCellCollapsed(d))e=d;d=c.getParent(d)}c.getParent(e)==c.getRoot()&&(e=null);return e};
-mxGraphView.prototype.updateEdgeBounds=function(a){var b=a.absolutePoints,c=b[0],d=b[b.length-1];if(c.x!=d.x||c.y!=d.y){var e=d.x-c.x,f=d.y-c.y;a.terminalDistance=Math.sqrt(e*e+f*f)}else a.terminalDistance=0;var d=0,g=[],f=c;if(null!=f){for(var c=f.x,k=f.y,l=c,m=k,n=1;n<b.length;n++){var p=b[n];null!=p&&(e=f.x-p.x,f=f.y-p.y,e=Math.sqrt(e*e+f*f),g.push(e),d+=e,f=p,c=Math.min(f.x,c),k=Math.min(f.y,k),l=Math.max(f.x,l),m=Math.max(f.y,m))}a.length=d;a.segments=g;a.x=c;a.y=k;a.width=Math.max(1,l-c);a.height=
-Math.max(1,m-k)}};
-mxGraphView.prototype.getPoint=function(a,b){var c=a.getCenterX(),d=a.getCenterY();if(null==a.segments||null!=b&&!b.relative)null!=b&&(m=b.offset,null!=m&&(c+=m.x,d+=m.y));else{for(var e=a.absolutePoints.length,f=Math.round(((null!=b?b.x/2:0)+.5)*a.length),g=a.segments[0],k=0,l=1;f>=Math.round(k+g)&&l<e-1;)k+=g,g=a.segments[l++];e=0==g?0:(f-k)/g;f=a.absolutePoints[l-1];l=a.absolutePoints[l];if(null!=f&&null!=l){k=c=d=0;if(null!=b){var d=b.y,m=b.offset;null!=m&&(c=m.x,k=m.y)}m=l.x-f.x;l=l.y-f.y;c=
-f.x+m*e+((0==g?0:l/g)*d+c)*this.scale;d=f.y+l*e-((0==g?0:m/g)*d-k)*this.scale}}return new mxPoint(c,d)};
-mxGraphView.prototype.getRelativePoint=function(a,b,c){var d=this.graph.getModel().getGeometry(a.cell);if(null!=d){var e=a.absolutePoints.length;if(d.relative&&1<e){for(var d=a.length,f=a.segments,g=a.absolutePoints[0],k=a.absolutePoints[1],l=mxUtils.ptSegDistSq(g.x,g.y,k.x,k.y,b,c),m=0,n=0,p=0,q=2;q<e;q++)n+=f[q-2],k=a.absolutePoints[q],g=mxUtils.ptSegDistSq(g.x,g.y,k.x,k.y,b,c),g<=l&&(l=g,m=q-1,p=n),g=k;e=f[m];g=a.absolutePoints[m];k=a.absolutePoints[m+1];l=k.x;f=k.y;a=g.x-l;m=g.y-f;l=a-(b-l);f=
-m-(c-f);f=l*a+f*m;a=Math.sqrt(0>=f?0:f*f/(a*a+m*m));a>e&&(a=e);e=Math.sqrt(mxUtils.ptSegDistSq(g.x,g.y,k.x,k.y,b,c));-1==mxUtils.relativeCcw(g.x,g.y,k.x,k.y,b,c)&&(e=-e);return new mxPoint((d/2-p-a)/d*-2,e/this.scale)}}return new mxPoint};
-mxGraphView.prototype.updateEdgeLabelOffset=function(a){var b=a.absolutePoints;a.absoluteOffset.x=a.getCenterX();a.absoluteOffset.y=a.getCenterY();if(null!=b&&0<b.length&&null!=a.segments){var c=this.graph.getCellGeometry(a.cell);if(c.relative){var d=this.getPoint(a,c);null!=d&&(a.absoluteOffset=d)}else{var d=b[0],e=b[b.length-1];if(null!=d&&null!=e){var b=e.x-d.x,f=e.y-d.y,g=e=0,c=c.offset;null!=c&&(e=c.x,g=c.y);c=d.y+f/2+g*this.scale;a.absoluteOffset.x=d.x+b/2+e*this.scale;a.absoluteOffset.y=c}}}};
-mxGraphView.prototype.getState=function(a,b){b=b||!1;var c=null;null!=a&&(c=this.states.get(a),b&&(null==c||this.updateStyle)&&this.graph.isCellVisible(a)&&(null==c?(c=this.createState(a),this.states.put(a,c)):c.style=this.graph.getCellStyle(a)));return c};mxGraphView.prototype.isRendering=function(){return this.rendering};mxGraphView.prototype.setRendering=function(a){this.rendering=a};mxGraphView.prototype.isAllowEval=function(){return this.allowEval};
-mxGraphView.prototype.setAllowEval=function(a){this.allowEval=a};mxGraphView.prototype.getStates=function(){return this.states};mxGraphView.prototype.setStates=function(a){this.states=a};mxGraphView.prototype.getCellStates=function(a){if(null==a)return this.states;for(var b=[],c=0;c<a.length;c++){var d=this.getState(a[c]);null!=d&&b.push(d)}return b};
-mxGraphView.prototype.removeState=function(a){var b=null;null!=a&&(b=this.states.remove(a),null!=b&&(this.graph.cellRenderer.destroy(b),b.invalid=!0,b.destroy()));return b};mxGraphView.prototype.createState=function(a){return new mxCellState(this,a,this.graph.getCellStyle(a))};mxGraphView.prototype.getCanvas=function(){return this.canvas};mxGraphView.prototype.getBackgroundPane=function(){return this.backgroundPane};mxGraphView.prototype.getDrawPane=function(){return this.drawPane};
-mxGraphView.prototype.getOverlayPane=function(){return this.overlayPane};mxGraphView.prototype.getDecoratorPane=function(){return this.decoratorPane};mxGraphView.prototype.isContainerEvent=function(a){a=mxEvent.getSource(a);return a==this.graph.container||a.parentNode==this.backgroundPane||null!=a.parentNode&&a.parentNode.parentNode==this.backgroundPane||a==this.canvas.parentNode||a==this.canvas||a==this.backgroundPane||a==this.drawPane||a==this.overlayPane||a==this.decoratorPane};
-mxGraphView.prototype.isScrollEvent=function(a){var b=mxUtils.getOffset(this.graph.container);a=new mxPoint(a.clientX-b.x,a.clientY-b.y);var b=this.graph.container.offsetWidth,c=this.graph.container.clientWidth;if(b>c&&a.x>c+2&&a.x<=b)return!0;b=this.graph.container.offsetHeight;c=this.graph.container.clientHeight;return b>c&&a.y>c+2&&a.y<=b?!0:!1};
-mxGraphView.prototype.init=function(){this.installListeners();var a=this.graph;a.dialect==mxConstants.DIALECT_SVG?this.createSvg():a.dialect==mxConstants.DIALECT_VML?this.createVml():this.createHtml()};
-mxGraphView.prototype.installListeners=function(){var a=this.graph,b=a.container;if(null!=b){mxClient.IS_TOUCH&&(mxEvent.addListener(b,"gesturestart",mxUtils.bind(this,function(b){a.fireGestureEvent(b);mxEvent.consume(b)})),mxEvent.addListener(b,"gesturechange",mxUtils.bind(this,function(b){a.fireGestureEvent(b);mxEvent.consume(b)})),mxEvent.addListener(b,"gestureend",mxUtils.bind(this,function(b){a.fireGestureEvent(b);mxEvent.consume(b)})));mxEvent.addGestureListeners(b,mxUtils.bind(this,function(b){!this.isContainerEvent(b)||
-(mxClient.IS_IE||mxClient.IS_IE11||mxClient.IS_GC||mxClient.IS_OP||mxClient.IS_SF)&&this.isScrollEvent(b)||a.fireMouseEvent(mxEvent.MOUSE_DOWN,new mxMouseEvent(b))}),mxUtils.bind(this,function(b){this.isContainerEvent(b)&&a.fireMouseEvent(mxEvent.MOUSE_MOVE,new mxMouseEvent(b))}),mxUtils.bind(this,function(b){this.isContainerEvent(b)&&a.fireMouseEvent(mxEvent.MOUSE_UP,new mxMouseEvent(b))}));mxEvent.addListener(b,"dblclick",mxUtils.bind(this,function(b){this.isContainerEvent(b)&&a.dblClick(b)}));
-var c=function(c){var d=null;mxClient.IS_TOUCH&&(d=mxEvent.getClientX(c),c=mxEvent.getClientY(c),c=mxUtils.convertPoint(b,d,c),d=a.view.getState(a.getCellAt(c.x,c.y)));return d};a.addMouseListener({mouseDown:function(b,c){a.popupMenuHandler.hideMenu()},mouseMove:function(){},mouseUp:function(){}});this.moveHandler=mxUtils.bind(this,function(b){null!=a.tooltipHandler&&a.tooltipHandler.isHideOnHover()&&a.tooltipHandler.hide();this.captureDocumentGesture&&a.isMouseDown&&null!=a.container&&!this.isContainerEvent(b)&&
-"none"!=a.container.style.display&&"hidden"!=a.container.style.visibility&&!mxEvent.isConsumed(b)&&a.fireMouseEvent(mxEvent.MOUSE_MOVE,new mxMouseEvent(b,c(b)))});this.endHandler=mxUtils.bind(this,function(b){this.captureDocumentGesture&&a.isMouseDown&&null!=a.container&&!this.isContainerEvent(b)&&"none"!=a.container.style.display&&"hidden"!=a.container.style.visibility&&a.fireMouseEvent(mxEvent.MOUSE_UP,new mxMouseEvent(b))});mxEvent.addGestureListeners(document,null,this.moveHandler,this.endHandler)}};
-mxGraphView.prototype.createHtml=function(){var a=this.graph.container;null!=a&&(this.canvas=this.createHtmlPane("100%","100%"),this.canvas.style.overflow="hidden",this.backgroundPane=this.createHtmlPane("1px","1px"),this.drawPane=this.createHtmlPane("1px","1px"),this.overlayPane=this.createHtmlPane("1px","1px"),this.decoratorPane=this.createHtmlPane("1px","1px"),this.canvas.appendChild(this.backgroundPane),this.canvas.appendChild(this.drawPane),this.canvas.appendChild(this.overlayPane),this.canvas.appendChild(this.decoratorPane),
-a.appendChild(this.canvas),this.updateContainerStyle(a),mxClient.IS_QUIRKS&&(a=mxUtils.bind(this,function(a){a=this.getGraphBounds();this.updateHtmlCanvasSize(a.x+a.width+this.graph.border,a.y+a.height+this.graph.border)}),mxEvent.addListener(window,"resize",a)))};
-mxGraphView.prototype.updateHtmlCanvasSize=function(a,b){if(null!=this.graph.container){var c=this.graph.container.offsetHeight;this.canvas.style.width=this.graph.container.offsetWidth<a?a+"px":"100%";this.canvas.style.height=c<b?b+"px":"100%"}};mxGraphView.prototype.createHtmlPane=function(a,b){var c=document.createElement("DIV");null!=a&&null!=b?(c.style.position="absolute",c.style.left="0px",c.style.top="0px",c.style.width=a,c.style.height=b):c.style.position="relative";return c};
-mxGraphView.prototype.createVml=function(){var a=this.graph.container;if(null!=a){var b=a.offsetWidth,c=a.offsetHeight;this.canvas=this.createVmlPane(b,c);this.canvas.style.overflow="hidden";this.backgroundPane=this.createVmlPane(b,c);this.drawPane=this.createVmlPane(b,c);this.overlayPane=this.createVmlPane(b,c);this.decoratorPane=this.createVmlPane(b,c);this.canvas.appendChild(this.backgroundPane);this.canvas.appendChild(this.drawPane);this.canvas.appendChild(this.overlayPane);this.canvas.appendChild(this.decoratorPane);
-a.appendChild(this.canvas)}};mxGraphView.prototype.createVmlPane=function(a,b){var c=document.createElement(mxClient.VML_PREFIX+":group");c.style.position="absolute";c.style.left="0px";c.style.top="0px";c.style.width=a+"px";c.style.height=b+"px";c.setAttribute("coordsize",a+","+b);c.setAttribute("coordorigin","0,0");return c};
-mxGraphView.prototype.createSvg=function(){var a=this.graph.container;this.canvas=document.createElementNS(mxConstants.NS_SVG,"g");this.backgroundPane=document.createElementNS(mxConstants.NS_SVG,"g");this.canvas.appendChild(this.backgroundPane);this.drawPane=document.createElementNS(mxConstants.NS_SVG,"g");this.canvas.appendChild(this.drawPane);this.overlayPane=document.createElementNS(mxConstants.NS_SVG,"g");this.canvas.appendChild(this.overlayPane);this.decoratorPane=document.createElementNS(mxConstants.NS_SVG,
-"g");this.canvas.appendChild(this.decoratorPane);var b=document.createElementNS(mxConstants.NS_SVG,"svg");b.style.width="100%";b.style.height="100%";b.style.display="block";b.appendChild(this.canvas);if(mxClient.IS_IE||mxClient.IS_IE11)b.style.overflow="hidden";null!=a&&(a.appendChild(b),this.updateContainerStyle(a))};
-mxGraphView.prototype.updateContainerStyle=function(a){var b=mxUtils.getCurrentStyle(a);null!=b&&"static"==b.position&&(a.style.position="relative");mxClient.IS_POINTER&&(a.style.touchAction="none")};
-mxGraphView.prototype.destroy=function(){var a=null!=this.canvas?this.canvas.ownerSVGElement:null;null==a&&(a=this.canvas);null!=a&&null!=a.parentNode&&(this.clear(this.currentRoot,!0),mxEvent.removeGestureListeners(document,null,this.moveHandler,this.endHandler),mxEvent.release(this.graph.container),a.parentNode.removeChild(a),this.decoratorPane=this.overlayPane=this.drawPane=this.backgroundPane=this.canvas=this.endHandler=this.moveHandler=null)};
-function mxCurrentRootChange(a,b){this.view=a;this.previous=this.root=b;this.isUp=null==b;if(!this.isUp)for(var c=this.view.currentRoot,d=this.view.graph.getModel();null!=c;){if(c==b){this.isUp=!0;break}c=d.getParent(c)}}
-mxCurrentRootChange.prototype.execute=function(){var a=this.view.currentRoot;this.view.currentRoot=this.previous;this.previous=a;a=this.view.graph.getTranslateForRoot(this.view.currentRoot);null!=a&&(this.view.translate=new mxPoint(-a.x,-a.y));this.isUp?(this.view.clear(this.view.currentRoot,!0),this.view.validate()):this.view.refresh();this.view.fireEvent(new mxEventObject(this.isUp?mxEvent.UP:mxEvent.DOWN,"root",this.view.currentRoot,"previous",this.previous));this.isUp=!this.isUp};
-function mxGraph(a,b,c,d){this.mouseListeners=null;this.renderHint=c;this.dialect=mxClient.IS_SVG?mxConstants.DIALECT_SVG:c==mxConstants.RENDERING_HINT_EXACT&&mxClient.IS_VML?mxConstants.DIALECT_VML:c==mxConstants.RENDERING_HINT_FASTEST?mxConstants.DIALECT_STRICTHTML:c==mxConstants.RENDERING_HINT_FASTER?mxConstants.DIALECT_PREFERHTML:mxConstants.DIALECT_MIXEDHTML;this.model=null!=b?b:new mxGraphModel;this.multiplicities=[];this.imageBundles=[];this.cellRenderer=this.createCellRenderer();this.setSelectionModel(this.createSelectionModel());
-this.setStylesheet(null!=d?d:this.createStylesheet());this.view=this.createGraphView();this.graphModelChangeListener=mxUtils.bind(this,function(a,b){this.graphModelChanged(b.getProperty("edit").changes)});this.model.addListener(mxEvent.CHANGE,this.graphModelChangeListener);this.createHandlers();null!=a&&this.init(a);this.view.revalidate()}mxLoadResources?mxResources.add(mxClient.basePath+"/resources/graph"):mxClient.defaultBundles.push(mxClient.basePath+"/resources/graph");mxGraph.prototype=new mxEventSource;
-mxGraph.prototype.constructor=mxGraph;mxGraph.prototype.EMPTY_ARRAY=[];mxGraph.prototype.mouseListeners=null;mxGraph.prototype.isMouseDown=!1;mxGraph.prototype.model=null;mxGraph.prototype.view=null;mxGraph.prototype.stylesheet=null;mxGraph.prototype.selectionModel=null;mxGraph.prototype.cellEditor=null;mxGraph.prototype.cellRenderer=null;mxGraph.prototype.multiplicities=null;mxGraph.prototype.renderHint=null;mxGraph.prototype.dialect=null;mxGraph.prototype.gridSize=10;
-mxGraph.prototype.gridEnabled=!0;mxGraph.prototype.portsEnabled=!0;mxGraph.prototype.nativeDblClickEnabled=!0;mxGraph.prototype.doubleTapEnabled=!0;mxGraph.prototype.doubleTapTimeout=500;mxGraph.prototype.doubleTapTolerance=25;mxGraph.prototype.lastTouchY=0;mxGraph.prototype.lastTouchY=0;mxGraph.prototype.lastTouchTime=0;mxGraph.prototype.tapAndHoldEnabled=!0;mxGraph.prototype.tapAndHoldDelay=500;mxGraph.prototype.tapAndHoldInProgress=!1;mxGraph.prototype.tapAndHoldValid=!1;
-mxGraph.prototype.initialTouchX=0;mxGraph.prototype.initialTouchY=0;mxGraph.prototype.tolerance=4;mxGraph.prototype.defaultOverlap=.5;mxGraph.prototype.defaultParent=null;mxGraph.prototype.alternateEdgeStyle=null;mxGraph.prototype.backgroundImage=null;mxGraph.prototype.pageVisible=!1;mxGraph.prototype.pageBreaksVisible=!1;mxGraph.prototype.pageBreakColor="gray";mxGraph.prototype.pageBreakDashed=!0;mxGraph.prototype.minPageBreakDist=20;mxGraph.prototype.preferPageSize=!1;
-mxGraph.prototype.pageFormat=mxConstants.PAGE_FORMAT_A4_PORTRAIT;mxGraph.prototype.pageScale=1.5;mxGraph.prototype.enabled=!0;mxGraph.prototype.escapeEnabled=!0;mxGraph.prototype.invokesStopCellEditing=!0;mxGraph.prototype.enterStopsCellEditing=!1;mxGraph.prototype.useScrollbarsForPanning=!0;mxGraph.prototype.exportEnabled=!0;mxGraph.prototype.importEnabled=!0;mxGraph.prototype.cellsLocked=!1;mxGraph.prototype.cellsCloneable=!0;mxGraph.prototype.foldingEnabled=!0;mxGraph.prototype.cellsEditable=!0;
-mxGraph.prototype.cellsDeletable=!0;mxGraph.prototype.cellsMovable=!0;mxGraph.prototype.edgeLabelsMovable=!0;mxGraph.prototype.vertexLabelsMovable=!1;mxGraph.prototype.dropEnabled=!1;mxGraph.prototype.splitEnabled=!0;mxGraph.prototype.cellsResizable=!0;mxGraph.prototype.cellsBendable=!0;mxGraph.prototype.cellsSelectable=!0;mxGraph.prototype.cellsDisconnectable=!0;mxGraph.prototype.autoSizeCells=!1;mxGraph.prototype.autoSizeCellsOnAdd=!1;mxGraph.prototype.autoScroll=!0;
-mxGraph.prototype.ignoreScrollbars=!1;mxGraph.prototype.translateToScrollPosition=!1;mxGraph.prototype.timerAutoScroll=!1;mxGraph.prototype.allowAutoPanning=!1;mxGraph.prototype.autoExtend=!0;mxGraph.prototype.maximumGraphBounds=null;mxGraph.prototype.minimumGraphSize=null;mxGraph.prototype.minimumContainerSize=null;mxGraph.prototype.maximumContainerSize=null;mxGraph.prototype.resizeContainer=!1;mxGraph.prototype.border=0;mxGraph.prototype.keepEdgesInForeground=!1;
-mxGraph.prototype.keepEdgesInBackground=!1;mxGraph.prototype.allowNegativeCoordinates=!0;mxGraph.prototype.constrainChildren=!0;mxGraph.prototype.constrainRelativeChildren=!1;mxGraph.prototype.extendParents=!0;mxGraph.prototype.extendParentsOnAdd=!0;mxGraph.prototype.extendParentsOnMove=!1;mxGraph.prototype.recursiveResize=!1;mxGraph.prototype.collapseToPreferredSize=!0;mxGraph.prototype.zoomFactor=1.2;mxGraph.prototype.keepSelectionVisibleOnZoom=!1;mxGraph.prototype.centerZoom=!0;
-mxGraph.prototype.resetViewOnRootChange=!0;mxGraph.prototype.resetEdgesOnResize=!1;mxGraph.prototype.resetEdgesOnMove=!1;mxGraph.prototype.resetEdgesOnConnect=!0;mxGraph.prototype.allowLoops=!1;mxGraph.prototype.defaultLoopStyle=mxEdgeStyle.Loop;mxGraph.prototype.multigraph=!0;mxGraph.prototype.connectableEdges=!1;mxGraph.prototype.allowDanglingEdges=!0;mxGraph.prototype.cloneInvalidEdges=!1;mxGraph.prototype.disconnectOnMove=!0;mxGraph.prototype.labelsVisible=!0;mxGraph.prototype.htmlLabels=!1;
-mxGraph.prototype.swimlaneSelectionEnabled=!0;mxGraph.prototype.swimlaneNesting=!0;mxGraph.prototype.swimlaneIndicatorColorAttribute=mxConstants.STYLE_FILLCOLOR;mxGraph.prototype.imageBundles=null;mxGraph.prototype.minFitScale=.1;mxGraph.prototype.maxFitScale=8;mxGraph.prototype.panDx=0;mxGraph.prototype.panDy=0;mxGraph.prototype.collapsedImage=new mxImage(mxClient.imageBasePath+"/collapsed.gif",9,9);mxGraph.prototype.expandedImage=new mxImage(mxClient.imageBasePath+"/expanded.gif",9,9);
-mxGraph.prototype.warningImage=new mxImage(mxClient.imageBasePath+"/warning"+(mxClient.IS_MAC?".png":".gif"),16,16);mxGraph.prototype.alreadyConnectedResource="none"!=mxClient.language?"alreadyConnected":"";mxGraph.prototype.containsValidationErrorsResource="none"!=mxClient.language?"containsValidationErrors":"";mxGraph.prototype.collapseExpandResource="none"!=mxClient.language?"collapse-expand":"";
-mxGraph.prototype.init=function(a){this.container=a;this.cellEditor=this.createCellEditor();this.view.init();this.sizeDidChange();mxEvent.addListener(a,"mouseleave",mxUtils.bind(this,function(){null!=this.tooltipHandler&&this.tooltipHandler.hide()}));mxClient.IS_IE&&(mxEvent.addListener(window,"unload",mxUtils.bind(this,function(){this.destroy()})),mxEvent.addListener(a,"selectstart",mxUtils.bind(this,function(a){return this.isEditing()||!this.isMouseDown&&!mxEvent.isShiftDown(a)})));8==document.documentMode&&
-a.insertAdjacentHTML("beforeend","<"+mxClient.VML_PREFIX+':group style="DISPLAY: none;"></'+mxClient.VML_PREFIX+":group>")};
-mxGraph.prototype.createHandlers=function(){this.tooltipHandler=this.createTooltipHandler();this.tooltipHandler.setEnabled(!1);this.selectionCellsHandler=this.createSelectionCellsHandler();this.connectionHandler=this.createConnectionHandler();this.connectionHandler.setEnabled(!1);this.graphHandler=this.createGraphHandler();this.panningHandler=this.createPanningHandler();this.panningHandler.panningEnabled=!1;this.popupMenuHandler=this.createPopupMenuHandler()};
-mxGraph.prototype.createTooltipHandler=function(){return new mxTooltipHandler(this)};mxGraph.prototype.createSelectionCellsHandler=function(){return new mxSelectionCellsHandler(this)};mxGraph.prototype.createConnectionHandler=function(){return new mxConnectionHandler(this)};mxGraph.prototype.createGraphHandler=function(){return new mxGraphHandler(this)};mxGraph.prototype.createPanningHandler=function(){return new mxPanningHandler(this)};mxGraph.prototype.createPopupMenuHandler=function(){return new mxPopupMenuHandler(this)};
-mxGraph.prototype.createSelectionModel=function(){return new mxGraphSelectionModel(this)};mxGraph.prototype.createStylesheet=function(){return new mxStylesheet};mxGraph.prototype.createGraphView=function(){return new mxGraphView(this)};mxGraph.prototype.createCellRenderer=function(){return new mxCellRenderer};mxGraph.prototype.createCellEditor=function(){return new mxCellEditor(this)};mxGraph.prototype.getModel=function(){return this.model};mxGraph.prototype.getView=function(){return this.view};
-mxGraph.prototype.getStylesheet=function(){return this.stylesheet};mxGraph.prototype.setStylesheet=function(a){this.stylesheet=a};mxGraph.prototype.getSelectionModel=function(){return this.selectionModel};mxGraph.prototype.setSelectionModel=function(a){this.selectionModel=a};
-mxGraph.prototype.getSelectionCellsForChanges=function(a){for(var b=[],c=0;c<a.length;c++){var d=a[c];if(d.constructor!=mxRootChange){var e=null;d instanceof mxChildChange&&null==d.previous?e=d.child:null!=d.cell&&d.cell instanceof mxCell&&(e=d.cell);null!=e&&0>mxUtils.indexOf(b,e)&&b.push(e)}}return this.getModel().getTopmostCells(b)};
-mxGraph.prototype.graphModelChanged=function(a){for(var b=0;b<a.length;b++)this.processChange(a[b]);this.removeSelectionCells(this.getRemovedCellsForChanges(a));this.view.validate();this.sizeDidChange()};mxGraph.prototype.getRemovedCellsForChanges=function(a){for(var b=[],c=0;c<a.length;c++){var d=a[c];if(d instanceof mxRootChange)break;else d instanceof mxChildChange?null!=d.previous&&null==d.parent&&(b=b.concat(this.model.getDescendants(d.child))):d instanceof mxVisibleChange&&(b=b.concat(this.model.getDescendants(d.cell)))}return b};
-mxGraph.prototype.processChange=function(a){if(a instanceof mxRootChange)this.clearSelection(),this.setDefaultParent(null),this.removeStateForCell(a.previous),this.resetViewOnRootChange&&(this.view.scale=1,this.view.translate.x=0,this.view.translate.y=0),this.fireEvent(new mxEventObject(mxEvent.ROOT));else if(a instanceof mxChildChange){var b=this.model.getParent(a.child);this.view.invalidate(a.child,!0,!0);if(null==b||this.isCellCollapsed(b))this.view.invalidate(a.child,!0,!0),this.removeStateForCell(a.child),
-this.view.currentRoot==a.child&&this.home();b!=a.previous&&(null!=b&&this.view.invalidate(b,!1,!1),null!=a.previous&&this.view.invalidate(a.previous,!1,!1))}else a instanceof mxTerminalChange||a instanceof mxGeometryChange?(a instanceof mxTerminalChange||null==a.previous&&null!=a.geometry||null!=a.previous&&!a.previous.equals(a.geometry))&&this.view.invalidate(a.cell):a instanceof mxValueChange?this.view.invalidate(a.cell,!1,!1):a instanceof mxStyleChange?(this.view.invalidate(a.cell,!0,!0),a=this.view.getState(a.cell),
-null!=a&&(a.style=null)):null!=a.cell&&a.cell instanceof mxCell&&this.removeStateForCell(a.cell)};mxGraph.prototype.removeStateForCell=function(a){for(var b=this.model.getChildCount(a),c=0;c<b;c++)this.removeStateForCell(this.model.getChildAt(a,c));this.view.invalidate(a,!1,!0);this.view.removeState(a)};
-mxGraph.prototype.addCellOverlay=function(a,b){null==a.overlays&&(a.overlays=[]);a.overlays.push(b);var c=this.view.getState(a);null!=c&&this.cellRenderer.redraw(c);this.fireEvent(new mxEventObject(mxEvent.ADD_OVERLAY,"cell",a,"overlay",b));return b};mxGraph.prototype.getCellOverlays=function(a){return a.overlays};
-mxGraph.prototype.removeCellOverlay=function(a,b){if(null==b)this.removeCellOverlays(a);else{var c=mxUtils.indexOf(a.overlays,b);0<=c?(a.overlays.splice(c,1),0==a.overlays.length&&(a.overlays=null),c=this.view.getState(a),null!=c&&this.cellRenderer.redraw(c),this.fireEvent(new mxEventObject(mxEvent.REMOVE_OVERLAY,"cell",a,"overlay",b))):b=null}return b};
-mxGraph.prototype.removeCellOverlays=function(a){var b=a.overlays;if(null!=b){a.overlays=null;var c=this.view.getState(a);null!=c&&this.cellRenderer.redraw(c);for(c=0;c<b.length;c++)this.fireEvent(new mxEventObject(mxEvent.REMOVE_OVERLAY,"cell",a,"overlay",b[c]))}return b};mxGraph.prototype.clearCellOverlays=function(a){a=null!=a?a:this.model.getRoot();this.removeCellOverlays(a);for(var b=this.model.getChildCount(a),c=0;c<b;c++){var d=this.model.getChildAt(a,c);this.clearCellOverlays(d)}};
-mxGraph.prototype.setCellWarning=function(a,b,c,d){if(null!=b&&0<b.length)return c=null!=c?c:this.warningImage,b=new mxCellOverlay(c,"<font color=red>"+b+"</font>"),d&&b.addListener(mxEvent.CLICK,mxUtils.bind(this,function(b,c){this.isEnabled()&&this.setSelectionCell(a)})),this.addCellOverlay(a,b);this.removeCellOverlays(a);return null};mxGraph.prototype.startEditing=function(a){this.startEditingAtCell(null,a)};
-mxGraph.prototype.startEditingAtCell=function(a,b){null!=b&&mxEvent.isMultiTouchEvent(b)||(null==a&&(a=this.getSelectionCell(),null==a||this.isCellEditable(a)||(a=null)),null!=a&&(this.fireEvent(new mxEventObject(mxEvent.START_EDITING,"cell",a,"event",b)),this.cellEditor.startEditing(a,b),this.fireEvent(new mxEventObject(mxEvent.EDITING_STARTED,"cell",a,"event",b))))};mxGraph.prototype.getEditingValue=function(a,b){return this.convertValueToString(a)};
-mxGraph.prototype.stopEditing=function(a){this.cellEditor.stopEditing(a);this.fireEvent(new mxEventObject(mxEvent.EDITING_STOPPED,"cancel",a))};mxGraph.prototype.labelChanged=function(a,b,c){this.model.beginUpdate();try{var d=a.value;this.cellLabelChanged(a,b,this.isAutoSizeCell(a));this.fireEvent(new mxEventObject(mxEvent.LABEL_CHANGED,"cell",a,"value",b,"old",d,"event",c))}finally{this.model.endUpdate()}return a};
-mxGraph.prototype.cellLabelChanged=function(a,b,c){this.model.beginUpdate();try{this.model.setValue(a,b),c&&this.cellSizeUpdated(a,!1)}finally{this.model.endUpdate()}};mxGraph.prototype.escape=function(a){this.fireEvent(new mxEventObject(mxEvent.ESCAPE,"event",a))};
-mxGraph.prototype.click=function(a){var b=a.getEvent(),c=a.getCell(),d=new mxEventObject(mxEvent.CLICK,"event",b,"cell",c);a.isConsumed()&&d.consume();this.fireEvent(d);if(this.isEnabled()&&!mxEvent.isConsumed(b)&&!d.isConsumed())if(null!=c){if(this.isTransparentClickEvent(b)){var e=!1;a=this.getCellAt(a.graphX,a.graphY,null,null,null,mxUtils.bind(this,function(a){a=this.isCellSelected(a.cell);e=e||a;return!e||a}));null!=a&&(c=a)}this.selectCellForEvent(c,b)}else c=null,this.isSwimlaneSelectionEnabled()&&
-(c=this.getSwimlaneAt(a.getGraphX(),a.getGraphY())),null!=c?this.selectCellForEvent(c,b):this.isToggleEvent(b)||this.clearSelection()};mxGraph.prototype.dblClick=function(a,b){var c=new mxEventObject(mxEvent.DOUBLE_CLICK,"event",a,"cell",b);this.fireEvent(c);!this.isEnabled()||mxEvent.isConsumed(a)||c.isConsumed()||null==b||!this.isCellEditable(b)||this.isEditing(b)||(this.startEditingAtCell(b,a),mxEvent.consume(a))};
-mxGraph.prototype.tapAndHold=function(a){var b=a.getEvent(),c=new mxEventObject(mxEvent.TAP_AND_HOLD,"event",b,"cell",a.getCell());this.fireEvent(c);c.isConsumed()&&(this.panningHandler.panningTrigger=!1);this.isEnabled()&&!mxEvent.isConsumed(b)&&!c.isConsumed()&&this.connectionHandler.isEnabled()&&(b=this.view.getState(this.connectionHandler.marker.getCell(a)),null!=b&&(this.connectionHandler.marker.currentColor=this.connectionHandler.marker.validColor,this.connectionHandler.marker.markedState=b,
-this.connectionHandler.marker.mark(),this.connectionHandler.first=new mxPoint(a.getGraphX(),a.getGraphY()),this.connectionHandler.edgeState=this.connectionHandler.createEdgeState(a),this.connectionHandler.previous=b,this.connectionHandler.fireEvent(new mxEventObject(mxEvent.START,"state",this.connectionHandler.previous))))};
-mxGraph.prototype.scrollPointToVisible=function(a,b,c,d){if(this.timerAutoScroll||!this.ignoreScrollbars&&!mxUtils.hasScrollbars(this.container))this.allowAutoPanning&&!this.panningHandler.isActive()&&(null==this.panningManager&&(this.panningManager=this.createPanningManager()),this.panningManager.panTo(a+this.panDx,b+this.panDy));else{var e=this.container;d=null!=d?d:20;if(a>=e.scrollLeft&&b>=e.scrollTop&&a<=e.scrollLeft+e.clientWidth&&b<=e.scrollTop+e.clientHeight){var f=e.scrollLeft+e.clientWidth-
-a;if(f<d){if(a=e.scrollLeft,e.scrollLeft+=d-f,c&&a==e.scrollLeft){if(this.dialect==mxConstants.DIALECT_SVG){a=this.view.getDrawPane().ownerSVGElement;var g=this.container.scrollWidth+d-f}else g=Math.max(e.clientWidth,e.scrollWidth)+d-f,a=this.view.getCanvas();a.style.width=g+"px";e.scrollLeft+=d-f}}else f=a-e.scrollLeft,f<d&&(e.scrollLeft-=d-f);f=e.scrollTop+e.clientHeight-b;f<d?(a=e.scrollTop,e.scrollTop+=d-f,a==e.scrollTop&&c&&(this.dialect==mxConstants.DIALECT_SVG?(a=this.view.getDrawPane().ownerSVGElement,
-b=this.container.scrollHeight+d-f):(b=Math.max(e.clientHeight,e.scrollHeight)+d-f,a=this.view.getCanvas()),a.style.height=b+"px",e.scrollTop+=d-f)):(f=b-e.scrollTop,f<d&&(e.scrollTop-=d-f))}}};mxGraph.prototype.createPanningManager=function(){return new mxPanningManager(this)};
-mxGraph.prototype.getBorderSizes=function(){var a=mxUtils.getCurrentStyle(this.container);return new mxRectangle(mxUtils.parseCssNumber(a.paddingLeft)+("none"!=a.borderLeftStyle?mxUtils.parseCssNumber(a.borderLeftWidth):0),mxUtils.parseCssNumber(a.paddingTop)+("none"!=a.borderTopStyle?mxUtils.parseCssNumber(a.borderTopWidth):0),mxUtils.parseCssNumber(a.paddingRight)+("none"!=a.borderRightStyle?mxUtils.parseCssNumber(a.borderRightWidth):0),mxUtils.parseCssNumber(a.paddingBottom)+("none"!=a.borderBottomStyle?
-mxUtils.parseCssNumber(a.borderBottomWidth):0))};mxGraph.prototype.getPreferredPageSize=function(a,b,c){a=this.view.translate;var d=this.pageFormat,e=this.pageScale,d=new mxRectangle(0,0,Math.ceil(d.width*e),Math.ceil(d.height*e));return new mxRectangle(0,0,(this.pageBreaksVisible?Math.ceil(b/d.width):1)*d.width+2+a.x,(this.pageBreaksVisible?Math.ceil(c/d.height):1)*d.height+2+a.y)};
-mxGraph.prototype.fit=function(a,b,c,d,e,f,g){if(null!=this.container){a=null!=a?a:this.getBorder();b=null!=b?b:!1;c=null!=c?c:0;d=null!=d?d:!0;e=null!=e?e:!1;f=null!=f?f:!1;var k=this.getBorderSizes(),l=this.container.offsetWidth-k.x-k.width-1,m=null!=g?g:this.container.offsetHeight-k.y-k.height-1;g=this.view.getGraphBounds();if(0<g.width&&0<g.height){b&&null!=g.x&&null!=g.y&&(g=g.clone(),g.width+=g.x,g.height+=g.y,g.x=0,g.y=0);var k=this.view.scale,n=g.width/k,p=g.height/k;null!=this.backgroundImage&&
-(n=Math.max(n,this.backgroundImage.width-g.x/k),p=Math.max(p,this.backgroundImage.height-g.y/k));var q=(b?a:2*a)+c+1,l=l-q,m=m-q;e=e?m/p:f?l/n:Math.min(l/n,m/p);null!=this.minFitScale&&(e=Math.max(e,this.minFitScale));null!=this.maxFitScale&&(e=Math.min(e,this.maxFitScale));if(d)b?this.view.scale!=e&&this.view.setScale(e):mxUtils.hasScrollbars(this.container)?(this.view.setScale(e),a=this.getGraphBounds(),null!=a.x&&(this.container.scrollLeft=a.x),null!=a.y&&(this.container.scrollTop=a.y)):this.view.scaleAndTranslate(e,
-null!=g.x?Math.floor(this.view.translate.x-g.x/k+a/e+c/2):a,null!=g.y?Math.floor(this.view.translate.y-g.y/k+a/e+c/2):a);else return e}}return this.view.scale};
-mxGraph.prototype.sizeDidChange=function(){var a=this.getGraphBounds();if(null!=this.container){var b=this.getBorder(),c=Math.max(0,a.x+a.width+2*b*this.view.scale),b=Math.max(0,a.y+a.height+2*b*this.view.scale);null!=this.minimumContainerSize&&(c=Math.max(c,this.minimumContainerSize.width),b=Math.max(b,this.minimumContainerSize.height));this.resizeContainer&&this.doResizeContainer(c,b);if(this.preferPageSize||!mxClient.IS_IE&&this.pageVisible){var d=this.getPreferredPageSize(a,Math.max(1,c),Math.max(1,
-b));null!=d&&(c=d.width*this.view.scale,b=d.height*this.view.scale)}null!=this.minimumGraphSize&&(c=Math.max(c,this.minimumGraphSize.width*this.view.scale),b=Math.max(b,this.minimumGraphSize.height*this.view.scale));c=Math.ceil(c);b=Math.ceil(b);this.dialect==mxConstants.DIALECT_SVG?(d=this.view.getDrawPane().ownerSVGElement,d.style.minWidth=Math.max(1,c)+"px",d.style.minHeight=Math.max(1,b)+"px",d.style.width="100%",d.style.height="100%"):mxClient.IS_QUIRKS?this.view.updateHtmlCanvasSize(Math.max(1,
-c),Math.max(1,b)):(this.view.canvas.style.minWidth=Math.max(1,c)+"px",this.view.canvas.style.minHeight=Math.max(1,b)+"px");this.updatePageBreaks(this.pageBreaksVisible,c,b)}this.fireEvent(new mxEventObject(mxEvent.SIZE,"bounds",a))};mxGraph.prototype.doResizeContainer=function(a,b){null!=this.maximumContainerSize&&(a=Math.min(this.maximumContainerSize.width,a),b=Math.min(this.maximumContainerSize.height,b));this.container.style.width=Math.ceil(a)+"px";this.container.style.height=Math.ceil(b)+"px"};
-mxGraph.prototype.updatePageBreaks=function(a,b,c){b=this.view.scale;c=this.view.translate;var d=this.pageFormat,e=b*this.pageScale,f=new mxRectangle(0,0,d.width*e,d.height*e),d=mxRectangle.fromRectangle(this.getGraphBounds());d.width=Math.max(1,d.width);d.height=Math.max(1,d.height);f.x=Math.floor((d.x-c.x*b)/f.width)*f.width+c.x*b;f.y=Math.floor((d.y-c.y*b)/f.height)*f.height+c.y*b;d.width=Math.ceil((d.width+(d.x-f.x))/f.width)*f.width;d.height=Math.ceil((d.height+(d.y-f.y))/f.height)*f.height;
-var g=(a=a&&Math.min(f.width,f.height)>this.minPageBreakDist)?Math.ceil(d.height/f.height)+1:0,k=a?Math.ceil(d.width/f.width)+1:0,l=(k-1)*f.width,m=(g-1)*f.height;null==this.horizontalPageBreaks&&0<g&&(this.horizontalPageBreaks=[]);null==this.verticalPageBreaks&&0<k&&(this.verticalPageBreaks=[]);a=mxUtils.bind(this,function(a){if(null!=a){for(var b=a==this.horizontalPageBreaks?g:k,c=0;c<=b;c++){var d=a==this.horizontalPageBreaks?[new mxPoint(Math.round(f.x),Math.round(f.y+c*f.height)),new mxPoint(Math.round(f.x+
-l),Math.round(f.y+c*f.height))]:[new mxPoint(Math.round(f.x+c*f.width),Math.round(f.y)),new mxPoint(Math.round(f.x+c*f.width),Math.round(f.y+m))];null!=a[c]?(a[c].points=d,a[c].redraw()):(d=new mxPolyline(d,this.pageBreakColor),d.dialect=this.dialect,d.pointerEvents=!1,d.isDashed=this.pageBreakDashed,d.init(this.view.backgroundPane),d.redraw(),a[c]=d)}for(c=b;c<a.length;c++)a[c].destroy();a.splice(b,a.length-b)}});a(this.horizontalPageBreaks);a(this.verticalPageBreaks)};
-mxGraph.prototype.getCellStyle=function(a){var b=this.model.getStyle(a);a=this.model.isEdge(a)?this.stylesheet.getDefaultEdgeStyle():this.stylesheet.getDefaultVertexStyle();null!=b&&(a=this.postProcessCellStyle(this.stylesheet.getCellStyle(b,a)));null==a&&(a=mxGraph.prototype.EMPTY_ARRAY);return a};
-mxGraph.prototype.postProcessCellStyle=function(a){if(null!=a){var b=a[mxConstants.STYLE_IMAGE],c=this.getImageFromBundles(b);null!=c?a[mxConstants.STYLE_IMAGE]=c:c=b;null!=c&&"data:image/"==c.substring(0,11)&&("data:image/svg+xml,<"==c.substring(0,20)?c=c.substring(0,19)+encodeURIComponent(c.substring(19)):"data:image/svg+xml,%3C"!=c.substring(0,22)&&(b=c.indexOf(","),0<b&&";base64,"!=c.substring(b-7,b+1)&&(c=c.substring(0,b)+";base64,"+c.substring(b+1))),a[mxConstants.STYLE_IMAGE]=c)}return a};
-mxGraph.prototype.setCellStyle=function(a,b){b=b||this.getSelectionCells();if(null!=b){this.model.beginUpdate();try{for(var c=0;c<b.length;c++)this.model.setStyle(b[c],a)}finally{this.model.endUpdate()}}};mxGraph.prototype.toggleCellStyle=function(a,b,c){c=c||this.getSelectionCell();return this.toggleCellStyles(a,b,[c])};
-mxGraph.prototype.toggleCellStyles=function(a,b,c){b=null!=b?b:!1;c=c||this.getSelectionCells();var d=null;if(null!=c&&0<c.length){var e=this.view.getState(c[0]),e=null!=e?e.style:this.getCellStyle(c[0]);null!=e&&(d=mxUtils.getValue(e,a,b)?0:1,this.setCellStyles(a,d,c))}return d};mxGraph.prototype.setCellStyles=function(a,b,c){c=c||this.getSelectionCells();mxUtils.setCellStyles(this.model,c,a,b)};mxGraph.prototype.toggleCellStyleFlags=function(a,b,c){this.setCellStyleFlags(a,b,null,c)};
-mxGraph.prototype.setCellStyleFlags=function(a,b,c,d){d=d||this.getSelectionCells();if(null!=d&&0<d.length){if(null==c){var e=this.view.getState(d[0]),e=null!=e?e.style:this.getCellStyle(d[0]);null!=e&&(c=(parseInt(e[a]||0)&b)!=b)}mxUtils.setCellStyleFlags(this.model,d,a,b,c)}};
-mxGraph.prototype.alignCells=function(a,b,c){null==b&&(b=this.getSelectionCells());if(null!=b&&1<b.length){if(null==c)for(var d=0;d<b.length;d++){var e=this.view.getState(b[d]);if(null!=e&&!this.model.isEdge(b[d]))if(null==c)if(a==mxConstants.ALIGN_CENTER){c=e.x+e.width/2;break}else if(a==mxConstants.ALIGN_RIGHT)c=e.x+e.width;else if(a==mxConstants.ALIGN_TOP)c=e.y;else if(a==mxConstants.ALIGN_MIDDLE){c=e.y+e.height/2;break}else c=a==mxConstants.ALIGN_BOTTOM?e.y+e.height:e.x;else c=a==mxConstants.ALIGN_RIGHT?
-Math.max(c,e.x+e.width):a==mxConstants.ALIGN_TOP?Math.min(c,e.y):a==mxConstants.ALIGN_BOTTOM?Math.max(c,e.y+e.height):Math.min(c,e.x)}if(null!=c){var f=this.view.scale;this.model.beginUpdate();try{for(d=0;d<b.length;d++)if(e=this.view.getState(b[d]),null!=e){var g=this.getCellGeometry(b[d]);null==g||this.model.isEdge(b[d])||(g=g.clone(),a==mxConstants.ALIGN_CENTER?g.x+=(c-e.x-e.width/2)/f:a==mxConstants.ALIGN_RIGHT?g.x+=(c-e.x-e.width)/f:a==mxConstants.ALIGN_TOP?g.y+=(c-e.y)/f:a==mxConstants.ALIGN_MIDDLE?
-g.y+=(c-e.y-e.height/2)/f:a==mxConstants.ALIGN_BOTTOM?g.y+=(c-e.y-e.height)/f:g.x+=(c-e.x)/f,this.resizeCell(b[d],g))}this.fireEvent(new mxEventObject(mxEvent.ALIGN_CELLS,"align",a,"cells",b))}finally{this.model.endUpdate()}}}return b};
-mxGraph.prototype.flipEdge=function(a){if(null!=a&&null!=this.alternateEdgeStyle){this.model.beginUpdate();try{var b=this.model.getStyle(a);null==b||0==b.length?this.model.setStyle(a,this.alternateEdgeStyle):this.model.setStyle(a,null);this.resetEdge(a);this.fireEvent(new mxEventObject(mxEvent.FLIP_EDGE,"edge",a))}finally{this.model.endUpdate()}}return a};mxGraph.prototype.addImageBundle=function(a){this.imageBundles.push(a)};
-mxGraph.prototype.removeImageBundle=function(a){for(var b=[],c=0;c<this.imageBundles.length;c++)this.imageBundles[c]!=a&&b.push(this.imageBundles[c]);this.imageBundles=b};mxGraph.prototype.getImageFromBundles=function(a){if(null!=a)for(var b=0;b<this.imageBundles.length;b++){var c=this.imageBundles[b].getImage(a);if(null!=c)return c}return null};
-mxGraph.prototype.orderCells=function(a,b){null==b&&(b=mxUtils.sortCells(this.getSelectionCells(),!0));this.model.beginUpdate();try{this.cellsOrdered(b,a),this.fireEvent(new mxEventObject(mxEvent.ORDER_CELLS,"back",a,"cells",b))}finally{this.model.endUpdate()}return b};
-mxGraph.prototype.cellsOrdered=function(a,b){if(null!=a){this.model.beginUpdate();try{for(var c=0;c<a.length;c++){var d=this.model.getParent(a[c]);b?this.model.add(d,a[c],c):this.model.add(d,a[c],this.model.getChildCount(d)-1)}this.fireEvent(new mxEventObject(mxEvent.CELLS_ORDERED,"back",b,"cells",a))}finally{this.model.endUpdate()}}};
-mxGraph.prototype.groupCells=function(a,b,c){null==c&&(c=mxUtils.sortCells(this.getSelectionCells(),!0));c=this.getCellsForGroup(c);null==a&&(a=this.createGroupCell(c));var d=this.getBoundsForGroup(a,c,b);if(0<c.length&&null!=d){var e=this.model.getParent(a);null==e&&(e=this.model.getParent(c[0]));this.model.beginUpdate();try{null==this.getCellGeometry(a)&&this.model.setGeometry(a,new mxGeometry);var f=this.model.getChildCount(e);this.cellsAdded([a],e,f,null,null,!1,!1,!1);f=this.model.getChildCount(a);
-this.cellsAdded(c,a,f,null,null,!1,!1,!1);this.cellsMoved(c,-d.x,-d.y,!1,!1,!1);this.cellsResized([a],[d],!1);this.fireEvent(new mxEventObject(mxEvent.GROUP_CELLS,"group",a,"border",b,"cells",c))}finally{this.model.endUpdate()}}return a};mxGraph.prototype.getCellsForGroup=function(a){var b=[];if(null!=a&&0<a.length){var c=this.model.getParent(a[0]);b.push(a[0]);for(var d=1;d<a.length;d++)this.model.getParent(a[d])==c&&b.push(a[d])}return b};
-mxGraph.prototype.getBoundsForGroup=function(a,b,c){b=this.getBoundingBoxFromGeometry(b,!0);null!=b&&(this.isSwimlane(a)&&(a=this.getStartSize(a),b.x-=a.width,b.y-=a.height,b.width+=a.width,b.height+=a.height),null!=c&&(b.x-=c,b.y-=c,b.width+=2*c,b.height+=2*c));return b};mxGraph.prototype.createGroupCell=function(a){a=new mxCell("");a.setVertex(!0);a.setConnectable(!1);return a};
-mxGraph.prototype.ungroupCells=function(a){var b=[];if(null==a){a=this.getSelectionCells();for(var c=[],d=0;d<a.length;d++)0<this.model.getChildCount(a[d])&&c.push(a[d]);a=c}if(null!=a&&0<a.length){this.model.beginUpdate();try{for(d=0;d<a.length;d++){var e=this.model.getChildren(a[d]);if(null!=e&&0<e.length){var e=e.slice(),f=this.model.getParent(a[d]),g=this.model.getChildCount(f);this.cellsAdded(e,f,g,null,null,!0);b=b.concat(e)}}this.removeCellsAfterUngroup(a);this.fireEvent(new mxEventObject(mxEvent.UNGROUP_CELLS,
-"cells",a))}finally{this.model.endUpdate()}}return b};mxGraph.prototype.removeCellsAfterUngroup=function(a){this.cellsRemoved(this.addAllEdges(a))};mxGraph.prototype.removeCellsFromParent=function(a){null==a&&(a=this.getSelectionCells());this.model.beginUpdate();try{var b=this.getDefaultParent(),c=this.model.getChildCount(b);this.cellsAdded(a,b,c,null,null,!0);this.fireEvent(new mxEventObject(mxEvent.REMOVE_CELLS_FROM_PARENT,"cells",a))}finally{this.model.endUpdate()}return a};
-mxGraph.prototype.updateGroupBounds=function(a,b,c,d,e,f,g){null==a&&(a=this.getSelectionCells());b=null!=b?b:0;c=null!=c?c:!1;d=null!=d?d:0;e=null!=e?e:0;f=null!=f?f:0;g=null!=g?g:0;this.model.beginUpdate();try{for(var k=a.length-1;0<=k;k--){var l=this.getCellGeometry(a[k]);if(null!=l){var m=this.getChildCells(a[k]);if(null!=m&&0<m.length){var n=this.getBoundingBoxFromGeometry(m,!0);if(null!=n&&0<n.width&&0<n.height){var p=0,q=0;if(this.isSwimlane(a[k]))var r=this.getStartSize(a[k]),p=r.width,q=
-r.height;l=l.clone();c&&(l.x=Math.round(l.x+n.x-b-p-g),l.y=Math.round(l.y+n.y-b-q-d));l.width=Math.round(n.width+2*b+p+g+e);l.height=Math.round(n.height+2*b+q+d+f);this.model.setGeometry(a[k],l);this.moveCells(m,b+p-n.x+g,b+q-n.y+d)}}}}}finally{this.model.endUpdate()}return a};
-mxGraph.prototype.getBoundingBox=function(a){var b=null;if(null!=a&&0<a.length)for(var c=0;c<a.length;c++)if(this.model.isVertex(a[c])||this.model.isEdge(a[c])){var d=this.view.getBoundingBox(this.view.getState(a[c]),!0);null!=d&&(null==b?b=mxRectangle.fromRectangle(d):b.add(d))}return b};
-mxGraph.prototype.cloneCells=function(a,b,c){b=null!=b?b:!0;var d=null;if(null!=a){for(var e=new mxDictionary,d=[],f=0;f<a.length;f++)e.put(a[f],!0),d.push(a[f]);if(0<d.length)for(var g=this.view.scale,k=this.view.translate,d=this.model.cloneCells(a,!0,c),f=0;f<a.length;f++)if(!b&&this.model.isEdge(d[f])&&null!=this.getEdgeValidationError(d[f],this.model.getTerminal(d[f],!0),this.model.getTerminal(d[f],!1)))d[f]=null;else{var l=this.model.getGeometry(d[f]);if(null!=l){var m=this.view.getState(a[f]),
-n=this.view.getState(this.model.getParent(a[f]));if(null!=m&&null!=n)if(c=n.origin.x,n=n.origin.y,this.model.isEdge(d[f])){for(var m=m.absolutePoints,p=this.model.getTerminal(a[f],!0);null!=p&&!e.get(p);)p=this.model.getParent(p);null==p&&l.setTerminalPoint(new mxPoint(m[0].x/g-k.x,m[0].y/g-k.y),!0);for(p=this.model.getTerminal(a[f],!1);null!=p&&!e.get(p);)p=this.model.getParent(p);null==p&&(p=m.length-1,l.setTerminalPoint(new mxPoint(m[p].x/g-k.x,m[p].y/g-k.y),!1));l=l.points;if(null!=l)for(m=0;m<
-l.length;m++)l[m].x+=c,l[m].y+=n}else l.translate(c,n)}}else d=[]}return d};mxGraph.prototype.insertVertex=function(a,b,c,d,e,f,g,k,l){b=this.createVertex(a,b,c,d,e,f,g,k,l);return this.addCell(b,a)};mxGraph.prototype.createVertex=function(a,b,c,d,e,f,g,k,l){a=new mxGeometry(d,e,f,g);a.relative=null!=l?l:!1;c=new mxCell(c,a,k);c.setId(b);c.setVertex(!0);c.setConnectable(!0);return c};mxGraph.prototype.insertEdge=function(a,b,c,d,e,f){b=this.createEdge(a,b,c,d,e,f);return this.addEdge(b,a,d,e)};
-mxGraph.prototype.createEdge=function(a,b,c,d,e,f){a=new mxCell(c,new mxGeometry,f);a.setId(b);a.setEdge(!0);a.geometry.relative=!0;return a};mxGraph.prototype.addEdge=function(a,b,c,d,e){return this.addCell(a,b,e,c,d)};mxGraph.prototype.addCell=function(a,b,c,d,e){return this.addCells([a],b,c,d,e)[0]};
-mxGraph.prototype.addCells=function(a,b,c,d,e){null==b&&(b=this.getDefaultParent());null==c&&(c=this.model.getChildCount(b));this.model.beginUpdate();try{this.cellsAdded(a,b,c,d,e,!1,!0),this.fireEvent(new mxEventObject(mxEvent.ADD_CELLS,"cells",a,"parent",b,"index",c,"source",d,"target",e))}finally{this.model.endUpdate()}return a};
-mxGraph.prototype.cellsAdded=function(a,b,c,d,e,f,g,k){if(null!=a&&null!=b&&null!=c){this.model.beginUpdate();try{for(var l=f?this.view.getState(b):null,m=null!=l?l.origin:null,n=new mxPoint(0,0),l=0;l<a.length;l++)if(null==a[l])c--;else{var p=this.model.getParent(a[l]);if(null!=m&&a[l]!=b&&b!=p){var q=this.view.getState(p),r=null!=q?q.origin:n,t=this.model.getGeometry(a[l]);if(null!=t){var u=r.x-m.x,x=r.y-m.y,t=t.clone();t.translate(u,x);t.relative||!this.model.isVertex(a[l])||this.isAllowNegativeCoordinates()||
-(t.x=Math.max(0,t.x),t.y=Math.max(0,t.y));this.model.setGeometry(a[l],t)}}b==p&&c+l>this.model.getChildCount(b)&&c--;this.model.add(b,a[l],c+l);this.autoSizeCellsOnAdd&&this.autoSizeCell(a[l],!0);(null==k||k)&&this.isExtendParentsOnAdd(a[l])&&this.isExtendParent(a[l])&&this.extendParent(a[l]);(null==g||g)&&this.constrainChild(a[l]);null!=d&&this.cellConnected(a[l],d,!0);null!=e&&this.cellConnected(a[l],e,!1)}this.fireEvent(new mxEventObject(mxEvent.CELLS_ADDED,"cells",a,"parent",b,"index",c,"source",
-d,"target",e,"absolute",f))}finally{this.model.endUpdate()}}};mxGraph.prototype.autoSizeCell=function(a,b){if(null!=b?b:1)for(var c=this.model.getChildCount(a),d=0;d<c;d++)this.autoSizeCell(this.model.getChildAt(a,d));this.getModel().isVertex(a)&&this.isAutoSizeCell(a)&&this.updateCellSize(a)};
-mxGraph.prototype.removeCells=function(a,b){b=null!=b?b:!0;null==a&&(a=this.getDeletableCells(this.getSelectionCells()));b&&(a=this.getDeletableCells(this.addAllEdges(a)));this.model.beginUpdate();try{this.cellsRemoved(a),this.fireEvent(new mxEventObject(mxEvent.REMOVE_CELLS,"cells",a,"includeEdges",b))}finally{this.model.endUpdate()}return a};
-mxGraph.prototype.cellsRemoved=function(a){if(null!=a&&0<a.length){var b=this.view.scale,c=this.view.translate;this.model.beginUpdate();try{for(var d=new mxDictionary,e=0;e<a.length;e++)d.put(a[e],!0);for(e=0;e<a.length;e++){for(var f=this.getAllEdges([a[e]]),g=mxUtils.bind(this,function(d,g){var l=this.model.getGeometry(d);if(null!=l){var m=this.view.getState(d);if(null!=m){for(var q=m.getVisibleTerminal(g),r=!1;null!=q;){if(a[e]==q){r=!0;break}q=this.model.getParent(q)}if(r){var q=c.x,r=c.y,t=this.view.getState(this.model.getParent(d));
-null!=t&&this.model.isVertex(t.cell)&&(q=t.x/b,r=t.y/b);l=l.clone();m=m.absolutePoints;t=g?0:m.length-1;l.setTerminalPoint(new mxPoint(m[t].x/b-q,m[t].y/b-r),g);this.model.setTerminal(f[k],null,g);this.model.setGeometry(f[k],l)}}}}),k=0;k<f.length;k++)d.get(f[k])||(g(f[k],!0),g(f[k],!1));this.model.remove(a[e])}this.fireEvent(new mxEventObject(mxEvent.CELLS_REMOVED,"cells",a))}finally{this.model.endUpdate()}}};
-mxGraph.prototype.splitEdge=function(a,b,c,d,e){d=d||0;e=e||0;var f=this.model.getParent(a),g=this.model.getTerminal(a,!0);this.model.beginUpdate();try{if(null==c){c=this.cloneCells([a])[0];var k=this.view.getState(a),l=this.getCellGeometry(c);if(null!=l&&null!=l.points&&null!=k){var m=this.view.translate,n=this.view.scale,p=mxUtils.findNearestSegment(k,(d+m.x)*n,(e+m.y)*n);l.points=l.points.slice(0,p);l=this.getCellGeometry(a);null!=l&&null!=l.points&&(l=l.clone(),l.points=l.points.slice(p),this.model.setGeometry(a,
-l))}}this.cellsMoved(b,d,e,!1,!1);this.cellsAdded(b,f,this.model.getChildCount(f),null,null,!0);this.cellsAdded([c],f,this.model.getChildCount(f),g,b[0],!1);this.cellConnected(a,b[0],!0);this.fireEvent(new mxEventObject(mxEvent.SPLIT_EDGE,"edge",a,"cells",b,"newEdge",c,"dx",d,"dy",e))}finally{this.model.endUpdate()}return c};
-mxGraph.prototype.toggleCells=function(a,b,c){null==b&&(b=this.getSelectionCells());c&&(b=this.addAllEdges(b));this.model.beginUpdate();try{this.cellsToggled(b,a),this.fireEvent(new mxEventObject(mxEvent.TOGGLE_CELLS,"show",a,"cells",b,"includeEdges",c))}finally{this.model.endUpdate()}return b};mxGraph.prototype.cellsToggled=function(a,b){if(null!=a&&0<a.length){this.model.beginUpdate();try{for(var c=0;c<a.length;c++)this.model.setVisible(a[c],b)}finally{this.model.endUpdate()}}};
-mxGraph.prototype.foldCells=function(a,b,c,d,e){b=null!=b?b:!1;null==c&&(c=this.getFoldableCells(this.getSelectionCells(),a));this.stopEditing(!1);this.model.beginUpdate();try{this.cellsFolded(c,a,b,d),this.fireEvent(new mxEventObject(mxEvent.FOLD_CELLS,"collapse",a,"recurse",b,"cells",c))}finally{this.model.endUpdate()}return c};
-mxGraph.prototype.cellsFolded=function(a,b,c,d){if(null!=a&&0<a.length){this.model.beginUpdate();try{for(var e=0;e<a.length;e++)if((!d||this.isCellFoldable(a[e],b))&&b!=this.isCellCollapsed(a[e])){this.model.setCollapsed(a[e],b);this.swapBounds(a[e],b);this.isExtendParent(a[e])&&this.extendParent(a[e]);if(c){var f=this.model.getChildren(a[e]);this.cellsFolded(f,b,c)}this.constrainChild(a[e])}this.fireEvent(new mxEventObject(mxEvent.CELLS_FOLDED,"cells",a,"collapse",b,"recurse",c))}finally{this.model.endUpdate()}}};
-mxGraph.prototype.swapBounds=function(a,b){if(null!=a){var c=this.model.getGeometry(a);null!=c&&(c=c.clone(),this.updateAlternateBounds(a,c,b),c.swap(),this.model.setGeometry(a,c))}};
-mxGraph.prototype.updateAlternateBounds=function(a,b,c){if(null!=a&&null!=b){c=this.view.getState(a);c=null!=c?c.style:this.getCellStyle(a);if(null==b.alternateBounds){var d=b;this.collapseToPreferredSize&&(a=this.getPreferredSizeForCell(a),null!=a&&(d=a,a=mxUtils.getValue(c,mxConstants.STYLE_STARTSIZE),0<a&&(d.height=Math.max(d.height,a))));b.alternateBounds=new mxRectangle(0,0,d.width,d.height)}if(null!=b.alternateBounds){b.alternateBounds.x=b.x;b.alternateBounds.y=b.y;var e=mxUtils.toRadians(c[mxConstants.STYLE_ROTATION]||
-0);0!=e&&(a=b.alternateBounds.getCenterX()-b.getCenterX(),c=b.alternateBounds.getCenterY()-b.getCenterY(),d=Math.cos(e),e=Math.sin(e),b.alternateBounds.x+=d*a-e*c-a,b.alternateBounds.y+=e*a+d*c-c)}}};mxGraph.prototype.addAllEdges=function(a){var b=a.slice();return mxUtils.removeDuplicates(b.concat(this.getAllEdges(a)))};
-mxGraph.prototype.getAllEdges=function(a){var b=[];if(null!=a)for(var c=0;c<a.length;c++){for(var d=this.model.getEdgeCount(a[c]),e=0;e<d;e++)b.push(this.model.getEdgeAt(a[c],e));d=this.model.getChildren(a[c]);b=b.concat(this.getAllEdges(d))}return b};mxGraph.prototype.updateCellSize=function(a,b){b=null!=b?b:!1;this.model.beginUpdate();try{this.cellSizeUpdated(a,b),this.fireEvent(new mxEventObject(mxEvent.UPDATE_CELL_SIZE,"cell",a,"ignoreChildren",b))}finally{this.model.endUpdate()}return a};
-mxGraph.prototype.cellSizeUpdated=function(a,b){if(null!=a){this.model.beginUpdate();try{var c=this.getPreferredSizeForCell(a),d=this.model.getGeometry(a);if(null!=c&&null!=d){var e=this.isCellCollapsed(a),d=d.clone();if(this.isSwimlane(a)){var f=this.view.getState(a),g=null!=f?f.style:this.getCellStyle(a),k=this.model.getStyle(a);null==k&&(k="");mxUtils.getValue(g,mxConstants.STYLE_HORIZONTAL,!0)?(k=mxUtils.setStyle(k,mxConstants.STYLE_STARTSIZE,c.height+8),e&&(d.height=c.height+8),d.width=c.width):
-(k=mxUtils.setStyle(k,mxConstants.STYLE_STARTSIZE,c.width+8),e&&(d.width=c.width+8),d.height=c.height);this.model.setStyle(a,k)}else d.width=c.width,d.height=c.height;if(!b&&!e){var l=this.view.getBounds(this.model.getChildren(a));if(null!=l){var m=this.view.translate,n=this.view.scale,p=(l.y+l.height)/n-d.y-m.y;d.width=Math.max(d.width,(l.x+l.width)/n-d.x-m.x);d.height=Math.max(d.height,p)}}this.cellsResized([a],[d],!1)}}finally{this.model.endUpdate()}}};
-mxGraph.prototype.getPreferredSizeForCell=function(a){var b=null;if(null!=a){var c=this.view.getState(a)||this.view.createState(a),d=c.style;if(!this.model.isEdge(a)){var e=d[mxConstants.STYLE_FONTSIZE]||mxConstants.DEFAULT_FONTSIZE;a=b=0;null==this.getImage(c)&&null==d[mxConstants.STYLE_IMAGE]||d[mxConstants.STYLE_SHAPE]!=mxConstants.SHAPE_LABEL||(d[mxConstants.STYLE_VERTICAL_ALIGN]==mxConstants.ALIGN_MIDDLE&&(b+=parseFloat(d[mxConstants.STYLE_IMAGE_WIDTH])||mxLabel.prototype.imageSize),d[mxConstants.STYLE_ALIGN]!=
-mxConstants.ALIGN_CENTER&&(a+=parseFloat(d[mxConstants.STYLE_IMAGE_HEIGHT])||mxLabel.prototype.imageSize));b+=2*(d[mxConstants.STYLE_SPACING]||0);b+=d[mxConstants.STYLE_SPACING_LEFT]||0;b+=d[mxConstants.STYLE_SPACING_RIGHT]||0;a+=2*(d[mxConstants.STYLE_SPACING]||0);a+=d[mxConstants.STYLE_SPACING_TOP]||0;a+=d[mxConstants.STYLE_SPACING_BOTTOM]||0;var f=this.getFoldingImage(c);null!=f&&(b+=f.width+8);f=this.cellRenderer.getLabelValue(c);null!=f&&0<f.length?(this.isHtmlLabel(c.cell)||(f=mxUtils.htmlEntities(f)),
-f=f.replace(/\n/g,"<br>"),e=mxUtils.getSizeForString(f,e,d[mxConstants.STYLE_FONTFAMILY]),c=e.width+b,a=e.height+a,mxUtils.getValue(d,mxConstants.STYLE_HORIZONTAL,!0)||(d=a,a=c,c=d),this.gridEnabled&&(c=this.snap(c+this.gridSize/2),a=this.snap(a+this.gridSize/2)),b=new mxRectangle(0,0,c,a)):(d=4*this.gridSize,b=new mxRectangle(0,0,d,d))}}return b};mxGraph.prototype.resizeCell=function(a,b,c){return this.resizeCells([a],[b],c)[0]};
-mxGraph.prototype.resizeCells=function(a,b,c){c=null!=c?c:this.isRecursiveResize();this.model.beginUpdate();try{this.cellsResized(a,b,c),this.fireEvent(new mxEventObject(mxEvent.RESIZE_CELLS,"cells",a,"bounds",b))}finally{this.model.endUpdate()}return a};
-mxGraph.prototype.cellsResized=function(a,b,c){c=null!=c?c:!1;if(null!=a&&null!=b&&a.length==b.length){this.model.beginUpdate();try{for(var d=0;d<a.length;d++)this.cellResized(a[d],b[d],!1,c),this.isExtendParent(a[d])&&this.extendParent(a[d]),this.constrainChild(a[d]);this.resetEdgesOnResize&&this.resetEdges(a);this.fireEvent(new mxEventObject(mxEvent.CELLS_RESIZED,"cells",a,"bounds",b))}finally{this.model.endUpdate()}}};
-mxGraph.prototype.cellResized=function(a,b,c,d){var e=this.model.getGeometry(a);if(null!=e&&(e.x!=b.x||e.y!=b.y||e.width!=b.width||e.height!=b.height)){e=e.clone();!c&&e.relative?(c=e.offset,null!=c&&(c.x+=b.x-e.x,c.y+=b.y-e.y)):(e.x=b.x,e.y=b.y);e.width=b.width;e.height=b.height;e.relative||!this.model.isVertex(a)||this.isAllowNegativeCoordinates()||(e.x=Math.max(0,e.x),e.y=Math.max(0,e.y));this.model.beginUpdate();try{d&&this.resizeChildCells(a,e),this.model.setGeometry(a,e),this.constrainChildCells(a)}finally{this.model.endUpdate()}}};
-mxGraph.prototype.resizeChildCells=function(a,b){for(var c=this.model.getGeometry(a),d=b.width/c.width,c=b.height/c.height,e=this.model.getChildCount(a),f=0;f<e;f++)this.scaleCell(this.model.getChildAt(a,f),d,c,!0)};mxGraph.prototype.constrainChildCells=function(a){for(var b=this.model.getChildCount(a),c=0;c<b;c++)this.constrainChild(this.model.getChildAt(a,c))};
-mxGraph.prototype.scaleCell=function(a,b,c,d){var e=this.model.getGeometry(a);if(null!=e){var f=this.view.getState(a),f=null!=f?f.style:this.getCellStyle(a),e=e.clone(),g=e.x,k=e.y,l=e.width,m=e.height;e.scale(b,c,"fixed"==f[mxConstants.STYLE_ASPECT]);"1"==f[mxConstants.STYLE_RESIZE_WIDTH]?e.width=l*b:"0"==f[mxConstants.STYLE_RESIZE_WIDTH]&&(e.width=l);"1"==f[mxConstants.STYLE_RESIZE_HEIGHT]?e.height=m*c:"0"==f[mxConstants.STYLE_RESIZE_HEIGHT]&&(e.height=m);this.isCellMovable(a)||(e.x=g,e.y=k);this.isCellResizable(a)||
-(e.width=l,e.height=m);this.model.isVertex(a)?this.cellResized(a,e,!0,d):this.model.setGeometry(a,e)}};mxGraph.prototype.extendParent=function(a){if(null!=a){var b=this.model.getParent(a),c=this.getCellGeometry(b);null==b||null==c||this.isCellCollapsed(b)||(a=this.getCellGeometry(a),null!=a&&!a.relative&&(c.width<a.x+a.width||c.height<a.y+a.height)&&(c=c.clone(),c.width=Math.max(c.width,a.x+a.width),c.height=Math.max(c.height,a.y+a.height),this.cellsResized([b],[c],!1)))}};
-mxGraph.prototype.importCells=function(a,b,c,d,e,f){return this.moveCells(a,b,c,!0,d,e,f)};
-mxGraph.prototype.moveCells=function(a,b,c,d,e,f,g){b=null!=b?b:0;c=null!=c?c:0;d=null!=d?d:!1;if(null!=a&&(0!=b||0!=c||d||null!=e)){a=this.model.getTopmostCells(a);this.model.beginUpdate();try{for(var k=new mxDictionary,l=0;l<a.length;l++)k.put(a[l],!0);for(var m=mxUtils.bind(this,function(a){for(;null!=a;){if(k.get(a))return!0;a=this.model.getParent(a)}return!1}),n=[],l=0;l<a.length;l++){var p=this.getCellGeometry(a[l]),q=this.model.getParent(a[l]);null!=p&&p.relative&&this.model.isEdge(q)&&(m(this.model.getTerminal(q,
-!0))||m(this.model.getTerminal(q,!1)))||n.push(a[l])}a=n;d&&(a=this.cloneCells(a,this.isCloneInvalidEdges(),g),null==e&&(e=this.getDefaultParent()));var r=this.isAllowNegativeCoordinates();null!=e&&this.setAllowNegativeCoordinates(!0);this.cellsMoved(a,b,c,!d&&this.isDisconnectOnMove()&&this.isAllowDanglingEdges(),null==e,this.isExtendParentsOnMove()&&null==e);this.setAllowNegativeCoordinates(r);if(null!=e){var t=this.model.getChildCount(e);this.cellsAdded(a,e,t,null,null,!0)}this.fireEvent(new mxEventObject(mxEvent.MOVE_CELLS,
-"cells",a,"dx",b,"dy",c,"clone",d,"target",e,"event",f))}finally{this.model.endUpdate()}}return a};
-mxGraph.prototype.cellsMoved=function(a,b,c,d,e,f){if(null!=a&&(0!=b||0!=c)){f=null!=f?f:!1;this.model.beginUpdate();try{d&&this.disconnectGraph(a);for(var g=0;g<a.length;g++)this.translateCell(a[g],b,c),f&&this.isExtendParent(a[g])?this.extendParent(a[g]):e&&this.constrainChild(a[g]);this.resetEdgesOnMove&&this.resetEdges(a);this.fireEvent(new mxEventObject(mxEvent.CELLS_MOVED,"cells",a,"dx",b,"dy",c,"disconnect",d))}finally{this.model.endUpdate()}}};
-mxGraph.prototype.translateCell=function(a,b,c){var d=this.model.getGeometry(a);if(null!=d){b=parseFloat(b);c=parseFloat(c);d=d.clone();d.translate(b,c);d.relative||!this.model.isVertex(a)||this.isAllowNegativeCoordinates()||(d.x=Math.max(0,parseFloat(d.x)),d.y=Math.max(0,parseFloat(d.y)));if(d.relative&&!this.model.isEdge(a)){var e=this.model.getParent(a),f=0;this.model.isVertex(e)&&(f=this.view.getState(e),e=null!=f?f.style:this.getCellStyle(e),f=mxUtils.getValue(e,mxConstants.STYLE_ROTATION,0));
-0!=f&&(f=mxUtils.toRadians(-f),e=Math.cos(f),f=Math.sin(f),c=mxUtils.getRotatedPoint(new mxPoint(b,c),e,f,new mxPoint(0,0)),b=c.x,c=c.y);null==d.offset?d.offset=new mxPoint(b,c):(d.offset.x=parseFloat(d.offset.x)+b,d.offset.y=parseFloat(d.offset.y)+c)}this.model.setGeometry(a,d)}};
-mxGraph.prototype.getCellContainmentArea=function(a){if(null!=a&&!this.model.isEdge(a)){var b=this.model.getParent(a);if(null!=b&&b!=this.getDefaultParent()){var c=this.model.getGeometry(b);if(null!=c){var d=a=0,e=c.width,c=c.height;if(this.isSwimlane(b)){var f=this.getStartSize(b),g=this.view.getState(b),k=null!=g?g.style:this.getCellStyle(b),b=mxUtils.getValue(k,mxConstants.STYLE_DIRECTION,mxConstants.DIRECTION_EAST),g=1==mxUtils.getValue(k,mxConstants.STYLE_FLIPH,0),k=1==mxUtils.getValue(k,mxConstants.STYLE_FLIPV,
-0);if(b==mxConstants.DIRECTION_SOUTH||b==mxConstants.DIRECTION_NORTH){var l=f.width;f.width=f.height;f.height=l}if(b==mxConstants.DIRECTION_EAST&&!k||b==mxConstants.DIRECTION_NORTH&&!g||b==mxConstants.DIRECTION_WEST&&k||b==mxConstants.DIRECTION_SOUTH&&g)a=f.width,d=f.height;e-=f.width;c-=f.height}return new mxRectangle(a,d,e,c)}}}return null};mxGraph.prototype.getMaximumGraphBounds=function(){return this.maximumGraphBounds};
-mxGraph.prototype.constrainChild=function(a,b){if(null!=a){var c=this.getCellGeometry(a);if(null!=c&&(this.isConstrainRelativeChildren()||!c.relative)){var d=this.model.getParent(a);this.getCellGeometry(d);var e=this.getMaximumGraphBounds();null!=e&&(d=this.getBoundingBoxFromGeometry([d],!1),null!=d&&(e=mxRectangle.fromRectangle(e),e.x-=d.x,e.y-=d.y));if(this.isConstrainChild(a)&&(d=this.getCellContainmentArea(a),null!=d)){var f=this.getOverlap(a);0<f&&(d=mxRectangle.fromRectangle(d),d.x-=d.width*
-f,d.y-=d.height*f,d.width+=2*d.width*f,d.height+=2*d.height*f);null==e?e=d:(e=mxRectangle.fromRectangle(e),e.intersect(d))}if(null!=e){d=[a];if(!this.isCellCollapsed(a))for(var f=this.model.getDescendants(a),g=0;g<f.length;g++)this.isCellVisible(f[g])&&d.push(f[g]);d=this.getBoundingBoxFromGeometry(d,!1);if(null!=d){c=c.clone();f=0;c.width>e.width&&(f=c.width-e.width,c.width-=f);d.x+d.width>e.x+e.width&&(f-=d.x+d.width-e.x-e.width-f);g=0;c.height>e.height&&(g=c.height-e.height,c.height-=g);d.y+d.height>
-e.y+e.height&&(g-=d.y+d.height-e.y-e.height-g);d.x<e.x&&(f-=d.x-e.x);d.y<e.y&&(g-=d.y-e.y);if(0!=f||0!=g)c.relative?(null==c.offset&&(c.offset=new mxPoint),c.offset.x+=f,c.offset.y+=g):(c.x+=f,c.y+=g);this.model.setGeometry(a,c)}}}}};
-mxGraph.prototype.resetEdges=function(a){if(null!=a){for(var b=new mxDictionary,c=0;c<a.length;c++)b.put(a[c],!0);this.model.beginUpdate();try{for(c=0;c<a.length;c++){var d=this.model.getEdges(a[c]);if(null!=d)for(var e=0;e<d.length;e++){var f=this.view.getState(d[e]),g=null!=f?f.getVisibleTerminal(!0):this.view.getVisibleTerminal(d[e],!0),k=null!=f?f.getVisibleTerminal(!1):this.view.getVisibleTerminal(d[e],!1);b.get(g)&&b.get(k)||this.resetEdge(d[e])}this.resetEdges(this.model.getChildren(a[c]))}}finally{this.model.endUpdate()}}};
-mxGraph.prototype.resetEdge=function(a){var b=this.model.getGeometry(a);null!=b&&null!=b.points&&0<b.points.length&&(b=b.clone(),b.points=[],this.model.setGeometry(a,b));return a};
-mxGraph.prototype.getOutlineConstraint=function(a,b,c){if(null!=b.shape){c=this.view.getPerimeterBounds(b);var d=b.style[mxConstants.STYLE_DIRECTION];if(d==mxConstants.DIRECTION_NORTH||d==mxConstants.DIRECTION_SOUTH){c.x+=c.width/2-c.height/2;c.y+=c.height/2-c.width/2;var e=c.width;c.width=c.height;c.height=e}var f=mxUtils.toRadians(b.shape.getShapeRotation());if(0!=f){var e=Math.cos(-f),f=Math.sin(-f),g=new mxPoint(c.getCenterX(),c.getCenterY());a=mxUtils.getRotatedPoint(a,e,f,g)}var g=f=1,k=0,l=
-0;if(this.getModel().isVertex(b.cell)){var m=b.style[mxConstants.STYLE_FLIPH],n=b.style[mxConstants.STYLE_FLIPV];null!=b.shape&&null!=b.shape.stencil&&(m=1==mxUtils.getValue(b.style,"stencilFlipH",0)||m,n=1==mxUtils.getValue(b.style,"stencilFlipV",0)||n);if(d==mxConstants.DIRECTION_NORTH||d==mxConstants.DIRECTION_SOUTH)e=m,m=n,n=e;m&&(f=-1,k=-c.width);n&&(g=-1,l=-c.height)}a=new mxPoint((a.x-c.x)*f-k+c.x,(a.y-c.y)*g-l+c.y);return new mxConnectionConstraint(new mxPoint(Math.round(1E3*(a.x-c.x)/c.width)/
-1E3,Math.round(1E3*(a.y-c.y)/c.height)/1E3),!1)}return null};mxGraph.prototype.getAllConnectionConstraints=function(a,b){return null!=a&&null!=a.shape&&null!=a.shape.stencil?a.shape.stencil.constraints:null};
-mxGraph.prototype.getConnectionConstraint=function(a,b,c){b=null;var d=a.style[c?mxConstants.STYLE_EXIT_X:mxConstants.STYLE_ENTRY_X];if(null!=d){var e=a.style[c?mxConstants.STYLE_EXIT_Y:mxConstants.STYLE_ENTRY_Y];null!=e&&(b=new mxPoint(parseFloat(d),parseFloat(e)))}d=!1;null!=b&&(d=mxUtils.getValue(a.style,c?mxConstants.STYLE_EXIT_PERIMETER:mxConstants.STYLE_ENTRY_PERIMETER,!0));return new mxConnectionConstraint(b,d)};
-mxGraph.prototype.setConnectionConstraint=function(a,b,c,d){if(null!=d){this.model.beginUpdate();try{null==d||null==d.point?(this.setCellStyles(c?mxConstants.STYLE_EXIT_X:mxConstants.STYLE_ENTRY_X,null,[a]),this.setCellStyles(c?mxConstants.STYLE_EXIT_Y:mxConstants.STYLE_ENTRY_Y,null,[a]),this.setCellStyles(c?mxConstants.STYLE_EXIT_PERIMETER:mxConstants.STYLE_ENTRY_PERIMETER,null,[a])):null!=d.point&&(this.setCellStyles(c?mxConstants.STYLE_EXIT_X:mxConstants.STYLE_ENTRY_X,d.point.x,[a]),this.setCellStyles(c?
-mxConstants.STYLE_EXIT_Y:mxConstants.STYLE_ENTRY_Y,d.point.y,[a]),d.perimeter?this.setCellStyles(c?mxConstants.STYLE_EXIT_PERIMETER:mxConstants.STYLE_ENTRY_PERIMETER,null,[a]):this.setCellStyles(c?mxConstants.STYLE_EXIT_PERIMETER:mxConstants.STYLE_ENTRY_PERIMETER,"0",[a]))}finally{this.model.endUpdate()}}};
-mxGraph.prototype.getConnectionPoint=function(a,b){var c=null;if(null!=a&&null!=b.point){var d=this.view.getPerimeterBounds(a),e=new mxPoint(d.getCenterX(),d.getCenterY()),c=a.style[mxConstants.STYLE_DIRECTION],f=0;null!=c&&(c==mxConstants.DIRECTION_NORTH?f+=270:c==mxConstants.DIRECTION_WEST?f+=180:c==mxConstants.DIRECTION_SOUTH&&(f+=90),c!=mxConstants.DIRECTION_NORTH&&c!=mxConstants.DIRECTION_SOUTH||d.rotate90());var c=new mxPoint(d.x+b.point.x*d.width,d.y+b.point.y*d.height),g=a.style[mxConstants.STYLE_ROTATION]||
-0;if(b.perimeter){if(0!=f){var k=d=0;90==f?k=1:180==f?d=-1:270==f&&(k=-1);c=mxUtils.getRotatedPoint(c,d,k,e)}c=this.view.getPerimeterPoint(a,c,!1)}else g+=f,this.getModel().isVertex(a.cell)&&(f=1==a.style[mxConstants.STYLE_FLIPH],k=1==a.style[mxConstants.STYLE_FLIPV],null!=a.shape&&null!=a.shape.stencil&&(f=1==mxUtils.getValue(a.style,"stencilFlipH",0)||f,k=1==mxUtils.getValue(a.style,"stencilFlipV",0)||k),f&&(c.x=2*d.getCenterX()-c.x),k&&(c.y=2*d.getCenterY()-c.y));0!=g&&null!=c&&(g=mxUtils.toRadians(g),
-d=Math.cos(g),k=Math.sin(g),c=mxUtils.getRotatedPoint(c,d,k,e))}null!=c&&(c.x=Math.round(c.x),c.y=Math.round(c.y));return c};mxGraph.prototype.connectCell=function(a,b,c,d){this.model.beginUpdate();try{var e=this.model.getTerminal(a,c);this.cellConnected(a,b,c,d);this.fireEvent(new mxEventObject(mxEvent.CONNECT_CELL,"edge",a,"terminal",b,"source",c,"previous",e))}finally{this.model.endUpdate()}return a};
-mxGraph.prototype.cellConnected=function(a,b,c,d){if(null!=a){this.model.beginUpdate();try{var e=this.model.getTerminal(a,c);this.setConnectionConstraint(a,b,c,d);this.isPortsEnabled()&&(d=null,this.isPort(b)&&(d=b.getId(),b=this.getTerminalForPort(b,c)),this.setCellStyles(c?mxConstants.STYLE_SOURCE_PORT:mxConstants.STYLE_TARGET_PORT,d,[a]));this.model.setTerminal(a,b,c);this.resetEdgesOnConnect&&this.resetEdge(a);this.fireEvent(new mxEventObject(mxEvent.CELL_CONNECTED,"edge",a,"terminal",b,"source",
-c,"previous",e))}finally{this.model.endUpdate()}}};
-mxGraph.prototype.disconnectGraph=function(a){if(null!=a){this.model.beginUpdate();try{for(var b=this.view.scale,c=this.view.translate,d=new mxDictionary,e=0;e<a.length;e++)d.put(a[e],!0);for(e=0;e<a.length;e++)if(this.model.isEdge(a[e])){var f=this.model.getGeometry(a[e]);if(null!=f){var g=this.view.getState(a[e]),k=this.view.getState(this.model.getParent(a[e]));if(null!=g&&null!=k){var f=f.clone(),l=-k.origin.x,m=-k.origin.y,n=g.absolutePoints,p=this.model.getTerminal(a[e],!0);if(null!=p&&this.isCellDisconnectable(a[e],
-p,!0)){for(;null!=p&&!d.get(p);)p=this.model.getParent(p);null==p&&(f.setTerminalPoint(new mxPoint(n[0].x/b-c.x+l,n[0].y/b-c.y+m),!0),this.model.setTerminal(a[e],null,!0))}var q=this.model.getTerminal(a[e],!1);if(null!=q&&this.isCellDisconnectable(a[e],q,!1)){for(;null!=q&&!d.get(q);)q=this.model.getParent(q);if(null==q){var r=n.length-1;f.setTerminalPoint(new mxPoint(n[r].x/b-c.x+l,n[r].y/b-c.y+m),!1);this.model.setTerminal(a[e],null,!1)}}this.model.setGeometry(a[e],f)}}}}finally{this.model.endUpdate()}}};
-mxGraph.prototype.getCurrentRoot=function(){return this.view.currentRoot};mxGraph.prototype.getTranslateForRoot=function(a){return null};mxGraph.prototype.isPort=function(a){return!1};mxGraph.prototype.getTerminalForPort=function(a,b){return this.model.getParent(a)};mxGraph.prototype.getChildOffsetForCell=function(a){return null};mxGraph.prototype.enterGroup=function(a){a=a||this.getSelectionCell();null!=a&&this.isValidRoot(a)&&(this.view.setCurrentRoot(a),this.clearSelection())};
-mxGraph.prototype.exitGroup=function(){var a=this.model.getRoot(),b=this.getCurrentRoot();if(null!=b){for(var c=this.model.getParent(b);c!=a&&!this.isValidRoot(c)&&this.model.getParent(c)!=a;)c=this.model.getParent(c);c==a||this.model.getParent(c)==a?this.view.setCurrentRoot(null):this.view.setCurrentRoot(c);null!=this.view.getState(b)&&this.setSelectionCell(b)}};mxGraph.prototype.home=function(){var a=this.getCurrentRoot();null!=a&&(this.view.setCurrentRoot(null),null!=this.view.getState(a)&&this.setSelectionCell(a))};
-mxGraph.prototype.isValidRoot=function(a){return null!=a};mxGraph.prototype.getGraphBounds=function(){return this.view.getGraphBounds()};mxGraph.prototype.getCellBounds=function(a,b,c){var d=[a];b&&(d=d.concat(this.model.getEdges(a)));d=this.view.getBounds(d);if(c){c=this.model.getChildCount(a);for(var e=0;e<c;e++){var f=this.getCellBounds(this.model.getChildAt(a,e),b,!0);null!=d?d.add(f):d=f}}return d};
-mxGraph.prototype.getBoundingBoxFromGeometry=function(a,b){b=null!=b?b:!1;var c=null;if(null!=a)for(var d=0;d<a.length;d++)if(b||this.model.isVertex(a[d])){var e=this.getCellGeometry(a[d]);if(null!=e){var f=null;if(this.model.isEdge(a[d])){f=function(a){null!=a&&(null==g?g=new mxRectangle(a.x,a.y,0,0):g.add(new mxRectangle(a.x,a.y,0,0)))};null==this.model.getTerminal(a[d],!0)&&f(e.getTerminalPoint(!0));null==this.model.getTerminal(a[d],!1)&&f(e.getTerminalPoint(!1));e=e.points;if(null!=e&&0<e.length)for(var g=
-new mxRectangle(e[0].x,e[0].y,0,0),k=1;k<e.length;k++)f(e[k]);f=g}else k=this.model.getParent(a[d]),e.relative?this.model.isVertex(k)&&k!=this.view.currentRoot&&(g=this.getBoundingBoxFromGeometry([k],!1),null!=g&&(f=new mxRectangle(e.x*g.width,e.y*g.height,e.width,e.height),0<=mxUtils.indexOf(a,k)&&(f.x+=g.x,f.y+=g.y))):(f=mxRectangle.fromRectangle(e),this.model.isVertex(k)&&0<=mxUtils.indexOf(a,k)&&(g=this.getBoundingBoxFromGeometry([k],!1),null!=g&&(f.x+=g.x,f.y+=g.y))),null!=f&&null!=e.offset&&
-(f.x+=e.offset.x,f.y+=e.offset.y);null!=f&&(null==c?c=mxRectangle.fromRectangle(f):c.add(f))}}return c};mxGraph.prototype.refresh=function(a){this.view.clear(a,null==a);this.view.validate();this.sizeDidChange();this.fireEvent(new mxEventObject(mxEvent.REFRESH))};mxGraph.prototype.snap=function(a){this.gridEnabled&&(a=Math.round(a/this.gridSize)*this.gridSize);return a};
-mxGraph.prototype.panGraph=function(a,b){if(this.useScrollbarsForPanning&&mxUtils.hasScrollbars(this.container))this.container.scrollLeft=-a,this.container.scrollTop=-b;else{var c=this.view.getCanvas();if(this.dialect==mxConstants.DIALECT_SVG)if(0==a&&0==b){if(mxClient.IS_IE?c.setAttribute("transform","translate("+a+","+b+")"):c.removeAttribute("transform"),null!=this.shiftPreview1){for(var d=this.shiftPreview1.firstChild;null!=d;){var e=d.nextSibling;this.container.appendChild(d);d=e}null!=this.shiftPreview1.parentNode&&
-this.shiftPreview1.parentNode.removeChild(this.shiftPreview1);this.shiftPreview1=null;this.container.appendChild(c.parentNode);for(d=this.shiftPreview2.firstChild;null!=d;)e=d.nextSibling,this.container.appendChild(d),d=e;null!=this.shiftPreview2.parentNode&&this.shiftPreview2.parentNode.removeChild(this.shiftPreview2);this.shiftPreview2=null}}else{c.setAttribute("transform","translate("+a+","+b+")");if(null==this.shiftPreview1){this.shiftPreview1=document.createElement("div");this.shiftPreview1.style.position=
-"absolute";this.shiftPreview1.style.overflow="visible";this.shiftPreview2=document.createElement("div");this.shiftPreview2.style.position="absolute";this.shiftPreview2.style.overflow="visible";for(var f=this.shiftPreview1,d=this.container.firstChild;null!=d;)e=d.nextSibling,d!=c.parentNode?f.appendChild(d):f=this.shiftPreview2,d=e;null!=this.shiftPreview1.firstChild&&this.container.insertBefore(this.shiftPreview1,c.parentNode);null!=this.shiftPreview2.firstChild&&this.container.appendChild(this.shiftPreview2)}this.shiftPreview1.style.left=
-a+"px";this.shiftPreview1.style.top=b+"px";this.shiftPreview2.style.left=a+"px";this.shiftPreview2.style.top=b+"px"}else c.style.left=a+"px",c.style.top=b+"px";this.panDx=a;this.panDy=b;this.fireEvent(new mxEventObject(mxEvent.PAN))}};mxGraph.prototype.zoomIn=function(){this.zoom(this.zoomFactor)};mxGraph.prototype.zoomOut=function(){this.zoom(1/this.zoomFactor)};
-mxGraph.prototype.zoomActual=function(){1==this.view.scale?this.view.setTranslate(0,0):(this.view.translate.x=0,this.view.translate.y=0,this.view.setScale(1))};mxGraph.prototype.zoomTo=function(a,b){this.zoom(a/this.view.scale,b)};
-mxGraph.prototype.center=function(a,b,c,d){a=null!=a?a:!0;b=null!=b?b:!0;c=null!=c?c:.5;d=null!=d?d:.5;var e=mxUtils.hasScrollbars(this.container),f=this.container.clientWidth,g=this.container.clientHeight,k=this.getGraphBounds(),l=this.view.translate,m=this.view.scale,n=a?f-k.width:0,p=b?g-k.height:0;e?(k.x-=l.x,k.y-=l.y,a=this.container.scrollWidth,b=this.container.scrollHeight,a>f&&(n=0),b>g&&(p=0),this.view.setTranslate(Math.floor(n/2-k.x),Math.floor(p/2-k.y)),this.container.scrollLeft=(a-f)/
-2,this.container.scrollTop=(b-g)/2):this.view.setTranslate(a?Math.floor(l.x-k.x*m+n*c/m):l.x,b?Math.floor(l.y-k.y*m+p*d/m):l.y)};
-mxGraph.prototype.zoom=function(a,b){b=null!=b?b:this.centerZoom;var c=Math.round(this.view.scale*a*100)/100,d=this.view.getState(this.getSelectionCell());a=c/this.view.scale;if(this.keepSelectionVisibleOnZoom&&null!=d)d=new mxRectangle(d.x*a,d.y*a,d.width*a,d.height*a),this.view.scale=c,this.scrollRectToVisible(d)||(this.view.revalidate(),this.view.setScale(c));else if(d=mxUtils.hasScrollbars(this.container),b&&!d){var d=this.container.offsetWidth,e=this.container.offsetHeight;if(1<a)var f=(a-1)/
-(2*c),d=d*-f,e=e*-f;else f=(1/a-1)/(2*this.view.scale),d*=f,e*=f;this.view.scaleAndTranslate(c,this.view.translate.x+d,this.view.translate.y+e)}else{var f=this.view.translate.x,g=this.view.translate.y,k=this.container.scrollLeft,l=this.container.scrollTop;this.view.setScale(c);d&&(e=d=0,b&&(d=this.container.offsetWidth*(a-1)/2,e=this.container.offsetHeight*(a-1)/2),this.container.scrollLeft=(this.view.translate.x-f)*this.view.scale+Math.round(k*a+d),this.container.scrollTop=(this.view.translate.y-
-g)*this.view.scale+Math.round(l*a+e))}};
-mxGraph.prototype.zoomToRect=function(a){var b=this.container.clientWidth/a.width/(this.container.clientHeight/a.height);a.x=Math.max(0,a.x);a.y=Math.max(0,a.y);var c=Math.min(this.container.scrollWidth,a.x+a.width),d=Math.min(this.container.scrollHeight,a.y+a.height);a.width=c-a.x;a.height=d-a.y;1>b?(b=a.height/b,c=(b-a.height)/2,a.height=b,a.y-=Math.min(a.y,c),d=Math.min(this.container.scrollHeight,a.y+a.height),a.height=d-a.y):(b*=a.width,c=(b-a.width)/2,a.width=b,a.x-=Math.min(a.x,c),c=Math.min(this.container.scrollWidth,
-a.x+a.width),a.width=c-a.x);b=this.container.clientWidth/a.width;c=this.view.scale*b;mxUtils.hasScrollbars(this.container)?(this.view.setScale(c),this.container.scrollLeft=Math.round(a.x*b),this.container.scrollTop=Math.round(a.y*b)):this.view.scaleAndTranslate(c,this.view.translate.x-a.x/this.view.scale,this.view.translate.y-a.y/this.view.scale)};
-mxGraph.prototype.scrollCellToVisible=function(a,b){var c=-this.view.translate.x,d=-this.view.translate.y,e=this.view.getState(a);null!=e&&(c=new mxRectangle(c+e.x,d+e.y,e.width,e.height),b&&null!=this.container&&(d=this.container.clientWidth,e=this.container.clientHeight,c.x=c.getCenterX()-d/2,c.width=d,c.y=c.getCenterY()-e/2,c.height=e),d=new mxPoint(this.view.translate.x,this.view.translate.y),this.scrollRectToVisible(c)&&(c=new mxPoint(this.view.translate.x,this.view.translate.y),this.view.translate.x=
-d.x,this.view.translate.y=d.y,this.view.setTranslate(c.x,c.y)))};
-mxGraph.prototype.scrollRectToVisible=function(a){var b=!1;if(null!=a){var c=this.container.offsetWidth,d=this.container.offsetHeight,e=Math.min(c,a.width),f=Math.min(d,a.height);if(mxUtils.hasScrollbars(this.container)){c=this.container;a.x+=this.view.translate.x;a.y+=this.view.translate.y;var g=c.scrollLeft-a.x,d=Math.max(g-c.scrollLeft,0);0<g?c.scrollLeft-=g+2:(g=a.x+e-c.scrollLeft-c.clientWidth,0<g&&(c.scrollLeft+=g+2));e=c.scrollTop-a.y;g=Math.max(0,e-c.scrollTop);0<e?c.scrollTop-=e+2:(e=a.y+
-f-c.scrollTop-c.clientHeight,0<e&&(c.scrollTop+=e+2));this.useScrollbarsForPanning||0==d&&0==g||this.view.setTranslate(d,g)}else{var g=-this.view.translate.x,k=-this.view.translate.y,l=this.view.scale;a.x+e>g+c&&(this.view.translate.x-=(a.x+e-c-g)/l,b=!0);a.y+f>k+d&&(this.view.translate.y-=(a.y+f-d-k)/l,b=!0);a.x<g&&(this.view.translate.x+=(g-a.x)/l,b=!0);a.y<k&&(this.view.translate.y+=(k-a.y)/l,b=!0);b&&(this.view.refresh(),null!=this.selectionCellsHandler&&this.selectionCellsHandler.refresh())}}return b};
-mxGraph.prototype.getCellGeometry=function(a){return this.model.getGeometry(a)};mxGraph.prototype.isCellVisible=function(a){return this.model.isVisible(a)};mxGraph.prototype.isCellCollapsed=function(a){return this.model.isCollapsed(a)};mxGraph.prototype.isCellConnectable=function(a){return this.model.isConnectable(a)};
-mxGraph.prototype.isOrthogonal=function(a){var b=a.style[mxConstants.STYLE_ORTHOGONAL];if(null!=b)return b;a=this.view.getEdgeStyle(a);return a==mxEdgeStyle.SegmentConnector||a==mxEdgeStyle.ElbowConnector||a==mxEdgeStyle.SideToSide||a==mxEdgeStyle.TopToBottom||a==mxEdgeStyle.EntityRelation||a==mxEdgeStyle.OrthConnector};mxGraph.prototype.isLoop=function(a){var b=a.getVisibleTerminalState(!0);a=a.getVisibleTerminalState(!1);return null!=b&&b==a};mxGraph.prototype.isCloneEvent=function(a){return mxEvent.isControlDown(a)};
-mxGraph.prototype.isTransparentClickEvent=function(a){return!1};mxGraph.prototype.isToggleEvent=function(a){return mxClient.IS_MAC?mxEvent.isMetaDown(a):mxEvent.isControlDown(a)};mxGraph.prototype.isGridEnabledEvent=function(a){return null!=a&&!mxEvent.isAltDown(a)};mxGraph.prototype.isConstrainedEvent=function(a){return mxEvent.isShiftDown(a)};mxGraph.prototype.isIgnoreTerminalEvent=function(a){return!1};mxGraph.prototype.validationAlert=function(a){mxUtils.alert(a)};
-mxGraph.prototype.isEdgeValid=function(a,b,c){return null==this.getEdgeValidationError(a,b,c)};
-mxGraph.prototype.getEdgeValidationError=function(a,b,c){if(null!=a&&!this.isAllowDanglingEdges()&&(null==b||null==c))return"";if(null!=a&&null==this.model.getTerminal(a,!0)&&null==this.model.getTerminal(a,!1))return null;if(!this.allowLoops&&b==c&&null!=b||!this.isValidConnection(b,c))return"";if(null!=b&&null!=c){var d="";if(!this.multigraph){var e=this.model.getEdgesBetween(b,c,!0);if(1<e.length||1==e.length&&e[0]!=a)d+=(mxResources.get(this.alreadyConnectedResource)||this.alreadyConnectedResource)+
-"\n"}var e=this.model.getDirectedEdgeCount(b,!0,a),f=this.model.getDirectedEdgeCount(c,!1,a);if(null!=this.multiplicities)for(var g=0;g<this.multiplicities.length;g++){var k=this.multiplicities[g].check(this,a,b,c,e,f);null!=k&&(d+=k)}k=this.validateEdge(a,b,c);null!=k&&(d+=k);return 0<d.length?d:null}return this.allowDanglingEdges?null:""};mxGraph.prototype.validateEdge=function(a,b,c){return null};
-mxGraph.prototype.validateGraph=function(a,b){a=null!=a?a:this.model.getRoot();b=null!=b?b:{};for(var c=!0,d=this.model.getChildCount(a),e=0;e<d;e++){var f=this.model.getChildAt(a,e),g=b;this.isValidRoot(f)&&(g={});g=this.validateGraph(f,g);null!=g?this.setCellWarning(f,g.replace(/\n/g,"<br>")):this.setCellWarning(f,null);c=c&&null==g}d="";this.isCellCollapsed(a)&&!c&&(d+=(mxResources.get(this.containsValidationErrorsResource)||this.containsValidationErrorsResource)+"\n");d=this.model.isEdge(a)?d+
-(this.getEdgeValidationError(a,this.model.getTerminal(a,!0),this.model.getTerminal(a,!1))||""):d+(this.getCellValidationError(a)||"");e=this.validateCell(a,b);null!=e&&(d+=e);null==this.model.getParent(a)&&this.view.validate();return 0<d.length||!c?d:null};
-mxGraph.prototype.getCellValidationError=function(a){var b=this.model.getDirectedEdgeCount(a,!0),c=this.model.getDirectedEdgeCount(a,!1);a=this.model.getValue(a);var d="";if(null!=this.multiplicities)for(var e=0;e<this.multiplicities.length;e++){var f=this.multiplicities[e];f.source&&mxUtils.isNode(a,f.type,f.attr,f.value)&&(b>f.max||b<f.min)?d+=f.countError+"\n":!f.source&&mxUtils.isNode(a,f.type,f.attr,f.value)&&(c>f.max||c<f.min)&&(d+=f.countError+"\n")}return 0<d.length?d:null};
-mxGraph.prototype.validateCell=function(a,b){return null};mxGraph.prototype.getBackgroundImage=function(){return this.backgroundImage};mxGraph.prototype.setBackgroundImage=function(a){this.backgroundImage=a};mxGraph.prototype.getFoldingImage=function(a){if(null!=a&&this.foldingEnabled&&!this.getModel().isEdge(a.cell)){var b=this.isCellCollapsed(a.cell);if(this.isCellFoldable(a.cell,!b))return b?this.collapsedImage:this.expandedImage}return null};
-mxGraph.prototype.convertValueToString=function(a){a=this.model.getValue(a);if(null!=a){if(mxUtils.isNode(a))return a.nodeName;if("function"==typeof a.toString)return a.toString()}return""};mxGraph.prototype.getLabel=function(a){var b="";if(this.labelsVisible&&null!=a){var c=this.view.getState(a),c=null!=c?c.style:this.getCellStyle(a);mxUtils.getValue(c,mxConstants.STYLE_NOLABEL,!1)||(b=this.convertValueToString(a))}return b};mxGraph.prototype.isHtmlLabel=function(a){return this.isHtmlLabels()};
-mxGraph.prototype.isHtmlLabels=function(){return this.htmlLabels};mxGraph.prototype.setHtmlLabels=function(a){this.htmlLabels=a};mxGraph.prototype.isWrapping=function(a){var b=this.view.getState(a);a=null!=b?b.style:this.getCellStyle(a);return null!=a?"wrap"==a[mxConstants.STYLE_WHITE_SPACE]:!1};mxGraph.prototype.isLabelClipped=function(a){var b=this.view.getState(a);a=null!=b?b.style:this.getCellStyle(a);return null!=a?"hidden"==a[mxConstants.STYLE_OVERFLOW]:!1};
-mxGraph.prototype.getTooltip=function(a,b,c,d){var e=null;null!=a&&(null==a.control||b!=a.control.node&&b.parentNode!=a.control.node||(e=this.collapseExpandResource,e=mxUtils.htmlEntities(mxResources.get(e)||e).replace(/\\n/g,"<br>")),null==e&&null!=a.overlays&&a.overlays.visit(function(a,c){null!=e||b!=c.node&&b.parentNode!=c.node||(e=c.overlay.toString())}),null==e&&(c=this.selectionCellsHandler.getHandler(a.cell),null!=c&&"function"==typeof c.getTooltipForNode&&(e=c.getTooltipForNode(b))),null==
-e&&(e=this.getTooltipForCell(a.cell)));return e};mxGraph.prototype.getTooltipForCell=function(a){return null!=a&&null!=a.getTooltip?a.getTooltip():this.convertValueToString(a)};mxGraph.prototype.getLinkForCell=function(a){return null};mxGraph.prototype.getCursorForMouseEvent=function(a){return this.getCursorForCell(a.getCell())};mxGraph.prototype.getCursorForCell=function(a){return null};
-mxGraph.prototype.getStartSize=function(a){var b=new mxRectangle,c=this.view.getState(a);a=null!=c?c.style:this.getCellStyle(a);null!=a&&(c=parseInt(mxUtils.getValue(a,mxConstants.STYLE_STARTSIZE,mxConstants.DEFAULT_STARTSIZE)),mxUtils.getValue(a,mxConstants.STYLE_HORIZONTAL,!0)?b.height=c:b.width=c);return b};mxGraph.prototype.getImage=function(a){return null!=a&&null!=a.style?a.style[mxConstants.STYLE_IMAGE]:null};
-mxGraph.prototype.getVerticalAlign=function(a){return null!=a&&null!=a.style?a.style[mxConstants.STYLE_VERTICAL_ALIGN]||mxConstants.ALIGN_MIDDLE:null};mxGraph.prototype.getIndicatorColor=function(a){return null!=a&&null!=a.style?a.style[mxConstants.STYLE_INDICATOR_COLOR]:null};mxGraph.prototype.getIndicatorGradientColor=function(a){return null!=a&&null!=a.style?a.style[mxConstants.STYLE_INDICATOR_GRADIENTCOLOR]:null};
-mxGraph.prototype.getIndicatorShape=function(a){return null!=a&&null!=a.style?a.style[mxConstants.STYLE_INDICATOR_SHAPE]:null};mxGraph.prototype.getIndicatorImage=function(a){return null!=a&&null!=a.style?a.style[mxConstants.STYLE_INDICATOR_IMAGE]:null};mxGraph.prototype.getBorder=function(){return this.border};mxGraph.prototype.setBorder=function(a){this.border=a};
-mxGraph.prototype.isSwimlane=function(a){if(null!=a&&this.model.getParent(a)!=this.model.getRoot()){var b=this.view.getState(a),b=null!=b?b.style:this.getCellStyle(a);if(null!=b&&!this.model.isEdge(a))return b[mxConstants.STYLE_SHAPE]==mxConstants.SHAPE_SWIMLANE}return!1};mxGraph.prototype.isResizeContainer=function(){return this.resizeContainer};mxGraph.prototype.setResizeContainer=function(a){this.resizeContainer=a};mxGraph.prototype.isEnabled=function(){return this.enabled};
-mxGraph.prototype.setEnabled=function(a){this.enabled=a};mxGraph.prototype.isEscapeEnabled=function(){return this.escapeEnabled};mxGraph.prototype.setEscapeEnabled=function(a){this.escapeEnabled=a};mxGraph.prototype.isInvokesStopCellEditing=function(){return this.invokesStopCellEditing};mxGraph.prototype.setInvokesStopCellEditing=function(a){this.invokesStopCellEditing=a};mxGraph.prototype.isEnterStopsCellEditing=function(){return this.enterStopsCellEditing};
-mxGraph.prototype.setEnterStopsCellEditing=function(a){this.enterStopsCellEditing=a};mxGraph.prototype.isCellLocked=function(a){var b=this.model.getGeometry(a);return this.isCellsLocked()||null!=b&&this.model.isVertex(a)&&b.relative};mxGraph.prototype.isCellsLocked=function(){return this.cellsLocked};mxGraph.prototype.setCellsLocked=function(a){this.cellsLocked=a};mxGraph.prototype.getCloneableCells=function(a){return this.model.filterCells(a,mxUtils.bind(this,function(a){return this.isCellCloneable(a)}))};
-mxGraph.prototype.isCellCloneable=function(a){var b=this.view.getState(a);a=null!=b?b.style:this.getCellStyle(a);return this.isCellsCloneable()&&0!=a[mxConstants.STYLE_CLONEABLE]};mxGraph.prototype.isCellsCloneable=function(){return this.cellsCloneable};mxGraph.prototype.setCellsCloneable=function(a){this.cellsCloneable=a};mxGraph.prototype.getExportableCells=function(a){return this.model.filterCells(a,mxUtils.bind(this,function(a){return this.canExportCell(a)}))};
-mxGraph.prototype.canExportCell=function(a){return this.exportEnabled};mxGraph.prototype.getImportableCells=function(a){return this.model.filterCells(a,mxUtils.bind(this,function(a){return this.canImportCell(a)}))};mxGraph.prototype.canImportCell=function(a){return this.importEnabled};mxGraph.prototype.isCellSelectable=function(a){return this.isCellsSelectable()};mxGraph.prototype.isCellsSelectable=function(){return this.cellsSelectable};
-mxGraph.prototype.setCellsSelectable=function(a){this.cellsSelectable=a};mxGraph.prototype.getDeletableCells=function(a){return this.model.filterCells(a,mxUtils.bind(this,function(a){return this.isCellDeletable(a)}))};mxGraph.prototype.isCellDeletable=function(a){var b=this.view.getState(a);a=null!=b?b.style:this.getCellStyle(a);return this.isCellsDeletable()&&0!=a[mxConstants.STYLE_DELETABLE]};mxGraph.prototype.isCellsDeletable=function(){return this.cellsDeletable};
-mxGraph.prototype.setCellsDeletable=function(a){this.cellsDeletable=a};mxGraph.prototype.isLabelMovable=function(a){return!this.isCellLocked(a)&&(this.model.isEdge(a)&&this.edgeLabelsMovable||this.model.isVertex(a)&&this.vertexLabelsMovable)};mxGraph.prototype.isCellRotatable=function(a){var b=this.view.getState(a);return 0!=(null!=b?b.style:this.getCellStyle(a))[mxConstants.STYLE_ROTATABLE]};mxGraph.prototype.getMovableCells=function(a){return this.model.filterCells(a,mxUtils.bind(this,function(a){return this.isCellMovable(a)}))};
-mxGraph.prototype.isCellMovable=function(a){var b=this.view.getState(a),b=null!=b?b.style:this.getCellStyle(a);return this.isCellsMovable()&&!this.isCellLocked(a)&&0!=b[mxConstants.STYLE_MOVABLE]};mxGraph.prototype.isCellsMovable=function(){return this.cellsMovable};mxGraph.prototype.setCellsMovable=function(a){this.cellsMovable=a};mxGraph.prototype.isGridEnabled=function(){return this.gridEnabled};mxGraph.prototype.setGridEnabled=function(a){this.gridEnabled=a};mxGraph.prototype.isPortsEnabled=function(){return this.portsEnabled};
-mxGraph.prototype.setPortsEnabled=function(a){this.portsEnabled=a};mxGraph.prototype.getGridSize=function(){return this.gridSize};mxGraph.prototype.setGridSize=function(a){this.gridSize=a};mxGraph.prototype.getTolerance=function(){return this.tolerance};mxGraph.prototype.setTolerance=function(a){this.tolerance=a};mxGraph.prototype.isVertexLabelsMovable=function(){return this.vertexLabelsMovable};mxGraph.prototype.setVertexLabelsMovable=function(a){this.vertexLabelsMovable=a};
-mxGraph.prototype.isEdgeLabelsMovable=function(){return this.edgeLabelsMovable};mxGraph.prototype.setEdgeLabelsMovable=function(a){this.edgeLabelsMovable=a};mxGraph.prototype.isSwimlaneNesting=function(){return this.swimlaneNesting};mxGraph.prototype.setSwimlaneNesting=function(a){this.swimlaneNesting=a};mxGraph.prototype.isSwimlaneSelectionEnabled=function(){return this.swimlaneSelectionEnabled};mxGraph.prototype.setSwimlaneSelectionEnabled=function(a){this.swimlaneSelectionEnabled=a};
-mxGraph.prototype.isMultigraph=function(){return this.multigraph};mxGraph.prototype.setMultigraph=function(a){this.multigraph=a};mxGraph.prototype.isAllowLoops=function(){return this.allowLoops};mxGraph.prototype.setAllowDanglingEdges=function(a){this.allowDanglingEdges=a};mxGraph.prototype.isAllowDanglingEdges=function(){return this.allowDanglingEdges};mxGraph.prototype.setConnectableEdges=function(a){this.connectableEdges=a};mxGraph.prototype.isConnectableEdges=function(){return this.connectableEdges};
-mxGraph.prototype.setCloneInvalidEdges=function(a){this.cloneInvalidEdges=a};mxGraph.prototype.isCloneInvalidEdges=function(){return this.cloneInvalidEdges};mxGraph.prototype.setAllowLoops=function(a){this.allowLoops=a};mxGraph.prototype.isDisconnectOnMove=function(){return this.disconnectOnMove};mxGraph.prototype.setDisconnectOnMove=function(a){this.disconnectOnMove=a};mxGraph.prototype.isDropEnabled=function(){return this.dropEnabled};
-mxGraph.prototype.setDropEnabled=function(a){this.dropEnabled=a};mxGraph.prototype.isSplitEnabled=function(){return this.splitEnabled};mxGraph.prototype.setSplitEnabled=function(a){this.splitEnabled=a};mxGraph.prototype.isCellResizable=function(a){var b=this.view.getState(a),b=null!=b?b.style:this.getCellStyle(a);return this.isCellsResizable()&&!this.isCellLocked(a)&&"0"!=mxUtils.getValue(b,mxConstants.STYLE_RESIZABLE,"1")};mxGraph.prototype.isCellsResizable=function(){return this.cellsResizable};
-mxGraph.prototype.setCellsResizable=function(a){this.cellsResizable=a};mxGraph.prototype.isTerminalPointMovable=function(a,b){return!0};mxGraph.prototype.isCellBendable=function(a){var b=this.view.getState(a),b=null!=b?b.style:this.getCellStyle(a);return this.isCellsBendable()&&!this.isCellLocked(a)&&0!=b[mxConstants.STYLE_BENDABLE]};mxGraph.prototype.isCellsBendable=function(){return this.cellsBendable};mxGraph.prototype.setCellsBendable=function(a){this.cellsBendable=a};
-mxGraph.prototype.isCellEditable=function(a){var b=this.view.getState(a),b=null!=b?b.style:this.getCellStyle(a);return this.isCellsEditable()&&!this.isCellLocked(a)&&0!=b[mxConstants.STYLE_EDITABLE]};mxGraph.prototype.isCellsEditable=function(){return this.cellsEditable};mxGraph.prototype.setCellsEditable=function(a){this.cellsEditable=a};mxGraph.prototype.isCellDisconnectable=function(a,b,c){return this.isCellsDisconnectable()&&!this.isCellLocked(a)};mxGraph.prototype.isCellsDisconnectable=function(){return this.cellsDisconnectable};
-mxGraph.prototype.setCellsDisconnectable=function(a){this.cellsDisconnectable=a};mxGraph.prototype.isValidSource=function(a){return null==a&&this.allowDanglingEdges||null!=a&&(!this.model.isEdge(a)||this.connectableEdges)&&this.isCellConnectable(a)};mxGraph.prototype.isValidTarget=function(a){return this.isValidSource(a)};mxGraph.prototype.isValidConnection=function(a,b){return this.isValidSource(a)&&this.isValidTarget(b)};mxGraph.prototype.setConnectable=function(a){this.connectionHandler.setEnabled(a)};
-mxGraph.prototype.isConnectable=function(a){return this.connectionHandler.isEnabled()};mxGraph.prototype.setTooltips=function(a){this.tooltipHandler.setEnabled(a)};mxGraph.prototype.setPanning=function(a){this.panningHandler.panningEnabled=a};mxGraph.prototype.isEditing=function(a){if(null!=this.cellEditor){var b=this.cellEditor.getEditingCell();return null==a?null!=b:a==b}return!1};
-mxGraph.prototype.isAutoSizeCell=function(a){var b=this.view.getState(a);a=null!=b?b.style:this.getCellStyle(a);return this.isAutoSizeCells()||1==a[mxConstants.STYLE_AUTOSIZE]};mxGraph.prototype.isAutoSizeCells=function(){return this.autoSizeCells};mxGraph.prototype.setAutoSizeCells=function(a){this.autoSizeCells=a};mxGraph.prototype.isExtendParent=function(a){return!this.getModel().isEdge(a)&&this.isExtendParents()};mxGraph.prototype.isExtendParents=function(){return this.extendParents};
-mxGraph.prototype.setExtendParents=function(a){this.extendParents=a};mxGraph.prototype.isExtendParentsOnAdd=function(a){return this.extendParentsOnAdd};mxGraph.prototype.setExtendParentsOnAdd=function(a){this.extendParentsOnAdd=a};mxGraph.prototype.isExtendParentsOnMove=function(){return this.extendParentsOnMove};mxGraph.prototype.setExtendParentsOnMove=function(a){this.extendParentsOnMove=a};mxGraph.prototype.isRecursiveResize=function(a){return this.recursiveResize};
-mxGraph.prototype.setRecursiveResize=function(a){this.recursiveResize=a};mxGraph.prototype.isConstrainChild=function(a){return this.isConstrainChildren()&&!this.getModel().isEdge(this.getModel().getParent(a))};mxGraph.prototype.isConstrainChildren=function(){return this.constrainChildren};mxGraph.prototype.setConstrainChildren=function(a){this.constrainChildren=a};mxGraph.prototype.isConstrainRelativeChildren=function(){return this.constrainRelativeChildren};
-mxGraph.prototype.setConstrainRelativeChildren=function(a){this.constrainRelativeChildren=a};mxGraph.prototype.isAllowNegativeCoordinates=function(){return this.allowNegativeCoordinates};mxGraph.prototype.setAllowNegativeCoordinates=function(a){this.allowNegativeCoordinates=a};mxGraph.prototype.getOverlap=function(a){return this.isAllowOverlapParent(a)?this.defaultOverlap:0};mxGraph.prototype.isAllowOverlapParent=function(a){return!1};
-mxGraph.prototype.getFoldableCells=function(a,b){return this.model.filterCells(a,mxUtils.bind(this,function(a){return this.isCellFoldable(a,b)}))};mxGraph.prototype.isCellFoldable=function(a,b){var c=this.view.getState(a),c=null!=c?c.style:this.getCellStyle(a);return 0<this.model.getChildCount(a)&&0!=c[mxConstants.STYLE_FOLDABLE]};
-mxGraph.prototype.isValidDropTarget=function(a,b,c){return null!=a&&(this.isSplitEnabled()&&this.isSplitTarget(a,b,c)||!this.model.isEdge(a)&&(this.isSwimlane(a)||0<this.model.getChildCount(a)&&!this.isCellCollapsed(a)))};
-mxGraph.prototype.isSplitTarget=function(a,b,c){return this.model.isEdge(a)&&null!=b&&1==b.length&&this.isCellConnectable(b[0])&&null==this.getEdgeValidationError(a,this.model.getTerminal(a,!0),b[0])?(c=this.model.getTerminal(a,!0),a=this.model.getTerminal(a,!1),!this.model.isAncestor(b[0],c)&&!this.model.isAncestor(b[0],a)):!1};
-mxGraph.prototype.getDropTarget=function(a,b,c,d){if(!this.isSwimlaneNesting())for(var e=0;e<a.length;e++)if(this.isSwimlane(a[e]))return null;e=mxUtils.convertPoint(this.container,mxEvent.getClientX(b),mxEvent.getClientY(b));e.x-=this.panDx;e.y-=this.panDy;e=this.getSwimlaneAt(e.x,e.y);if(null==c)c=e;else if(null!=e){for(var f=this.model.getParent(e);null!=f&&this.isSwimlane(f)&&f!=c;)f=this.model.getParent(f);f==c&&(c=e)}for(;null!=c&&!this.isValidDropTarget(c,a,b)&&!this.model.isLayer(c);)c=this.model.getParent(c);
-if(null==d||!d)for(var g=c;null!=g&&0>mxUtils.indexOf(a,g);)g=this.model.getParent(g);return this.model.isLayer(c)||null!=g?null:c};mxGraph.prototype.getDefaultParent=function(){var a=this.getCurrentRoot();null==a&&(a=this.defaultParent,null==a&&(a=this.model.getRoot(),a=this.model.getChildAt(a,0)));return a};mxGraph.prototype.setDefaultParent=function(a){this.defaultParent=a};mxGraph.prototype.getSwimlane=function(a){for(;null!=a&&!this.isSwimlane(a);)a=this.model.getParent(a);return a};
-mxGraph.prototype.getSwimlaneAt=function(a,b,c){c=c||this.getDefaultParent();if(null!=c)for(var d=this.model.getChildCount(c),e=0;e<d;e++){var f=this.model.getChildAt(c,e),g=this.getSwimlaneAt(a,b,f);if(null!=g)return g;if(this.isSwimlane(f)&&(g=this.view.getState(f),this.intersects(g,a,b)))return f}return null};
-mxGraph.prototype.getCellAt=function(a,b,c,d,e,f){d=null!=d?d:!0;e=null!=e?e:!0;null==c&&(c=this.getCurrentRoot(),null==c&&(c=this.getModel().getRoot()));if(null!=c)for(var g=this.model.getChildCount(c)-1;0<=g;g--){var k=this.model.getChildAt(c,g),l=this.getCellAt(a,b,k,d,e,f);if(null!=l)return l;if(this.isCellVisible(k)&&(e&&this.model.isEdge(k)||d&&this.model.isVertex(k))&&(l=this.view.getState(k),null!=l&&(null==f||!f(l,a,b))&&this.intersects(l,a,b)))return k}return null};
-mxGraph.prototype.intersects=function(a,b,c){if(null!=a){var d=a.absolutePoints;if(null!=d){a=this.tolerance*this.tolerance;for(var e=d[0],f=1;f<d.length;f++){var g=d[f];if(mxUtils.ptSegDistSq(e.x,e.y,g.x,g.y,b,c)<=a)return!0;e=g}}else if(e=mxUtils.toRadians(mxUtils.getValue(a.style,mxConstants.STYLE_ROTATION)||0),0!=e&&(d=Math.cos(-e),e=Math.sin(-e),f=new mxPoint(a.getCenterX(),a.getCenterY()),e=mxUtils.getRotatedPoint(new mxPoint(b,c),d,e,f),b=e.x,c=e.y),mxUtils.contains(a,b,c))return!0}return!1};
-mxGraph.prototype.hitsSwimlaneContent=function(a,b,c){var d=this.getView().getState(a);a=this.getStartSize(a);if(null!=d){var e=this.getView().getScale();b-=d.x;c-=d.y;if(0<a.width&&0<b&&b>a.width*e||0<a.height&&0<c&&c>a.height*e)return!0}return!1};mxGraph.prototype.getChildVertices=function(a){return this.getChildCells(a,!0,!1)};mxGraph.prototype.getChildEdges=function(a){return this.getChildCells(a,!1,!0)};
-mxGraph.prototype.getChildCells=function(a,b,c){a=null!=a?a:this.getDefaultParent();a=this.model.getChildCells(a,null!=b?b:!1,null!=c?c:!1);b=[];for(c=0;c<a.length;c++)this.isCellVisible(a[c])&&b.push(a[c]);return b};mxGraph.prototype.getConnections=function(a,b){return this.getEdges(a,b,!0,!0,!1)};mxGraph.prototype.getIncomingEdges=function(a,b){return this.getEdges(a,b,!0,!1,!1)};mxGraph.prototype.getOutgoingEdges=function(a,b){return this.getEdges(a,b,!1,!0,!1)};
-mxGraph.prototype.getEdges=function(a,b,c,d,e,f){c=null!=c?c:!0;d=null!=d?d:!0;e=null!=e?e:!0;f=null!=f?f:!1;for(var g=[],k=this.isCellCollapsed(a),l=this.model.getChildCount(a),m=0;m<l;m++){var n=this.model.getChildAt(a,m);if(k||!this.isCellVisible(n))g=g.concat(this.model.getEdges(n,c,d))}g=g.concat(this.model.getEdges(a,c,d));k=[];for(m=0;m<g.length;m++)n=this.view.getState(g[m]),l=null!=n?n.getVisibleTerminal(!0):this.view.getVisibleTerminal(g[m],!0),n=null!=n?n.getVisibleTerminal(!1):this.view.getVisibleTerminal(g[m],
-!1),(e&&l==n||l!=n&&(c&&n==a&&(null==b||this.isValidAncestor(l,b,f))||d&&l==a&&(null==b||this.isValidAncestor(n,b,f))))&&k.push(g[m]);return k};mxGraph.prototype.isValidAncestor=function(a,b,c){return c?this.model.isAncestor(b,a):this.model.getParent(a)==b};
-mxGraph.prototype.getOpposites=function(a,b,c,d){c=null!=c?c:!0;d=null!=d?d:!0;var e=[],f=new mxDictionary;if(null!=a)for(var g=0;g<a.length;g++){var k=this.view.getState(a[g]),l=null!=k?k.getVisibleTerminal(!0):this.view.getVisibleTerminal(a[g],!0),k=null!=k?k.getVisibleTerminal(!1):this.view.getVisibleTerminal(a[g],!1);l==b&&null!=k&&k!=b&&d?f.get(k)||(f.put(k,!0),e.push(k)):k==b&&null!=l&&l!=b&&c&&!f.get(l)&&(f.put(l,!0),e.push(l))}return e};
-mxGraph.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.view.getState(d[f]),k=null!=g?g.getVisibleTerminal(!0):this.view.getVisibleTerminal(d[f],!0),g=null!=g?g.getVisibleTerminal(!1):this.view.getVisibleTerminal(d[f],!1);(k==a&&g==b||!c&&k==b&&g==a)&&e.push(d[f])}return e};
-mxGraph.prototype.getPointForEvent=function(a,b){var c=mxUtils.convertPoint(this.container,mxEvent.getClientX(a),mxEvent.getClientY(a)),d=this.view.scale,e=this.view.translate,f=0!=b?this.gridSize/2:0;c.x=this.snap(c.x/d-e.x-f);c.y=this.snap(c.y/d-e.y-f);return c};
-mxGraph.prototype.getCells=function(a,b,c,d,e,f){f=null!=f?f:[];if(0<c||0<d){var g=this.getModel(),k=a+c,l=b+d;null==e&&(e=this.getCurrentRoot(),null==e&&(e=g.getRoot()));if(null!=e)for(var m=g.getChildCount(e),n=0;n<m;n++){var p=g.getChildAt(e,n),q=this.view.getState(p);if(null!=q&&this.isCellVisible(p)){var r=mxUtils.getValue(q.style,mxConstants.STYLE_ROTATION)||0;0!=r&&(q=mxUtils.getBoundingBox(q,r));(g.isEdge(p)||g.isVertex(p))&&q.x>=a&&q.y+q.height<=l&&q.y>=b&&q.x+q.width<=k?f.push(p):this.getCells(a,
-b,c,d,p,f)}}}return f};mxGraph.prototype.getCellsBeyond=function(a,b,c,d,e){var f=[];if(d||e)if(null==c&&(c=this.getDefaultParent()),null!=c)for(var g=this.model.getChildCount(c),k=0;k<g;k++){var l=this.model.getChildAt(c,k),m=this.view.getState(l);this.isCellVisible(l)&&null!=m&&(!d||m.x>=a)&&(!e||m.y>=b)&&f.push(l)}return f};
-mxGraph.prototype.findTreeRoots=function(a,b,c){b=null!=b?b:!1;c=null!=c?c:!1;var d=[];if(null!=a){for(var e=this.getModel(),f=e.getChildCount(a),g=null,k=0,l=0;l<f;l++){var m=e.getChildAt(a,l);if(this.model.isVertex(m)&&this.isCellVisible(m)){for(var n=this.getConnections(m,b?a:null),p=0,q=0,r=0;r<n.length;r++)this.view.getVisibleTerminal(n[r],!0)==m?p++:q++;(c&&0==p&&0<q||!c&&0==q&&0<p)&&d.push(m);n=c?q-p:p-q;n>k&&(k=n,g=m)}}0==d.length&&null!=g&&d.push(g)}return d};
-mxGraph.prototype.traverse=function(a,b,c,d,e,f){if(null!=c&&null!=a&&(b=null!=b?b:!0,f=null!=f?f:!1,e=e||new mxDictionary,!e.get(a)&&(e.put(a,!0),d=c(a,d),null==d||d))&&(d=this.model.getEdgeCount(a),0<d))for(var g=0;g<d;g++){var k=this.model.getEdgeAt(a,g),l=this.model.getTerminal(k,!0)==a;b&&!f!=l||(l=this.model.getTerminal(k,!l),this.traverse(l,b,c,k,e,f))}};mxGraph.prototype.isCellSelected=function(a){return this.getSelectionModel().isSelected(a)};mxGraph.prototype.isSelectionEmpty=function(){return this.getSelectionModel().isEmpty()};
-mxGraph.prototype.clearSelection=function(){return this.getSelectionModel().clear()};mxGraph.prototype.getSelectionCount=function(){return this.getSelectionModel().cells.length};mxGraph.prototype.getSelectionCell=function(){return this.getSelectionModel().cells[0]};mxGraph.prototype.getSelectionCells=function(){return this.getSelectionModel().cells.slice()};mxGraph.prototype.setSelectionCell=function(a){this.getSelectionModel().setCell(a)};mxGraph.prototype.setSelectionCells=function(a){this.getSelectionModel().setCells(a)};
-mxGraph.prototype.addSelectionCell=function(a){this.getSelectionModel().addCell(a)};mxGraph.prototype.addSelectionCells=function(a){this.getSelectionModel().addCells(a)};mxGraph.prototype.removeSelectionCell=function(a){this.getSelectionModel().removeCell(a)};mxGraph.prototype.removeSelectionCells=function(a){this.getSelectionModel().removeCells(a)};mxGraph.prototype.selectRegion=function(a,b){var c=this.getCells(a.x,a.y,a.width,a.height);this.selectCellsForEvent(c,b);return c};
-mxGraph.prototype.selectNextCell=function(){this.selectCell(!0)};mxGraph.prototype.selectPreviousCell=function(){this.selectCell()};mxGraph.prototype.selectParentCell=function(){this.selectCell(!1,!0)};mxGraph.prototype.selectChildCell=function(){this.selectCell(!1,!1,!0)};
-mxGraph.prototype.selectCell=function(a,b,c){var d=this.selectionModel,e=0<d.cells.length?d.cells[0]:null;1<d.cells.length&&d.clear();var d=null!=e?this.model.getParent(e):this.getDefaultParent(),f=this.model.getChildCount(d);null==e&&0<f?(a=this.model.getChildAt(d,0),this.setSelectionCell(a)):null!=e&&!b||null==this.view.getState(d)||null==this.model.getGeometry(d)?null!=e&&c?0<this.model.getChildCount(e)&&(a=this.model.getChildAt(e,0),this.setSelectionCell(a)):0<f&&(b=d.getIndex(e),a?(b++,a=this.model.getChildAt(d,
-b%f)):(b--,a=this.model.getChildAt(d,0>b?f-1:b)),this.setSelectionCell(a)):this.getCurrentRoot()!=d&&this.setSelectionCell(d)};mxGraph.prototype.selectAll=function(a,b){a=a||this.getDefaultParent();var c=b?this.model.filterDescendants(function(b){return b!=a},a):this.model.getChildren(a);null!=c&&this.setSelectionCells(c)};mxGraph.prototype.selectVertices=function(a){this.selectCells(!0,!1,a)};mxGraph.prototype.selectEdges=function(a){this.selectCells(!1,!0,a)};
-mxGraph.prototype.selectCells=function(a,b,c){c=c||this.getDefaultParent();var d=mxUtils.bind(this,function(c){return null!=this.view.getState(c)&&(0==this.model.getChildCount(c)&&this.model.isVertex(c)&&a&&!this.model.isEdge(this.model.getParent(c))||this.model.isEdge(c)&&b)});c=this.model.filterDescendants(d,c);this.setSelectionCells(c)};
-mxGraph.prototype.selectCellForEvent=function(a,b){var c=this.isCellSelected(a);this.isToggleEvent(b)?c?this.removeSelectionCell(a):this.addSelectionCell(a):c&&1==this.getSelectionCount()||this.setSelectionCell(a)};mxGraph.prototype.selectCellsForEvent=function(a,b){this.isToggleEvent(b)?this.addSelectionCells(a):this.setSelectionCells(a)};
-mxGraph.prototype.createHandler=function(a){var b=null;if(null!=a)if(this.model.isEdge(a.cell))var b=a.getVisibleTerminalState(!0),c=a.getVisibleTerminalState(!1),d=this.getCellGeometry(a.cell),b=this.view.getEdgeStyle(a,null!=d?d.points:null,b,c),b=this.createEdgeHandler(a,b);else b=this.createVertexHandler(a);return b};mxGraph.prototype.createVertexHandler=function(a){return new mxVertexHandler(a)};
-mxGraph.prototype.createEdgeHandler=function(a,b){return b==mxEdgeStyle.Loop||b==mxEdgeStyle.ElbowConnector||b==mxEdgeStyle.SideToSide||b==mxEdgeStyle.TopToBottom?this.createElbowEdgeHandler(a):b==mxEdgeStyle.SegmentConnector||b==mxEdgeStyle.OrthConnector?this.createEdgeSegmentHandler(a):new mxEdgeHandler(a)};mxGraph.prototype.createEdgeSegmentHandler=function(a){return new mxEdgeSegmentHandler(a)};mxGraph.prototype.createElbowEdgeHandler=function(a){return new mxElbowEdgeHandler(a)};
-mxGraph.prototype.addMouseListener=function(a){null==this.mouseListeners&&(this.mouseListeners=[]);this.mouseListeners.push(a)};mxGraph.prototype.removeMouseListener=function(a){if(null!=this.mouseListeners)for(var b=0;b<this.mouseListeners.length;b++)if(this.mouseListeners[b]==a){this.mouseListeners.splice(b,1);break}};
-mxGraph.prototype.updateMouseEvent=function(a,b){if(null==a.graphX||null==a.graphY){var c=mxUtils.convertPoint(this.container,a.getX(),a.getY());a.graphX=c.x-this.panDx;a.graphY=c.y-this.panDy;null==a.getCell()&&this.isMouseDown&&b==mxEvent.MOUSE_MOVE&&(a.state=this.view.getState(this.getCellAt(c.x,c.y,null,null,null,function(a){return null==a.shape||a.shape.paintBackground!=mxRectangleShape.prototype.paintBackground||"1"==mxUtils.getValue(a.style,mxConstants.STYLE_POINTER_EVENTS,"1")||null!=a.shape.fill&&
-a.shape.fill!=mxConstants.NONE})))}return a};mxGraph.prototype.getStateForTouchEvent=function(a){var b=mxEvent.getClientX(a);a=mxEvent.getClientY(a);b=mxUtils.convertPoint(this.container,b,a);return this.view.getState(this.getCellAt(b.x,b.y))};
-mxGraph.prototype.isEventIgnored=function(a,b,c){var d=mxEvent.isMouseEvent(b.getEvent()),e=!1;b.getEvent()==this.lastEvent?e=!0:this.lastEvent=b.getEvent();null!=this.eventSource&&a!=mxEvent.MOUSE_MOVE?(mxEvent.removeGestureListeners(this.eventSource,null,this.mouseMoveRedirect,this.mouseUpRedirect),this.eventSource=this.mouseUpRedirect=this.mouseMoveRedirect=null):mxClient.IS_GC||null==this.eventSource||b.getSource()==this.eventSource?!mxClient.IS_TOUCH||a!=mxEvent.MOUSE_DOWN||d||mxEvent.isPenEvent(b.getEvent())||
-(this.eventSource=b.getSource(),this.mouseMoveRedirect=mxUtils.bind(this,function(a){this.fireMouseEvent(mxEvent.MOUSE_MOVE,new mxMouseEvent(a,this.getStateForTouchEvent(a)))}),this.mouseUpRedirect=mxUtils.bind(this,function(a){this.fireMouseEvent(mxEvent.MOUSE_UP,new mxMouseEvent(a,this.getStateForTouchEvent(a)))}),mxEvent.addGestureListeners(this.eventSource,null,this.mouseMoveRedirect,this.mouseUpRedirect)):e=!0;this.isSyntheticEventIgnored(a,b,c)&&(e=!0);if(!mxEvent.isPopupTrigger(this.lastEvent)&&
-a!=mxEvent.MOUSE_MOVE&&2==this.lastEvent.detail)return!0;a==mxEvent.MOUSE_UP&&this.isMouseDown?this.isMouseDown=!1:a!=mxEvent.MOUSE_DOWN||this.isMouseDown?!e&&((!mxClient.IS_FF||a!=mxEvent.MOUSE_MOVE)&&this.isMouseDown&&this.isMouseTrigger!=d||a==mxEvent.MOUSE_DOWN&&this.isMouseDown||a==mxEvent.MOUSE_UP&&!this.isMouseDown)&&(e=!0):(this.isMouseDown=!0,this.isMouseTrigger=d);e||a!=mxEvent.MOUSE_DOWN||(this.lastMouseX=b.getX(),this.lastMouseY=b.getY());return e};
-mxGraph.prototype.isSyntheticEventIgnored=function(a,b,c){c=!1;b=mxEvent.isMouseEvent(b.getEvent());this.ignoreMouseEvents&&b&&a!=mxEvent.MOUSE_MOVE?(this.ignoreMouseEvents=a!=mxEvent.MOUSE_UP,c=!0):mxClient.IS_FF&&!b&&a==mxEvent.MOUSE_UP&&(this.ignoreMouseEvents=!0);return c};
-mxGraph.prototype.isEventSourceIgnored=function(a,b){var c=b.getSource(),d=null!=c.nodeName?c.nodeName.toLowerCase():"",e=!mxEvent.isMouseEvent(b.getEvent())||mxEvent.isLeftMouseButton(b.getEvent());return a==mxEvent.MOUSE_DOWN&&e&&("select"==d||"option"==d||"input"==d&&"checkbox"!=c.type&&"radio"!=c.type&&"button"!=c.type&&"submit"!=c.type&&"file"!=c.type)};mxGraph.prototype.getEventState=function(a){return a};
-mxGraph.prototype.fireMouseEvent=function(a,b,c){if(this.isEventSourceIgnored(a,b))null!=this.tooltipHandler&&this.tooltipHandler.hide();else{null==c&&(c=this);b=this.updateMouseEvent(b,a);if(!this.nativeDblClickEnabled&&!mxEvent.isPopupTrigger(b.getEvent())||this.doubleTapEnabled&&mxClient.IS_TOUCH&&(mxEvent.isTouchEvent(b.getEvent())||mxEvent.isPenEvent(b.getEvent()))){var d=(new Date).getTime();if(!mxClient.IS_QUIRKS&&a==mxEvent.MOUSE_DOWN||mxClient.IS_QUIRKS&&a==mxEvent.MOUSE_UP&&!this.fireDoubleClick)if(null!=
-this.lastTouchEvent&&this.lastTouchEvent!=b.getEvent()&&d-this.lastTouchTime<this.doubleTapTimeout&&Math.abs(this.lastTouchX-b.getX())<this.doubleTapTolerance&&Math.abs(this.lastTouchY-b.getY())<this.doubleTapTolerance&&2>this.doubleClickCounter){if(this.doubleClickCounter++,d=!1,a==mxEvent.MOUSE_UP?b.getCell()==this.lastTouchCell&&null!=this.lastTouchCell&&(this.lastTouchTime=0,d=this.lastTouchCell,this.lastTouchCell=null,mxClient.IS_QUIRKS&&b.getSource().fireEvent("ondblclick"),this.dblClick(b.getEvent(),
-d),d=!0):(this.fireDoubleClick=!0,this.lastTouchTime=0),!mxClient.IS_QUIRKS||d){mxEvent.consume(b.getEvent());return}}else{if(null==this.lastTouchEvent||this.lastTouchEvent!=b.getEvent())this.lastTouchCell=b.getCell(),this.lastTouchX=b.getX(),this.lastTouchY=b.getY(),this.lastTouchTime=d,this.lastTouchEvent=b.getEvent(),this.doubleClickCounter=0}else if((this.isMouseDown||a==mxEvent.MOUSE_UP)&&this.fireDoubleClick){this.fireDoubleClick=!1;d=this.lastTouchCell;this.lastTouchCell=null;this.isMouseDown=
-!1;(null!=d||(mxEvent.isTouchEvent(b.getEvent())||mxEvent.isPenEvent(b.getEvent()))&&(mxClient.IS_GC||mxClient.IS_SF))&&Math.abs(this.lastTouchX-b.getX())<this.doubleTapTolerance&&Math.abs(this.lastTouchY-b.getY())<this.doubleTapTolerance?this.dblClick(b.getEvent(),d):mxEvent.consume(b.getEvent());return}}if(!this.isEventIgnored(a,b,c)){b.state=this.getEventState(b.getState());this.fireEvent(new mxEventObject(mxEvent.FIRE_MOUSE_EVENT,"eventName",a,"event",b));if(mxClient.IS_OP||mxClient.IS_SF||mxClient.IS_GC||
-mxClient.IS_IE11||mxClient.IS_IE&&mxClient.IS_SVG||b.getEvent().target!=this.container){if(a==mxEvent.MOUSE_MOVE&&this.isMouseDown&&this.autoScroll&&!mxEvent.isMultiTouchEvent(b.getEvent))this.scrollPointToVisible(b.getGraphX(),b.getGraphY(),this.autoExtend);else if(a==mxEvent.MOUSE_UP&&this.ignoreScrollbars&&this.translateToScrollPosition&&(0!=this.container.scrollLeft||0!=this.container.scrollTop)){var d=this.view.scale,e=this.view.translate;this.view.setTranslate(e.x-this.container.scrollLeft/
-d,e.y-this.container.scrollTop/d);this.container.scrollLeft=0;this.container.scrollTop=0}if(null!=this.mouseListeners)for(d=[c,b],b.getEvent().preventDefault||(b.getEvent().returnValue=!0),e=0;e<this.mouseListeners.length;e++){var f=this.mouseListeners[e];a==mxEvent.MOUSE_DOWN?f.mouseDown.apply(f,d):a==mxEvent.MOUSE_MOVE?f.mouseMove.apply(f,d):a==mxEvent.MOUSE_UP&&f.mouseUp.apply(f,d)}a==mxEvent.MOUSE_UP&&this.click(b)}(mxEvent.isTouchEvent(b.getEvent())||mxEvent.isPenEvent(b.getEvent()))&&a==mxEvent.MOUSE_DOWN&&
-this.tapAndHoldEnabled&&!this.tapAndHoldInProgress?(this.tapAndHoldInProgress=!0,this.initialTouchX=b.getGraphX(),this.initialTouchY=b.getGraphY(),this.tapAndHoldThread&&window.clearTimeout(this.tapAndHoldThread),this.tapAndHoldThread=window.setTimeout(mxUtils.bind(this,function(){this.tapAndHoldValid&&this.tapAndHold(b);this.tapAndHoldValid=this.tapAndHoldInProgress=!1}),this.tapAndHoldDelay),this.tapAndHoldValid=!0):a==mxEvent.MOUSE_UP?this.tapAndHoldValid=this.tapAndHoldInProgress=!1:this.tapAndHoldValid&&
-(this.tapAndHoldValid=Math.abs(this.initialTouchX-b.getGraphX())<this.tolerance&&Math.abs(this.initialTouchY-b.getGraphY())<this.tolerance);a==mxEvent.MOUSE_DOWN&&this.isEditing()&&!this.cellEditor.isEventSource(b.getEvent())&&this.stopEditing(!this.isInvokesStopCellEditing());this.consumeMouseEvent(a,b,c)}}};mxGraph.prototype.consumeMouseEvent=function(a,b,c){a==mxEvent.MOUSE_DOWN&&mxEvent.isTouchEvent(b.getEvent())&&b.consume(!1)};
-mxGraph.prototype.fireGestureEvent=function(a,b){this.lastTouchTime=0;this.fireEvent(new mxEventObject(mxEvent.GESTURE,"event",a,"cell",b))};
-mxGraph.prototype.destroy=function(){this.destroyed||(this.destroyed=!0,null!=this.tooltipHandler&&this.tooltipHandler.destroy(),null!=this.selectionCellsHandler&&this.selectionCellsHandler.destroy(),null!=this.panningHandler&&this.panningHandler.destroy(),null!=this.popupMenuHandler&&this.popupMenuHandler.destroy(),null!=this.connectionHandler&&this.connectionHandler.destroy(),null!=this.graphHandler&&this.graphHandler.destroy(),null!=this.cellEditor&&this.cellEditor.destroy(),null!=this.view&&this.view.destroy(),
-null!=this.model&&null!=this.graphModelChangeListener&&(this.model.removeListener(this.graphModelChangeListener),this.graphModelChangeListener=null),this.container=null)};function mxCellOverlay(a,b,c,d,e,f){this.image=a;this.tooltip=b;this.align=null!=c?c:this.align;this.verticalAlign=null!=d?d:this.verticalAlign;this.offset=null!=e?e:new mxPoint;this.cursor=null!=f?f:"help"}mxCellOverlay.prototype=new mxEventSource;mxCellOverlay.prototype.constructor=mxCellOverlay;mxCellOverlay.prototype.image=null;
-mxCellOverlay.prototype.tooltip=null;mxCellOverlay.prototype.align=mxConstants.ALIGN_RIGHT;mxCellOverlay.prototype.verticalAlign=mxConstants.ALIGN_BOTTOM;mxCellOverlay.prototype.offset=null;mxCellOverlay.prototype.cursor=null;mxCellOverlay.prototype.defaultOverlap=.5;
-mxCellOverlay.prototype.getBounds=function(a){var b=a.view.graph.getModel().isEdge(a.cell),c=a.view.scale,d=this.image.width,e=this.image.height;if(b)if(b=a.absolutePoints,1==b.length%2)b=b[Math.floor(b.length/2)];else{var f=b.length/2;a=b[f-1];b=b[f];b=new mxPoint(a.x+(b.x-a.x)/2,a.y+(b.y-a.y)/2)}else b=new mxPoint,b.x=this.align==mxConstants.ALIGN_LEFT?a.x:this.align==mxConstants.ALIGN_CENTER?a.x+a.width/2:a.x+a.width,b.y=this.verticalAlign==mxConstants.ALIGN_TOP?a.y:this.verticalAlign==mxConstants.ALIGN_MIDDLE?
-a.y+a.height/2:a.y+a.height;return new mxRectangle(Math.round(b.x-(d*this.defaultOverlap-this.offset.x)*c),Math.round(b.y-(e*this.defaultOverlap-this.offset.y)*c),d*c,e*c)};mxCellOverlay.prototype.toString=function(){return this.tooltip};function mxOutline(a,b){this.source=a;null!=b&&this.init(b)}mxOutline.prototype.source=null;mxOutline.prototype.outline=null;mxOutline.prototype.graphRenderHint=mxConstants.RENDERING_HINT_FASTER;mxOutline.prototype.enabled=!0;mxOutline.prototype.showViewport=!0;
-mxOutline.prototype.border=10;mxOutline.prototype.sizerSize=8;mxOutline.prototype.labelsVisible=!1;mxOutline.prototype.updateOnPan=!1;mxOutline.prototype.sizerImage=null;mxOutline.prototype.minScale=1E-4;mxOutline.prototype.suspended=!1;mxOutline.prototype.forceVmlHandles=8==document.documentMode;mxOutline.prototype.createGraph=function(a){a=new mxGraph(a,this.source.getModel(),this.graphRenderHint,this.source.getStylesheet());a.foldingEnabled=!1;a.autoScroll=!1;return a};
-mxOutline.prototype.init=function(a){this.outline=this.createGraph(a);var b=this.outline.graphModelChanged;this.outline.graphModelChanged=mxUtils.bind(this,function(a){this.suspended||null==this.outline||b.apply(this.outline,arguments)});mxClient.IS_SVG&&(a=this.outline.getView().getCanvas().parentNode,a.setAttribute("shape-rendering","optimizeSpeed"),a.setAttribute("image-rendering","optimizeSpeed"));this.outline.labelsVisible=this.labelsVisible;this.outline.setEnabled(!1);this.updateHandler=mxUtils.bind(this,
-function(a,b){this.suspended||this.active||this.update()});this.source.getModel().addListener(mxEvent.CHANGE,this.updateHandler);this.outline.addMouseListener(this);a=this.source.getView();a.addListener(mxEvent.SCALE,this.updateHandler);a.addListener(mxEvent.TRANSLATE,this.updateHandler);a.addListener(mxEvent.SCALE_AND_TRANSLATE,this.updateHandler);a.addListener(mxEvent.DOWN,this.updateHandler);a.addListener(mxEvent.UP,this.updateHandler);mxEvent.addListener(this.source.container,"scroll",this.updateHandler);
-this.panHandler=mxUtils.bind(this,function(a){this.updateOnPan&&this.updateHandler.apply(this,arguments)});this.source.addListener(mxEvent.PAN,this.panHandler);this.refreshHandler=mxUtils.bind(this,function(a){this.outline.setStylesheet(this.source.getStylesheet());this.outline.refresh()});this.source.addListener(mxEvent.REFRESH,this.refreshHandler);this.bounds=new mxRectangle(0,0,0,0);this.selectionBorder=new mxRectangleShape(this.bounds,null,mxConstants.OUTLINE_COLOR,mxConstants.OUTLINE_STROKEWIDTH);
-this.selectionBorder.dialect=this.outline.dialect;this.forceVmlHandles&&(this.selectionBorder.isHtmlAllowed=function(){return!1});this.selectionBorder.init(this.outline.getView().getOverlayPane());a=mxUtils.bind(this,function(a){var b=mxEvent.getSource(a),c=mxUtils.bind(this,function(a){this.outline.fireMouseEvent(mxEvent.MOUSE_MOVE,new mxMouseEvent(a))}),f=mxUtils.bind(this,function(a){mxEvent.removeGestureListeners(b,null,c,f);this.outline.fireMouseEvent(mxEvent.MOUSE_UP,new mxMouseEvent(a))});
-mxEvent.addGestureListeners(b,null,c,f);this.outline.fireMouseEvent(mxEvent.MOUSE_DOWN,new mxMouseEvent(a))});mxEvent.addGestureListeners(this.selectionBorder.node,a);this.sizer=this.createSizer();this.forceVmlHandles&&(this.sizer.isHtmlAllowed=function(){return!1});this.sizer.init(this.outline.getView().getOverlayPane());this.enabled&&(this.sizer.node.style.cursor="nwse-resize");mxEvent.addGestureListeners(this.sizer.node,a);this.selectionBorder.node.style.display=this.showViewport?"":"none";this.sizer.node.style.display=
-this.selectionBorder.node.style.display;this.selectionBorder.node.style.cursor="move";this.update(!1)};mxOutline.prototype.isEnabled=function(){return this.enabled};mxOutline.prototype.setEnabled=function(a){this.enabled=a};mxOutline.prototype.setZoomEnabled=function(a){this.sizer.node.style.visibility=a?"visible":"hidden"};mxOutline.prototype.refresh=function(){this.update(!0)};
-mxOutline.prototype.createSizer=function(){var a=null!=this.sizerImage?new mxImageShape(new mxRectangle(0,0,this.sizerImage.width,this.sizerImage.height),this.sizerImage.src):new mxRectangleShape(new mxRectangle(0,0,this.sizerSize,this.sizerSize),mxConstants.OUTLINE_HANDLE_FILLCOLOR,mxConstants.OUTLINE_HANDLE_STROKECOLOR);a.dialect=this.outline.dialect;return a};mxOutline.prototype.getSourceContainerSize=function(){return new mxRectangle(0,0,this.source.container.scrollWidth,this.source.container.scrollHeight)};
-mxOutline.prototype.getOutlineOffset=function(a){return null};mxOutline.prototype.getSourceGraphBounds=function(){return this.source.getGraphBounds()};
-mxOutline.prototype.update=function(a){if(null!=this.source&&null!=this.source.container&&null!=this.outline&&null!=this.outline.container){var b=this.source.view.scale,c=this.getSourceGraphBounds(),c=new mxRectangle(c.x/b+this.source.panDx,c.y/b+this.source.panDy,c.width/b,c.height/b),d=new mxRectangle(0,0,this.source.container.clientWidth/b,this.source.container.clientHeight/b),e=c.clone();e.add(d);d=this.getSourceContainerSize();b=Math.min(Math.max(0,this.outline.container.clientWidth-this.border)/
-Math.max(d.width/b,e.width),Math.max(0,this.outline.container.clientHeight-this.border)/Math.max(d.height/b,e.height));d=isNaN(b)?this.minScale:Math.max(this.minScale,b);if(0<d){this.outline.getView().scale!=d&&(this.outline.getView().scale=d,a=!0);b=this.outline.getView();b.currentRoot!=this.source.getView().currentRoot&&b.setCurrentRoot(this.source.getView().currentRoot);var e=this.source.view.translate,f=e.x+this.source.panDx,g=e.y+this.source.panDy,d=this.getOutlineOffset(d);null!=d&&(f+=d.x,
-g+=d.y);0>c.x&&(f-=c.x);0>c.y&&(g-=c.y);if(b.translate.x!=f||b.translate.y!=g)b.translate.x=f,b.translate.y=g,a=!0;var c=b.translate,d=this.source.getView().scale,f=d/b.scale,g=1/b.scale,k=this.source.container;this.bounds=new mxRectangle((c.x-e.x-this.source.panDx)/g,(c.y-e.y-this.source.panDy)/g,k.clientWidth/f,k.clientHeight/f);this.bounds.x+=this.source.container.scrollLeft*b.scale/d;this.bounds.y+=this.source.container.scrollTop*b.scale/d;c=this.selectionBorder.bounds;if(c.x!=this.bounds.x||
-c.y!=this.bounds.y||c.width!=this.bounds.width||c.height!=this.bounds.height)this.selectionBorder.bounds=this.bounds,this.selectionBorder.redraw();c=this.sizer.bounds;b=new mxRectangle(this.bounds.x+this.bounds.width-c.width/2,this.bounds.y+this.bounds.height-c.height/2,c.width,c.height);if(c.x!=b.x||c.y!=b.y||c.width!=b.width||c.height!=b.height)this.sizer.bounds=b,"hidden"!=this.sizer.node.style.visibility&&this.sizer.redraw();a&&this.outline.view.revalidate()}}};
-mxOutline.prototype.mouseDown=function(a,b){if(this.enabled&&this.showViewport){var c=mxEvent.isMouseEvent(b.getEvent())?0:this.source.tolerance,c=this.source.allowHandleBoundsCheck&&(mxClient.IS_IE||0<c)?new mxRectangle(b.getGraphX()-c,b.getGraphY()-c,2*c,2*c):null;this.zoom=b.isSource(this.sizer)||null!=c&&mxUtils.intersects(shape.bounds,c);this.startX=b.getX();this.startY=b.getY();this.active=!0;this.source.useScrollbarsForPanning&&mxUtils.hasScrollbars(this.source.container)?(this.dx0=this.source.container.scrollLeft,
-this.dy0=this.source.container.scrollTop):this.dy0=this.dx0=0}b.consume()};
-mxOutline.prototype.mouseMove=function(a,b){if(this.active){this.selectionBorder.node.style.display=this.showViewport?"":"none";this.sizer.node.style.display=this.selectionBorder.node.style.display;var c=this.getTranslateForEvent(b),d=c.x,e=c.y;if(this.zoom)c=this.source.container,e=d/(c.clientWidth/c.clientHeight),c=new mxRectangle(this.bounds.x,this.bounds.y,Math.max(1,this.bounds.width+d),Math.max(1,this.bounds.height+e)),this.selectionBorder.bounds=c,this.selectionBorder.redraw();else{var f=this.outline.getView().scale,
-c=new mxRectangle(this.bounds.x+d,this.bounds.y+e,this.bounds.width,this.bounds.height);this.selectionBorder.bounds=c;this.selectionBorder.redraw();d=d/f*this.source.getView().scale;e=e/f*this.source.getView().scale;this.source.panGraph(-d-this.dx0,-e-this.dy0)}d=this.sizer.bounds;this.sizer.bounds=new mxRectangle(c.x+c.width-d.width/2,c.y+c.height-d.height/2,d.width,d.height);"hidden"!=this.sizer.node.style.visibility&&this.sizer.redraw();b.consume()}};
-mxOutline.prototype.getTranslateForEvent=function(a){return new mxPoint(a.getX()-this.startX,a.getY()-this.startY)};
-mxOutline.prototype.mouseUp=function(a,b){if(this.active){var c=this.getTranslateForEvent(b),d=c.x,c=c.y;if(0<Math.abs(d)||0<Math.abs(c)){if(this.zoom){var c=this.selectionBorder.bounds.width,e=this.source.getView().scale;this.source.zoomTo(Math.max(this.minScale,e-d*e/c),!1)}else this.source.useScrollbarsForPanning&&mxUtils.hasScrollbars(this.source.container)||(this.source.panGraph(0,0),d/=this.outline.getView().scale,c/=this.outline.getView().scale,e=this.source.getView().translate,this.source.getView().setTranslate(e.x-
-d,e.y-c));this.update();b.consume()}this.index=null;this.active=!1}};
-mxOutline.prototype.destroy=function(){null!=this.source&&(this.source.removeListener(this.panHandler),this.source.removeListener(this.refreshHandler),this.source.getModel().removeListener(this.updateHandler),this.source.getView().removeListener(this.updateHandler),mxEvent.addListener(this.source.container,"scroll",this.updateHandler),this.source=null);null!=this.outline&&(this.outline.removeMouseListener(this),this.outline.destroy(),this.outline=null);null!=this.selectionBorder&&(this.selectionBorder.destroy(),
-this.selectionBorder=null);null!=this.sizer&&(this.sizer.destroy(),this.sizer=null)};function mxMultiplicity(a,b,c,d,e,f,g,k,l,m){this.source=a;this.type=b;this.attr=c;this.value=d;this.min=null!=e?e:0;this.max=null!=f?f:"n";this.validNeighbors=g;this.countError=mxResources.get(k)||k;this.typeError=mxResources.get(l)||l;this.validNeighborsAllowed=null!=m?m:!0}mxMultiplicity.prototype.type=null;mxMultiplicity.prototype.attr=null;mxMultiplicity.prototype.value=null;mxMultiplicity.prototype.source=null;
-mxMultiplicity.prototype.min=null;mxMultiplicity.prototype.max=null;mxMultiplicity.prototype.validNeighbors=null;mxMultiplicity.prototype.validNeighborsAllowed=!0;mxMultiplicity.prototype.countError=null;mxMultiplicity.prototype.typeError=null;
-mxMultiplicity.prototype.check=function(a,b,c,d,e,f){var g="";if(this.source&&this.checkTerminal(a,c,b)||!this.source&&this.checkTerminal(a,d,b))null!=this.countError&&(this.source&&(0==this.max||e>=this.max)||!this.source&&(0==this.max||f>=this.max))&&(g+=this.countError+"\n"),null!=this.validNeighbors&&null!=this.typeError&&0<this.validNeighbors.length&&(this.checkNeighbors(a,b,c,d)||(g+=this.typeError+"\n"));return 0<g.length?g:null};
-mxMultiplicity.prototype.checkNeighbors=function(a,b,c,d){b=a.model.getValue(c);d=a.model.getValue(d);c=!this.validNeighborsAllowed;for(var e=this.validNeighbors,f=0;f<e.length;f++)if(this.source&&this.checkType(a,d,e[f])){c=this.validNeighborsAllowed;break}else if(!this.source&&this.checkType(a,b,e[f])){c=this.validNeighborsAllowed;break}return c};mxMultiplicity.prototype.checkTerminal=function(a,b,c){b=a.model.getValue(b);return this.checkType(a,b,this.type,this.attr,this.value)};
-mxMultiplicity.prototype.checkType=function(a,b,c,d,e){return null!=b?isNaN(b.nodeType)?b==c:mxUtils.isNode(b,c,d,e):!1};function mxLayoutManager(a){this.undoHandler=mxUtils.bind(this,function(a,c){this.isEnabled()&&this.beforeUndo(c.getProperty("edit"))});this.moveHandler=mxUtils.bind(this,function(a,c){this.isEnabled()&&this.cellsMoved(c.getProperty("cells"),c.getProperty("event"))});this.setGraph(a)}mxLayoutManager.prototype=new mxEventSource;mxLayoutManager.prototype.constructor=mxLayoutManager;
-mxLayoutManager.prototype.graph=null;mxLayoutManager.prototype.bubbling=!0;mxLayoutManager.prototype.enabled=!0;mxLayoutManager.prototype.updateHandler=null;mxLayoutManager.prototype.moveHandler=null;mxLayoutManager.prototype.isEnabled=function(){return this.enabled};mxLayoutManager.prototype.setEnabled=function(a){this.enabled=a};mxLayoutManager.prototype.isBubbling=function(){return this.bubbling};mxLayoutManager.prototype.setBubbling=function(a){this.bubbling=a};
-mxLayoutManager.prototype.getGraph=function(){return this.graph};mxLayoutManager.prototype.setGraph=function(a){if(null!=this.graph){var b=this.graph.getModel();b.removeListener(this.undoHandler);this.graph.removeListener(this.moveHandler)}this.graph=a;null!=this.graph&&(b=this.graph.getModel(),b.addListener(mxEvent.BEFORE_UNDO,this.undoHandler),this.graph.addListener(mxEvent.MOVE_CELLS,this.moveHandler))};mxLayoutManager.prototype.getLayout=function(a){return null};
-mxLayoutManager.prototype.beforeUndo=function(a){a=this.getCellsForChanges(a.changes);for(var b=this.getGraph().getModel(),c=[],d=0;d<a.length;d++)c=c.concat(b.getDescendants(a[d]));a=c;if(this.isBubbling())for(c=b.getParents(a);0<c.length;)a=a.concat(c),c=b.getParents(c);this.executeLayoutForCells(a)};mxLayoutManager.prototype.executeLayoutForCells=function(a){a=mxUtils.sortCells(a,!0);a=a.concat(a.slice().reverse());this.layoutCells(a)};
-mxLayoutManager.prototype.cellsMoved=function(a,b){if(null!=a&&null!=b)for(var c=mxUtils.convertPoint(this.getGraph().container,mxEvent.getClientX(b),mxEvent.getClientY(b)),d=this.getGraph().getModel(),e=0;e<a.length;e++){var f=d.getParent(a[e]);0>mxUtils.indexOf(a,f)&&(f=this.getLayout(f),null!=f&&f.moveCell(a[e],c.x,c.y))}};
-mxLayoutManager.prototype.getCellsForChanges=function(a){for(var b=new mxDictionary,c=[],d=0;d<a.length;d++){var e=a[d];if(e instanceof mxRootChange)return[];for(var e=this.getCellsForChange(e),f=0;f<e.length;f++)null==e[f]||b.get(e[f])||(b.put(e[f],!0),c.push(e[f]))}return c};
-mxLayoutManager.prototype.getCellsForChange=function(a){var b=this.getGraph().getModel();return a instanceof mxChildChange?[a.child,a.previous,b.getParent(a.child)]:a instanceof mxTerminalChange||a instanceof mxGeometryChange?[a.cell,b.getParent(a.cell)]:a instanceof mxVisibleChange||a instanceof mxStyleChange?[a.cell]:[]};
-mxLayoutManager.prototype.layoutCells=function(a){if(0<a.length){var b=this.getGraph().getModel();b.beginUpdate();try{for(var c=null,d=0;d<a.length;d++)a[d]!=b.getRoot()&&a[d]!=c&&this.executeLayout(this.getLayout(a[d]),a[d])&&(c=a[d]);this.fireEvent(new mxEventObject(mxEvent.LAYOUT_CELLS,"cells",a))}finally{b.endUpdate()}}};mxLayoutManager.prototype.executeLayout=function(a,b){var c=!1;null!=a&&null!=b&&(a.execute(b),c=!0);return c};mxLayoutManager.prototype.destroy=function(){this.setGraph(null)};
-function mxSwimlaneManager(a,b,c,d){this.horizontal=null!=b?b:!0;this.addEnabled=null!=c?c:!0;this.resizeEnabled=null!=d?d:!0;this.addHandler=mxUtils.bind(this,function(a,b){this.isEnabled()&&this.isAddEnabled()&&this.cellsAdded(b.getProperty("cells"))});this.resizeHandler=mxUtils.bind(this,function(a,b){this.isEnabled()&&this.isResizeEnabled()&&this.cellsResized(b.getProperty("cells"))});this.setGraph(a)}mxSwimlaneManager.prototype=new mxEventSource;mxSwimlaneManager.prototype.constructor=mxSwimlaneManager;
-mxSwimlaneManager.prototype.graph=null;mxSwimlaneManager.prototype.enabled=!0;mxSwimlaneManager.prototype.horizontal=!0;mxSwimlaneManager.prototype.addEnabled=!0;mxSwimlaneManager.prototype.resizeEnabled=!0;mxSwimlaneManager.prototype.addHandler=null;mxSwimlaneManager.prototype.resizeHandler=null;mxSwimlaneManager.prototype.isEnabled=function(){return this.enabled};mxSwimlaneManager.prototype.setEnabled=function(a){this.enabled=a};mxSwimlaneManager.prototype.isHorizontal=function(){return this.horizontal};
-mxSwimlaneManager.prototype.setHorizontal=function(a){this.horizontal=a};mxSwimlaneManager.prototype.isAddEnabled=function(){return this.addEnabled};mxSwimlaneManager.prototype.setAddEnabled=function(a){this.addEnabled=a};mxSwimlaneManager.prototype.isResizeEnabled=function(){return this.resizeEnabled};mxSwimlaneManager.prototype.setResizeEnabled=function(a){this.resizeEnabled=a};mxSwimlaneManager.prototype.getGraph=function(){return this.graph};
-mxSwimlaneManager.prototype.setGraph=function(a){null!=this.graph&&(this.graph.removeListener(this.addHandler),this.graph.removeListener(this.resizeHandler));this.graph=a;null!=this.graph&&(this.graph.addListener(mxEvent.ADD_CELLS,this.addHandler),this.graph.addListener(mxEvent.CELLS_RESIZED,this.resizeHandler))};mxSwimlaneManager.prototype.isSwimlaneIgnored=function(a){return!this.getGraph().isSwimlane(a)};
-mxSwimlaneManager.prototype.isCellHorizontal=function(a){return this.graph.isSwimlane(a)?(a=this.graph.getCellStyle(a),1==mxUtils.getValue(a,mxConstants.STYLE_HORIZONTAL,1)):!this.isHorizontal()};mxSwimlaneManager.prototype.cellsAdded=function(a){if(null!=a){var b=this.getGraph().getModel();b.beginUpdate();try{for(var c=0;c<a.length;c++)this.isSwimlaneIgnored(a[c])||this.swimlaneAdded(a[c])}finally{b.endUpdate()}}};
-mxSwimlaneManager.prototype.swimlaneAdded=function(a){for(var b=this.getGraph().getModel(),c=b.getParent(a),d=b.getChildCount(c),e=null,f=0;f<d;f++){var g=b.getChildAt(c,f);if(g!=a&&!this.isSwimlaneIgnored(g)&&(e=b.getGeometry(g),null!=e))break}null!=e&&(b=null!=c?this.isCellHorizontal(c):this.horizontal,this.resizeSwimlane(a,e.width,e.height,b))};
-mxSwimlaneManager.prototype.cellsResized=function(a){if(null!=a){var b=this.getGraph().getModel();b.beginUpdate();try{for(var c=0;c<a.length;c++)if(!this.isSwimlaneIgnored(a[c])){var d=b.getGeometry(a[c]);if(null!=d){for(var e=new mxRectangle(0,0,d.width,d.height),f=a[c],g=f;null!=g;){var f=g,g=b.getParent(g),k=this.graph.isSwimlane(g)?this.graph.getStartSize(g):new mxRectangle;e.width+=k.width;e.height+=k.height}var l=null!=g?this.isCellHorizontal(g):this.horizontal;this.resizeSwimlane(f,e.width,
-e.height,l)}}}finally{b.endUpdate()}}};
-mxSwimlaneManager.prototype.resizeSwimlane=function(a,b,c,d){var e=this.getGraph().getModel();e.beginUpdate();try{var f=this.isCellHorizontal(a);if(!this.isSwimlaneIgnored(a)){var g=e.getGeometry(a);null!=g&&(d&&g.height!=c||!d&&g.width!=b)&&(g=g.clone(),d?g.height=c:g.width=b,e.setGeometry(a,g))}var k=this.graph.isSwimlane(a)?this.graph.getStartSize(a):new mxRectangle;b-=k.width;c-=k.height;var l=e.getChildCount(a);for(d=0;d<l;d++){var m=e.getChildAt(a,d);this.resizeSwimlane(m,b,c,f)}}finally{e.endUpdate()}};
-mxSwimlaneManager.prototype.destroy=function(){this.setGraph(null)};
-function mxTemporaryCellStates(a,b,c,d,e){b=null!=b?b:1;this.view=a;this.oldValidateCellState=a.validateCellState;this.oldBounds=a.getGraphBounds();this.oldStates=a.getStates();this.oldScale=a.getScale();this.oldDoRedrawShape=a.graph.cellRenderer.doRedrawShape;var f=this;null!=e&&(a.graph.cellRenderer.doRedrawShape=function(b){var c=b.shape.paint;b.shape.paint=function(a){var d=e(b);null!=d&&a.setLink(d);c.apply(this,arguments);null!=d&&a.setLink(null)};f.oldDoRedrawShape.apply(a.graph.cellRenderer,
-arguments);b.shape.paint=c});a.validateCellState=function(b,c){return null==b||null==d||d(b)?f.oldValidateCellState.apply(a,arguments):null};a.setStates(new mxDictionary);a.setScale(b);if(null!=c){a.resetValidationState();b=null;for(var g=0;g<c.length;g++){var k=a.getBoundingBox(a.validateCellState(a.validateCell(c[g])));null==b?b=k:b.add(k)}a.setGraphBounds(b||new mxRectangle)}}mxTemporaryCellStates.prototype.view=null;mxTemporaryCellStates.prototype.oldStates=null;
-mxTemporaryCellStates.prototype.oldBounds=null;mxTemporaryCellStates.prototype.oldScale=null;mxTemporaryCellStates.prototype.destroy=function(){this.view.setScale(this.oldScale);this.view.setStates(this.oldStates);this.view.setGraphBounds(this.oldBounds);this.view.validateCellState=this.oldValidateCellState;this.view.graph.cellRenderer.doRedrawShape=this.oldDoRedrawShape};function mxCellStatePreview(a){this.deltas=new mxDictionary;this.graph=a}mxCellStatePreview.prototype.graph=null;
-mxCellStatePreview.prototype.deltas=null;mxCellStatePreview.prototype.count=0;mxCellStatePreview.prototype.isEmpty=function(){return 0==this.count};mxCellStatePreview.prototype.moveState=function(a,b,c,d,e){d=null!=d?d:!0;e=null!=e?e:!0;var f=this.deltas.get(a.cell);null==f?(f={point:new mxPoint(b,c),state:a},this.deltas.put(a.cell,f),this.count++):d?(f.point.x+=b,f.point.y+=c):(f.point.x=b,f.point.y=c);e&&this.addEdges(a);return f.point};
-mxCellStatePreview.prototype.show=function(a){this.deltas.visit(mxUtils.bind(this,function(a,c){this.translateState(c.state,c.point.x,c.point.y)}));this.deltas.visit(mxUtils.bind(this,function(b,c){this.revalidateState(c.state,c.point.x,c.point.y,a)}))};
-mxCellStatePreview.prototype.translateState=function(a,b,c){if(null!=a){var d=this.graph.getModel();if(d.isVertex(a.cell)){a.view.updateCellState(a);var e=d.getGeometry(a.cell);0==b&&0==c||null==e||e.relative&&null==this.deltas.get(a.cell)||(a.x+=b,a.y+=c)}for(var e=d.getChildCount(a.cell),f=0;f<e;f++)this.translateState(a.view.getState(d.getChildAt(a.cell,f)),b,c)}};
-mxCellStatePreview.prototype.revalidateState=function(a,b,c,d){if(null!=a){var e=this.graph.getModel();e.isEdge(a.cell)&&a.view.updateCellState(a);var f=this.graph.getCellGeometry(a.cell),g=a.view.getState(e.getParent(a.cell));0==b&&0==c||null==f||!f.relative||!e.isVertex(a.cell)||null!=g&&!e.isVertex(g.cell)&&null==this.deltas.get(a.cell)||(a.x+=b,a.y+=c);this.graph.cellRenderer.redraw(a);null!=d&&d(a);f=e.getChildCount(a.cell);for(g=0;g<f;g++)this.revalidateState(this.graph.view.getState(e.getChildAt(a.cell,
-g)),b,c,d)}};mxCellStatePreview.prototype.addEdges=function(a){for(var b=this.graph.getModel(),c=b.getEdgeCount(a.cell),d=0;d<c;d++){var e=a.view.getState(b.getEdgeAt(a.cell,d));null!=e&&this.moveState(e,0,0)}};function mxConnectionConstraint(a,b,c){this.point=a;this.perimeter=null!=b?b:!0;this.name=c}mxConnectionConstraint.prototype.point=null;mxConnectionConstraint.prototype.perimeter=null;mxConnectionConstraint.prototype.name=null;
-function mxGraphHandler(a){this.graph=a;this.graph.addMouseListener(this);this.panHandler=mxUtils.bind(this,function(){this.updatePreviewShape();this.updateHint()});this.graph.addListener(mxEvent.PAN,this.panHandler);this.escapeHandler=mxUtils.bind(this,function(a,c){this.reset()});this.graph.addListener(mxEvent.ESCAPE,this.escapeHandler)}mxGraphHandler.prototype.graph=null;mxGraphHandler.prototype.maxCells=mxClient.IS_IE?20:50;mxGraphHandler.prototype.enabled=!0;
-mxGraphHandler.prototype.highlightEnabled=!0;mxGraphHandler.prototype.cloneEnabled=!0;mxGraphHandler.prototype.moveEnabled=!0;mxGraphHandler.prototype.guidesEnabled=!1;mxGraphHandler.prototype.guide=null;mxGraphHandler.prototype.currentDx=null;mxGraphHandler.prototype.currentDy=null;mxGraphHandler.prototype.updateCursor=!0;mxGraphHandler.prototype.selectEnabled=!0;mxGraphHandler.prototype.removeCellsFromParent=!0;mxGraphHandler.prototype.connectOnDrop=!1;mxGraphHandler.prototype.scrollOnMove=!0;
-mxGraphHandler.prototype.minimumSize=6;mxGraphHandler.prototype.previewColor="black";mxGraphHandler.prototype.htmlPreview=!1;mxGraphHandler.prototype.shape=null;mxGraphHandler.prototype.scaleGrid=!1;mxGraphHandler.prototype.rotationEnabled=!0;mxGraphHandler.prototype.isEnabled=function(){return this.enabled};mxGraphHandler.prototype.setEnabled=function(a){this.enabled=a};mxGraphHandler.prototype.isCloneEnabled=function(){return this.cloneEnabled};
-mxGraphHandler.prototype.setCloneEnabled=function(a){this.cloneEnabled=a};mxGraphHandler.prototype.isMoveEnabled=function(){return this.moveEnabled};mxGraphHandler.prototype.setMoveEnabled=function(a){this.moveEnabled=a};mxGraphHandler.prototype.isSelectEnabled=function(){return this.selectEnabled};mxGraphHandler.prototype.setSelectEnabled=function(a){this.selectEnabled=a};mxGraphHandler.prototype.isRemoveCellsFromParent=function(){return this.removeCellsFromParent};
-mxGraphHandler.prototype.setRemoveCellsFromParent=function(a){this.removeCellsFromParent=a};mxGraphHandler.prototype.getInitialCellForEvent=function(a){return a.getCell()};mxGraphHandler.prototype.isDelayedSelection=function(a,b){return this.graph.isCellSelected(a)};mxGraphHandler.prototype.consumeMouseEvent=function(a,b){b.consume()};
-mxGraphHandler.prototype.mouseDown=function(a,b){if(!b.isConsumed()&&this.isEnabled()&&this.graph.isEnabled()&&null!=b.getState()&&!mxEvent.isMultiTouchEvent(b.getEvent())){var c=this.getInitialCellForEvent(b);this.delayedSelection=this.isDelayedSelection(c,b);this.cell=null;this.isSelectEnabled()&&!this.delayedSelection&&this.graph.selectCellForEvent(c,b.getEvent());if(this.isMoveEnabled()){var d=this.graph.model,e=d.getGeometry(c);this.graph.isCellMovable(c)&&(!d.isEdge(c)||1<this.graph.getSelectionCount()||
-null!=e.points&&0<e.points.length||null==d.getTerminal(c,!0)||null==d.getTerminal(c,!1)||this.graph.allowDanglingEdges||this.graph.isCloneEvent(b.getEvent())&&this.graph.isCellsCloneable())?this.start(c,b.getX(),b.getY()):this.delayedSelection&&(this.cell=c);this.cellWasClicked=!0;this.consumeMouseEvent(mxEvent.MOUSE_DOWN,b)}}};
-mxGraphHandler.prototype.getGuideStates=function(){var a=this.graph.getDefaultParent(),b=this.graph.getModel(),c=mxUtils.bind(this,function(a){return null!=this.graph.view.getState(a)&&b.isVertex(a)&&null!=b.getGeometry(a)&&!b.getGeometry(a).relative});return this.graph.view.getCellStates(b.filterDescendants(c,a))};mxGraphHandler.prototype.getCells=function(a){return!this.delayedSelection&&this.graph.isCellMovable(a)?[a]:this.graph.getMovableCells(this.graph.getSelectionCells())};
-mxGraphHandler.prototype.getPreviewBounds=function(a){a=this.getBoundingBox(a);null!=a&&(a.width=Math.max(0,a.width-1),a.height=Math.max(0,a.height-1),a.width<this.minimumSize?(a.x-=(this.minimumSize-a.width)/2,a.width=this.minimumSize):(a.x=Math.round(a.x),a.width=Math.ceil(a.width)),a.height<this.minimumSize?(a.y-=(this.minimumSize-a.height)/2,a.height=this.minimumSize):(a.y=Math.round(a.y),a.height=Math.ceil(a.height)));return a};
-mxGraphHandler.prototype.getBoundingBox=function(a){var b=null;if(null!=a&&0<a.length)for(var c=this.graph.getModel(),d=0;d<a.length;d++)if(c.isVertex(a[d])||c.isEdge(a[d])){var e=this.graph.view.getState(a[d]);if(null!=e){var f=e;c.isVertex(a[d])&&null!=e.shape&&null!=e.shape.boundingBox&&(f=e.shape.boundingBox);null==b?b=mxRectangle.fromRectangle(f):b.add(f)}}return b};
-mxGraphHandler.prototype.createPreviewShape=function(a){a=new mxRectangleShape(a,null,this.previewColor);a.isDashed=!0;this.htmlPreview?(a.dialect=mxConstants.DIALECT_STRICTHTML,a.init(this.graph.container)):(a.dialect=this.graph.dialect!=mxConstants.DIALECT_SVG?mxConstants.DIALECT_VML:mxConstants.DIALECT_SVG,a.init(this.graph.getView().getOverlayPane()),a.pointerEvents=!1,mxClient.IS_IOS&&(a.getSvgScreenOffset=function(){return 0}));return a};
-mxGraphHandler.prototype.start=function(a,b,c){this.cell=a;this.first=mxUtils.convertPoint(this.graph.container,b,c);this.cells=this.getCells(this.cell);this.bounds=this.graph.getView().getBounds(this.cells);this.pBounds=this.getPreviewBounds(this.cells);this.guidesEnabled&&(this.guide=new mxGuide(this.graph,this.getGuideStates()))};mxGraphHandler.prototype.useGuidesForEvent=function(a){return null!=this.guide?this.guide.isEnabledForEvent(a.getEvent()):!0};
-mxGraphHandler.prototype.snap=function(a){var b=this.scaleGrid?this.graph.view.scale:1;a.x=this.graph.snap(a.x/b)*b;a.y=this.graph.snap(a.y/b)*b;return a};mxGraphHandler.prototype.getDelta=function(a){a=mxUtils.convertPoint(this.graph.container,a.getX(),a.getY());var b=this.graph.view.scale;return new mxPoint(this.roundLength((a.x-this.first.x)/b)*b,this.roundLength((a.y-this.first.y)/b)*b)};mxGraphHandler.prototype.updateHint=function(a){};mxGraphHandler.prototype.removeHint=function(){};
-mxGraphHandler.prototype.roundLength=function(a){return Math.round(2*a)/2};
-mxGraphHandler.prototype.mouseMove=function(a,b){var c=this.graph;if(!b.isConsumed()&&c.isMouseDown&&null!=this.cell&&null!=this.first&&null!=this.bounds)if(mxEvent.isMultiTouchEvent(b.getEvent()))this.reset();else{var d=this.getDelta(b),e=d.x,d=d.y,f=c.tolerance;if(null!=this.shape||Math.abs(e)>f||Math.abs(d)>f){null==this.highlight&&(this.highlight=new mxCellHighlight(this.graph,mxConstants.DROP_TARGET_COLOR,3));null==this.shape&&(this.shape=this.createPreviewShape(this.bounds));var g=c.isGridEnabledEvent(b.getEvent()),
-f=!0;if(null!=this.guide&&this.useGuidesForEvent(b))d=this.guide.move(this.bounds,new mxPoint(e,d),g),f=!1,e=d.x,d=d.y;else if(g)var k=c.getView().translate,l=c.getView().scale,g=this.bounds.x-(c.snap(this.bounds.x/l-k.x)+k.x)*l,k=this.bounds.y-(c.snap(this.bounds.y/l-k.y)+k.y)*l,d=this.snap(new mxPoint(e,d)),e=d.x-g,d=d.y-k;null!=this.guide&&f&&this.guide.hide();c.isConstrainedEvent(b.getEvent())&&(Math.abs(e)>Math.abs(d)?d=0:e=0);this.currentDx=e;this.currentDy=d;this.updatePreviewShape();f=null;
-d=b.getCell();g=c.isCloneEvent(b.getEvent())&&c.isCellsCloneable()&&this.isCloneEnabled();c.isDropEnabled()&&this.highlightEnabled&&(f=c.getDropTarget(this.cells,b.getEvent(),d,g));e=c.getView().getState(f);k=!1;null==e||c.model.getParent(this.cell)==f&&!g?(this.target=null,this.connectOnDrop&&null!=d&&1==this.cells.length&&c.getModel().isVertex(d)&&c.isCellConnectable(d)&&(e=c.getView().getState(d),null!=e&&(c=null==c.getEdgeValidationError(null,this.cell,d)?mxConstants.VALID_COLOR:mxConstants.INVALID_CONNECT_TARGET_COLOR,
-this.setHighlightColor(c),k=!0))):(this.target!=f&&(this.target=f,this.setHighlightColor(mxConstants.DROP_TARGET_COLOR)),k=!0);null!=e&&k?this.highlight.highlight(e):this.highlight.hide()}this.updateHint(b);this.consumeMouseEvent(mxEvent.MOUSE_MOVE,b);mxEvent.consume(b.getEvent())}else!this.isMoveEnabled()&&!this.isCloneEnabled()||!this.updateCursor||b.isConsumed()||null==b.getState()&&null==b.sourceState||c.isMouseDown||(e=c.getCursorForMouseEvent(b),null==e&&c.isEnabled()&&c.isCellMovable(b.getCell())&&
-(e=c.getModel().isEdge(b.getCell())?mxConstants.CURSOR_MOVABLE_EDGE:mxConstants.CURSOR_MOVABLE_VERTEX),null!=e&&null!=b.sourceState&&b.sourceState.setCursor(e))};mxGraphHandler.prototype.updatePreviewShape=function(){null!=this.shape&&(this.shape.bounds=new mxRectangle(Math.round(this.pBounds.x+this.currentDx-this.graph.panDx),Math.round(this.pBounds.y+this.currentDy-this.graph.panDy),this.pBounds.width,this.pBounds.height),this.shape.redraw())};
-mxGraphHandler.prototype.setHighlightColor=function(a){null!=this.highlight&&this.highlight.setHighlightColor(a)};
-mxGraphHandler.prototype.mouseUp=function(a,b){if(!b.isConsumed()){var c=this.graph;if(null!=this.cell&&null!=this.first&&null!=this.shape&&null!=this.currentDx&&null!=this.currentDy){var d=b.getCell();if(this.connectOnDrop&&null==this.target&&null!=d&&c.getModel().isVertex(d)&&c.isCellConnectable(d)&&c.isEdgeValid(null,this.cell,d))c.connectionHandler.connect(this.cell,d,b.getEvent());else{var d=c.isCloneEvent(b.getEvent())&&c.isCellsCloneable()&&this.isCloneEnabled(),e=c.getView().scale,f=this.roundLength(this.currentDx/
-e),e=this.roundLength(this.currentDy/e),g=this.target;c.isSplitEnabled()&&c.isSplitTarget(g,this.cells,b.getEvent())?c.splitEdge(g,this.cells,null,f,e):this.moveCells(this.cells,f,e,d,this.target,b.getEvent())}}else this.isSelectEnabled()&&this.delayedSelection&&null!=this.cell&&this.selectDelayed(b)}this.cellWasClicked&&this.consumeMouseEvent(mxEvent.MOUSE_UP,b);this.reset()};
-mxGraphHandler.prototype.selectDelayed=function(a){this.graph.isCellSelected(this.cell)&&this.graph.popupMenuHandler.isPopupTrigger(a)||this.graph.selectCellForEvent(this.cell,a.getEvent())};mxGraphHandler.prototype.reset=function(){this.destroyShapes();this.removeHint();this.delayedSelection=this.cellWasClicked=!1;this.target=this.cell=this.first=this.guides=this.currentDy=this.currentDx=null};
-mxGraphHandler.prototype.shouldRemoveCellsFromParent=function(a,b,c){if(this.graph.getModel().isVertex(a)&&(a=this.graph.getView().getState(a),null!=a)){c=mxUtils.convertPoint(this.graph.container,mxEvent.getClientX(c),mxEvent.getClientY(c));var d=mxUtils.toRadians(mxUtils.getValue(a.style,mxConstants.STYLE_ROTATION)||0);if(0!=d){b=Math.cos(-d);var d=Math.sin(-d),e=new mxPoint(a.getCenterX(),a.getCenterY());c=mxUtils.getRotatedPoint(c,b,d,e)}return!mxUtils.contains(a,c.x,c.y)}return!1};
-mxGraphHandler.prototype.moveCells=function(a,b,c,d,e,f){d&&(a=this.graph.getCloneableCells(a));null==e&&this.isRemoveCellsFromParent()&&this.shouldRemoveCellsFromParent(this.graph.getModel().getParent(this.cell),a,f)&&(e=this.graph.getDefaultParent());d=d&&!this.graph.isCellLocked(e||this.graph.getDefaultParent());a=this.graph.moveCells(a,b-this.graph.panDx/this.graph.view.scale,c-this.graph.panDy/this.graph.view.scale,d,e,f);this.isSelectEnabled()&&this.scrollOnMove&&this.graph.scrollCellToVisible(a[0]);
-d&&this.graph.setSelectionCells(a)};mxGraphHandler.prototype.destroyShapes=function(){null!=this.shape&&(this.shape.destroy(),this.shape=null);null!=this.guide&&(this.guide.destroy(),this.guide=null);null!=this.highlight&&(this.highlight.destroy(),this.highlight=null)};
-mxGraphHandler.prototype.destroy=function(){this.graph.removeMouseListener(this);this.graph.removeListener(this.panHandler);null!=this.escapeHandler&&(this.graph.removeListener(this.escapeHandler),this.escapeHandler=null);this.destroyShapes();this.removeHint()};
-function mxPanningHandler(a){null!=a&&(this.graph=a,this.graph.addMouseListener(this),this.forcePanningHandler=mxUtils.bind(this,function(a,c){var b=c.getProperty("eventName"),e=c.getProperty("event");b==mxEvent.MOUSE_DOWN&&this.isForcePanningEvent(e)&&(this.start(e),this.active=!0,this.fireEvent(new mxEventObject(mxEvent.PAN_START,"event",e)),e.consume())}),this.graph.addListener(mxEvent.FIRE_MOUSE_EVENT,this.forcePanningHandler),this.gestureHandler=mxUtils.bind(this,function(a,c){if(this.isPinchEnabled()){var b=
-c.getProperty("event");mxEvent.isConsumed(b)||"gesturestart"!=b.type?"gestureend"==b.type&&null!=this.initialScale&&(this.initialScale=null):(this.initialScale=this.graph.view.scale,this.active||null==this.mouseDownEvent||(this.start(this.mouseDownEvent),this.mouseDownEvent=null));if(null!=this.initialScale){var e=Math.round(this.initialScale*b.scale*100)/100;null!=this.minScale&&(e=Math.max(this.minScale,e));null!=this.maxScale&&(e=Math.min(this.maxScale,e));this.graph.view.scale!=e&&(this.graph.zoomTo(e),
-mxEvent.consume(b))}}}),this.graph.addListener(mxEvent.GESTURE,this.gestureHandler),this.mouseUpListener=mxUtils.bind(this,function(){this.active&&this.reset()}),mxEvent.addListener(document,"mouseup",this.mouseUpListener))}mxPanningHandler.prototype=new mxEventSource;mxPanningHandler.prototype.constructor=mxPanningHandler;mxPanningHandler.prototype.graph=null;mxPanningHandler.prototype.useLeftButtonForPanning=!1;mxPanningHandler.prototype.usePopupTrigger=!0;
-mxPanningHandler.prototype.ignoreCell=!1;mxPanningHandler.prototype.previewEnabled=!0;mxPanningHandler.prototype.useGrid=!1;mxPanningHandler.prototype.panningEnabled=!0;mxPanningHandler.prototype.pinchEnabled=!0;mxPanningHandler.prototype.maxScale=8;mxPanningHandler.prototype.minScale=.01;mxPanningHandler.prototype.dx=null;mxPanningHandler.prototype.dy=null;mxPanningHandler.prototype.startX=0;mxPanningHandler.prototype.startY=0;
-mxPanningHandler.prototype.isActive=function(){return this.active||null!=this.initialScale};mxPanningHandler.prototype.isPanningEnabled=function(){return this.panningEnabled};mxPanningHandler.prototype.setPanningEnabled=function(a){this.panningEnabled=a};mxPanningHandler.prototype.isPinchEnabled=function(){return this.pinchEnabled};mxPanningHandler.prototype.setPinchEnabled=function(a){this.pinchEnabled=a};
-mxPanningHandler.prototype.isPanningTrigger=function(a){var b=a.getEvent();return this.useLeftButtonForPanning&&null==a.getState()&&mxEvent.isLeftMouseButton(b)||mxEvent.isControlDown(b)&&mxEvent.isShiftDown(b)||this.usePopupTrigger&&mxEvent.isPopupTrigger(b)};mxPanningHandler.prototype.isForcePanningEvent=function(a){return this.ignoreCell||mxEvent.isMultiTouchEvent(a.getEvent())};
-mxPanningHandler.prototype.mouseDown=function(a,b){this.mouseDownEvent=b;!b.isConsumed()&&this.isPanningEnabled()&&!this.active&&this.isPanningTrigger(b)&&(this.start(b),this.consumePanningTrigger(b))};mxPanningHandler.prototype.start=function(a){this.dx0=-this.graph.container.scrollLeft;this.dy0=-this.graph.container.scrollTop;this.startX=a.getX();this.startY=a.getY();this.dy=this.dx=null;this.panningTrigger=!0};mxPanningHandler.prototype.consumePanningTrigger=function(a){a.consume()};
-mxPanningHandler.prototype.mouseMove=function(a,b){this.dx=b.getX()-this.startX;this.dy=b.getY()-this.startY;if(this.active)this.previewEnabled&&(this.useGrid&&(this.dx=this.graph.snap(this.dx),this.dy=this.graph.snap(this.dy)),this.graph.panGraph(this.dx+this.dx0,this.dy+this.dy0)),this.fireEvent(new mxEventObject(mxEvent.PAN,"event",b));else if(this.panningTrigger){var c=this.active;this.active=Math.abs(this.dx)>this.graph.tolerance||Math.abs(this.dy)>this.graph.tolerance;!c&&this.active&&this.fireEvent(new mxEventObject(mxEvent.PAN_START,
-"event",b))}(this.active||this.panningTrigger)&&b.consume()};mxPanningHandler.prototype.mouseUp=function(a,b){if(this.active){if(null!=this.dx&&null!=this.dy){if(!this.graph.useScrollbarsForPanning||!mxUtils.hasScrollbars(this.graph.container)){var c=this.graph.getView().scale,d=this.graph.getView().translate;this.graph.panGraph(0,0);this.panGraph(d.x+this.dx/c,d.y+this.dy/c)}b.consume()}this.fireEvent(new mxEventObject(mxEvent.PAN_END,"event",b))}this.reset()};
-mxPanningHandler.prototype.reset=function(){this.panningTrigger=!1;this.mouseDownEvent=null;this.active=!1;this.dy=this.dx=null};mxPanningHandler.prototype.panGraph=function(a,b){this.graph.getView().setTranslate(a,b)};mxPanningHandler.prototype.destroy=function(){this.graph.removeMouseListener(this);this.graph.removeListener(this.forcePanningHandler);this.graph.removeListener(this.gestureHandler);mxEvent.removeListener(document,"mouseup",this.mouseUpListener)};
-function mxPopupMenuHandler(a,b){null!=a&&(this.graph=a,this.factoryMethod=b,this.graph.addMouseListener(this),this.gestureHandler=mxUtils.bind(this,function(a,b){this.inTolerance=!1}),this.graph.addListener(mxEvent.GESTURE,this.gestureHandler),this.init())}mxPopupMenuHandler.prototype=new mxPopupMenu;mxPopupMenuHandler.prototype.constructor=mxPopupMenuHandler;mxPopupMenuHandler.prototype.graph=null;mxPopupMenuHandler.prototype.selectOnPopup=!0;
-mxPopupMenuHandler.prototype.clearSelectionOnBackground=!0;mxPopupMenuHandler.prototype.triggerX=null;mxPopupMenuHandler.prototype.triggerY=null;mxPopupMenuHandler.prototype.screenX=null;mxPopupMenuHandler.prototype.screenY=null;mxPopupMenuHandler.prototype.init=function(){mxPopupMenu.prototype.init.apply(this);mxEvent.addGestureListeners(this.div,mxUtils.bind(this,function(a){this.graph.tooltipHandler.hide()}))};mxPopupMenuHandler.prototype.isSelectOnPopup=function(a){return this.selectOnPopup};
-mxPopupMenuHandler.prototype.mouseDown=function(a,b){this.isEnabled()&&!mxEvent.isMultiTouchEvent(b.getEvent())&&(this.hideMenu(),this.triggerX=b.getGraphX(),this.triggerY=b.getGraphY(),this.screenX=mxEvent.getMainEvent(b.getEvent()).screenX,this.screenY=mxEvent.getMainEvent(b.getEvent()).screenY,this.popupTrigger=this.isPopupTrigger(b),this.inTolerance=!0)};
-mxPopupMenuHandler.prototype.mouseMove=function(a,b){this.inTolerance&&null!=this.screenX&&null!=this.screenY&&(Math.abs(mxEvent.getMainEvent(b.getEvent()).screenX-this.screenX)>this.graph.tolerance||Math.abs(mxEvent.getMainEvent(b.getEvent()).screenY-this.screenY)>this.graph.tolerance)&&(this.inTolerance=!1)};
-mxPopupMenuHandler.prototype.mouseUp=function(a,b){if(this.popupTrigger&&this.inTolerance&&null!=this.triggerX&&null!=this.triggerY){var c=this.getCellForPopupEvent(b);this.graph.isEnabled()&&this.isSelectOnPopup(b)&&null!=c&&!this.graph.isCellSelected(c)?this.graph.setSelectionCell(c):this.clearSelectionOnBackground&&null==c&&this.graph.clearSelection();this.graph.tooltipHandler.hide();var d=mxUtils.getScrollOrigin();this.popup(b.getX()+d.x+1,b.getY()+d.y+1,c,b.getEvent());b.consume()}this.inTolerance=
-this.popupTrigger=!1};mxPopupMenuHandler.prototype.getCellForPopupEvent=function(a){return a.getCell()};mxPopupMenuHandler.prototype.destroy=function(){this.graph.removeMouseListener(this);this.graph.removeListener(this.gestureHandler);mxPopupMenu.prototype.destroy.apply(this)};
-function mxCellMarker(a,b,c,d){mxEventSource.call(this);null!=a&&(this.graph=a,this.validColor=null!=b?b:mxConstants.DEFAULT_VALID_COLOR,this.invalidColor=null!=b?c:mxConstants.DEFAULT_INVALID_COLOR,this.hotspot=null!=d?d:mxConstants.DEFAULT_HOTSPOT,this.highlight=new mxCellHighlight(a))}mxUtils.extend(mxCellMarker,mxEventSource);mxCellMarker.prototype.graph=null;mxCellMarker.prototype.enabled=!0;mxCellMarker.prototype.hotspot=mxConstants.DEFAULT_HOTSPOT;mxCellMarker.prototype.hotspotEnabled=!1;
-mxCellMarker.prototype.validColor=null;mxCellMarker.prototype.invalidColor=null;mxCellMarker.prototype.currentColor=null;mxCellMarker.prototype.validState=null;mxCellMarker.prototype.markedState=null;mxCellMarker.prototype.setEnabled=function(a){this.enabled=a};mxCellMarker.prototype.isEnabled=function(){return this.enabled};mxCellMarker.prototype.setHotspot=function(a){this.hotspot=a};mxCellMarker.prototype.getHotspot=function(){return this.hotspot};
-mxCellMarker.prototype.setHotspotEnabled=function(a){this.hotspotEnabled=a};mxCellMarker.prototype.isHotspotEnabled=function(){return this.hotspotEnabled};mxCellMarker.prototype.hasValidState=function(){return null!=this.validState};mxCellMarker.prototype.getValidState=function(){return this.validState};mxCellMarker.prototype.getMarkedState=function(){return this.markedState};mxCellMarker.prototype.reset=function(){this.validState=null;null!=this.markedState&&(this.markedState=null,this.unmark())};
-mxCellMarker.prototype.process=function(a){var b=null;this.isEnabled()&&(b=this.getState(a),this.setCurrentState(b,a));return b};mxCellMarker.prototype.setCurrentState=function(a,b,c){var d=null!=a?this.isValidState(a):!1;c=null!=c?c:this.getMarkerColor(b.getEvent(),a,d);this.validState=d?a:null;if(a!=this.markedState||c!=this.currentColor)this.currentColor=c,null!=a&&null!=this.currentColor?(this.markedState=a,this.mark()):null!=this.markedState&&(this.markedState=null,this.unmark())};
-mxCellMarker.prototype.markCell=function(a,b){var c=this.graph.getView().getState(a);null!=c&&(this.currentColor=null!=b?b:this.validColor,this.markedState=c,this.mark())};mxCellMarker.prototype.mark=function(){this.highlight.setHighlightColor(this.currentColor);this.highlight.highlight(this.markedState);this.fireEvent(new mxEventObject(mxEvent.MARK,"state",this.markedState))};mxCellMarker.prototype.unmark=function(){this.mark()};mxCellMarker.prototype.isValidState=function(a){return!0};
-mxCellMarker.prototype.getMarkerColor=function(a,b,c){return c?this.validColor:this.invalidColor};mxCellMarker.prototype.getState=function(a){var b=this.graph.getView(),c=this.getCell(a),b=this.getStateToMark(b.getState(c));return null!=b&&this.intersects(b,a)?b:null};mxCellMarker.prototype.getCell=function(a){return a.getCell()};mxCellMarker.prototype.getStateToMark=function(a){return a};
-mxCellMarker.prototype.intersects=function(a,b){return this.hotspotEnabled?mxUtils.intersectsHotspot(a,b.getGraphX(),b.getGraphY(),this.hotspot,mxConstants.MIN_HOTSPOT_SIZE,mxConstants.MAX_HOTSPOT_SIZE):!0};mxCellMarker.prototype.destroy=function(){this.graph.getView().removeListener(this.resetHandler);this.graph.getModel().removeListener(this.resetHandler);this.highlight.destroy()};
-function mxSelectionCellsHandler(a){mxEventSource.call(this);this.graph=a;this.handlers=new mxDictionary;this.graph.addMouseListener(this);this.refreshHandler=mxUtils.bind(this,function(a,c){this.isEnabled()&&this.refresh()});this.graph.getSelectionModel().addListener(mxEvent.CHANGE,this.refreshHandler);this.graph.getModel().addListener(mxEvent.CHANGE,this.refreshHandler);this.graph.getView().addListener(mxEvent.SCALE,this.refreshHandler);this.graph.getView().addListener(mxEvent.TRANSLATE,this.refreshHandler);
-this.graph.getView().addListener(mxEvent.SCALE_AND_TRANSLATE,this.refreshHandler);this.graph.getView().addListener(mxEvent.DOWN,this.refreshHandler);this.graph.getView().addListener(mxEvent.UP,this.refreshHandler)}mxUtils.extend(mxSelectionCellsHandler,mxEventSource);mxSelectionCellsHandler.prototype.graph=null;mxSelectionCellsHandler.prototype.enabled=!0;mxSelectionCellsHandler.prototype.refreshHandler=null;mxSelectionCellsHandler.prototype.maxHandlers=100;
-mxSelectionCellsHandler.prototype.handlers=null;mxSelectionCellsHandler.prototype.isEnabled=function(){return this.enabled};mxSelectionCellsHandler.prototype.setEnabled=function(a){this.enabled=a};mxSelectionCellsHandler.prototype.getHandler=function(a){return this.handlers.get(a)};mxSelectionCellsHandler.prototype.reset=function(){this.handlers.visit(function(a,b){b.reset.apply(b)})};
-mxSelectionCellsHandler.prototype.refresh=function(){var a=this.handlers;this.handlers=new mxDictionary;for(var b=this.graph.getSelectionCells(),c=0;c<b.length;c++){var d=this.graph.view.getState(b[c]);if(null!=d){var e=a.remove(b[c]);null!=e&&(e.state!=d?(e.destroy(),e=null):this.isHandlerActive(e)||(null!=e.refresh&&e.refresh(),e.redraw()));null==e&&(e=this.graph.createHandler(d),this.fireEvent(new mxEventObject(mxEvent.ADD,"state",d)));null!=e&&this.handlers.put(b[c],e)}}a.visit(mxUtils.bind(this,
-function(a,b){this.fireEvent(new mxEventObject(mxEvent.REMOVE,"state",b.state));b.destroy()}))};mxSelectionCellsHandler.prototype.isHandlerActive=function(a){return null!=a.index};mxSelectionCellsHandler.prototype.updateHandler=function(a){var b=this.handlers.remove(a.cell);null!=b&&(b.destroy(),b=this.graph.createHandler(a),null!=b&&this.handlers.put(a.cell,b))};
-mxSelectionCellsHandler.prototype.mouseDown=function(a,b){if(this.graph.isEnabled()&&this.isEnabled()){var c=[a,b];this.handlers.visit(function(a,b){b.mouseDown.apply(b,c)})}};mxSelectionCellsHandler.prototype.mouseMove=function(a,b){if(this.graph.isEnabled()&&this.isEnabled()){var c=[a,b];this.handlers.visit(function(a,b){b.mouseMove.apply(b,c)})}};
-mxSelectionCellsHandler.prototype.mouseUp=function(a,b){if(this.graph.isEnabled()&&this.isEnabled()){var c=[a,b];this.handlers.visit(function(a,b){b.mouseUp.apply(b,c)})}};mxSelectionCellsHandler.prototype.destroy=function(){this.graph.removeMouseListener(this);null!=this.refreshHandler&&(this.graph.getSelectionModel().removeListener(this.refreshHandler),this.graph.getModel().removeListener(this.refreshHandler),this.graph.getView().removeListener(this.refreshHandler),this.refreshHandler=null)};
-function mxConnectionHandler(a,b){mxEventSource.call(this);null!=a&&(this.graph=a,this.factoryMethod=b,this.init(),this.escapeHandler=mxUtils.bind(this,function(a,b){this.reset()}),this.graph.addListener(mxEvent.ESCAPE,this.escapeHandler))}mxUtils.extend(mxConnectionHandler,mxEventSource);mxConnectionHandler.prototype.graph=null;mxConnectionHandler.prototype.factoryMethod=!0;mxConnectionHandler.prototype.moveIconFront=!1;mxConnectionHandler.prototype.moveIconBack=!1;
-mxConnectionHandler.prototype.connectImage=null;mxConnectionHandler.prototype.targetConnectImage=!1;mxConnectionHandler.prototype.enabled=!0;mxConnectionHandler.prototype.select=!0;mxConnectionHandler.prototype.createTarget=!1;mxConnectionHandler.prototype.marker=null;mxConnectionHandler.prototype.constraintHandler=null;mxConnectionHandler.prototype.error=null;mxConnectionHandler.prototype.waypointsEnabled=!1;mxConnectionHandler.prototype.ignoreMouseDown=!1;mxConnectionHandler.prototype.first=null;
-mxConnectionHandler.prototype.connectIconOffset=new mxPoint(0,mxConstants.TOOLTIP_VERTICAL_OFFSET);mxConnectionHandler.prototype.edgeState=null;mxConnectionHandler.prototype.changeHandler=null;mxConnectionHandler.prototype.drillHandler=null;mxConnectionHandler.prototype.mouseDownCounter=0;mxConnectionHandler.prototype.movePreviewAway=mxClient.IS_VML;mxConnectionHandler.prototype.outlineConnect=!1;mxConnectionHandler.prototype.livePreview=!1;mxConnectionHandler.prototype.cursor=null;
-mxConnectionHandler.prototype.insertBeforeSource=!1;mxConnectionHandler.prototype.isEnabled=function(){return this.enabled};mxConnectionHandler.prototype.setEnabled=function(a){this.enabled=a};mxConnectionHandler.prototype.isInsertBefore=function(a,b,c,d,e){return this.insertBeforeSource&&b!=c};mxConnectionHandler.prototype.isCreateTarget=function(a){return this.createTarget};mxConnectionHandler.prototype.setCreateTarget=function(a){this.createTarget=a};
-mxConnectionHandler.prototype.createShape=function(){var a=this.livePreview&&null!=this.edgeState?this.graph.cellRenderer.createShape(this.edgeState):new mxPolyline([],mxConstants.INVALID_COLOR);a.dialect=this.graph.dialect!=mxConstants.DIALECT_SVG?mxConstants.DIALECT_VML:mxConstants.DIALECT_SVG;a.scale=this.graph.view.scale;a.pointerEvents=!1;a.isDashed=!0;a.init(this.graph.getView().getOverlayPane());mxEvent.redirectMouseEvents(a.node,this.graph,null);return a};
-mxConnectionHandler.prototype.init=function(){this.graph.addMouseListener(this);this.marker=this.createMarker();this.constraintHandler=new mxConstraintHandler(this.graph);this.changeHandler=mxUtils.bind(this,function(a){null!=this.iconState&&(this.iconState=this.graph.getView().getState(this.iconState.cell));null!=this.iconState?(this.redrawIcons(this.icons,this.iconState),this.constraintHandler.reset()):null!=this.previous&&null==this.graph.view.getState(this.previous.cell)&&this.reset()});this.graph.getModel().addListener(mxEvent.CHANGE,
-this.changeHandler);this.graph.getView().addListener(mxEvent.SCALE,this.changeHandler);this.graph.getView().addListener(mxEvent.TRANSLATE,this.changeHandler);this.graph.getView().addListener(mxEvent.SCALE_AND_TRANSLATE,this.changeHandler);this.drillHandler=mxUtils.bind(this,function(a){this.reset()});this.graph.addListener(mxEvent.START_EDITING,this.drillHandler);this.graph.getView().addListener(mxEvent.DOWN,this.drillHandler);this.graph.getView().addListener(mxEvent.UP,this.drillHandler)};
-mxConnectionHandler.prototype.isConnectableCell=function(a){return!0};
-mxConnectionHandler.prototype.createMarker=function(){var a=new mxCellMarker(this.graph);a.hotspotEnabled=!0;a.getCell=mxUtils.bind(this,function(b){var c=mxCellMarker.prototype.getCell.apply(a,arguments);this.error=null;null==c&&null!=this.currentPoint&&(c=this.graph.getCellAt(this.currentPoint.x,this.currentPoint.y));if(null!=c&&!this.graph.isCellConnectable(c)){var d=this.graph.getModel().getParent(c);this.graph.getModel().isVertex(d)&&this.graph.isCellConnectable(d)&&(c=d)}if(this.graph.isSwimlane(c)&&
-null!=this.currentPoint&&this.graph.hitsSwimlaneContent(c,this.currentPoint.x,this.currentPoint.y)||!this.isConnectableCell(c))c=null;null!=c?this.isConnecting()?null!=this.previous&&(this.error=this.validateConnection(this.previous.cell,c),null!=this.error&&0==this.error.length&&(c=null,this.isCreateTarget(b.getEvent())&&(this.error=null))):this.isValidSource(c,b)||(c=null):!this.isConnecting()||this.isCreateTarget(b.getEvent())||this.graph.allowDanglingEdges||(this.error="");return c});a.isValidState=
-mxUtils.bind(this,function(b){return this.isConnecting()?null==this.error:mxCellMarker.prototype.isValidState.apply(a,arguments)});a.getMarkerColor=mxUtils.bind(this,function(b,c,d){return null==this.connectImage||this.isConnecting()?mxCellMarker.prototype.getMarkerColor.apply(a,arguments):null});a.intersects=mxUtils.bind(this,function(b,c){return null!=this.connectImage||this.isConnecting()?!0:mxCellMarker.prototype.intersects.apply(a,arguments)});return a};
-mxConnectionHandler.prototype.start=function(a,b,c,d){this.previous=a;this.first=new mxPoint(b,c);this.edgeState=null!=d?d:this.createEdgeState(null);this.marker.currentColor=this.marker.validColor;this.marker.markedState=a;this.marker.mark();this.fireEvent(new mxEventObject(mxEvent.START,"state",this.previous))};mxConnectionHandler.prototype.isConnecting=function(){return null!=this.first&&null!=this.shape};mxConnectionHandler.prototype.isValidSource=function(a,b){return this.graph.isValidSource(a)};
-mxConnectionHandler.prototype.isValidTarget=function(a){return!0};mxConnectionHandler.prototype.validateConnection=function(a,b){return this.isValidTarget(b)?this.graph.getEdgeValidationError(null,a,b):""};mxConnectionHandler.prototype.getConnectImage=function(a){return this.connectImage};mxConnectionHandler.prototype.isMoveIconToFrontForState=function(a){return null!=a.text&&a.text.node.parentNode==this.graph.container?!0:this.moveIconFront};
-mxConnectionHandler.prototype.createIcons=function(a){var b=this.getConnectImage(a);if(null!=b&&null!=a){this.iconState=a;var c=[],d=new mxRectangle(0,0,b.width,b.height),e=new mxImageShape(d,b.src,null,null,0);e.preserveImageAspect=!1;this.isMoveIconToFrontForState(a)?(e.dialect=mxConstants.DIALECT_STRICTHTML,e.init(this.graph.container)):(e.dialect=this.graph.dialect==mxConstants.DIALECT_SVG?mxConstants.DIALECT_SVG:mxConstants.DIALECT_VML,e.init(this.graph.getView().getOverlayPane()),this.moveIconBack&&
-null!=e.node.previousSibling&&e.node.parentNode.insertBefore(e.node,e.node.parentNode.firstChild));e.node.style.cursor=mxConstants.CURSOR_CONNECT;var f=mxUtils.bind(this,function(){return null!=this.currentState?this.currentState:a}),b=mxUtils.bind(this,function(a){mxEvent.isConsumed(a)||(this.icon=e,this.graph.fireMouseEvent(mxEvent.MOUSE_DOWN,new mxMouseEvent(a,f())))});mxEvent.redirectMouseEvents(e.node,this.graph,f,b);c.push(e);this.redrawIcons(c,this.iconState);return c}return null};
-mxConnectionHandler.prototype.redrawIcons=function(a,b){if(null!=a&&null!=a[0]&&null!=b){var c=this.getIconPosition(a[0],b);a[0].bounds.x=c.x;a[0].bounds.y=c.y;a[0].redraw()}};
-mxConnectionHandler.prototype.getIconPosition=function(a,b){var c=this.graph.getView().scale,d=b.getCenterX(),e=b.getCenterY();if(this.graph.isSwimlane(b.cell)){var f=this.graph.getStartSize(b.cell),d=0!=f.width?b.x+f.width*c/2:d,e=0!=f.height?b.y+f.height*c/2:e,f=mxUtils.toRadians(mxUtils.getValue(b.style,mxConstants.STYLE_ROTATION)||0);if(0!=f)var c=Math.cos(f),f=Math.sin(f),g=new mxPoint(b.getCenterX(),b.getCenterY()),e=mxUtils.getRotatedPoint(new mxPoint(d,e),c,f,g),d=e.x,e=e.y}return new mxPoint(d-
-a.bounds.width/2,e-a.bounds.height/2)};mxConnectionHandler.prototype.destroyIcons=function(){if(null!=this.icons){for(var a=0;a<this.icons.length;a++)this.icons[a].destroy();this.iconState=this.selectedIcon=this.icon=this.icons=null}};mxConnectionHandler.prototype.isStartEvent=function(a){return null!=this.constraintHandler.currentFocus&&null!=this.constraintHandler.currentConstraint||null!=this.previous&&null==this.error&&(null==this.icons||null!=this.icons&&null!=this.icon)};
-mxConnectionHandler.prototype.mouseDown=function(a,b){this.mouseDownCounter++;if(this.isEnabled()&&this.graph.isEnabled()&&!b.isConsumed()&&!this.isConnecting()&&this.isStartEvent(b)){null!=this.constraintHandler.currentConstraint&&null!=this.constraintHandler.currentFocus&&null!=this.constraintHandler.currentPoint?(this.sourceConstraint=this.constraintHandler.currentConstraint,this.previous=this.constraintHandler.currentFocus,this.first=this.constraintHandler.currentPoint.clone()):this.first=new mxPoint(b.getGraphX(),
-b.getGraphY());this.edgeState=this.createEdgeState(b);this.mouseDownCounter=1;this.waypointsEnabled&&null==this.shape&&(this.waypoints=null,this.shape=this.createShape(),null!=this.edgeState&&this.shape.apply(this.edgeState));if(null==this.previous&&null!=this.edgeState){var c=this.graph.getPointForEvent(b.getEvent());this.edgeState.cell.geometry.setTerminalPoint(c,!0)}this.fireEvent(new mxEventObject(mxEvent.START,"state",this.previous));b.consume()}this.selectedIcon=this.icon;this.icon=null};
-mxConnectionHandler.prototype.isImmediateConnectSource=function(a){return!this.graph.isCellMovable(a.cell)};mxConnectionHandler.prototype.createEdgeState=function(a){return null};
-mxConnectionHandler.prototype.isOutlineConnectEvent=function(a){var b=mxUtils.getOffset(this.graph.container),c=a.getEvent(),d=mxEvent.getClientX(c),c=mxEvent.getClientY(c),e=document.documentElement,f=this.currentPoint.x-this.graph.container.scrollLeft+b.x-((window.pageXOffset||e.scrollLeft)-(e.clientLeft||0)),b=this.currentPoint.y-this.graph.container.scrollTop+b.y-((window.pageYOffset||e.scrollTop)-(e.clientTop||0));return this.outlineConnect&&!mxEvent.isShiftDown(a.getEvent())&&(a.isSource(this.marker.highlight.shape)||
-mxEvent.isAltDown(a.getEvent())&&null!=a.getState()||this.marker.highlight.isHighlightAt(d,c)||(f!=d||b!=c)&&null==a.getState()&&this.marker.highlight.isHighlightAt(f,b))};
-mxConnectionHandler.prototype.updateCurrentState=function(a,b){this.constraintHandler.update(a,null==this.first,!1,null==this.first||a.isSource(this.marker.highlight.shape)?null:b);if(null!=this.constraintHandler.currentFocus&&null!=this.constraintHandler.currentConstraint)null!=this.marker.highlight&&null!=this.marker.highlight.state&&this.marker.highlight.state.cell==this.constraintHandler.currentFocus.cell?"transparent"!=this.marker.highlight.shape.stroke&&(this.marker.highlight.shape.stroke="transparent",
-this.marker.highlight.repaint()):this.marker.markCell(this.constraintHandler.currentFocus.cell,"transparent"),null!=this.previous&&(this.error=this.validateConnection(this.previous.cell,this.constraintHandler.currentFocus.cell),null==this.error?this.currentState=this.constraintHandler.currentFocus:this.constraintHandler.reset());else{this.graph.isIgnoreTerminalEvent(a.getEvent())?(this.marker.reset(),this.currentState=null):(this.marker.process(a),this.currentState=this.marker.getValidState(),null==
-this.currentState||this.isCellEnabled(this.currentState.cell)||(this.currentState=null));var c=this.isOutlineConnectEvent(a);null!=this.currentState&&c&&(a.isSource(this.marker.highlight.shape)&&(b=new mxPoint(a.getGraphX(),a.getGraphY())),c=this.graph.getOutlineConstraint(b,this.currentState,a),this.constraintHandler.setFocus(a,this.currentState,!1),this.constraintHandler.currentConstraint=c,this.constraintHandler.currentPoint=b);this.outlineConnect&&null!=this.marker.highlight&&null!=this.marker.highlight.shape&&
-(c=this.graph.view.scale,null!=this.constraintHandler.currentConstraint&&null!=this.constraintHandler.currentFocus?(this.marker.highlight.shape.stroke=mxConstants.OUTLINE_HIGHLIGHT_COLOR,this.marker.highlight.shape.strokewidth=mxConstants.OUTLINE_HIGHLIGHT_STROKEWIDTH/c/c,this.marker.highlight.repaint()):this.marker.hasValidState()&&(this.marker.getValidState()!=a.getState()?(this.marker.highlight.shape.stroke="transparent",this.currentState=null):this.marker.highlight.shape.stroke=mxConstants.DEFAULT_VALID_COLOR,
-this.marker.highlight.shape.strokewidth=mxConstants.HIGHLIGHT_STROKEWIDTH/c/c,this.marker.highlight.repaint()))}};mxConnectionHandler.prototype.isCellEnabled=function(a){return!0};mxConnectionHandler.prototype.convertWaypoint=function(a){var b=this.graph.getView().getScale(),c=this.graph.getView().getTranslate();a.x=a.x/b-c.x;a.y=a.y/b-c.y};
-mxConnectionHandler.prototype.snapToPreview=function(a,b){if(!mxEvent.isAltDown(a.getEvent())&&null!=this.previous){var c=this.graph.gridSize*this.graph.view.scale/2,d=null!=this.sourceConstraint?this.first:new mxPoint(this.previous.getCenterX(),this.previous.getCenterY());Math.abs(d.x-a.getGraphX())<c&&(b.x=d.x);Math.abs(d.y-a.getGraphY())<c&&(b.y=d.y)}};
-mxConnectionHandler.prototype.mouseMove=function(a,b){if(b.isConsumed()||!this.ignoreMouseDown&&null==this.first&&this.graph.isMouseDown)this.constraintHandler.reset();else{this.isEnabled()||null==this.currentState||(this.destroyIcons(),this.currentState=null);var c=this.graph.getView(),d=c.scale,e=c.translate,c=new mxPoint(b.getGraphX(),b.getGraphY());this.error=null;this.graph.isGridEnabledEvent(b.getEvent())&&(c=new mxPoint((this.graph.snap(c.x/d-e.x)+e.x)*d,(this.graph.snap(c.y/d-e.y)+e.y)*d));
-this.snapToPreview(b,c);this.currentPoint=c;(null!=this.first||this.isEnabled()&&this.graph.isEnabled())&&this.updateCurrentState(b,c);if(null!=this.first){var f=null,d=c;null!=this.constraintHandler.currentConstraint&&null!=this.constraintHandler.currentFocus&&null!=this.constraintHandler.currentPoint?(f=this.constraintHandler.currentConstraint,d=this.constraintHandler.currentPoint.clone()):null!=this.previous&&!this.graph.isIgnoreTerminalEvent(b.getEvent())&&mxEvent.isShiftDown(b.getEvent())&&(Math.abs(this.previous.getCenterX()-
-c.x)<Math.abs(this.previous.getCenterY()-c.y)?c.x=this.previous.getCenterX():c.y=this.previous.getCenterY());e=this.first;if(null!=this.selectedIcon){var g=this.selectedIcon.bounds.width,k=this.selectedIcon.bounds.height;null!=this.currentState&&this.targetConnectImage?(g=this.getIconPosition(this.selectedIcon,this.currentState),this.selectedIcon.bounds.x=g.x,this.selectedIcon.bounds.y=g.y):(g=new mxRectangle(b.getGraphX()+this.connectIconOffset.x,b.getGraphY()+this.connectIconOffset.y,g,k),this.selectedIcon.bounds=
-g);this.selectedIcon.redraw()}null!=this.edgeState?(this.updateEdgeState(d,f),d=this.edgeState.absolutePoints[this.edgeState.absolutePoints.length-1],e=this.edgeState.absolutePoints[0]):(null!=this.currentState&&null==this.constraintHandler.currentConstraint&&(g=this.getTargetPerimeterPoint(this.currentState,b),null!=g&&(d=g)),null==this.sourceConstraint&&null!=this.previous&&(g=this.getSourcePerimeterPoint(this.previous,null!=this.waypoints&&0<this.waypoints.length?this.waypoints[0]:d,b),null!=g&&
-(e=g)));if(null==this.currentState&&this.movePreviewAway){g=e;null!=this.edgeState&&2<=this.edgeState.absolutePoints.length&&(f=this.edgeState.absolutePoints[this.edgeState.absolutePoints.length-2],null!=f&&(g=f));f=d.x-g.x;g=d.y-g.y;k=Math.sqrt(f*f+g*g);if(0==k)return;this.originalPoint=d.clone();d.x-=4*f/k;d.y-=4*g/k}else this.originalPoint=null;null==this.shape&&(f=Math.abs(c.x-this.first.x),g=Math.abs(c.y-this.first.y),f>this.graph.tolerance||g>this.graph.tolerance)&&(this.shape=this.createShape(),
-null!=this.edgeState&&this.shape.apply(this.edgeState),this.updateCurrentState(b,c));null!=this.shape&&(null!=this.edgeState?this.shape.points=this.edgeState.absolutePoints:(c=[e],null!=this.waypoints&&(c=c.concat(this.waypoints)),c.push(d),this.shape.points=c),this.drawPreview());null!=this.cursor&&(this.graph.container.style.cursor=this.cursor);mxEvent.consume(b.getEvent());b.consume()}else this.isEnabled()&&this.graph.isEnabled()?this.previous!=this.currentState&&null==this.edgeState?(this.destroyIcons(),
-null!=this.currentState&&null==this.error&&null==this.constraintHandler.currentConstraint&&(this.icons=this.createIcons(this.currentState),null==this.icons&&(this.currentState.setCursor(mxConstants.CURSOR_CONNECT),b.consume())),this.previous=this.currentState):this.previous!=this.currentState||null==this.currentState||null!=this.icons||this.graph.isMouseDown||b.consume():this.constraintHandler.reset();if(!this.graph.isMouseDown&&null!=this.currentState&&null!=this.icons){c=!1;d=b.getSource();for(e=
-0;e<this.icons.length&&!c;e++)c=d==this.icons[e].node||d.parentNode==this.icons[e].node;c||this.updateIcons(this.currentState,this.icons,b)}}};
-mxConnectionHandler.prototype.updateEdgeState=function(a,b){null!=this.sourceConstraint&&null!=this.sourceConstraint.point&&(this.edgeState.style[mxConstants.STYLE_EXIT_X]=this.sourceConstraint.point.x,this.edgeState.style[mxConstants.STYLE_EXIT_Y]=this.sourceConstraint.point.y);null!=b&&null!=b.point?(this.edgeState.style[mxConstants.STYLE_ENTRY_X]=b.point.x,this.edgeState.style[mxConstants.STYLE_ENTRY_Y]=b.point.y):(delete this.edgeState.style[mxConstants.STYLE_ENTRY_X],delete this.edgeState.style[mxConstants.STYLE_ENTRY_Y]);
-this.edgeState.absolutePoints=[null,null!=this.currentState?null:a];this.graph.view.updateFixedTerminalPoint(this.edgeState,this.previous,!0,this.sourceConstraint);null!=this.currentState&&(null==b&&(b=this.graph.getConnectionConstraint(this.edgeState,this.previous,!1)),this.edgeState.setAbsoluteTerminalPoint(null,!1),this.graph.view.updateFixedTerminalPoint(this.edgeState,this.currentState,!1,b));var c=null;if(null!=this.waypoints)for(var c=[],d=0;d<this.waypoints.length;d++){var e=this.waypoints[d].clone();
-this.convertWaypoint(e);c[d]=e}this.graph.view.updatePoints(this.edgeState,c,this.previous,this.currentState);this.graph.view.updateFloatingTerminalPoints(this.edgeState,this.previous,this.currentState)};
-mxConnectionHandler.prototype.getTargetPerimeterPoint=function(a,b){var c=null,d=a.view,e=d.getPerimeterFunction(a);if(null!=e){var f=null!=this.waypoints&&0<this.waypoints.length?this.waypoints[this.waypoints.length-1]:new mxPoint(this.previous.getCenterX(),this.previous.getCenterY()),d=e(d.getPerimeterBounds(a),this.edgeState,f,!1);null!=d&&(c=d)}else c=new mxPoint(a.getCenterX(),a.getCenterY());return c};
-mxConnectionHandler.prototype.getSourcePerimeterPoint=function(a,b,c){c=null;var d=a.view,e=d.getPerimeterFunction(a),f=new mxPoint(a.getCenterX(),a.getCenterY());if(null!=e){var g=mxUtils.getValue(a.style,mxConstants.STYLE_ROTATION,0),k=Math.PI/180*-g;0!=g&&(b=mxUtils.getRotatedPoint(new mxPoint(b.x,b.y),Math.cos(k),Math.sin(k),f));a=e(d.getPerimeterBounds(a),a,b,!1);null!=a&&(0!=g&&(a=mxUtils.getRotatedPoint(new mxPoint(a.x,a.y),Math.cos(-k),Math.sin(-k),f)),c=a)}else c=f;return c};
-mxConnectionHandler.prototype.updateIcons=function(a,b,c){};mxConnectionHandler.prototype.isStopEvent=function(a){return null!=a.getState()};
-mxConnectionHandler.prototype.addWaypointForEvent=function(a){var b=mxUtils.convertPoint(this.graph.container,a.getX(),a.getY()),c=Math.abs(b.x-this.first.x),b=Math.abs(b.y-this.first.y);if(null!=this.waypoints||1<this.mouseDownCounter&&(c>this.graph.tolerance||b>this.graph.tolerance))null==this.waypoints&&(this.waypoints=[]),c=this.graph.view.scale,b=new mxPoint(this.graph.snap(a.getGraphX()/c)*c,this.graph.snap(a.getGraphY()/c)*c),this.waypoints.push(b)};
-mxConnectionHandler.prototype.mouseUp=function(a,b){if(!b.isConsumed()&&this.isConnecting()){if(this.waypointsEnabled&&!this.isStopEvent(b)){this.addWaypointForEvent(b);b.consume();return}if(null==this.error){var c=null!=this.previous?this.previous.cell:null,d=null;null!=this.constraintHandler.currentConstraint&&null!=this.constraintHandler.currentFocus&&(d=this.constraintHandler.currentFocus.cell);null==d&&null!=this.currentState&&(d=this.currentState.cell);this.connect(c,d,b.getEvent(),b.getCell())}else null!=
-this.previous&&null!=this.marker.validState&&this.previous.cell==this.marker.validState.cell&&this.graph.selectCellForEvent(this.marker.source,evt),0<this.error.length&&this.graph.validationAlert(this.error);this.destroyIcons();b.consume()}null!=this.first&&this.reset()};
-mxConnectionHandler.prototype.reset=function(){null!=this.shape&&(this.shape.destroy(),this.shape=null);null!=this.cursor&&null!=this.graph.container&&(this.graph.container.style.cursor="");this.destroyIcons();this.marker.reset();this.constraintHandler.reset();this.sourceConstraint=this.error=this.previous=this.edgeState=this.currentPoint=this.originalPoint=null;this.mouseDownCounter=0;this.first=null;this.fireEvent(new mxEventObject(mxEvent.RESET))};
-mxConnectionHandler.prototype.drawPreview=function(){this.updatePreview(null==this.error);this.shape.redraw()};mxConnectionHandler.prototype.updatePreview=function(a){this.shape.strokewidth=this.getEdgeWidth(a);this.shape.stroke=this.getEdgeColor(a)};mxConnectionHandler.prototype.getEdgeColor=function(a){return a?mxConstants.VALID_COLOR:mxConstants.INVALID_COLOR};mxConnectionHandler.prototype.getEdgeWidth=function(a){return a?3:1};
-mxConnectionHandler.prototype.connect=function(a,b,c,d){if(null!=b||this.isCreateTarget(c)||this.graph.allowDanglingEdges){var e=this.graph.getModel(),f=!1,g=null;e.beginUpdate();try{if(null!=a&&null==b&&!this.graph.isIgnoreTerminalEvent(c)&&this.isCreateTarget(c)&&(b=this.createTargetVertex(c,a),null!=b)){d=this.graph.getDropTarget([b],c,d);f=!0;if(null!=d&&this.graph.getModel().isEdge(d))d=this.graph.getDefaultParent();else{var k=this.graph.getView().getState(d);if(null!=k){var l=e.getGeometry(b);
-l.x-=k.origin.x;l.y-=k.origin.y}}this.graph.addCell(b,d)}var m=this.graph.getDefaultParent();null!=a&&null!=b&&e.getParent(a)==e.getParent(b)&&e.getParent(e.getParent(a))!=e.getRoot()&&(m=e.getParent(a),null!=a.geometry&&a.geometry.relative&&null!=b.geometry&&b.geometry.relative&&(m=e.getParent(m)));var n=k=null;null!=this.edgeState&&(k=this.edgeState.cell.value,n=this.edgeState.cell.style);g=this.insertEdge(m,null,k,a,b,n);if(null!=g){this.graph.setConnectionConstraint(g,a,!0,this.sourceConstraint);
-this.graph.setConnectionConstraint(g,b,!1,this.constraintHandler.currentConstraint);null!=this.edgeState&&e.setGeometry(g,this.edgeState.cell.geometry);e.getParent(a);if(this.isInsertBefore(g,a,b,c,d)){m=null;for(l=a;null!=l.parent&&null!=l.geometry&&l.geometry.relative&&l.parent!=g.parent;)l=this.graph.model.getParent(l);null!=l&&null!=l.parent&&l.parent==g.parent&&(m=l.parent.getIndex(l),l.parent.insert(g,m))}var p=e.getGeometry(g);null==p&&(p=new mxGeometry,p.relative=!0,e.setGeometry(g,p));if(null!=
-this.waypoints&&0<this.waypoints.length){var q=this.graph.view.scale,r=this.graph.view.translate;p.points=[];for(a=0;a<this.waypoints.length;a++){var t=this.waypoints[a];p.points.push(new mxPoint(t.x/q-r.x,t.y/q-r.y))}}if(null==b){var u=this.graph.view.translate,q=this.graph.view.scale,t=null!=this.originalPoint?new mxPoint(this.originalPoint.x/q-u.x,this.originalPoint.y/q-u.y):new mxPoint(this.currentPoint.x/q-u.x,this.currentPoint.y/q-u.y);t.x-=this.graph.panDx/this.graph.view.scale;t.y-=this.graph.panDy/
-this.graph.view.scale;p.setTerminalPoint(t,!1)}this.fireEvent(new mxEventObject(mxEvent.CONNECT,"cell",g,"terminal",b,"event",c,"target",d,"terminalInserted",f))}}catch(x){mxLog.show(),mxLog.debug(x.message)}finally{e.endUpdate()}this.select&&this.selectCells(g,f?b:null)}};mxConnectionHandler.prototype.selectCells=function(a,b){this.graph.setSelectionCell(a)};
-mxConnectionHandler.prototype.insertEdge=function(a,b,c,d,e,f){if(null==this.factoryMethod)return this.graph.insertEdge(a,b,c,d,e,f);b=this.createEdge(c,d,e,f);return b=this.graph.addEdge(b,a,d,e)};
-mxConnectionHandler.prototype.createTargetVertex=function(a,b){for(var c=this.graph.getCellGeometry(b);null!=c&&c.relative;)b=this.graph.getModel().getParent(b),c=this.graph.getCellGeometry(b);var d=this.graph.cloneCells([b])[0],c=this.graph.getModel().getGeometry(d);if(null!=c){var e=this.graph.view.translate,f=this.graph.view.scale,g=new mxPoint(this.currentPoint.x/f-e.x,this.currentPoint.y/f-e.y);c.x=Math.round(g.x-c.width/2-this.graph.panDx/f);c.y=Math.round(g.y-c.height/2-this.graph.panDy/f);
-g=this.getAlignmentTolerance();if(0<g){var k=this.graph.view.getState(b);if(null!=k){var l=k.x/f-e.x,e=k.y/f-e.y;Math.abs(l-c.x)<=g&&(c.x=Math.round(l));Math.abs(e-c.y)<=g&&(c.y=Math.round(e))}}}return d};mxConnectionHandler.prototype.getAlignmentTolerance=function(a){return this.graph.isGridEnabled()?this.graph.gridSize/2:this.graph.tolerance};
-mxConnectionHandler.prototype.createEdge=function(a,b,c,d){var e=null;null!=this.factoryMethod&&(e=this.factoryMethod(b,c,d));null==e&&(e=new mxCell(a||""),e.setEdge(!0),e.setStyle(d),a=new mxGeometry,a.relative=!0,e.setGeometry(a));return e};
-mxConnectionHandler.prototype.destroy=function(){this.graph.removeMouseListener(this);null!=this.shape&&(this.shape.destroy(),this.shape=null);null!=this.marker&&(this.marker.destroy(),this.marker=null);null!=this.constraintHandler&&(this.constraintHandler.destroy(),this.constraintHandler=null);null!=this.changeHandler&&(this.graph.getModel().removeListener(this.changeHandler),this.graph.getView().removeListener(this.changeHandler),this.changeHandler=null);null!=this.drillHandler&&(this.graph.removeListener(this.drillHandler),
-this.graph.getView().removeListener(this.drillHandler),this.drillHandler=null);null!=this.escapeHandler&&(this.graph.removeListener(this.escapeHandler),this.escapeHandler=null)};
-function mxConstraintHandler(a){this.graph=a;this.resetHandler=mxUtils.bind(this,function(a,c){null!=this.currentFocus&&null==this.graph.view.getState(this.currentFocus.cell)?this.reset():this.redraw()});this.graph.model.addListener(mxEvent.CHANGE,this.resetHandler);this.graph.view.addListener(mxEvent.SCALE_AND_TRANSLATE,this.resetHandler);this.graph.view.addListener(mxEvent.TRANSLATE,this.resetHandler);this.graph.view.addListener(mxEvent.SCALE,this.resetHandler);this.graph.addListener(mxEvent.ROOT,
-this.resetHandler)}mxConstraintHandler.prototype.pointImage=new mxImage(mxClient.imageBasePath+"/point.gif",5,5);mxConstraintHandler.prototype.graph=null;mxConstraintHandler.prototype.enabled=!0;mxConstraintHandler.prototype.highlightColor=mxConstants.DEFAULT_VALID_COLOR;mxConstraintHandler.prototype.isEnabled=function(){return this.enabled};mxConstraintHandler.prototype.setEnabled=function(a){this.enabled=a};
-mxConstraintHandler.prototype.reset=function(){if(null!=this.focusIcons){for(var a=0;a<this.focusIcons.length;a++)this.focusIcons[a].destroy();this.focusIcons=null}null!=this.focusHighlight&&(this.focusHighlight.destroy(),this.focusHighlight=null);this.focusPoints=this.currentFocus=this.currentPoint=this.currentFocusArea=this.currentConstraint=null};mxConstraintHandler.prototype.getTolerance=function(a){return this.graph.getTolerance()};
-mxConstraintHandler.prototype.getImageForConstraint=function(a,b,c){return this.pointImage};mxConstraintHandler.prototype.isEventIgnored=function(a,b){return!1};mxConstraintHandler.prototype.isStateIgnored=function(a,b){return!1};mxConstraintHandler.prototype.destroyIcons=function(){if(null!=this.focusIcons){for(var a=0;a<this.focusIcons.length;a++)this.focusIcons[a].destroy();this.focusPoints=this.focusIcons=null}};
-mxConstraintHandler.prototype.destroyFocusHighlight=function(){null!=this.focusHighlight&&(this.focusHighlight.destroy(),this.focusHighlight=null)};mxConstraintHandler.prototype.isKeepFocusEvent=function(a){return mxEvent.isShiftDown(a.getEvent())};
-mxConstraintHandler.prototype.getCellForEvent=function(a,b){var c=a.getCell();null!=c||null==b||a.getGraphX()==b.x&&a.getGraphY()==b.y||(c=this.graph.getCellAt(b.x,b.y));if(null!=c&&!this.graph.isCellConnectable(c)){var d=this.graph.getModel().getParent(c);this.graph.getModel().isVertex(d)&&this.graph.isCellConnectable(d)&&(c=d)}return this.graph.isCellLocked(c)?null:c};
-mxConstraintHandler.prototype.update=function(a,b,c,d){if(this.isEnabled()&&!this.isEventIgnored(a)){null==this.mouseleaveHandler&&null!=this.graph.container&&(this.mouseleaveHandler=mxUtils.bind(this,function(){this.reset()}),mxEvent.addListener(this.graph.container,"mouseleave",this.resetHandler));var e=this.getTolerance(a),f=null!=d?d.x:a.getGraphX(),g=null!=d?d.y:a.getGraphY(),f=new mxRectangle(f-e,g-e,2*e,2*e),e=new mxRectangle(a.getGraphX()-e,a.getGraphY()-e,2*e,2*e),k=this.graph.view.getState(this.getCellForEvent(a,
-d));this.isKeepFocusEvent(a)||null!=this.currentFocusArea&&null!=this.currentFocus&&null==k&&this.graph.getModel().isVertex(this.currentFocus.cell)&&mxUtils.intersects(this.currentFocusArea,e)||k==this.currentFocus||(this.currentFocus=this.currentFocusArea=null,this.setFocus(a,k,b));a=this.currentPoint=this.currentConstraint=null;if(null!=this.focusIcons&&null!=this.constraints&&(null==k||this.currentFocus==k))for(var g=e.getCenterX(),l=e.getCenterY(),m=0;m<this.focusIcons.length;m++){var n=g-this.focusIcons[m].bounds.getCenterX(),
-p=l-this.focusIcons[m].bounds.getCenterY(),n=n*n+p*p;if((this.intersects(this.focusIcons[m],e,b,c)||null!=d&&this.intersects(this.focusIcons[m],f,b,c))&&(null==a||n<a)){this.currentConstraint=this.constraints[m];this.currentPoint=this.focusPoints[m];a=n;n=this.focusIcons[m].bounds.clone();n.grow(mxConstants.HIGHLIGHT_SIZE+1);--n.width;--n.height;if(null==this.focusHighlight){p=this.createHighlightShape();p.dialect=this.graph.dialect==mxConstants.DIALECT_SVG?mxConstants.DIALECT_SVG:mxConstants.DIALECT_VML;
-p.pointerEvents=!1;p.init(this.graph.getView().getOverlayPane());this.focusHighlight=p;var q=mxUtils.bind(this,function(){return null!=this.currentFocus?this.currentFocus:k});mxEvent.redirectMouseEvents(p.node,this.graph,q)}this.focusHighlight.bounds=n;this.focusHighlight.redraw()}}null==this.currentConstraint&&this.destroyFocusHighlight()}else this.currentPoint=this.currentFocus=this.currentConstraint=null};
-mxConstraintHandler.prototype.redraw=function(){if(null!=this.currentFocus&&null!=this.constraints&&null!=this.focusIcons){var a=this.graph.view.getState(this.currentFocus.cell);this.currentFocus=a;this.currentFocusArea=new mxRectangle(a.x,a.y,a.width,a.height);for(var b=0;b<this.constraints.length;b++){var c=this.graph.getConnectionPoint(a,this.constraints[b]),d=this.getImageForConstraint(a,this.constraints[b],c),d=new mxRectangle(Math.round(c.x-d.width/2),Math.round(c.y-d.height/2),d.width,d.height);
-this.focusIcons[b].bounds=d;this.focusIcons[b].redraw();this.currentFocusArea.add(this.focusIcons[b].bounds);this.focusPoints[b]=c}}};
-mxConstraintHandler.prototype.setFocus=function(a,b,c){this.constraints=null!=b&&!this.isStateIgnored(b,c)&&this.graph.isCellConnectable(b.cell)?this.isEnabled()?this.graph.getAllConnectionConstraints(b,c)||[]:[]:null;if(null!=this.constraints){this.currentFocus=b;this.currentFocusArea=new mxRectangle(b.x,b.y,b.width,b.height);if(null!=this.focusIcons){for(c=0;c<this.focusIcons.length;c++)this.focusIcons[c].destroy();this.focusPoints=this.focusIcons=null}this.focusPoints=[];this.focusIcons=[];for(c=
-0;c<this.constraints.length;c++){var d=this.graph.getConnectionPoint(b,this.constraints[c]),e=this.getImageForConstraint(b,this.constraints[c],d),f=e.src,e=new mxRectangle(Math.round(d.x-e.width/2),Math.round(d.y-e.height/2),e.width,e.height),f=new mxImageShape(e,f);f.dialect=this.graph.dialect!=mxConstants.DIALECT_SVG?mxConstants.DIALECT_MIXEDHTML:mxConstants.DIALECT_SVG;f.preserveImageAspect=!1;f.init(this.graph.getView().getDecoratorPane());(mxClient.IS_QUIRKS||8==document.documentMode)&&mxEvent.addListener(f.node,
-"dragstart",function(a){mxEvent.consume(a);return!1});null!=f.node.previousSibling&&f.node.parentNode.insertBefore(f.node,f.node.parentNode.firstChild);e=mxUtils.bind(this,function(){return null!=this.currentFocus?this.currentFocus:b});f.redraw();mxEvent.redirectMouseEvents(f.node,this.graph,e);this.currentFocusArea.add(f.bounds);this.focusIcons.push(f);this.focusPoints.push(d)}this.currentFocusArea.grow(this.getTolerance(a))}else this.destroyIcons(),this.destroyFocusHighlight()};
-mxConstraintHandler.prototype.createHighlightShape=function(){var a=new mxRectangleShape(null,this.highlightColor,this.highlightColor,mxConstants.HIGHLIGHT_STROKEWIDTH);a.opacity=mxConstants.HIGHLIGHT_OPACITY;return a};mxConstraintHandler.prototype.intersects=function(a,b,c,d){return mxUtils.intersects(a.bounds,b)};
-mxConstraintHandler.prototype.destroy=function(){this.reset();null!=this.resetHandler&&(this.graph.model.removeListener(this.resetHandler),this.graph.view.removeListener(this.resetHandler),this.graph.removeListener(this.resetHandler),this.resetHandler=null);null!=this.mouseleaveHandler&&null!=this.graph.container&&(mxEvent.removeListener(this.graph.container,"mouseleave",this.mouseleaveHandler),this.mouseleaveHandler=null)};
-function mxRubberband(a){null!=a&&(this.graph=a,this.graph.addMouseListener(this),this.forceRubberbandHandler=mxUtils.bind(this,function(a,c){var b=c.getProperty("eventName"),e=c.getProperty("event");if(b==mxEvent.MOUSE_DOWN&&this.isForceRubberbandEvent(e)){var b=mxUtils.getOffset(this.graph.container),f=mxUtils.getScrollOrigin(this.graph.container);f.x-=b.x;f.y-=b.y;this.start(e.getX()+f.x,e.getY()+f.y);e.consume(!1)}}),this.graph.addListener(mxEvent.FIRE_MOUSE_EVENT,this.forceRubberbandHandler),
-this.panHandler=mxUtils.bind(this,function(){this.repaint()}),this.graph.addListener(mxEvent.PAN,this.panHandler),this.gestureHandler=mxUtils.bind(this,function(a,c){null!=this.first&&this.reset()}),this.graph.addListener(mxEvent.GESTURE,this.gestureHandler),mxClient.IS_IE&&mxEvent.addListener(window,"unload",mxUtils.bind(this,function(){this.destroy()})))}mxRubberband.prototype.defaultOpacity=20;mxRubberband.prototype.enabled=!0;mxRubberband.prototype.div=null;mxRubberband.prototype.sharedDiv=null;
-mxRubberband.prototype.currentX=0;mxRubberband.prototype.currentY=0;mxRubberband.prototype.fadeOut=!1;mxRubberband.prototype.isEnabled=function(){return this.enabled};mxRubberband.prototype.setEnabled=function(a){this.enabled=a};mxRubberband.prototype.isForceRubberbandEvent=function(a){return mxEvent.isAltDown(a.getEvent())};
-mxRubberband.prototype.mouseDown=function(a,b){if(!b.isConsumed()&&this.isEnabled()&&this.graph.isEnabled()&&null==b.getState()&&!mxEvent.isMultiTouchEvent(b.getEvent())){var c=mxUtils.getOffset(this.graph.container),d=mxUtils.getScrollOrigin(this.graph.container);d.x-=c.x;d.y-=c.y;this.start(b.getX()+d.x,b.getY()+d.y);b.consume(!1)}};
-mxRubberband.prototype.start=function(a,b){function c(a){a=new mxMouseEvent(a);var b=mxUtils.convertPoint(d,a.getX(),a.getY());a.graphX=b.x;a.graphY=b.y;return a}this.first=new mxPoint(a,b);var d=this.graph.container;this.dragHandler=mxUtils.bind(this,function(a){this.mouseMove(this.graph,c(a))});this.dropHandler=mxUtils.bind(this,function(a){this.mouseUp(this.graph,c(a))});mxClient.IS_FF&&mxEvent.addGestureListeners(document,null,this.dragHandler,this.dropHandler)};
-mxRubberband.prototype.mouseMove=function(a,b){if(!b.isConsumed()&&null!=this.first){var c=mxUtils.getScrollOrigin(this.graph.container),d=mxUtils.getOffset(this.graph.container);c.x-=d.x;c.y-=d.y;var d=b.getX()+c.x,c=b.getY()+c.y,e=this.first.x-d,f=this.first.y-c,g=this.graph.tolerance;if(null!=this.div||Math.abs(e)>g||Math.abs(f)>g)null==this.div&&(this.div=this.createShape()),mxUtils.clearSelection(),this.update(d,c),b.consume()}};
-mxRubberband.prototype.createShape=function(){null==this.sharedDiv&&(this.sharedDiv=document.createElement("div"),this.sharedDiv.className="mxRubberband",mxUtils.setOpacity(this.sharedDiv,this.defaultOpacity));this.graph.container.appendChild(this.sharedDiv);var a=this.sharedDiv;mxClient.IS_SVG&&(!mxClient.IS_IE||10<=document.documentMode)&&this.fadeOut&&(this.sharedDiv=null);return a};mxRubberband.prototype.isActive=function(a,b){return null!=this.div&&"none"!=this.div.style.display};
-mxRubberband.prototype.mouseUp=function(a,b){var c=this.isActive();this.reset();c&&(this.execute(b.getEvent()),b.consume())};mxRubberband.prototype.execute=function(a){var b=new mxRectangle(this.x,this.y,this.width,this.height);this.graph.selectRegion(b,a)};
-mxRubberband.prototype.reset=function(){if(null!=this.div)if(mxClient.IS_SVG&&(!mxClient.IS_IE||10<=document.documentMode)&&this.fadeOut){var a=this.div;mxUtils.setPrefixedStyle(a.style,"transition","all 0.2s linear");a.style.pointerEvents="none";a.style.opacity=0;window.setTimeout(function(){a.parentNode.removeChild(a)},200)}else this.div.parentNode.removeChild(this.div);mxEvent.removeGestureListeners(document,null,this.dragHandler,this.dropHandler);this.dropHandler=this.dragHandler=null;this.currentY=
-this.currentX=0;this.div=this.first=null};mxRubberband.prototype.update=function(a,b){this.currentX=a;this.currentY=b;this.repaint()};
-mxRubberband.prototype.repaint=function(){if(null!=this.div){var a=this.currentX-this.graph.panDx,b=this.currentY-this.graph.panDy;this.x=Math.min(this.first.x,a);this.y=Math.min(this.first.y,b);this.width=Math.max(this.first.x,a)-this.x;this.height=Math.max(this.first.y,b)-this.y;a=mxClient.IS_VML?this.graph.panDy:0;this.div.style.left=this.x+(mxClient.IS_VML?this.graph.panDx:0)+"px";this.div.style.top=this.y+a+"px";this.div.style.width=Math.max(1,this.width)+"px";this.div.style.height=Math.max(1,
-this.height)+"px"}};mxRubberband.prototype.destroy=function(){this.destroyed||(this.destroyed=!0,this.graph.removeMouseListener(this),this.graph.removeListener(this.forceRubberbandHandler),this.graph.removeListener(this.panHandler),this.reset(),null!=this.sharedDiv&&(this.sharedDiv=null))};function mxHandle(a,b,c){this.graph=a.view.graph;this.state=a;this.cursor=null!=b?b:this.cursor;this.image=null!=c?c:this.image;this.init()}mxHandle.prototype.cursor="default";mxHandle.prototype.image=null;
-mxHandle.prototype.ignoreGrid=!1;mxHandle.prototype.getPosition=function(a){};mxHandle.prototype.setPosition=function(a,b,c){};mxHandle.prototype.execute=function(){};mxHandle.prototype.copyStyle=function(a){this.graph.setCellStyles(a,this.state.style[a],[this.state.cell])};
-mxHandle.prototype.processEvent=function(a){var b=this.graph.view.scale,c=this.graph.view.translate,c=new mxPoint(a.getGraphX()/b-c.x,a.getGraphY()/b-c.y);null!=this.shape&&null!=this.shape.bounds&&(c.x-=this.shape.bounds.width/b/4,c.y-=this.shape.bounds.height/b/4);var b=-mxUtils.toRadians(this.getRotation()),d=-mxUtils.toRadians(this.getTotalRotation())-b,c=this.flipPoint(this.rotatePoint(this.snapPoint(this.rotatePoint(c,b),this.ignoreGrid||!this.graph.isGridEnabledEvent(a.getEvent())),d));this.setPosition(this.state.getPaintBounds(),
-c,a);this.positionChanged();this.redraw()};mxHandle.prototype.positionChanged=function(){null!=this.state.text&&this.state.text.apply(this.state);null!=this.state.shape&&this.state.shape.apply(this.state);this.graph.cellRenderer.redraw(this.state,!0)};mxHandle.prototype.getRotation=function(){return null!=this.state.shape?this.state.shape.getRotation():0};mxHandle.prototype.getTotalRotation=function(){return null!=this.state.shape?this.state.shape.getShapeRotation():0};
-mxHandle.prototype.init=function(){var a=this.isHtmlRequired();null!=this.image?(this.shape=new mxImageShape(new mxRectangle(0,0,this.image.width,this.image.height),this.image.src),this.shape.preserveImageAspect=!1):this.shape=this.createShape(a);this.initShape(a)};mxHandle.prototype.createShape=function(a){a=new mxRectangle(0,0,mxConstants.HANDLE_SIZE,mxConstants.HANDLE_SIZE);return new mxRectangleShape(a,mxConstants.HANDLE_FILLCOLOR,mxConstants.HANDLE_STROKECOLOR)};
-mxHandle.prototype.initShape=function(a){a&&this.shape.isHtmlAllowed()?(this.shape.dialect=mxConstants.DIALECT_STRICTHTML,this.shape.init(this.graph.container)):(this.shape.dialect=this.graph.dialect!=mxConstants.DIALECT_SVG?mxConstants.DIALECT_MIXEDHTML:mxConstants.DIALECT_SVG,null!=this.cursor&&this.shape.init(this.graph.getView().getOverlayPane()));mxEvent.redirectMouseEvents(this.shape.node,this.graph,this.state);this.shape.node.style.cursor=this.cursor};
-mxHandle.prototype.redraw=function(){if(null!=this.shape&&null!=this.state.shape){var a=this.getPosition(this.state.getPaintBounds());if(null!=a){var b=mxUtils.toRadians(this.getTotalRotation()),a=this.rotatePoint(this.flipPoint(a),b),b=this.graph.view.scale,c=this.graph.view.translate;this.shape.bounds.x=Math.floor((a.x+c.x)*b-this.shape.bounds.width/2);this.shape.bounds.y=Math.floor((a.y+c.y)*b-this.shape.bounds.height/2);this.shape.redraw()}}};
-mxHandle.prototype.isHtmlRequired=function(){return null!=this.state.text&&this.state.text.node.parentNode==this.graph.container};mxHandle.prototype.rotatePoint=function(a,b){var c=this.state.getCellBounds(),c=new mxPoint(c.getCenterX(),c.getCenterY());return mxUtils.getRotatedPoint(a,Math.cos(b),Math.sin(b),c)};
-mxHandle.prototype.flipPoint=function(a){if(null!=this.state.shape){var b=this.state.getCellBounds();this.state.shape.flipH&&(a.x=2*b.x+b.width-a.x);this.state.shape.flipV&&(a.y=2*b.y+b.height-a.y)}return a};mxHandle.prototype.snapPoint=function(a,b){b||(a.x=this.graph.snap(a.x),a.y=this.graph.snap(a.y));return a};mxHandle.prototype.setVisible=function(a){null!=this.shape&&null!=this.shape.node&&(this.shape.node.style.display=a?"":"none")};
-mxHandle.prototype.reset=function(){this.setVisible(!0);this.state.style=this.graph.getCellStyle(this.state.cell);this.positionChanged()};mxHandle.prototype.destroy=function(){null!=this.shape&&(this.shape.destroy(),this.shape=null)};
-function mxVertexHandler(a){null!=a&&(this.state=a,this.init(),this.escapeHandler=mxUtils.bind(this,function(a,c){this.livePreview&&null!=this.index&&(this.state.view.graph.cellRenderer.redraw(this.state,!0),this.state.view.invalidate(this.state.cell),this.state.invalid=!1,this.state.view.validate());this.reset()}),this.state.view.graph.addListener(mxEvent.ESCAPE,this.escapeHandler))}mxVertexHandler.prototype.graph=null;mxVertexHandler.prototype.state=null;mxVertexHandler.prototype.singleSizer=!1;
-mxVertexHandler.prototype.index=null;mxVertexHandler.prototype.allowHandleBoundsCheck=!0;mxVertexHandler.prototype.handleImage=null;mxVertexHandler.prototype.tolerance=0;mxVertexHandler.prototype.rotationEnabled=!1;mxVertexHandler.prototype.parentHighlightEnabled=!1;mxVertexHandler.prototype.rotationRaster=!0;mxVertexHandler.prototype.rotationCursor="crosshair";mxVertexHandler.prototype.livePreview=!1;mxVertexHandler.prototype.manageSizers=!1;mxVertexHandler.prototype.constrainGroupByChildren=!1;
-mxVertexHandler.prototype.rotationHandleVSpacing=-16;mxVertexHandler.prototype.horizontalOffset=0;mxVertexHandler.prototype.verticalOffset=0;
-mxVertexHandler.prototype.init=function(){this.graph=this.state.view.graph;this.selectionBounds=this.getSelectionBounds(this.state);this.bounds=new mxRectangle(this.selectionBounds.x,this.selectionBounds.y,this.selectionBounds.width,this.selectionBounds.height);this.selectionBorder=this.createSelectionShape(this.bounds);this.selectionBorder.dialect=this.graph.dialect!=mxConstants.DIALECT_SVG?mxConstants.DIALECT_VML:mxConstants.DIALECT_SVG;this.selectionBorder.pointerEvents=!1;this.selectionBorder.rotation=
-Number(this.state.style[mxConstants.STYLE_ROTATION]||"0");this.selectionBorder.init(this.graph.getView().getOverlayPane());mxEvent.redirectMouseEvents(this.selectionBorder.node,this.graph,this.state);this.graph.isCellMovable(this.state.cell)&&this.selectionBorder.setCursor(mxConstants.CURSOR_MOVABLE_VERTEX);if(0>=mxGraphHandler.prototype.maxCells||this.graph.getSelectionCount()<mxGraphHandler.prototype.maxCells){var a=this.graph.isCellResizable(this.state.cell);this.sizers=[];if(a||this.graph.isLabelMovable(this.state.cell)&&
-2<=this.state.width&&2<=this.state.height){var b=0;a&&(this.singleSizer||(this.sizers.push(this.createSizer("nw-resize",b++)),this.sizers.push(this.createSizer("n-resize",b++)),this.sizers.push(this.createSizer("ne-resize",b++)),this.sizers.push(this.createSizer("w-resize",b++)),this.sizers.push(this.createSizer("e-resize",b++)),this.sizers.push(this.createSizer("sw-resize",b++)),this.sizers.push(this.createSizer("s-resize",b++))),this.sizers.push(this.createSizer("se-resize",b++)));a=this.graph.model.getGeometry(this.state.cell);
-null==a||a.relative||this.graph.isSwimlane(this.state.cell)||!this.graph.isLabelMovable(this.state.cell)||(this.labelShape=this.createSizer(mxConstants.CURSOR_LABEL_HANDLE,mxEvent.LABEL_HANDLE,mxConstants.LABEL_HANDLE_SIZE,mxConstants.LABEL_HANDLE_FILLCOLOR),this.sizers.push(this.labelShape))}else this.graph.isCellMovable(this.state.cell)&&!this.graph.isCellResizable(this.state.cell)&&2>this.state.width&&2>this.state.height&&(this.labelShape=this.createSizer(mxConstants.CURSOR_MOVABLE_VERTEX,mxEvent.LABEL_HANDLE,
-null,mxConstants.LABEL_HANDLE_FILLCOLOR),this.sizers.push(this.labelShape))}this.isRotationHandleVisible()&&(this.rotationShape=this.createSizer(this.rotationCursor,mxEvent.ROTATION_HANDLE,mxConstants.HANDLE_SIZE+3,mxConstants.HANDLE_FILLCOLOR),this.sizers.push(this.rotationShape));this.customHandles=this.createCustomHandles();this.redraw();this.constrainGroupByChildren&&this.updateMinBounds()};
-mxVertexHandler.prototype.isRotationHandleVisible=function(){return this.graph.isEnabled()&&this.rotationEnabled&&this.graph.isCellRotatable(this.state.cell)&&(0>=mxGraphHandler.prototype.maxCells||this.graph.getSelectionCount()<mxGraphHandler.prototype.maxCells)&&2<=this.state.width&&2<=this.state.height};mxVertexHandler.prototype.isConstrainedEvent=function(a){return mxEvent.isShiftDown(a.getEvent())||"fixed"==this.state.style[mxConstants.STYLE_ASPECT]};
-mxVertexHandler.prototype.isCenteredEvent=function(a,b){return!1};mxVertexHandler.prototype.createCustomHandles=function(){return null};
-mxVertexHandler.prototype.updateMinBounds=function(){var a=this.graph.getChildCells(this.state.cell);if(0<a.length&&(this.minBounds=this.graph.view.getBounds(a),null!=this.minBounds)){var a=this.state.view.scale,b=this.state.view.translate;this.minBounds.x-=this.state.x;this.minBounds.y-=this.state.y;this.minBounds.x/=a;this.minBounds.y/=a;this.minBounds.width/=a;this.minBounds.height/=a;this.x0=this.state.x/a-b.x;this.y0=this.state.y/a-b.y}};
-mxVertexHandler.prototype.getSelectionBounds=function(a){return new mxRectangle(Math.round(a.x),Math.round(a.y),Math.round(a.width),Math.round(a.height))};mxVertexHandler.prototype.createParentHighlightShape=function(a){return this.createSelectionShape(a)};mxVertexHandler.prototype.createSelectionShape=function(a){a=new mxRectangleShape(a,null,this.getSelectionColor());a.strokewidth=this.getSelectionStrokeWidth();a.isDashed=this.isSelectionDashed();return a};
-mxVertexHandler.prototype.getSelectionColor=function(){return mxConstants.VERTEX_SELECTION_COLOR};mxVertexHandler.prototype.getSelectionStrokeWidth=function(){return mxConstants.VERTEX_SELECTION_STROKEWIDTH};mxVertexHandler.prototype.isSelectionDashed=function(){return mxConstants.VERTEX_SELECTION_DASHED};
-mxVertexHandler.prototype.createSizer=function(a,b,c,d){c=c||mxConstants.HANDLE_SIZE;c=new mxRectangle(0,0,c,c);d=this.createSizerShape(c,b,d);d.isHtmlAllowed()&&null!=this.state.text&&this.state.text.node.parentNode==this.graph.container?(--d.bounds.height,--d.bounds.width,d.dialect=mxConstants.DIALECT_STRICTHTML,d.init(this.graph.container)):(d.dialect=this.graph.dialect!=mxConstants.DIALECT_SVG?mxConstants.DIALECT_MIXEDHTML:mxConstants.DIALECT_SVG,d.init(this.graph.getView().getOverlayPane()));
-mxEvent.redirectMouseEvents(d.node,this.graph,this.state);this.graph.isEnabled()&&d.setCursor(a);this.isSizerVisible(b)||(d.visible=!1);return d};mxVertexHandler.prototype.isSizerVisible=function(a){return!0};
-mxVertexHandler.prototype.createSizerShape=function(a,b,c){return null!=this.handleImage?(a=new mxRectangle(a.x,a.y,this.handleImage.width,this.handleImage.height),a=new mxImageShape(a,this.handleImage.src),a.preserveImageAspect=!1,a):b==mxEvent.ROTATION_HANDLE?new mxEllipse(a,c||mxConstants.HANDLE_FILLCOLOR,mxConstants.HANDLE_STROKECOLOR):new mxRectangleShape(a,c||mxConstants.HANDLE_FILLCOLOR,mxConstants.HANDLE_STROKECOLOR)};
-mxVertexHandler.prototype.moveSizerTo=function(a,b,c){null!=a&&(a.bounds.x=Math.floor(b-a.bounds.width/2),a.bounds.y=Math.floor(c-a.bounds.height/2),null!=a.node&&"none"!=a.node.style.display&&a.redraw())};
-mxVertexHandler.prototype.getHandleForEvent=function(a){function b(b){return null!=b&&(a.isSource(b)||null!=d&&mxUtils.intersects(b.bounds,d)&&"none"!=b.node.style.display&&"hidden"!=b.node.style.visibility)}var c=mxEvent.isMouseEvent(a.getEvent())?1:this.tolerance,d=this.allowHandleBoundsCheck&&(mxClient.IS_IE||0<c)?new mxRectangle(a.getGraphX()-c,a.getGraphY()-c,2*c,2*c):null;if(null!=this.customHandles&&this.isCustomHandleEvent(a))for(c=this.customHandles.length-1;0<=c;c--)if(b(this.customHandles[c].shape))return mxEvent.CUSTOM_HANDLE-
-c;if(b(this.rotationShape))return mxEvent.ROTATION_HANDLE;if(b(this.labelShape))return mxEvent.LABEL_HANDLE;if(null!=this.sizers)for(c=0;c<this.sizers.length;c++)if(b(this.sizers[c]))return c;return null};mxVertexHandler.prototype.isCustomHandleEvent=function(a){return!0};
-mxVertexHandler.prototype.mouseDown=function(a,b){var c=mxEvent.isMouseEvent(b.getEvent())?0:this.tolerance;!b.isConsumed()&&this.graph.isEnabled()&&(0<c||b.getState()==this.state)&&(c=this.getHandleForEvent(b),null!=c&&(this.start(b.getGraphX(),b.getGraphY(),c),b.consume()))};mxVertexHandler.prototype.isLivePreviewBorder=function(){return null!=this.state.shape&&null==this.state.shape.fill&&null==this.state.shape.stroke};
-mxVertexHandler.prototype.start=function(a,b,c){this.inTolerance=!0;this.childOffsetY=this.childOffsetX=0;this.index=c;this.startX=a;this.startY=b;a=this.state.view.graph.model;b=a.getParent(this.state.cell);this.state.view.currentRoot!=b&&(a.isVertex(b)||a.isEdge(b))&&(this.parentState=this.state.view.graph.view.getState(b));this.selectionBorder.node.style.display=c==mxEvent.ROTATION_HANDLE?"inline":"none";if(!this.livePreview||this.isLivePreviewBorder())this.preview=this.createSelectionShape(this.bounds),
-mxClient.IS_SVG&&0!=Number(this.state.style[mxConstants.STYLE_ROTATION]||"0")||null==this.state.text||this.state.text.node.parentNode!=this.graph.container?(this.preview.dialect=this.graph.dialect!=mxConstants.DIALECT_SVG?mxConstants.DIALECT_VML:mxConstants.DIALECT_SVG,this.preview.init(this.graph.view.getOverlayPane())):(this.preview.dialect=mxConstants.DIALECT_STRICTHTML,this.preview.init(this.graph.container));if(this.livePreview)for(this.hideSizers(),c==mxEvent.ROTATION_HANDLE?this.rotationShape.node.style.display=
-"":c==mxEvent.LABEL_HANDLE?this.labelShape.node.style.display="":null!=this.sizers&&null!=this.sizers[c]?this.sizers[c].node.style.display="":c<=mxEvent.CUSTOM_HANDLE&&null!=this.customHandles&&this.customHandles[mxEvent.CUSTOM_HANDLE-c].setVisible(!0),c=this.graph.getEdges(this.state.cell),this.edgeHandlers=[],a=0;a<c.length;a++)b=this.graph.selectionCellsHandler.getHandler(c[a]),null!=b&&this.edgeHandlers.push(b)};
-mxVertexHandler.prototype.setHandlesVisible=function(a){if(null!=this.sizers)for(var b=0;b<this.sizers.length;b++)this.sizers[b].node.style.display=a?"":"none";if(null!=this.customHandles)for(b=0;b<this.customHandles.length;b++)this.customHandles[b].setVisible(a)};mxVertexHandler.prototype.hideSizers=function(){this.setHandlesVisible(!1)};
-mxVertexHandler.prototype.checkTolerance=function(a){this.inTolerance&&null!=this.startX&&null!=this.startY&&(mxEvent.isMouseEvent(a.getEvent())||Math.abs(a.getGraphX()-this.startX)>this.graph.tolerance||Math.abs(a.getGraphY()-this.startY)>this.graph.tolerance)&&(this.inTolerance=!1)};mxVertexHandler.prototype.updateHint=function(a){};mxVertexHandler.prototype.removeHint=function(){};mxVertexHandler.prototype.roundAngle=function(a){return Math.round(10*a)/10};
-mxVertexHandler.prototype.roundLength=function(a){return Math.round(a)};
-mxVertexHandler.prototype.mouseMove=function(a,b){b.isConsumed()||null==this.index?this.graph.isMouseDown||null==this.getHandleForEvent(b)||b.consume(!1):(this.checkTolerance(b),this.inTolerance||(this.index<=mxEvent.CUSTOM_HANDLE?null!=this.customHandles&&(this.customHandles[mxEvent.CUSTOM_HANDLE-this.index].processEvent(b),this.customHandles[mxEvent.CUSTOM_HANDLE-this.index].active=!0):this.index==mxEvent.LABEL_HANDLE?this.moveLabel(b):this.index==mxEvent.ROTATION_HANDLE?this.rotateVertex(b):this.resizeVertex(b),
-this.updateHint(b)),b.consume())};mxVertexHandler.prototype.moveLabel=function(a){var b=new mxPoint(a.getGraphX(),a.getGraphY()),c=this.graph.view.translate,d=this.graph.view.scale;this.graph.isGridEnabledEvent(a.getEvent())&&(b.x=(this.graph.snap(b.x/d-c.x)+c.x)*d,b.y=(this.graph.snap(b.y/d-c.y)+c.y)*d);this.moveSizerTo(this.sizers[null!=this.rotationShape?this.sizers.length-2:this.sizers.length-1],b.x,b.y)};
-mxVertexHandler.prototype.rotateVertex=function(a){var b=new mxPoint(a.getGraphX(),a.getGraphY()),c=this.state.x+this.state.width/2-b.x,d=this.state.y+this.state.height/2-b.y;this.currentAlpha=0!=c?180*Math.atan(d/c)/Math.PI+90:0>d?180:0;0<c&&(this.currentAlpha-=180);this.rotationRaster&&this.graph.isGridEnabledEvent(a.getEvent())?(c=b.x-this.state.getCenterX(),d=b.y-this.state.getCenterY(),a=Math.max(1,5*Math.min(3,Math.max(0,Math.round(80/Math.abs(3*Math.abs(Math.sqrt(c*c+d*d)-20)))))),this.currentAlpha=
-Math.round(this.currentAlpha/a)*a):this.currentAlpha=this.roundAngle(this.currentAlpha);this.selectionBorder.rotation=this.currentAlpha;this.selectionBorder.redraw();this.livePreview&&this.redrawHandles()};
-mxVertexHandler.prototype.resizeVertex=function(a){var b=new mxPoint(this.state.getCenterX(),this.state.getCenterY()),c=mxUtils.toRadians(this.state.style[mxConstants.STYLE_ROTATION]||"0"),d=new mxPoint(a.getGraphX(),a.getGraphY()),e=this.graph.view.translate,f=this.graph.view.scale,g=Math.cos(-c),k=Math.sin(-c),l=d.x-this.startX,d=d.y-this.startY,m=k*l+g*d,l=g*l-k*d,d=m,g=this.graph.getCellGeometry(this.state.cell);this.unscaledBounds=this.union(g,l/f,d/f,this.index,this.graph.isGridEnabledEvent(a.getEvent()),
-1,new mxPoint(0,0),this.isConstrainedEvent(a),this.isCenteredEvent(this.state,a));g.relative||(k=this.graph.getMaximumGraphBounds(),null!=k&&null!=this.parentState&&(k=mxRectangle.fromRectangle(k),k.x-=(this.parentState.x-e.x*f)/f,k.y-=(this.parentState.y-e.y*f)/f),this.graph.isConstrainChild(this.state.cell)&&(l=this.graph.getCellContainmentArea(this.state.cell),null!=l&&(d=this.graph.getOverlap(this.state.cell),0<d&&(l=mxRectangle.fromRectangle(l),l.x-=l.width*d,l.y-=l.height*d,l.width+=2*l.width*
-d,l.height+=2*l.height*d),null==k?k=l:(k=mxRectangle.fromRectangle(k),k.intersect(l)))),null!=k&&(this.unscaledBounds.x<k.x&&(this.unscaledBounds.width-=k.x-this.unscaledBounds.x,this.unscaledBounds.x=k.x),this.unscaledBounds.y<k.y&&(this.unscaledBounds.height-=k.y-this.unscaledBounds.y,this.unscaledBounds.y=k.y),this.unscaledBounds.x+this.unscaledBounds.width>k.x+k.width&&(this.unscaledBounds.width-=this.unscaledBounds.x+this.unscaledBounds.width-k.x-k.width),this.unscaledBounds.y+this.unscaledBounds.height>
-k.y+k.height&&(this.unscaledBounds.height-=this.unscaledBounds.y+this.unscaledBounds.height-k.y-k.height)));this.bounds=new mxRectangle((null!=this.parentState?this.parentState.x:e.x*f)+this.unscaledBounds.x*f,(null!=this.parentState?this.parentState.y:e.y*f)+this.unscaledBounds.y*f,this.unscaledBounds.width*f,this.unscaledBounds.height*f);g.relative&&null!=this.parentState&&(this.bounds.x+=this.state.x-this.parentState.x,this.bounds.y+=this.state.y-this.parentState.y);g=Math.cos(c);k=Math.sin(c);
-c=new mxPoint(this.bounds.getCenterX(),this.bounds.getCenterY());l=c.x-b.x;d=c.y-b.y;b=g*l-k*d-l;c=k*l+g*d-d;l=this.bounds.x-this.state.x;d=this.bounds.y-this.state.y;e=g*l-k*d;g=k*l+g*d;this.bounds.x+=b;this.bounds.y+=c;this.unscaledBounds.x=this.roundLength(this.unscaledBounds.x+b/f);this.unscaledBounds.y=this.roundLength(this.unscaledBounds.y+c/f);this.unscaledBounds.width=this.roundLength(this.unscaledBounds.width);this.unscaledBounds.height=this.roundLength(this.unscaledBounds.height);this.graph.isCellCollapsed(this.state.cell)||
-0==b&&0==c?this.childOffsetY=this.childOffsetX=0:(this.childOffsetX=this.state.x-this.bounds.x+e,this.childOffsetY=this.state.y-this.bounds.y+g);this.livePreview&&this.updateLivePreview(a);null!=this.preview&&this.drawPreview()};
-mxVertexHandler.prototype.updateLivePreview=function(a){var b=this.graph.view.scale,c=this.graph.view.translate;a=this.state.clone();this.state.x=this.bounds.x;this.state.y=this.bounds.y;this.state.origin=new mxPoint(this.state.x/b-c.x,this.state.y/b-c.y);this.state.width=this.bounds.width;this.state.height=this.bounds.height;this.state.unscaledWidth=null;b=this.state.absoluteOffset;new mxPoint(b.x,b.y);this.state.absoluteOffset.x=0;this.state.absoluteOffset.y=0;b=this.graph.getCellGeometry(this.state.cell);
-null!=b&&(c=b.offset||this.EMPTY_POINT,null==c||b.relative||(this.state.absoluteOffset.x=this.state.view.scale*c.x,this.state.absoluteOffset.y=this.state.view.scale*c.y),this.state.view.updateVertexLabelOffset(this.state));this.state.view.graph.cellRenderer.redraw(this.state,!0);this.state.view.invalidate(this.state.cell);this.state.invalid=!1;this.state.view.validate();this.redrawHandles();this.state.setState(a)};
-mxVertexHandler.prototype.mouseUp=function(a,b){if(null!=this.index&&null!=this.state){var c=new mxPoint(b.getGraphX(),b.getGraphY());this.graph.getModel().beginUpdate();try{if(this.index<=mxEvent.CUSTOM_HANDLE)null!=this.customHandles&&(this.customHandles[mxEvent.CUSTOM_HANDLE-this.index].active=!1,this.customHandles[mxEvent.CUSTOM_HANDLE-this.index].execute());else if(this.index==mxEvent.ROTATION_HANDLE)if(null!=this.currentAlpha){var d=this.currentAlpha-(this.state.style[mxConstants.STYLE_ROTATION]||
-0);0!=d&&this.rotateCell(this.state.cell,d)}else this.rotateClick();else{var e=this.graph.isGridEnabledEvent(b.getEvent()),f=mxUtils.toRadians(this.state.style[mxConstants.STYLE_ROTATION]||"0"),g=Math.cos(-f),k=Math.sin(-f),l=c.x-this.startX,m=c.y-this.startY,c=k*l+g*m,l=g*l-k*m,m=c,n=this.graph.view.scale,p=this.isRecursiveResize(this.state,b);this.resizeCell(this.state.cell,this.roundLength(l/n),this.roundLength(m/n),this.index,e,this.isConstrainedEvent(b),p)}}finally{this.graph.getModel().endUpdate()}b.consume();
-this.reset()}};mxVertexHandler.prototype.isRecursiveResize=function(a,b){return this.graph.isRecursiveResize(this.state)};mxVertexHandler.prototype.rotateClick=function(){};
-mxVertexHandler.prototype.rotateCell=function(a,b,c){if(0!=b){var d=this.graph.getModel();if(d.isVertex(a)||d.isEdge(a)){if(!d.isEdge(a)){var e=this.graph.view.getState(a),e=null!=e?e.style:this.graph.getCellStyle(a);null!=e&&this.graph.setCellStyles(mxConstants.STYLE_ROTATION,(e[mxConstants.STYLE_ROTATION]||0)+b,[a])}e=this.graph.getCellGeometry(a);if(null!=e){var f=this.graph.getCellGeometry(c);null==f||d.isEdge(c)||(e=e.clone(),e.rotate(b,new mxPoint(f.width/2,f.height/2)),d.setGeometry(a,e));
-if(d.isVertex(a)&&!e.relative||d.isEdge(a))for(c=d.getChildCount(a),e=0;e<c;e++)this.rotateCell(d.getChildAt(a,e),b,a)}}}};
-mxVertexHandler.prototype.reset=function(){null!=this.sizers&&null!=this.index&&null!=this.sizers[this.index]&&"none"==this.sizers[this.index].node.style.display&&(this.sizers[this.index].node.style.display="");this.index=this.inTolerance=this.currentAlpha=null;null!=this.preview&&(this.preview.destroy(),this.preview=null);if(this.livePreview&&null!=this.sizers)for(var a=0;a<this.sizers.length;a++)null!=this.sizers[a]&&(this.sizers[a].node.style.display="");if(null!=this.customHandles)for(a=0;a<this.customHandles.length;a++)this.customHandles[a].active?
-(this.customHandles[a].active=!1,this.customHandles[a].reset()):this.customHandles[a].setVisible(!0);null!=this.selectionBorder&&(this.selectionBorder.node.style.display="inline",this.selectionBounds=this.getSelectionBounds(this.state),this.bounds=new mxRectangle(this.selectionBounds.x,this.selectionBounds.y,this.selectionBounds.width,this.selectionBounds.height),this.drawPreview());this.removeHint();this.redrawHandles();this.unscaledBounds=this.edgeHandlers=null};
-mxVertexHandler.prototype.resizeCell=function(a,b,c,d,e,f,g){e=this.graph.model.getGeometry(a);null!=e&&(d==mxEvent.LABEL_HANDLE?(c=this.graph.view.scale,b=Math.round((this.labelShape.bounds.getCenterX()-this.startX)/c),c=Math.round((this.labelShape.bounds.getCenterY()-this.startY)/c),e=e.clone(),null==e.offset?e.offset=new mxPoint(b,c):(e.offset.x+=b,e.offset.y+=c),this.graph.model.setGeometry(a,e)):null!=this.unscaledBounds&&(c=this.graph.view.scale,0==this.childOffsetX&&0==this.childOffsetY||this.moveChildren(a,
-Math.round(this.childOffsetX/c),Math.round(this.childOffsetY/c)),this.graph.resizeCell(a,this.unscaledBounds,g)))};mxVertexHandler.prototype.moveChildren=function(a,b,c){for(var d=this.graph.getModel(),e=d.getChildCount(a),f=0;f<e;f++){var g=d.getChildAt(a,f),k=this.graph.getCellGeometry(g);null!=k&&(k=k.clone(),k.translate(b,c),d.setGeometry(g,k))}};
-mxVertexHandler.prototype.union=function(a,b,c,d,e,f,g,k,l){if(this.singleSizer)return d=a.x+a.width+b,g=a.y+a.height+c,e&&(d=this.graph.snap(d/f)*f,g=this.graph.snap(g/f)*f),f=new mxRectangle(a.x,a.y,0,0),f.add(new mxRectangle(d,g,0,0)),f;var m=a.width,n=a.height,p=a.x-g.x*f,q=p+m;a=a.y-g.y*f;var r=a+n,t=p+m/2,u=a+n/2;4<d?(r+=c,e&&(r=this.graph.snap(r/f)*f)):3>d&&(a+=c,e&&(a=this.graph.snap(a/f)*f));if(0==d||3==d||5==d)p+=b,e&&(p=this.graph.snap(p/f)*f);else if(2==d||4==d||7==d)q+=b,e&&(q=this.graph.snap(q/
-f)*f);e=q-p;c=r-a;k&&(k=this.graph.getCellGeometry(this.state.cell),null!=k&&(k=k.width/k.height,1==d||2==d||7==d||6==d?e=c*k:c=e/k,0==d&&(p=q-e,a=r-c)));l&&(e+=e-m,c+=c-n,p+=t-(p+e/2),a+=u-(a+c/2));0>e&&(p+=e,e=Math.abs(e));0>c&&(a+=c,c=Math.abs(c));d=new mxRectangle(p+g.x*f,a+g.y*f,e,c);null!=this.minBounds&&(d.width=Math.max(d.width,this.minBounds.x*f+this.minBounds.width*f+Math.max(0,this.x0*f-d.x)),d.height=Math.max(d.height,this.minBounds.y*f+this.minBounds.height*f+Math.max(0,this.y0*f-d.y)));
-return d};mxVertexHandler.prototype.redraw=function(){this.selectionBounds=this.getSelectionBounds(this.state);this.bounds=new mxRectangle(this.selectionBounds.x,this.selectionBounds.y,this.selectionBounds.width,this.selectionBounds.height);this.redrawHandles();this.drawPreview()};
-mxVertexHandler.prototype.getHandlePadding=function(){var a=new mxPoint(0,0),b=this.tolerance;null!=this.sizers&&0<this.sizers.length&&null!=this.sizers[0]&&(this.bounds.width<2*this.sizers[0].bounds.width+2*b||this.bounds.height<2*this.sizers[0].bounds.height+2*b)&&(b/=2,a.x=this.sizers[0].bounds.width+b,a.y=this.sizers[0].bounds.height+b);return a};
-mxVertexHandler.prototype.redrawHandles=function(){var a=this.tolerance;this.verticalOffset=this.horizontalOffset=0;var b=this.bounds;if(null!=this.sizers&&0<this.sizers.length&&null!=this.sizers[0]){if(null==this.index&&this.manageSizers&&8<=this.sizers.length){var c=this.getHandlePadding();this.horizontalOffset=c.x;this.verticalOffset=c.y;if(0!=this.horizontalOffset||0!=this.verticalOffset)b=new mxRectangle(b.x,b.y,b.width,b.height),b.x-=this.horizontalOffset/2,b.width+=this.horizontalOffset,b.y-=
-this.verticalOffset/2,b.height+=this.verticalOffset;8<=this.sizers.length&&(b.width<2*this.sizers[0].bounds.width+2*a||b.height<2*this.sizers[0].bounds.height+2*a?(this.sizers[0].node.style.display="none",this.sizers[2].node.style.display="none",this.sizers[5].node.style.display="none",this.sizers[7].node.style.display="none"):(this.sizers[0].node.style.display="",this.sizers[2].node.style.display="",this.sizers[5].node.style.display="",this.sizers[7].node.style.display=""))}a=b.x+b.width;c=b.y+b.height;
-if(this.singleSizer)this.moveSizerTo(this.sizers[0],a,c);else{var d=b.x+b.width/2,e=b.y+b.height/2;if(8<=this.sizers.length){var f="nw-resize n-resize ne-resize e-resize se-resize s-resize sw-resize w-resize".split(" "),g=mxUtils.toRadians(this.state.style[mxConstants.STYLE_ROTATION]||"0"),k=Math.cos(g),l=Math.sin(g),g=Math.round(4*g/Math.PI),m=new mxPoint(b.getCenterX(),b.getCenterY()),n=mxUtils.getRotatedPoint(new mxPoint(b.x,b.y),k,l,m);this.moveSizerTo(this.sizers[0],n.x,n.y);this.sizers[0].setCursor(f[mxUtils.mod(0+
-g,f.length)]);n.x=d;n.y=b.y;n=mxUtils.getRotatedPoint(n,k,l,m);this.moveSizerTo(this.sizers[1],n.x,n.y);this.sizers[1].setCursor(f[mxUtils.mod(1+g,f.length)]);n.x=a;n.y=b.y;n=mxUtils.getRotatedPoint(n,k,l,m);this.moveSizerTo(this.sizers[2],n.x,n.y);this.sizers[2].setCursor(f[mxUtils.mod(2+g,f.length)]);n.x=b.x;n.y=e;n=mxUtils.getRotatedPoint(n,k,l,m);this.moveSizerTo(this.sizers[3],n.x,n.y);this.sizers[3].setCursor(f[mxUtils.mod(7+g,f.length)]);n.x=a;n.y=e;n=mxUtils.getRotatedPoint(n,k,l,m);this.moveSizerTo(this.sizers[4],
-n.x,n.y);this.sizers[4].setCursor(f[mxUtils.mod(3+g,f.length)]);n.x=b.x;n.y=c;n=mxUtils.getRotatedPoint(n,k,l,m);this.moveSizerTo(this.sizers[5],n.x,n.y);this.sizers[5].setCursor(f[mxUtils.mod(6+g,f.length)]);n.x=d;n.y=c;n=mxUtils.getRotatedPoint(n,k,l,m);this.moveSizerTo(this.sizers[6],n.x,n.y);this.sizers[6].setCursor(f[mxUtils.mod(5+g,f.length)]);n.x=a;n.y=c;n=mxUtils.getRotatedPoint(n,k,l,m);this.moveSizerTo(this.sizers[7],n.x,n.y);this.sizers[7].setCursor(f[mxUtils.mod(4+g,f.length)]);this.moveSizerTo(this.sizers[8],
-d+this.state.absoluteOffset.x,e+this.state.absoluteOffset.y)}else 2<=this.state.width&&2<=this.state.height?this.moveSizerTo(this.sizers[0],d+this.state.absoluteOffset.x,e+this.state.absoluteOffset.y):this.moveSizerTo(this.sizers[0],this.state.x,this.state.y)}}null!=this.rotationShape&&(g=mxUtils.toRadians(null!=this.currentAlpha?this.currentAlpha:this.state.style[mxConstants.STYLE_ROTATION]||"0"),k=Math.cos(g),l=Math.sin(g),m=new mxPoint(this.state.getCenterX(),this.state.getCenterY()),n=mxUtils.getRotatedPoint(this.getRotationHandlePosition(),
-k,l,m),null!=this.rotationShape.node&&(this.moveSizerTo(this.rotationShape,n.x,n.y),this.rotationShape.node.style.visibility=this.state.view.graph.isEditing()?"hidden":""));null!=this.selectionBorder&&(this.selectionBorder.rotation=Number(this.state.style[mxConstants.STYLE_ROTATION]||"0"));if(null!=this.edgeHandlers)for(b=0;b<this.edgeHandlers.length;b++)this.edgeHandlers[b].redraw();if(null!=this.customHandles)for(b=0;b<this.customHandles.length;b++)a=this.customHandles[b].shape.node.style.display,
-this.customHandles[b].redraw(),this.customHandles[b].shape.node.style.display=a,this.customHandles[b].shape.node.style.visibility=this.graph.isEditing()?"hidden":"";this.updateParentHighlight()};mxVertexHandler.prototype.getRotationHandlePosition=function(){return new mxPoint(this.bounds.x+this.bounds.width/2,this.bounds.y+this.rotationHandleVSpacing)};
-mxVertexHandler.prototype.updateParentHighlight=function(){if(null!=this.selectionBorder)if(null!=this.parentHighlight){var a=this.graph.model.getParent(this.state.cell);if(this.graph.model.isVertex(a)){var a=this.graph.view.getState(a),b=this.parentHighlight.bounds;null==a||b.x==a.x&&b.y==a.y&&b.width==a.width&&b.height==a.height||(this.parentHighlight.bounds=a,this.parentHighlight.redraw())}else this.parentHighlight.destroy(),this.parentHighlight=null}else this.parentHighlightEnabled&&(a=this.graph.model.getParent(this.state.cell),
-this.graph.model.isVertex(a)&&(a=this.graph.view.getState(a),null!=a&&(this.parentHighlight=this.createParentHighlightShape(a),this.parentHighlight.dialect=this.graph.dialect!=mxConstants.DIALECT_SVG?mxConstants.DIALECT_VML:mxConstants.DIALECT_SVG,this.parentHighlight.pointerEvents=!1,this.parentHighlight.rotation=Number(a.style[mxConstants.STYLE_ROTATION]||"0"),this.parentHighlight.init(this.graph.getView().getOverlayPane()))))};
-mxVertexHandler.prototype.drawPreview=function(){null!=this.preview&&(this.preview.bounds=this.bounds,this.preview.node.parentNode==this.graph.container&&(this.preview.bounds.width=Math.max(0,this.preview.bounds.width-1),this.preview.bounds.height=Math.max(0,this.preview.bounds.height-1)),this.preview.rotation=Number(this.state.style[mxConstants.STYLE_ROTATION]||"0"),this.preview.redraw());this.selectionBorder.bounds=this.bounds;this.selectionBorder.redraw();null!=this.parentHighlight&&this.parentHighlight.redraw()};
-mxVertexHandler.prototype.destroy=function(){null!=this.escapeHandler&&(this.state.view.graph.removeListener(this.escapeHandler),this.escapeHandler=null);null!=this.preview&&(this.preview.destroy(),this.preview=null);null!=this.parentHighlight&&(this.parentHighlight.destroy(),this.parentHighlight=null);null!=this.selectionBorder&&(this.selectionBorder.destroy(),this.selectionBorder=null);this.labelShape=null;this.removeHint();if(null!=this.sizers){for(var a=0;a<this.sizers.length;a++)this.sizers[a].destroy();
-this.sizers=null}if(null!=this.customHandles){for(a=0;a<this.customHandles.length;a++)this.customHandles[a].destroy();this.customHandles=null}};function mxEdgeHandler(a){null!=a&&(this.state=a,this.init(),this.escapeHandler=mxUtils.bind(this,function(b,c){var d=null!=this.index;this.reset();d&&this.graph.cellRenderer.redraw(this.state,!1,a.view.isRendering())}),this.state.view.graph.addListener(mxEvent.ESCAPE,this.escapeHandler))}mxEdgeHandler.prototype.graph=null;mxEdgeHandler.prototype.state=null;
-mxEdgeHandler.prototype.marker=null;mxEdgeHandler.prototype.constraintHandler=null;mxEdgeHandler.prototype.error=null;mxEdgeHandler.prototype.shape=null;mxEdgeHandler.prototype.bends=null;mxEdgeHandler.prototype.labelShape=null;mxEdgeHandler.prototype.cloneEnabled=!0;mxEdgeHandler.prototype.addEnabled=!1;mxEdgeHandler.prototype.removeEnabled=!1;mxEdgeHandler.prototype.dblClickRemoveEnabled=!1;mxEdgeHandler.prototype.mergeRemoveEnabled=!1;mxEdgeHandler.prototype.straightRemoveEnabled=!1;
-mxEdgeHandler.prototype.virtualBendsEnabled=!1;mxEdgeHandler.prototype.virtualBendOpacity=20;mxEdgeHandler.prototype.parentHighlightEnabled=!1;mxEdgeHandler.prototype.preferHtml=!1;mxEdgeHandler.prototype.allowHandleBoundsCheck=!0;mxEdgeHandler.prototype.snapToTerminals=!1;mxEdgeHandler.prototype.handleImage=null;mxEdgeHandler.prototype.tolerance=0;mxEdgeHandler.prototype.outlineConnect=!1;mxEdgeHandler.prototype.manageLabelHandle=!1;
-mxEdgeHandler.prototype.init=function(){this.graph=this.state.view.graph;this.marker=this.createMarker();this.constraintHandler=new mxConstraintHandler(this.graph);this.points=[];this.abspoints=this.getSelectionPoints(this.state);this.shape=this.createSelectionShape(this.abspoints);this.shape.dialect=this.graph.dialect!=mxConstants.DIALECT_SVG?mxConstants.DIALECT_MIXEDHTML:mxConstants.DIALECT_SVG;this.shape.init(this.graph.getView().getOverlayPane());this.shape.pointerEvents=!1;this.shape.setCursor(mxConstants.CURSOR_MOVABLE_EDGE);
-mxEvent.redirectMouseEvents(this.shape.node,this.graph,this.state);this.preferHtml=null!=this.state.text&&this.state.text.node.parentNode==this.graph.container;if(!this.preferHtml){var a=this.state.getVisibleTerminalState(!0);null!=a&&(this.preferHtml=null!=a.text&&a.text.node.parentNode==this.graph.container);this.preferHtml||(a=this.state.getVisibleTerminalState(!1),null!=a&&(this.preferHtml=null!=a.text&&a.text.node.parentNode==this.graph.container))}this.parentHighlightEnabled&&(a=this.graph.model.getParent(this.state.cell),
-this.graph.model.isVertex(a)&&(a=this.graph.view.getState(a),null!=a&&(this.parentHighlight=this.createParentHighlightShape(a),this.parentHighlight.dialect=this.graph.dialect!=mxConstants.DIALECT_SVG?mxConstants.DIALECT_VML:mxConstants.DIALECT_SVG,this.parentHighlight.pointerEvents=!1,this.parentHighlight.rotation=Number(a.style[mxConstants.STYLE_ROTATION]||"0"),this.parentHighlight.init(this.graph.getView().getOverlayPane()))));if(this.graph.getSelectionCount()<mxGraphHandler.prototype.maxCells||
-0>=mxGraphHandler.prototype.maxCells)this.bends=this.createBends(),this.isVirtualBendsEnabled()&&(this.virtualBends=this.createVirtualBends());this.label=new mxPoint(this.state.absoluteOffset.x,this.state.absoluteOffset.y);this.labelShape=this.createLabelHandleShape();this.initBend(this.labelShape);this.labelShape.setCursor(mxConstants.CURSOR_LABEL_HANDLE);this.customHandles=this.createCustomHandles();this.redraw()};mxEdgeHandler.prototype.createCustomHandles=function(){return null};
-mxEdgeHandler.prototype.isVirtualBendsEnabled=function(a){return this.virtualBendsEnabled&&(null==this.state.style[mxConstants.STYLE_EDGE]||this.state.style[mxConstants.STYLE_EDGE]==mxConstants.NONE||1==this.state.style[mxConstants.STYLE_NOEDGESTYLE])&&"arrow"!=mxUtils.getValue(this.state.style,mxConstants.STYLE_SHAPE,null)};mxEdgeHandler.prototype.isAddPointEvent=function(a){return mxEvent.isShiftDown(a)};mxEdgeHandler.prototype.isRemovePointEvent=function(a){return mxEvent.isShiftDown(a)};
-mxEdgeHandler.prototype.getSelectionPoints=function(a){return a.absolutePoints};mxEdgeHandler.prototype.createParentHighlightShape=function(a){a=new mxRectangleShape(a,null,this.getSelectionColor());a.strokewidth=this.getSelectionStrokeWidth();a.isDashed=this.isSelectionDashed();return a};mxEdgeHandler.prototype.createSelectionShape=function(a){a=new this.state.shape.constructor;a.outline=!0;a.apply(this.state);a.isDashed=this.isSelectionDashed();a.stroke=this.getSelectionColor();a.isShadow=!1;return a};
-mxEdgeHandler.prototype.getSelectionColor=function(){return mxConstants.EDGE_SELECTION_COLOR};mxEdgeHandler.prototype.getSelectionStrokeWidth=function(){return mxConstants.EDGE_SELECTION_STROKEWIDTH};mxEdgeHandler.prototype.isSelectionDashed=function(){return mxConstants.EDGE_SELECTION_DASHED};mxEdgeHandler.prototype.isConnectableCell=function(a){return!0};mxEdgeHandler.prototype.getCellAt=function(a,b){return this.outlineConnect?null:this.graph.getCellAt(a,b)};
-mxEdgeHandler.prototype.createMarker=function(){var a=new mxCellMarker(this.graph),b=this;a.getCell=function(a){var c=mxCellMarker.prototype.getCell.apply(this,arguments);c!=b.state.cell&&null!=c||null==b.currentPoint||(c=b.graph.getCellAt(b.currentPoint.x,b.currentPoint.y));if(null!=c&&!this.graph.isCellConnectable(c)){var e=this.graph.getModel().getParent(c);this.graph.getModel().isVertex(e)&&this.graph.isCellConnectable(e)&&(c=e)}e=b.graph.getModel();if(this.graph.isSwimlane(c)&&null!=b.currentPoint&&
-this.graph.hitsSwimlaneContent(c,b.currentPoint.x,b.currentPoint.y)||!b.isConnectableCell(c)||c==b.state.cell||null!=c&&!b.graph.connectableEdges&&e.isEdge(c)||e.isAncestor(b.state.cell,c))c=null;this.graph.isCellConnectable(c)||(c=null);return c};a.isValidState=function(a){var c=b.graph.getModel(),c=b.graph.view.getTerminalPort(a,b.graph.view.getState(c.getTerminal(b.state.cell,!b.isSource)),!b.isSource),c=null!=c?c.cell:null;b.error=b.validateConnection(b.isSource?a.cell:c,b.isSource?c:a.cell);
-return null==b.error};return a};mxEdgeHandler.prototype.validateConnection=function(a,b){return this.graph.getEdgeValidationError(this.state.cell,a,b)};
-mxEdgeHandler.prototype.createBends=function(){for(var a=this.state.cell,b=[],c=0;c<this.abspoints.length;c++)if(this.isHandleVisible(c)){var d=c==this.abspoints.length-1,e=0==c||d;(e||this.graph.isCellBendable(a))&&mxUtils.bind(this,function(a){var d=this.createHandleShape(a);this.initBend(d,mxUtils.bind(this,mxUtils.bind(this,function(){this.dblClickRemoveEnabled&&this.removePoint(this.state,a)})));this.isHandleEnabled(c)&&d.setCursor(e?mxConstants.CURSOR_TERMINAL_HANDLE:mxConstants.CURSOR_BEND_HANDLE);
-b.push(d);e||(this.points.push(new mxPoint(0,0)),d.node.style.visibility="hidden")})(c)}return b};mxEdgeHandler.prototype.createVirtualBends=function(){var a=[];if(this.graph.isCellBendable(this.state.cell))for(var b=1;b<this.abspoints.length;b++)mxUtils.bind(this,function(b){this.initBend(b);b.setCursor(mxConstants.CURSOR_VIRTUAL_BEND_HANDLE);a.push(b)})(this.createHandleShape());return a};mxEdgeHandler.prototype.isHandleEnabled=function(a){return!0};
-mxEdgeHandler.prototype.isHandleVisible=function(a){var b=this.state.getVisibleTerminalState(!0),c=this.state.getVisibleTerminalState(!1),d=this.graph.getCellGeometry(this.state.cell);return(null!=d?this.graph.view.getEdgeStyle(this.state,d.points,b,c):null)!=mxEdgeStyle.EntityRelation||0==a||a==this.abspoints.length-1};
-mxEdgeHandler.prototype.createHandleShape=function(a){if(null!=this.handleImage)return a=new mxImageShape(new mxRectangle(0,0,this.handleImage.width,this.handleImage.height),this.handleImage.src),a.preserveImageAspect=!1,a;a=mxConstants.HANDLE_SIZE;this.preferHtml&&--a;return new mxRectangleShape(new mxRectangle(0,0,a,a),mxConstants.HANDLE_FILLCOLOR,mxConstants.HANDLE_STROKECOLOR)};
-mxEdgeHandler.prototype.createLabelHandleShape=function(){if(null!=this.labelHandleImage){var a=new mxImageShape(new mxRectangle(0,0,this.labelHandleImage.width,this.labelHandleImage.height),this.labelHandleImage.src);a.preserveImageAspect=!1;return a}a=mxConstants.LABEL_HANDLE_SIZE;return new mxRectangleShape(new mxRectangle(0,0,a,a),mxConstants.LABEL_HANDLE_FILLCOLOR,mxConstants.HANDLE_STROKECOLOR)};
-mxEdgeHandler.prototype.initBend=function(a,b){this.preferHtml?(a.dialect=mxConstants.DIALECT_STRICTHTML,a.init(this.graph.container)):(a.dialect=this.graph.dialect!=mxConstants.DIALECT_SVG?mxConstants.DIALECT_MIXEDHTML:mxConstants.DIALECT_SVG,a.init(this.graph.getView().getOverlayPane()));mxEvent.redirectMouseEvents(a.node,this.graph,this.state,null,null,null,b);(mxClient.IS_QUIRKS||8==document.documentMode)&&mxEvent.addListener(a.node,"dragstart",function(a){mxEvent.consume(a);return!1});mxClient.IS_TOUCH&&
-a.node.setAttribute("pointer-events","none")};
-mxEdgeHandler.prototype.getHandleForEvent=function(a){function b(b){if(null!=b&&"none"!=b.node.style.display&&"hidden"!=b.node.style.visibility&&(a.isSource(b)||null!=d&&mxUtils.intersects(b.bounds,d))){var c=a.getGraphX()-b.bounds.getCenterX();b=a.getGraphY()-b.bounds.getCenterY();c=c*c+b*b;if(null==e||c<=e)return e=c,!0}return!1}var c=mxEvent.isMouseEvent(a.getEvent())?1:this.tolerance,d=this.allowHandleBoundsCheck&&(mxClient.IS_IE||0<c)?new mxRectangle(a.getGraphX()-c,a.getGraphY()-c,2*c,2*c):
-null,e=null,c=null;if(null!=this.customHandles&&this.isCustomHandleEvent(a))for(var f=this.customHandles.length-1;0<=f;f--)if(b(this.customHandles[f].shape))return mxEvent.CUSTOM_HANDLE-f;if(a.isSource(this.state.text)||b(this.labelShape))c=mxEvent.LABEL_HANDLE;if(null!=this.bends)for(f=0;f<this.bends.length;f++)b(this.bends[f])&&(c=f);if(null!=this.virtualBends&&this.isAddVirtualBendEvent(a))for(f=0;f<this.virtualBends.length;f++)b(this.virtualBends[f])&&(c=mxEvent.VIRTUAL_HANDLE-f);return c};
-mxEdgeHandler.prototype.isAddVirtualBendEvent=function(a){return!0};mxEdgeHandler.prototype.isCustomHandleEvent=function(a){return!0};
-mxEdgeHandler.prototype.mouseDown=function(a,b){var c=this.getHandleForEvent(b);if(null!=this.bends&&null!=this.bends[c]){var d=this.bends[c].bounds;this.snapPoint=new mxPoint(d.getCenterX(),d.getCenterY())}if(this.addEnabled&&null==c&&this.isAddPointEvent(b.getEvent()))this.addPoint(this.state,b.getEvent()),b.consume();else if(null!=c&&!b.isConsumed()&&this.graph.isEnabled()){if(this.removeEnabled&&this.isRemovePointEvent(b.getEvent()))this.removePoint(this.state,c);else if(c!=mxEvent.LABEL_HANDLE||
-this.graph.isLabelMovable(b.getCell()))c<=mxEvent.VIRTUAL_HANDLE&&mxUtils.setOpacity(this.virtualBends[mxEvent.VIRTUAL_HANDLE-c].node,100),this.start(b.getX(),b.getY(),c);b.consume()}};
-mxEdgeHandler.prototype.start=function(a,b,c){this.startX=a;this.startY=b;this.isSource=null==this.bends?!1:0==c;this.isTarget=null==this.bends?!1:c==this.bends.length-1;this.isLabel=c==mxEvent.LABEL_HANDLE;if(this.isSource||this.isTarget){if(a=this.state.cell,b=this.graph.model.getTerminal(a,this.isSource),null==b&&this.graph.isTerminalPointMovable(a,this.isSource)||null!=b&&this.graph.isCellDisconnectable(a,b,this.isSource))this.index=c}else this.index=c;if(this.index<=mxEvent.CUSTOM_HANDLE&&this.index>
-mxEvent.VIRTUAL_HANDLE&&null!=this.customHandles)for(c=0;c<this.customHandles.length;c++)c!=mxEvent.CUSTOM_HANDLE-this.index&&this.customHandles[c].setVisible(!1)};mxEdgeHandler.prototype.clonePreviewState=function(a,b){return this.state.clone()};mxEdgeHandler.prototype.getSnapToTerminalTolerance=function(){return this.graph.gridSize*this.graph.view.scale/2};mxEdgeHandler.prototype.updateHint=function(a,b){};mxEdgeHandler.prototype.removeHint=function(){};mxEdgeHandler.prototype.roundLength=function(a){return Math.round(a)};
-mxEdgeHandler.prototype.isSnapToTerminalsEvent=function(a){return this.snapToTerminals&&!mxEvent.isAltDown(a.getEvent())};
-mxEdgeHandler.prototype.getPointForEvent=function(a){var b=this.graph.getView(),c=b.scale,d=new mxPoint(this.roundLength(a.getGraphX()/c)*c,this.roundLength(a.getGraphY()/c)*c),e=this.getSnapToTerminalTolerance(),f=!1,g=!1;if(0<e&&this.isSnapToTerminalsEvent(a)){var k=function(a){null!=a&&l.call(this,new mxPoint(b.getRoutingCenterX(a),b.getRoutingCenterY(a)))},l=function(a){if(null!=a){var b=a.x;Math.abs(d.x-b)<e&&(d.x=b,f=!0);a=a.y;Math.abs(d.y-a)<e&&(d.y=a,g=!0)}};k.call(this,this.state.getVisibleTerminalState(!0));
-k.call(this,this.state.getVisibleTerminalState(!1));if(null!=this.state.absolutePoints)for(k=0;k<this.state.absolutePoints.length;k++)l.call(this,this.state.absolutePoints[k])}this.graph.isGridEnabledEvent(a.getEvent())&&(a=b.translate,f||(d.x=(this.graph.snap(d.x/c-a.x)+a.x)*c),g||(d.y=(this.graph.snap(d.y/c-a.y)+a.y)*c));return d};
-mxEdgeHandler.prototype.getPreviewTerminalState=function(a){this.constraintHandler.update(a,this.isSource,!0,a.isSource(this.marker.highlight.shape)?null:this.currentPoint);if(null!=this.constraintHandler.currentFocus&&null!=this.constraintHandler.currentConstraint)return null!=this.marker.highlight&&null!=this.marker.highlight.state&&this.marker.highlight.state.cell==this.constraintHandler.currentFocus.cell?"transparent"!=this.marker.highlight.shape.stroke&&(this.marker.highlight.shape.stroke="transparent",
-this.marker.highlight.repaint()):this.marker.markCell(this.constraintHandler.currentFocus.cell,"transparent"),a=this.graph.getModel(),a=this.graph.view.getTerminalPort(this.state,this.graph.view.getState(a.getTerminal(this.state.cell,!this.isSource)),!this.isSource),a=null!=a?a.cell:null,this.error=this.validateConnection(this.isSource?this.constraintHandler.currentFocus.cell:a,this.isSource?a:this.constraintHandler.currentFocus.cell),a=null,null==this.error?a=this.constraintHandler.currentFocus:
-this.constraintHandler.reset(),a;if(this.graph.isIgnoreTerminalEvent(a.getEvent()))return this.marker.reset(),null;this.marker.process(a);a=this.marker.getValidState();null!=a&&this.graph.isCellLocked(a.cell)&&this.marker.reset();return this.marker.getValidState()};
-mxEdgeHandler.prototype.getPreviewPoints=function(a,b){var c=this.graph.getCellGeometry(this.state.cell),c=null!=c.points?c.points.slice():null,d=new mxPoint(a.x,a.y),e=null;if(this.isSource||this.isTarget)this.graph.resetEdgesOnConnect&&(c=null);else if(this.convertPoint(d,!1),null==c)c=[d];else{this.index<=mxEvent.VIRTUAL_HANDLE&&c.splice(mxEvent.VIRTUAL_HANDLE-this.index,0,d);if(!this.isSource&&!this.isTarget){for(var f=0;f<this.bends.length;f++)if(f!=this.index){var g=this.bends[f];null!=g&&mxUtils.contains(g.bounds,
-a.x,a.y)&&(this.index<=mxEvent.VIRTUAL_HANDLE?c.splice(mxEvent.VIRTUAL_HANDLE-this.index,1):c.splice(this.index-1,1),e=c)}if(null==e&&this.straightRemoveEnabled&&(null==b||!mxEvent.isAltDown(b.getEvent()))){f=this.graph.tolerance*this.graph.tolerance;g=this.state.absolutePoints.slice();g[this.index]=a;var k=this.state.getVisibleTerminalState(!0);if(null!=k){var l=this.graph.getConnectionConstraint(this.state,k,!0);if(null==l||null==this.graph.getConnectionPoint(k,l))g[0]=new mxPoint(k.view.getRoutingCenterX(k),
-k.view.getRoutingCenterY(k))}k=this.state.getVisibleTerminalState(!1);null!=k&&(l=this.graph.getConnectionConstraint(this.state,k,!1),null==l||null==this.graph.getConnectionPoint(k,l))&&(g[g.length-1]=new mxPoint(k.view.getRoutingCenterX(k),k.view.getRoutingCenterY(k)));l=this.index;0<l&&l<g.length-1&&mxUtils.ptSegDistSq(g[l-1].x,g[l-1].y,g[l+1].x,g[l+1].y,a.x,a.y)<f&&(c.splice(l-1,1),e=c)}}null==e&&this.index>mxEvent.VIRTUAL_HANDLE&&(c[this.index-1]=d)}return null!=e?e:c};
-mxEdgeHandler.prototype.isOutlineConnectEvent=function(a){var b=mxUtils.getOffset(this.graph.container),c=a.getEvent(),d=mxEvent.getClientX(c),c=mxEvent.getClientY(c),e=document.documentElement,f=this.currentPoint.x-this.graph.container.scrollLeft+b.x-((window.pageXOffset||e.scrollLeft)-(e.clientLeft||0)),b=this.currentPoint.y-this.graph.container.scrollTop+b.y-((window.pageYOffset||e.scrollTop)-(e.clientTop||0));return this.outlineConnect&&!mxEvent.isShiftDown(a.getEvent())&&(a.isSource(this.marker.highlight.shape)||
-mxEvent.isAltDown(a.getEvent())&&null!=a.getState()||this.marker.highlight.isHighlightAt(d,c)||(f!=d||b!=c)&&null==a.getState()&&this.marker.highlight.isHighlightAt(f,b))};
-mxEdgeHandler.prototype.updatePreviewState=function(a,b,c,d,e){var f=this.isSource?c:this.state.getVisibleTerminalState(!0),g=this.isTarget?c:this.state.getVisibleTerminalState(!1),k=this.graph.getConnectionConstraint(a,f,!0),l=this.graph.getConnectionConstraint(a,g,!1),m=this.constraintHandler.currentConstraint;null==m&&e&&(null!=c?(d.isSource(this.marker.highlight.shape)&&(b=new mxPoint(d.getGraphX(),d.getGraphY())),m=this.graph.getOutlineConstraint(b,c,d),this.constraintHandler.setFocus(d,c,this.isSource),
-this.constraintHandler.currentConstraint=m,this.constraintHandler.currentPoint=b):m=new mxConnectionConstraint);if(this.outlineConnect&&null!=this.marker.highlight&&null!=this.marker.highlight.shape){var n=this.graph.view.scale;null!=this.constraintHandler.currentConstraint&&null!=this.constraintHandler.currentFocus?(this.marker.highlight.shape.stroke=e?mxConstants.OUTLINE_HIGHLIGHT_COLOR:"transparent",this.marker.highlight.shape.strokewidth=mxConstants.OUTLINE_HIGHLIGHT_STROKEWIDTH/n/n,this.marker.highlight.repaint()):
-this.marker.hasValidState()&&(this.marker.highlight.shape.stroke=this.marker.getValidState()==d.getState()?mxConstants.DEFAULT_VALID_COLOR:"transparent",this.marker.highlight.shape.strokewidth=mxConstants.HIGHLIGHT_STROKEWIDTH/n/n,this.marker.highlight.repaint())}this.isSource?k=m:this.isTarget&&(l=m);if(this.isSource||this.isTarget)null!=m&&null!=m.point?(a.style[this.isSource?mxConstants.STYLE_EXIT_X:mxConstants.STYLE_ENTRY_X]=m.point.x,a.style[this.isSource?mxConstants.STYLE_EXIT_Y:mxConstants.STYLE_ENTRY_Y]=
-m.point.y):(delete a.style[this.isSource?mxConstants.STYLE_EXIT_X:mxConstants.STYLE_ENTRY_X],delete a.style[this.isSource?mxConstants.STYLE_EXIT_Y:mxConstants.STYLE_ENTRY_Y]);a.setVisibleTerminalState(f,!0);a.setVisibleTerminalState(g,!1);this.isSource&&null==f||a.view.updateFixedTerminalPoint(a,f,!0,k);this.isTarget&&null==g||a.view.updateFixedTerminalPoint(a,g,!1,l);(this.isSource||this.isTarget)&&null==c&&(a.setAbsoluteTerminalPoint(b,this.isSource),null==this.marker.getMarkedState()&&(this.error=
-this.graph.allowDanglingEdges?null:""));a.view.updatePoints(a,this.points,f,g);a.view.updateFloatingTerminalPoints(a,f,g)};
-mxEdgeHandler.prototype.mouseMove=function(a,b){if(null!=this.index&&null!=this.marker){this.currentPoint=this.getPointForEvent(b);this.error=null;!this.graph.isIgnoreTerminalEvent(b.getEvent())&&mxEvent.isShiftDown(b.getEvent())&&null!=this.snapPoint&&(Math.abs(this.snapPoint.x-this.currentPoint.x)<Math.abs(this.snapPoint.y-this.currentPoint.y)?this.currentPoint.x=this.snapPoint.x:this.currentPoint.y=this.snapPoint.y);if(this.index<=mxEvent.CUSTOM_HANDLE&&this.index>mxEvent.VIRTUAL_HANDLE)null!=
-this.customHandles&&this.customHandles[mxEvent.CUSTOM_HANDLE-this.index].processEvent(b);else if(this.isLabel)this.label.x=this.currentPoint.x,this.label.y=this.currentPoint.y;else{this.points=this.getPreviewPoints(this.currentPoint,b);var c=this.isSource||this.isTarget?this.getPreviewTerminalState(b):null;if(null!=this.constraintHandler.currentConstraint&&null!=this.constraintHandler.currentFocus&&null!=this.constraintHandler.currentPoint)this.currentPoint=this.constraintHandler.currentPoint.clone();
-else if(this.outlineConnect){var d=this.isSource||this.isTarget?this.isOutlineConnectEvent(b):!1;d?c=this.marker.highlight.state:null!=c&&c!=b.getState()&&null!=this.marker.highlight.shape&&(this.marker.highlight.shape.stroke="transparent",this.marker.highlight.repaint(),c=null)}null!=c&&this.graph.isCellLocked(c.cell)&&(c=null,this.marker.reset());var e=this.clonePreviewState(this.currentPoint,null!=c?c.cell:null);this.updatePreviewState(e,this.currentPoint,c,b,d);this.setPreviewColor(null==this.error?
-this.marker.validColor:this.marker.invalidColor);this.abspoints=e.absolutePoints;this.active=!0}this.updateHint(b,this.currentPoint);this.drawPreview();mxEvent.consume(b.getEvent());b.consume()}else mxClient.IS_IE&&null!=this.getHandleForEvent(b)&&b.consume(!1)};
-mxEdgeHandler.prototype.mouseUp=function(a,b){if(null!=this.index&&null!=this.marker){var c=this.state.cell;if(b.getX()!=this.startX||b.getY()!=this.startY){var d=!this.graph.isIgnoreTerminalEvent(b.getEvent())&&this.graph.isCloneEvent(b.getEvent())&&this.cloneEnabled&&this.graph.isCellsCloneable();if(null!=this.error)0<this.error.length&&this.graph.validationAlert(this.error);else if(this.index<=mxEvent.CUSTOM_HANDLE&&this.index>mxEvent.VIRTUAL_HANDLE){if(null!=this.customHandles){var e=this.graph.getModel();
-e.beginUpdate();try{this.customHandles[mxEvent.CUSTOM_HANDLE-this.index].execute()}finally{e.endUpdate()}}}else if(this.isLabel)this.moveLabel(this.state,this.label.x,this.label.y);else if(this.isSource||this.isTarget){var f=null;null!=this.constraintHandler.currentConstraint&&null!=this.constraintHandler.currentFocus&&(f=this.constraintHandler.currentFocus.cell);null==f&&this.marker.hasValidState()&&null!=this.marker.highlight&&null!=this.marker.highlight.shape&&"transparent"!=this.marker.highlight.shape.stroke&&
-"white"!=this.marker.highlight.shape.stroke&&(f=this.marker.validState.cell);if(null!=f){var e=this.graph.getModel(),g=e.getParent(c);e.beginUpdate();try{if(d){var k=e.getGeometry(c),d=this.graph.cloneCells([c])[0];e.add(g,d,e.getChildCount(g));null!=k&&(k=k.clone(),e.setGeometry(d,k));var l=e.getTerminal(c,!this.isSource);this.graph.connectCell(d,l,!this.isSource);c=d}c=this.connect(c,f,this.isSource,d,b)}finally{e.endUpdate()}}else this.graph.isAllowDanglingEdges()&&(e=this.abspoints[this.isSource?
-0:this.abspoints.length-1],e.x=this.roundLength(e.x/this.graph.view.scale-this.graph.view.translate.x),e.y=this.roundLength(e.y/this.graph.view.scale-this.graph.view.translate.y),f=this.graph.getView().getState(this.graph.getModel().getParent(c)),null!=f&&(e.x-=f.origin.x,e.y-=f.origin.y),e.x-=this.graph.panDx/this.graph.view.scale,e.y-=this.graph.panDy/this.graph.view.scale,c=this.changeTerminalPoint(c,e,this.isSource,d))}else this.active?c=this.changePoints(c,this.points,d):(this.graph.getView().invalidate(this.state.cell),
-this.graph.getView().validate(this.state.cell))}null!=this.marker&&(this.reset(),c!=this.state.cell&&this.graph.setSelectionCell(c));b.consume()}};
-mxEdgeHandler.prototype.reset=function(){this.active&&this.refresh();this.snapPoint=this.points=this.label=this.index=this.error=null;this.active=this.isTarget=this.isSource=this.isLabel=!1;if(this.livePreview&&null!=this.sizers)for(var a=0;a<this.sizers.length;a++)null!=this.sizers[a]&&(this.sizers[a].node.style.display="");null!=this.marker&&this.marker.reset();null!=this.constraintHandler&&this.constraintHandler.reset();if(null!=this.customHandles)for(a=0;a<this.customHandles.length;a++)this.customHandles[a].reset();
-this.setPreviewColor(mxConstants.EDGE_SELECTION_COLOR);this.removeHint();this.redraw()};mxEdgeHandler.prototype.setPreviewColor=function(a){null!=this.shape&&(this.shape.stroke=a)};
-mxEdgeHandler.prototype.convertPoint=function(a,b){var c=this.graph.getView().getScale(),d=this.graph.getView().getTranslate();b&&(a.x=this.graph.snap(a.x),a.y=this.graph.snap(a.y));a.x=Math.round(a.x/c-d.x);a.y=Math.round(a.y/c-d.y);c=this.graph.getView().getState(this.graph.getModel().getParent(this.state.cell));null!=c&&(a.x-=c.origin.x,a.y-=c.origin.y);return a};
-mxEdgeHandler.prototype.moveLabel=function(a,b,c){var d=this.graph.getModel(),e=d.getGeometry(a.cell);if(null!=e){var f=this.graph.getView().scale,e=e.clone();if(e.relative){var g=this.graph.getView().getRelativePoint(a,b,c);e.x=Math.round(1E4*g.x)/1E4;e.y=Math.round(g.y);e.offset=new mxPoint(0,0);g=this.graph.view.getPoint(a,e);e.offset=new mxPoint(Math.round((b-g.x)/f),Math.round((c-g.y)/f))}else{var k=a.absolutePoints,g=k[0],k=k[k.length-1];null!=g&&null!=k&&(e.offset=new mxPoint(Math.round((b-
-(g.x+(k.x-g.x)/2))/f),Math.round((c-(g.y+(k.y-g.y)/2))/f)),e.x=0,e.y=0)}d.setGeometry(a.cell,e)}};mxEdgeHandler.prototype.connect=function(a,b,c,d,e){d=this.graph.getModel();d.getParent(a);d.beginUpdate();try{var f=this.constraintHandler.currentConstraint;null==f&&(f=new mxConnectionConstraint);this.graph.connectCell(a,b,c,f)}finally{d.endUpdate()}return a};
-mxEdgeHandler.prototype.changeTerminalPoint=function(a,b,c,d){var e=this.graph.getModel();e.beginUpdate();try{if(d){var f=e.getParent(a),g=e.getTerminal(a,!c);a=this.graph.cloneCells([a])[0];e.add(f,a,e.getChildCount(f));e.setTerminal(a,g,!c)}var k=e.getGeometry(a);null!=k&&(k=k.clone(),k.setTerminalPoint(b,c),e.setGeometry(a,k),this.graph.connectCell(a,null,c,new mxConnectionConstraint))}finally{e.endUpdate()}return a};
-mxEdgeHandler.prototype.changePoints=function(a,b,c){var d=this.graph.getModel();d.beginUpdate();try{if(c){var e=d.getParent(a),f=d.getTerminal(a,!0),g=d.getTerminal(a,!1);a=this.graph.cloneCells([a])[0];d.add(e,a,d.getChildCount(e));d.setTerminal(a,f,!0);d.setTerminal(a,g,!1)}var k=d.getGeometry(a);null!=k&&(k=k.clone(),k.points=b,d.setGeometry(a,k))}finally{d.endUpdate()}return a};
-mxEdgeHandler.prototype.addPoint=function(a,b){var c=mxUtils.convertPoint(this.graph.container,mxEvent.getClientX(b),mxEvent.getClientY(b)),d=this.graph.isGridEnabledEvent(b);this.convertPoint(c,d);this.addPointAt(a,c.x,c.y);mxEvent.consume(b)};
-mxEdgeHandler.prototype.addPointAt=function(a,b,c){var d=this.graph.getCellGeometry(a.cell);b=new mxPoint(b,c);if(null!=d){var d=d.clone(),e=this.graph.view.translate;c=this.graph.view.scale;var e=new mxPoint(e.x*c,e.y*c),f=this.graph.model.getParent(this.state.cell);this.graph.model.isVertex(f)&&(e=this.graph.view.getState(f),e=new mxPoint(e.x,e.y));c=mxUtils.findNearestSegment(a,b.x*c+e.x,b.y*c+e.y);null==d.points?d.points=[b]:d.points.splice(c,0,b);this.graph.getModel().setGeometry(a.cell,d);this.refresh();
-this.redraw()}};mxEdgeHandler.prototype.removePoint=function(a,b){if(0<b&&b<this.abspoints.length-1){var c=this.graph.getCellGeometry(this.state.cell);null!=c&&null!=c.points&&(c=c.clone(),c.points.splice(b-1,1),this.graph.getModel().setGeometry(a.cell,c),this.refresh(),this.redraw())}};
-mxEdgeHandler.prototype.getHandleFillColor=function(a){a=0==a;var b=this.state.cell,c=this.graph.getModel().getTerminal(b,a),d=mxConstants.HANDLE_FILLCOLOR;null!=c&&!this.graph.isCellDisconnectable(b,c,a)||null==c&&!this.graph.isTerminalPointMovable(b,a)?d=mxConstants.LOCKED_HANDLE_FILLCOLOR:null!=c&&this.graph.isCellDisconnectable(b,c,a)&&(d=mxConstants.CONNECT_HANDLE_FILLCOLOR);return d};
-mxEdgeHandler.prototype.redraw=function(){this.abspoints=this.state.absolutePoints.slice();this.redrawHandles();var a=this.graph.getModel().getGeometry(this.state.cell).points;if(null!=this.bends&&0<this.bends.length&&null!=a){null==this.points&&(this.points=[]);for(var b=1;b<this.bends.length-1;b++)null!=this.bends[b]&&null!=this.abspoints[b]&&(this.points[b-1]=a[b-1])}this.drawPreview()};
-mxEdgeHandler.prototype.redrawHandles=function(){var a=this.state.cell,b=this.labelShape.bounds;this.label=new mxPoint(this.state.absoluteOffset.x,this.state.absoluteOffset.y);this.labelShape.bounds=new mxRectangle(Math.round(this.label.x-b.width/2),Math.round(this.label.y-b.height/2),b.width,b.height);b=this.graph.getLabel(a);this.labelShape.visible=null!=b&&0<b.length&&this.graph.isLabelMovable(a);if(null!=this.bends&&0<this.bends.length){var c=this.abspoints.length-1,a=this.abspoints[0],d=a.x,
-e=a.y,b=this.bends[0].bounds;this.bends[0].bounds=new mxRectangle(Math.floor(d-b.width/2),Math.floor(e-b.height/2),b.width,b.height);this.bends[0].fill=this.getHandleFillColor(0);this.bends[0].redraw();this.manageLabelHandle&&this.checkLabelHandle(this.bends[0].bounds);var c=this.abspoints[c],d=c.x,e=c.y,f=this.bends.length-1,b=this.bends[f].bounds;this.bends[f].bounds=new mxRectangle(Math.floor(d-b.width/2),Math.floor(e-b.height/2),b.width,b.height);this.bends[f].fill=this.getHandleFillColor(f);
-this.bends[f].redraw();this.manageLabelHandle&&this.checkLabelHandle(this.bends[f].bounds);this.redrawInnerBends(a,c)}if(null!=this.abspoints&&null!=this.virtualBends&&0<this.virtualBends.length)for(a=this.abspoints[0],c=0;c<this.virtualBends.length;c++)null!=this.virtualBends[c]&&null!=this.abspoints[c+1]&&(d=this.abspoints[c+1],b=this.virtualBends[c],b.bounds=new mxRectangle(Math.floor(a.x+(d.x-a.x)/2-b.bounds.width/2),Math.floor(a.y+(d.y-a.y)/2-b.bounds.height/2),b.bounds.width,b.bounds.height),
-b.redraw(),mxUtils.setOpacity(b.node,this.virtualBendOpacity),a=d,this.manageLabelHandle&&this.checkLabelHandle(b.bounds));null!=this.labelShape&&this.labelShape.redraw();if(null!=this.customHandles)for(c=0;c<this.customHandles.length;c++)this.customHandles[c].redraw()};
-mxEdgeHandler.prototype.setHandlesVisible=function(a){if(null!=this.bends)for(var b=0;b<this.bends.length;b++)this.bends[b].node.style.display=a?"":"none";if(null!=this.virtualBends)for(b=0;b<this.virtualBends.length;b++)this.virtualBends[b].node.style.display=a?"":"none";null!=this.labelShape&&(this.labelShape.node.style.display=a?"":"none");if(null!=this.customHandles)for(b=0;b<this.customHandles.length;b++)this.customHandles[b].setVisible(a)};
-mxEdgeHandler.prototype.redrawInnerBends=function(a,b){for(var c=1;c<this.bends.length-1;c++)if(null!=this.bends[c])if(null!=this.abspoints[c]){var d=this.abspoints[c].x,e=this.abspoints[c].y,f=this.bends[c].bounds;this.bends[c].node.style.visibility="visible";this.bends[c].bounds=new mxRectangle(Math.round(d-f.width/2),Math.round(e-f.height/2),f.width,f.height);this.manageLabelHandle?this.checkLabelHandle(this.bends[c].bounds):null==this.handleImage&&this.labelShape.visible&&mxUtils.intersects(this.bends[c].bounds,
-this.labelShape.bounds)&&(w=mxConstants.HANDLE_SIZE+3,h=mxConstants.HANDLE_SIZE+3,this.bends[c].bounds=new mxRectangle(Math.round(d-w/2),Math.round(e-h/2),w,h));this.bends[c].redraw()}else this.bends[c].destroy(),this.bends[c]=null};mxEdgeHandler.prototype.checkLabelHandle=function(a){if(null!=this.labelShape){var b=this.labelShape.bounds;mxUtils.intersects(a,b)&&(a.getCenterY()<b.getCenterY()?b.y=a.y+a.height:b.y=a.y-b.height)}};
-mxEdgeHandler.prototype.drawPreview=function(){if(this.isLabel){var a=this.labelShape.bounds,a=new mxRectangle(Math.round(this.label.x-a.width/2),Math.round(this.label.y-a.height/2),a.width,a.height);this.labelShape.bounds=a;this.labelShape.redraw()}else null!=this.shape&&(this.shape.apply(this.state),this.shape.points=this.abspoints,this.shape.scale=this.state.view.scale,this.shape.isDashed=this.isSelectionDashed(),this.shape.stroke=this.getSelectionColor(),this.shape.strokewidth=this.getSelectionStrokeWidth()/
-this.shape.scale/this.shape.scale,this.shape.isShadow=!1,this.shape.redraw());null!=this.parentHighlight&&this.parentHighlight.redraw()};
-mxEdgeHandler.prototype.refresh=function(){this.abspoints=this.getSelectionPoints(this.state);this.points=[];null!=this.shape&&(this.shape.points=this.abspoints);null!=this.bends&&(this.destroyBends(this.bends),this.bends=this.createBends());null!=this.virtualBends&&(this.destroyBends(this.virtualBends),this.virtualBends=this.createVirtualBends());null!=this.customHandles&&(this.destroyBends(this.customHandles),this.customHandles=this.createCustomHandles());null!=this.labelShape&&null!=this.labelShape.node&&
-null!=this.labelShape.node.parentNode&&this.labelShape.node.parentNode.appendChild(this.labelShape.node)};mxEdgeHandler.prototype.destroyBends=function(a){if(null!=a)for(var b=0;b<a.length;b++)null!=a[b]&&a[b].destroy()};
-mxEdgeHandler.prototype.destroy=function(){null!=this.escapeHandler&&(this.state.view.graph.removeListener(this.escapeHandler),this.escapeHandler=null);null!=this.marker&&(this.marker.destroy(),this.marker=null);null!=this.shape&&(this.shape.destroy(),this.shape=null);null!=this.parentHighlight&&(this.parentHighlight.destroy(),this.parentHighlight=null);null!=this.labelShape&&(this.labelShape.destroy(),this.labelShape=null);null!=this.constraintHandler&&(this.constraintHandler.destroy(),this.constraintHandler=
-null);this.destroyBends(this.virtualBends);this.virtualBends=null;this.destroyBends(this.customHandles);this.customHandles=null;this.destroyBends(this.bends);this.bends=null;this.removeHint()};function mxElbowEdgeHandler(a){mxEdgeHandler.call(this,a)}mxUtils.extend(mxElbowEdgeHandler,mxEdgeHandler);mxElbowEdgeHandler.prototype.flipEnabled=!0;mxElbowEdgeHandler.prototype.doubleClickOrientationResource="none"!=mxClient.language?"doubleClickOrientation":"";
-mxElbowEdgeHandler.prototype.createBends=function(){var a=[],b=this.createHandleShape(0);this.initBend(b);b.setCursor(mxConstants.CURSOR_TERMINAL_HANDLE);a.push(b);a.push(this.createVirtualBend(mxUtils.bind(this,function(a){!mxEvent.isConsumed(a)&&this.flipEnabled&&(this.graph.flipEdge(this.state.cell,a),mxEvent.consume(a))})));this.points.push(new mxPoint(0,0));b=this.createHandleShape(2);this.initBend(b);b.setCursor(mxConstants.CURSOR_TERMINAL_HANDLE);a.push(b);return a};
-mxElbowEdgeHandler.prototype.createVirtualBend=function(a){var b=this.createHandleShape();this.initBend(b,a);b.setCursor(this.getCursorForBend());this.graph.isCellBendable(this.state.cell)||(b.node.style.display="none");return b};
-mxElbowEdgeHandler.prototype.getCursorForBend=function(){return this.state.style[mxConstants.STYLE_EDGE]==mxEdgeStyle.TopToBottom||this.state.style[mxConstants.STYLE_EDGE]==mxConstants.EDGESTYLE_TOPTOBOTTOM||(this.state.style[mxConstants.STYLE_EDGE]==mxEdgeStyle.ElbowConnector||this.state.style[mxConstants.STYLE_EDGE]==mxConstants.EDGESTYLE_ELBOW)&&this.state.style[mxConstants.STYLE_ELBOW]==mxConstants.ELBOW_VERTICAL?"row-resize":"col-resize"};
-mxElbowEdgeHandler.prototype.getTooltipForNode=function(a){var b=null;null==this.bends||null==this.bends[1]||a!=this.bends[1].node&&a.parentNode!=this.bends[1].node||(b=this.doubleClickOrientationResource,b=mxResources.get(b)||b);return b};
-mxElbowEdgeHandler.prototype.convertPoint=function(a,b){var c=this.graph.getView().getScale(),d=this.graph.getView().getTranslate(),e=this.state.origin;b&&(a.x=this.graph.snap(a.x),a.y=this.graph.snap(a.y));a.x=Math.round(a.x/c-d.x-e.x);a.y=Math.round(a.y/c-d.y-e.y);return a};
-mxElbowEdgeHandler.prototype.redrawInnerBends=function(a,b){var c=this.graph.getModel().getGeometry(this.state.cell),d=this.state.absolutePoints,e=null;1<d.length?(a=d[1],b=d[d.length-2]):null!=c.points&&0<c.points.length&&(e=d[0]);e=null==e?new mxPoint(a.x+(b.x-a.x)/2,a.y+(b.y-a.y)/2):new mxPoint(this.graph.getView().scale*(e.x+this.graph.getView().translate.x+this.state.origin.x),this.graph.getView().scale*(e.y+this.graph.getView().translate.y+this.state.origin.y));d=this.bends[1].bounds;c=d.width;
-d=d.height;c=new mxRectangle(Math.round(e.x-c/2),Math.round(e.y-d/2),c,d);this.manageLabelHandle?this.checkLabelHandle(c):null==this.handleImage&&this.labelShape.visible&&mxUtils.intersects(c,this.labelShape.bounds)&&(c=mxConstants.HANDLE_SIZE+3,d=mxConstants.HANDLE_SIZE+3,c=new mxRectangle(Math.floor(e.x-c/2),Math.floor(e.y-d/2),c,d));this.bends[1].bounds=c;this.bends[1].redraw();this.manageLabelHandle&&this.checkLabelHandle(this.bends[1].bounds)};
-function mxEdgeSegmentHandler(a){mxEdgeHandler.call(this,a)}mxUtils.extend(mxEdgeSegmentHandler,mxElbowEdgeHandler);
-mxEdgeSegmentHandler.prototype.getCurrentPoints=function(){var a=this.state.absolutePoints;if(null!=a){var b=Math.max(1,this.graph.view.scale);if(2==a.length||3==a.length&&(Math.abs(a[0].x-a[1].x)<b&&Math.abs(a[1].x-a[2].x)<b||Math.abs(a[0].y-a[1].y)<b&&Math.abs(a[1].y-a[2].y)<b))var b=a[0].x+(a[a.length-1].x-a[0].x)/2,c=a[0].y+(a[a.length-1].y-a[0].y)/2,a=[a[0],new mxPoint(b,c),new mxPoint(b,c),a[a.length-1]]}return a};
-mxEdgeSegmentHandler.prototype.getPreviewPoints=function(a){if(this.isSource||this.isTarget)return mxElbowEdgeHandler.prototype.getPreviewPoints.apply(this,arguments);var b=this.getCurrentPoints(),c=this.convertPoint(b[0].clone(),!1);a=this.convertPoint(a.clone(),!1);for(var d=[],e=1;e<b.length;e++){var f=this.convertPoint(b[e].clone(),!1);e==this.index&&(0==Math.round(c.x-f.x)&&(c.x=a.x,f.x=a.x),0==Math.round(c.y-f.y)&&(c.y=a.y,f.y=a.y));e<b.length-1&&d.push(f);c=f}if(1==d.length){var b=this.state.getVisibleTerminalState(!0),
-c=this.state.getVisibleTerminalState(!1),f=this.state.view.getScale(),g=this.state.view.getTranslate(),e=d[0].x*f+g.x,f=d[0].y*f+g.y;if(null!=b&&mxUtils.contains(b,e,f)||null!=c&&mxUtils.contains(c,e,f))d=[a,a]}return d};
-mxEdgeSegmentHandler.prototype.updatePreviewState=function(a,b,c,d){mxEdgeHandler.prototype.updatePreviewState.apply(this,arguments);if(!this.isSource&&!this.isTarget){b=this.convertPoint(b.clone(),!1);for(var e=a.absolutePoints,f=e[0],g=e[1],k=[],l=2;l<e.length;l++){var m=e[l];0==Math.round(f.x-g.x)&&0==Math.round(g.x-m.x)||0==Math.round(f.y-g.y)&&0==Math.round(g.y-m.y)||k.push(this.convertPoint(g.clone(),!1));f=g;g=m}f=this.state.getVisibleTerminalState(!0);g=this.state.getVisibleTerminalState(!1);
-l=this.state.absolutePoints;if(0==k.length&&(0==Math.round(e[0].x-e[e.length-1].x)||0==Math.round(e[0].y-e[e.length-1].y)))k=[b,b];else if(5==e.length&&2==k.length&&null!=f&&null!=g&&null!=l&&0==Math.round(l[0].x-l[l.length-1].x)){var k=this.graph.getView(),l=k.getScale(),m=k.getTranslate(),e=k.getRoutingCenterY(f)/l-m.y,n=this.graph.getConnectionConstraint(a,f,!0);null!=n&&(n=this.graph.getConnectionPoint(f,n),null!=n&&(this.convertPoint(n,!1),e=n.y));k=k.getRoutingCenterY(g)/l-m.y;if(l=this.graph.getConnectionConstraint(a,
-g,!1))n=this.graph.getConnectionPoint(g,l),null!=n&&(this.convertPoint(n,!1),k=n.y);k=[new mxPoint(b.x,e),new mxPoint(b.x,k)]}this.points=k;a.view.updateFixedTerminalPoints(a,f,g);a.view.updatePoints(a,this.points,f,g);a.view.updateFloatingTerminalPoints(a,f,g)}};
-mxEdgeSegmentHandler.prototype.connect=function(a,b,c,d,e){var f=this.graph.getModel(),g=f.getGeometry(a),k=null;if(null!=g&&null!=g.points&&0<g.points.length)for(var l=this.abspoints,m=l[0],n=l[1],k=[],p=2;p<l.length;p++){var q=l[p];0==Math.round(m.x-n.x)&&0==Math.round(n.x-q.x)||0==Math.round(m.y-n.y)&&0==Math.round(n.y-q.y)||k.push(this.convertPoint(n.clone(),!1));m=n;n=q}f.beginUpdate();try{null!=k&&(g=f.getGeometry(a),null!=g&&(g=g.clone(),g.points=k,f.setGeometry(a,g))),a=mxEdgeHandler.prototype.connect.apply(this,
-arguments)}finally{f.endUpdate()}return a};mxEdgeSegmentHandler.prototype.getTooltipForNode=function(a){return null};mxEdgeSegmentHandler.prototype.start=function(a,b,c){mxEdgeHandler.prototype.start.apply(this,arguments);null==this.bends[c]||this.isSource||this.isTarget||mxUtils.setOpacity(this.bends[c].node,100)};
-mxEdgeSegmentHandler.prototype.createBends=function(){var a=[],b=this.createHandleShape(0);this.initBend(b);b.setCursor(mxConstants.CURSOR_TERMINAL_HANDLE);a.push(b);var c=this.getCurrentPoints();if(this.graph.isCellBendable(this.state.cell)){null==this.points&&(this.points=[]);for(var d=0;d<c.length-1;d++){b=this.createVirtualBend();a.push(b);var e=0==Math.round(c[d].x-c[d+1].x);0==Math.round(c[d].y-c[d+1].y)&&d<c.length-2&&(e=0==Math.round(c[d].x-c[d+2].x));b.setCursor(e?"col-resize":"row-resize");
-this.points.push(new mxPoint(0,0))}}b=this.createHandleShape(c.length);this.initBend(b);b.setCursor(mxConstants.CURSOR_TERMINAL_HANDLE);a.push(b);return a};mxEdgeSegmentHandler.prototype.redraw=function(){this.refresh();mxEdgeHandler.prototype.redraw.apply(this,arguments)};
-mxEdgeSegmentHandler.prototype.redrawInnerBends=function(a,b){if(this.graph.isCellBendable(this.state.cell)){var c=this.getCurrentPoints();if(null!=c&&1<c.length){var d=!1;if(4==c.length&&0==Math.round(c[1].x-c[2].x)&&0==Math.round(c[1].y-c[2].y))if(d=!0,0==Math.round(c[0].y-c[c.length-1].y)){var e=c[0].x+(c[c.length-1].x-c[0].x)/2;c[1]=new mxPoint(e,c[1].y);c[2]=new mxPoint(e,c[2].y)}else e=c[0].y+(c[c.length-1].y-c[0].y)/2,c[1]=new mxPoint(c[1].x,e),c[2]=new mxPoint(c[2].x,e);for(e=0;e<c.length-
-1;e++)if(null!=this.bends[e+1]){a=c[e];b=c[e+1];var f=new mxPoint(a.x+(b.x-a.x)/2,a.y+(b.y-a.y)/2),g=this.bends[e+1].bounds;this.bends[e+1].bounds=new mxRectangle(Math.floor(f.x-g.width/2),Math.floor(f.y-g.height/2),g.width,g.height);this.bends[e+1].redraw();this.manageLabelHandle&&this.checkLabelHandle(this.bends[e+1].bounds)}d&&(mxUtils.setOpacity(this.bends[1].node,this.virtualBendOpacity),mxUtils.setOpacity(this.bends[3].node,this.virtualBendOpacity))}}};
-function mxKeyHandler(a,b){null!=a&&(this.graph=a,this.target=b||document.documentElement,this.normalKeys=[],this.shiftKeys=[],this.controlKeys=[],this.controlShiftKeys=[],this.keydownHandler=mxUtils.bind(this,function(a){this.keyDown(a)}),mxEvent.addListener(this.target,"keydown",this.keydownHandler),mxClient.IS_IE&&mxEvent.addListener(window,"unload",mxUtils.bind(this,function(){this.destroy()})))}mxKeyHandler.prototype.graph=null;mxKeyHandler.prototype.target=null;
-mxKeyHandler.prototype.normalKeys=null;mxKeyHandler.prototype.shiftKeys=null;mxKeyHandler.prototype.controlKeys=null;mxKeyHandler.prototype.controlShiftKeys=null;mxKeyHandler.prototype.enabled=!0;mxKeyHandler.prototype.isEnabled=function(){return this.enabled};mxKeyHandler.prototype.setEnabled=function(a){this.enabled=a};mxKeyHandler.prototype.bindKey=function(a,b){this.normalKeys[a]=b};mxKeyHandler.prototype.bindShiftKey=function(a,b){this.shiftKeys[a]=b};
-mxKeyHandler.prototype.bindControlKey=function(a,b){this.controlKeys[a]=b};mxKeyHandler.prototype.bindControlShiftKey=function(a,b){this.controlShiftKeys[a]=b};mxKeyHandler.prototype.isControlDown=function(a){return mxEvent.isControlDown(a)};mxKeyHandler.prototype.getFunction=function(a){return null==a||mxEvent.isAltDown(a)?null:this.isControlDown(a)?mxEvent.isShiftDown(a)?this.controlShiftKeys[a.keyCode]:this.controlKeys[a.keyCode]:mxEvent.isShiftDown(a)?this.shiftKeys[a.keyCode]:this.normalKeys[a.keyCode]};
-mxKeyHandler.prototype.isGraphEvent=function(a){var b=mxEvent.getSource(a);return b==this.target||b.parentNode==this.target||null!=this.graph.cellEditor&&this.graph.cellEditor.isEventSource(a)?!0:mxUtils.isAncestorNode(this.graph.container,b)};mxKeyHandler.prototype.keyDown=function(a){if(this.isEnabledForEvent(a))if(27==a.keyCode)this.escape(a);else if(!this.isEventIgnored(a)){var b=this.getFunction(a);null!=b&&(b(a),mxEvent.consume(a))}};
-mxKeyHandler.prototype.isEnabledForEvent=function(a){return this.graph.isEnabled()&&!mxEvent.isConsumed(a)&&this.isGraphEvent(a)&&this.isEnabled()};mxKeyHandler.prototype.isEventIgnored=function(a){return this.graph.isEditing()};mxKeyHandler.prototype.escape=function(a){this.graph.isEscapeEnabled()&&this.graph.escape(a)};
-mxKeyHandler.prototype.destroy=function(){null!=this.target&&null!=this.keydownHandler&&(mxEvent.removeListener(this.target,"keydown",this.keydownHandler),this.keydownHandler=null);this.target=null};function mxTooltipHandler(a,b){null!=a&&(this.graph=a,this.delay=b||500,this.graph.addMouseListener(this))}mxTooltipHandler.prototype.zIndex=10005;mxTooltipHandler.prototype.graph=null;mxTooltipHandler.prototype.delay=null;mxTooltipHandler.prototype.ignoreTouchEvents=!0;
-mxTooltipHandler.prototype.hideOnHover=!1;mxTooltipHandler.prototype.destroyed=!1;mxTooltipHandler.prototype.enabled=!0;mxTooltipHandler.prototype.isEnabled=function(){return this.enabled};mxTooltipHandler.prototype.setEnabled=function(a){this.enabled=a};mxTooltipHandler.prototype.isHideOnHover=function(){return this.hideOnHover};mxTooltipHandler.prototype.setHideOnHover=function(a){this.hideOnHover=a};
-mxTooltipHandler.prototype.init=function(){null!=document.body&&(this.div=document.createElement("div"),this.div.className="mxTooltip",this.div.style.visibility="hidden",document.body.appendChild(this.div),mxEvent.addGestureListeners(this.div,mxUtils.bind(this,function(a){this.hideTooltip()})))};mxTooltipHandler.prototype.getStateForEvent=function(a){return a.getState()};mxTooltipHandler.prototype.mouseDown=function(a,b){this.reset(b,!1);this.hideTooltip()};
-mxTooltipHandler.prototype.mouseMove=function(a,b){if(b.getX()!=this.lastX||b.getY()!=this.lastY){this.reset(b,!0);var c=this.getStateForEvent(b);(this.isHideOnHover()||c!=this.state||b.getSource()!=this.node&&(!this.stateSource||null!=c&&this.stateSource==(b.isSource(c.shape)||!b.isSource(c.text))))&&this.hideTooltip()}this.lastX=b.getX();this.lastY=b.getY()};mxTooltipHandler.prototype.mouseUp=function(a,b){this.reset(b,!0);this.hideTooltip()};
-mxTooltipHandler.prototype.resetTimer=function(){null!=this.thread&&(window.clearTimeout(this.thread),this.thread=null)};
-mxTooltipHandler.prototype.reset=function(a,b,c){if(!this.ignoreTouchEvents||mxEvent.isMouseEvent(a.getEvent()))if(this.resetTimer(),c=null!=c?c:this.getStateForEvent(a),b&&this.isEnabled()&&null!=c&&(null==this.div||"hidden"==this.div.style.visibility)){var d=a.getSource(),e=a.getX(),f=a.getY(),g=a.isSource(c.shape)||a.isSource(c.text);this.thread=window.setTimeout(mxUtils.bind(this,function(){if(!this.graph.isEditing()&&!this.graph.popupMenuHandler.isMenuShowing()&&!this.graph.isMouseDown){var a=
-this.graph.getTooltip(c,d,e,f);this.show(a,e,f);this.state=c;this.node=d;this.stateSource=g}}),this.delay)}};mxTooltipHandler.prototype.hide=function(){this.resetTimer();this.hideTooltip()};mxTooltipHandler.prototype.hideTooltip=function(){null!=this.div&&(this.div.style.visibility="hidden",this.div.innerHTML="")};
-mxTooltipHandler.prototype.show=function(a,b,c){if(!this.destroyed&&null!=a&&0<a.length){null==this.div&&this.init();var d=mxUtils.getScrollOrigin();this.div.style.zIndex=this.zIndex;this.div.style.left=b+d.x+"px";this.div.style.top=c+mxConstants.TOOLTIP_VERTICAL_OFFSET+d.y+"px";mxUtils.isNode(a)?(this.div.innerHTML="",this.div.appendChild(a)):this.div.innerHTML=a.replace(/\n/g,"<br>");this.div.style.visibility="";mxUtils.fit(this.div)}};
-mxTooltipHandler.prototype.destroy=function(){this.destroyed||(this.graph.removeMouseListener(this),mxEvent.release(this.div),null!=this.div&&null!=this.div.parentNode&&this.div.parentNode.removeChild(this.div),this.destroyed=!0,this.div=null)};function mxCellTracker(a,b,c){mxCellMarker.call(this,a,b);this.graph.addMouseListener(this);null!=c&&(this.getCell=c);mxClient.IS_IE&&mxEvent.addListener(window,"unload",mxUtils.bind(this,function(){this.destroy()}))}mxUtils.extend(mxCellTracker,mxCellMarker);
-mxCellTracker.prototype.mouseDown=function(a,b){};mxCellTracker.prototype.mouseMove=function(a,b){this.isEnabled()&&this.process(b)};mxCellTracker.prototype.mouseUp=function(a,b){};mxCellTracker.prototype.destroy=function(){this.destroyed||(this.destroyed=!0,this.graph.removeMouseListener(this),mxCellMarker.prototype.destroy.apply(this))};
-function mxCellHighlight(a,b,c,d){null!=a&&(this.graph=a,this.highlightColor=null!=b?b:mxConstants.DEFAULT_VALID_COLOR,this.strokeWidth=null!=c?c:mxConstants.HIGHLIGHT_STROKEWIDTH,this.dashed=null!=d?d:!1,this.opacity=mxConstants.HIGHLIGHT_OPACITY,this.repaintHandler=mxUtils.bind(this,function(){if(null!=this.state){var a=this.graph.view.getState(this.state.cell);null==a?this.hide():(this.state=a,this.repaint())}}),this.graph.getView().addListener(mxEvent.SCALE,this.repaintHandler),this.graph.getView().addListener(mxEvent.TRANSLATE,
-this.repaintHandler),this.graph.getView().addListener(mxEvent.SCALE_AND_TRANSLATE,this.repaintHandler),this.graph.getModel().addListener(mxEvent.CHANGE,this.repaintHandler),this.resetHandler=mxUtils.bind(this,function(){this.hide()}),this.graph.getView().addListener(mxEvent.DOWN,this.resetHandler),this.graph.getView().addListener(mxEvent.UP,this.resetHandler))}mxCellHighlight.prototype.keepOnTop=!1;mxCellHighlight.prototype.graph=!0;mxCellHighlight.prototype.state=null;
-mxCellHighlight.prototype.spacing=2;mxCellHighlight.prototype.resetHandler=null;mxCellHighlight.prototype.setHighlightColor=function(a){this.highlightColor=a;null!=this.shape&&(this.shape.stroke=a)};mxCellHighlight.prototype.drawHighlight=function(){this.shape=this.createShape();this.repaint();this.keepOnTop||this.shape.node.parentNode.firstChild==this.shape.node||this.shape.node.parentNode.insertBefore(this.shape.node,this.shape.node.parentNode.firstChild)};
-mxCellHighlight.prototype.createShape=function(){var a=this.graph.cellRenderer.createShape(this.state);a.svgStrokeTolerance=this.graph.tolerance;a.points=this.state.absolutePoints;a.apply(this.state);a.stroke=this.highlightColor;a.opacity=this.opacity;a.isDashed=this.dashed;a.isShadow=!1;a.dialect=this.graph.dialect!=mxConstants.DIALECT_SVG?mxConstants.DIALECT_VML:mxConstants.DIALECT_SVG;a.init(this.graph.getView().getOverlayPane());mxEvent.redirectMouseEvents(a.node,this.graph,this.state);this.graph.dialect!=
-mxConstants.DIALECT_SVG?a.pointerEvents=!1:a.svgPointerEvents="stroke";return a};mxCellHighlight.prototype.getStrokeWidth=function(a){return this.strokeWidth};
-mxCellHighlight.prototype.repaint=function(){if(null!=this.state&&null!=this.shape){this.shape.scale=this.state.view.scale;this.graph.model.isEdge(this.state.cell)?(this.shape.strokewidth=this.getStrokeWidth(),this.shape.points=this.state.absolutePoints,this.shape.outline=!1):(this.shape.bounds=new mxRectangle(this.state.x-this.spacing,this.state.y-this.spacing,this.state.width+2*this.spacing,this.state.height+2*this.spacing),this.shape.rotation=Number(this.state.style[mxConstants.STYLE_ROTATION]||
-"0"),this.shape.strokewidth=this.getStrokeWidth()/this.state.view.scale,this.shape.outline=!0);null!=this.state.shape&&this.shape.setCursor(this.state.shape.getCursor());if(mxClient.IS_QUIRKS||8==document.documentMode)"transparent"==this.shape.stroke?(this.shape.stroke="white",this.shape.opacity=1):this.shape.opacity=this.opacity;this.shape.redraw()}};mxCellHighlight.prototype.hide=function(){this.highlight(null)};
-mxCellHighlight.prototype.highlight=function(a){this.state!=a&&(null!=this.shape&&(this.shape.destroy(),this.shape=null),this.state=a,null!=this.state&&this.drawHighlight())};mxCellHighlight.prototype.isHighlightAt=function(a,b){var c=!1;if(null!=this.shape&&null!=document.elementFromPoint&&!mxClient.IS_QUIRKS)for(var d=document.elementFromPoint(a,b);null!=d;){if(d==this.shape.node){c=!0;break}d=d.parentNode}return c};
-mxCellHighlight.prototype.destroy=function(){this.graph.getView().removeListener(this.resetHandler);this.graph.getView().removeListener(this.repaintHandler);this.graph.getModel().removeListener(this.repaintHandler);null!=this.shape&&(this.shape.destroy(),this.shape=null)};
-function mxDefaultKeyHandler(a){if(null!=a){this.editor=a;this.handler=new mxKeyHandler(a.graph);var b=this.handler.escape;this.handler.escape=function(c){b.apply(this,arguments);a.hideProperties();a.fireEvent(new mxEventObject(mxEvent.ESCAPE,"event",c))}}}mxDefaultKeyHandler.prototype.editor=null;mxDefaultKeyHandler.prototype.handler=null;
-mxDefaultKeyHandler.prototype.bindAction=function(a,b,c){var d=mxUtils.bind(this,function(){this.editor.execute(b)});c?this.handler.bindControlKey(a,d):this.handler.bindKey(a,d)};mxDefaultKeyHandler.prototype.destroy=function(){this.handler.destroy();this.handler=null};function mxDefaultPopupMenu(a){this.config=a}mxDefaultPopupMenu.prototype.imageBasePath=null;mxDefaultPopupMenu.prototype.config=null;
-mxDefaultPopupMenu.prototype.createMenu=function(a,b,c,d){if(null!=this.config){var e=this.createConditions(a,c,d);this.addItems(a,b,c,d,e,this.config.firstChild,null)}};
-mxDefaultPopupMenu.prototype.addItems=function(a,b,c,d,e,f,g){for(var k=!1;null!=f;){if("add"==f.nodeName){var l=f.getAttribute("if");if(null==l||e[l]){var l=f.getAttribute("as"),l=mxResources.get(l)||l,m=mxUtils.eval(mxUtils.getTextContent(f)),n=f.getAttribute("action"),p=f.getAttribute("icon"),q=f.getAttribute("iconCls"),r=f.getAttribute("enabled-if"),r=null==r||e[r];k&&(b.addSeparator(g),k=!1);null!=p&&this.imageBasePath&&(p=this.imageBasePath+p);l=this.addAction(b,a,l,p,m,n,c,g,q,r);this.addItems(a,
-b,c,d,e,f.firstChild,l)}}else"separator"==f.nodeName&&(k=!0);f=f.nextSibling}};mxDefaultPopupMenu.prototype.addAction=function(a,b,c,d,e,f,g,k,l,m){return a.addItem(c,d,function(a){"function"==typeof e&&e.call(b,b,g,a);null!=f&&b.execute(f,g,a)},k,l,m)};
-mxDefaultPopupMenu.prototype.createConditions=function(a,b,c){var d=a.graph.getModel(),e=d.getChildCount(b),f=[];f.nocell=null==b;f.ncells=1<a.graph.getSelectionCount();f.notRoot=d.getRoot()!=d.getParent(a.graph.getDefaultParent());f.cell=null!=b;d=null!=b&&1==a.graph.getSelectionCount();f.nonEmpty=d&&0<e;f.expandable=d&&a.graph.isCellFoldable(b,!1);f.collapsable=d&&a.graph.isCellFoldable(b,!0);f.validRoot=d&&a.graph.isValidRoot(b);f.emptyValidRoot=f.validRoot&&0==e;f.swimlane=d&&a.graph.isSwimlane(b);
-e=this.config.getElementsByTagName("condition");for(d=0;d<e.length;d++){var g=mxUtils.eval(mxUtils.getTextContent(e[d])),k=e[d].getAttribute("name");null!=k&&"function"==typeof g&&(f[k]=g(a,b,c))}return f};function mxDefaultToolbar(a,b){this.editor=b;null!=a&&null!=b&&this.init(a)}mxDefaultToolbar.prototype.editor=null;mxDefaultToolbar.prototype.toolbar=null;mxDefaultToolbar.prototype.resetHandler=null;mxDefaultToolbar.prototype.spacing=4;mxDefaultToolbar.prototype.connectOnDrop=!1;
-mxDefaultToolbar.prototype.init=function(a){null!=a&&(this.toolbar=new mxToolbar(a),this.toolbar.addListener(mxEvent.SELECT,mxUtils.bind(this,function(a,c){var b=c.getProperty("function");this.editor.insertFunction=null!=b?mxUtils.bind(this,function(){b.apply(this,arguments);this.toolbar.resetMode()}):null})),this.resetHandler=mxUtils.bind(this,function(){null!=this.toolbar&&this.toolbar.resetMode(!0)}),this.editor.graph.addListener(mxEvent.DOUBLE_CLICK,this.resetHandler),this.editor.addListener(mxEvent.ESCAPE,
-this.resetHandler))};mxDefaultToolbar.prototype.addItem=function(a,b,c,d){var e=mxUtils.bind(this,function(){null!=c&&0<c.length&&this.editor.execute(c)});return this.toolbar.addItem(a,b,e,d)};mxDefaultToolbar.prototype.addSeparator=function(a){a=a||mxClient.imageBasePath+"/separator.gif";this.toolbar.addSeparator(a)};mxDefaultToolbar.prototype.addCombo=function(){return this.toolbar.addCombo()};mxDefaultToolbar.prototype.addActionCombo=function(a){return this.toolbar.addActionCombo(a)};
-mxDefaultToolbar.prototype.addActionOption=function(a,b,c){var d=mxUtils.bind(this,function(){this.editor.execute(c)});this.addOption(a,b,d)};mxDefaultToolbar.prototype.addOption=function(a,b,c){return this.toolbar.addOption(a,b,c)};mxDefaultToolbar.prototype.addMode=function(a,b,c,d,e){var f=mxUtils.bind(this,function(){this.editor.setMode(c);null!=e&&e(this.editor)});return this.toolbar.addSwitchMode(a,b,f,d)};
-mxDefaultToolbar.prototype.addPrototype=function(a,b,c,d,e,f){var g=mxUtils.bind(this,function(){return"function"==typeof c?c():null!=c?this.editor.graph.cloneCells([c])[0]:null}),k=mxUtils.bind(this,function(a,b){"function"==typeof e?e(this.editor,g(),a,b):this.drop(g(),a,b);this.toolbar.resetMode();mxEvent.consume(a)});a=this.toolbar.addMode(a,b,k,d,null,f);this.installDropHandler(a,function(a,b,c){k(b,c)});return a};
-mxDefaultToolbar.prototype.drop=function(a,b,c){var d=this.editor.graph,e=d.getModel();if(null!=c&&!e.isEdge(c)&&this.connectOnDrop&&d.isCellConnectable(c))this.connect(a,b,c);else{for(;null!=c&&!d.isValidDropTarget(c,[a],b);)c=e.getParent(c);this.insert(a,b,c)}};
-mxDefaultToolbar.prototype.insert=function(a,b,c){var d=this.editor.graph;if(d.canImportCell(a)){var e=mxEvent.getClientX(b),f=mxEvent.getClientY(b),e=mxUtils.convertPoint(d.container,e,f);return d.isSplitEnabled()&&d.isSplitTarget(c,[a],b)?d.splitEdge(c,[a],null,e.x,e.y):this.editor.addVertex(c,a,e.x,e.y)}return null};
-mxDefaultToolbar.prototype.connect=function(a,b,c){b=this.editor.graph;var d=b.getModel();if(null!=c&&b.isCellConnectable(a)&&b.isEdgeValid(null,c,a)){var e=null;d.beginUpdate();try{var f=d.getGeometry(c),g=d.getGeometry(a).clone();g.x=f.x+(f.width-g.width)/2;g.y=f.y+(f.height-g.height)/2;var k=this.spacing*b.gridSize,l=20*d.getDirectedEdgeCount(c,!0);this.editor.horizontalFlow?g.x+=(g.width+f.width)/2+k+l:g.y+=(g.height+f.height)/2+k+l;a.setGeometry(g);var m=d.getParent(c);b.addCell(a,m);b.constrainChild(a);
-e=this.editor.createEdge(c,a);if(null==d.getGeometry(e)){var n=new mxGeometry;n.relative=!0;d.setGeometry(e,n)}b.addEdge(e,m,c,a)}finally{d.endUpdate()}b.setSelectionCells([a,e]);b.scrollCellToVisible(a)}};
-mxDefaultToolbar.prototype.installDropHandler=function(a,b){var c=document.createElement("img");c.setAttribute("src",a.getAttribute("src"));var d=mxUtils.bind(this,function(e){c.style.width=2*a.offsetWidth+"px";c.style.height=2*a.offsetHeight+"px";mxUtils.makeDraggable(a,this.editor.graph,b,c);mxEvent.removeListener(c,"load",d)});mxClient.IS_IE?d():mxEvent.addListener(c,"load",d)};
-mxDefaultToolbar.prototype.destroy=function(){null!=this.resetHandler&&(this.editor.graph.removeListener("dblclick",this.resetHandler),this.editor.removeListener("escape",this.resetHandler),this.resetHandler=null);null!=this.toolbar&&(this.toolbar.destroy(),this.toolbar=null)};
-function mxEditor(a){this.actions=[];this.addActions();if(null!=document.body){this.cycleAttributeValues=[];this.popupHandler=new mxDefaultPopupMenu;this.undoManager=new mxUndoManager;this.graph=this.createGraph();this.toolbar=this.createToolbar();this.keyHandler=new mxDefaultKeyHandler(this);this.configure(a);this.graph.swimlaneIndicatorColorAttribute=this.cycleAttributeName;if(null!=this.onInit)this.onInit();mxClient.IS_IE&&mxEvent.addListener(window,"unload",mxUtils.bind(this,function(){this.destroy()}))}}
-mxLoadResources?mxResources.add(mxClient.basePath+"/resources/editor"):mxClient.defaultBundles.push(mxClient.basePath+"/resources/editor");mxEditor.prototype=new mxEventSource;mxEditor.prototype.constructor=mxEditor;mxEditor.prototype.askZoomResource="none"!=mxClient.language?"askZoom":"";mxEditor.prototype.lastSavedResource="none"!=mxClient.language?"lastSaved":"";mxEditor.prototype.currentFileResource="none"!=mxClient.language?"currentFile":"";
-mxEditor.prototype.propertiesResource="none"!=mxClient.language?"properties":"";mxEditor.prototype.tasksResource="none"!=mxClient.language?"tasks":"";mxEditor.prototype.helpResource="none"!=mxClient.language?"help":"";mxEditor.prototype.outlineResource="none"!=mxClient.language?"outline":"";mxEditor.prototype.outline=null;mxEditor.prototype.graph=null;mxEditor.prototype.graphRenderHint=null;mxEditor.prototype.toolbar=null;mxEditor.prototype.status=null;mxEditor.prototype.popupHandler=null;
-mxEditor.prototype.undoManager=null;mxEditor.prototype.keyHandler=null;mxEditor.prototype.actions=null;mxEditor.prototype.dblClickAction="edit";mxEditor.prototype.swimlaneRequired=!1;mxEditor.prototype.disableContextMenu=!0;mxEditor.prototype.insertFunction=null;mxEditor.prototype.forcedInserting=!1;mxEditor.prototype.templates=null;mxEditor.prototype.defaultEdge=null;mxEditor.prototype.defaultEdgeStyle=null;mxEditor.prototype.defaultGroup=null;mxEditor.prototype.groupBorderSize=null;
-mxEditor.prototype.filename=null;mxEditor.prototype.linefeed="&#xa;";mxEditor.prototype.postParameterName="xml";mxEditor.prototype.escapePostData=!0;mxEditor.prototype.urlPost=null;mxEditor.prototype.urlImage=null;mxEditor.prototype.horizontalFlow=!1;mxEditor.prototype.layoutDiagram=!1;mxEditor.prototype.swimlaneSpacing=0;mxEditor.prototype.maintainSwimlanes=!1;mxEditor.prototype.layoutSwimlanes=!1;mxEditor.prototype.cycleAttributeValues=null;mxEditor.prototype.cycleAttributeIndex=0;
-mxEditor.prototype.cycleAttributeName="fillColor";mxEditor.prototype.tasks=null;mxEditor.prototype.tasksWindowImage=null;mxEditor.prototype.tasksTop=20;mxEditor.prototype.help=null;mxEditor.prototype.helpWindowImage=null;mxEditor.prototype.urlHelp=null;mxEditor.prototype.helpWidth=300;mxEditor.prototype.helpHeight=260;mxEditor.prototype.propertiesWidth=240;mxEditor.prototype.propertiesHeight=null;mxEditor.prototype.movePropertiesDialog=!1;mxEditor.prototype.validating=!1;
-mxEditor.prototype.modified=!1;mxEditor.prototype.isModified=function(){return this.modified};mxEditor.prototype.setModified=function(a){this.modified=a};
-mxEditor.prototype.addActions=function(){this.addAction("save",function(a){a.save()});this.addAction("print",function(a){(new mxPrintPreview(a.graph,1)).open()});this.addAction("show",function(a){mxUtils.show(a.graph,null,10,10)});this.addAction("exportImage",function(a){var b=a.getUrlImage();if(null==b||mxClient.IS_LOCAL)a.execute("show");else{var c=mxUtils.getViewXml(a.graph,1),c=mxUtils.getXml(c,"\n");mxUtils.submit(b,a.postParameterName+"="+encodeURIComponent(c),document,"_blank")}});this.addAction("refresh",
-function(a){a.graph.refresh()});this.addAction("cut",function(a){a.graph.isEnabled()&&mxClipboard.cut(a.graph)});this.addAction("copy",function(a){a.graph.isEnabled()&&mxClipboard.copy(a.graph)});this.addAction("paste",function(a){a.graph.isEnabled()&&mxClipboard.paste(a.graph)});this.addAction("delete",function(a){a.graph.isEnabled()&&a.graph.removeCells()});this.addAction("group",function(a){a.graph.isEnabled()&&a.graph.setSelectionCell(a.groupCells())});this.addAction("ungroup",function(a){a.graph.isEnabled()&&
-a.graph.setSelectionCells(a.graph.ungroupCells())});this.addAction("removeFromParent",function(a){a.graph.isEnabled()&&a.graph.removeCellsFromParent()});this.addAction("undo",function(a){a.graph.isEnabled()&&a.undo()});this.addAction("redo",function(a){a.graph.isEnabled()&&a.redo()});this.addAction("zoomIn",function(a){a.graph.zoomIn()});this.addAction("zoomOut",function(a){a.graph.zoomOut()});this.addAction("actualSize",function(a){a.graph.zoomActual()});this.addAction("fit",function(a){a.graph.fit()});
-this.addAction("showProperties",function(a,b){a.showProperties(b)});this.addAction("selectAll",function(a){a.graph.isEnabled()&&a.graph.selectAll()});this.addAction("selectNone",function(a){a.graph.isEnabled()&&a.graph.clearSelection()});this.addAction("selectVertices",function(a){a.graph.isEnabled()&&a.graph.selectVertices()});this.addAction("selectEdges",function(a){a.graph.isEnabled()&&a.graph.selectEdges()});this.addAction("edit",function(a,b){a.graph.isEnabled()&&a.graph.isCellEditable(b)&&a.graph.startEditingAtCell(b)});
-this.addAction("toBack",function(a,b){a.graph.isEnabled()&&a.graph.orderCells(!0)});this.addAction("toFront",function(a,b){a.graph.isEnabled()&&a.graph.orderCells(!1)});this.addAction("enterGroup",function(a,b){a.graph.enterGroup(b)});this.addAction("exitGroup",function(a){a.graph.exitGroup()});this.addAction("home",function(a){a.graph.home()});this.addAction("selectPrevious",function(a){a.graph.isEnabled()&&a.graph.selectPreviousCell()});this.addAction("selectNext",function(a){a.graph.isEnabled()&&
-a.graph.selectNextCell()});this.addAction("selectParent",function(a){a.graph.isEnabled()&&a.graph.selectParentCell()});this.addAction("selectChild",function(a){a.graph.isEnabled()&&a.graph.selectChildCell()});this.addAction("collapse",function(a){a.graph.isEnabled()&&a.graph.foldCells(!0)});this.addAction("collapseAll",function(a){if(a.graph.isEnabled()){var b=a.graph.getChildVertices();a.graph.foldCells(!0,!1,b)}});this.addAction("expand",function(a){a.graph.isEnabled()&&a.graph.foldCells(!1)});
-this.addAction("expandAll",function(a){if(a.graph.isEnabled()){var b=a.graph.getChildVertices();a.graph.foldCells(!1,!1,b)}});this.addAction("bold",function(a){a.graph.isEnabled()&&a.graph.toggleCellStyleFlags(mxConstants.STYLE_FONTSTYLE,mxConstants.FONT_BOLD)});this.addAction("italic",function(a){a.graph.isEnabled()&&a.graph.toggleCellStyleFlags(mxConstants.STYLE_FONTSTYLE,mxConstants.FONT_ITALIC)});this.addAction("underline",function(a){a.graph.isEnabled()&&a.graph.toggleCellStyleFlags(mxConstants.STYLE_FONTSTYLE,
-mxConstants.FONT_UNDERLINE)});this.addAction("alignCellsLeft",function(a){a.graph.isEnabled()&&a.graph.alignCells(mxConstants.ALIGN_LEFT)});this.addAction("alignCellsCenter",function(a){a.graph.isEnabled()&&a.graph.alignCells(mxConstants.ALIGN_CENTER)});this.addAction("alignCellsRight",function(a){a.graph.isEnabled()&&a.graph.alignCells(mxConstants.ALIGN_RIGHT)});this.addAction("alignCellsTop",function(a){a.graph.isEnabled()&&a.graph.alignCells(mxConstants.ALIGN_TOP)});this.addAction("alignCellsMiddle",
-function(a){a.graph.isEnabled()&&a.graph.alignCells(mxConstants.ALIGN_MIDDLE)});this.addAction("alignCellsBottom",function(a){a.graph.isEnabled()&&a.graph.alignCells(mxConstants.ALIGN_BOTTOM)});this.addAction("alignFontLeft",function(a){a.graph.setCellStyles(mxConstants.STYLE_ALIGN,mxConstants.ALIGN_LEFT)});this.addAction("alignFontCenter",function(a){a.graph.isEnabled()&&a.graph.setCellStyles(mxConstants.STYLE_ALIGN,mxConstants.ALIGN_CENTER)});this.addAction("alignFontRight",function(a){a.graph.isEnabled()&&
-a.graph.setCellStyles(mxConstants.STYLE_ALIGN,mxConstants.ALIGN_RIGHT)});this.addAction("alignFontTop",function(a){a.graph.isEnabled()&&a.graph.setCellStyles(mxConstants.STYLE_VERTICAL_ALIGN,mxConstants.ALIGN_TOP)});this.addAction("alignFontMiddle",function(a){a.graph.isEnabled()&&a.graph.setCellStyles(mxConstants.STYLE_VERTICAL_ALIGN,mxConstants.ALIGN_MIDDLE)});this.addAction("alignFontBottom",function(a){a.graph.isEnabled()&&a.graph.setCellStyles(mxConstants.STYLE_VERTICAL_ALIGN,mxConstants.ALIGN_BOTTOM)});
-this.addAction("zoom",function(a){var b=100*a.graph.getView().scale,b=parseFloat(mxUtils.prompt(mxResources.get(a.askZoomResource)||a.askZoomResource,b))/100;isNaN(b)||a.graph.getView().setScale(b)});this.addAction("toggleTasks",function(a){null!=a.tasks?a.tasks.setVisible(!a.tasks.isVisible()):a.showTasks()});this.addAction("toggleHelp",function(a){null!=a.help?a.help.setVisible(!a.help.isVisible()):a.showHelp()});this.addAction("toggleOutline",function(a){null==a.outline?a.showOutline():a.outline.setVisible(!a.outline.isVisible())});
-this.addAction("toggleConsole",function(a){mxLog.setVisible(!mxLog.isVisible())})};mxEditor.prototype.configure=function(a){null!=a&&((new mxCodec(a.ownerDocument)).decode(a,this),this.resetHistory())};mxEditor.prototype.resetFirstTime=function(){document.cookie="mxgraph=seen; expires=Fri, 27 Jul 2001 02:47:11 UTC; path=/"};mxEditor.prototype.resetHistory=function(){this.lastSnapshot=(new Date).getTime();this.undoManager.clear();this.ignoredChanges=0;this.setModified(!1)};
-mxEditor.prototype.addAction=function(a,b){this.actions[a]=b};mxEditor.prototype.execute=function(a,b,c){var d=this.actions[a];if(null!=d)try{var e=arguments;e[0]=this;d.apply(this,e)}catch(f){throw mxUtils.error("Cannot execute "+a+": "+f.message,280,!0),f;}else mxUtils.error("Cannot find action "+a,280,!0)};mxEditor.prototype.addTemplate=function(a,b){this.templates[a]=b};mxEditor.prototype.getTemplate=function(a){return this.templates[a]};
-mxEditor.prototype.createGraph=function(){var a=new mxGraph(null,null,this.graphRenderHint);a.setTooltips(!0);a.setPanning(!0);this.installDblClickHandler(a);this.installUndoHandler(a);this.installDrillHandler(a);this.installChangeHandler(a);this.installInsertHandler(a);a.popupMenuHandler.factoryMethod=mxUtils.bind(this,function(a,c,d){return this.createPopupMenu(a,c,d)});a.connectionHandler.factoryMethod=mxUtils.bind(this,function(a,c){return this.createEdge(a,c)});this.createSwimlaneManager(a);
-this.createLayoutManager(a);return a};mxEditor.prototype.createSwimlaneManager=function(a){a=new mxSwimlaneManager(a,!1);a.isHorizontal=mxUtils.bind(this,function(){return this.horizontalFlow});a.isEnabled=mxUtils.bind(this,function(){return this.maintainSwimlanes});return a};
-mxEditor.prototype.createLayoutManager=function(a){var b=new mxLayoutManager(a),c=this;b.getLayout=function(b){var d=null,f=c.graph.getModel();null!=f.getParent(b)&&(c.layoutSwimlanes&&a.isSwimlane(b)?(null==c.swimlaneLayout&&(c.swimlaneLayout=c.createSwimlaneLayout()),d=c.swimlaneLayout):c.layoutDiagram&&(a.isValidRoot(b)||null==f.getParent(f.getParent(b)))&&(null==c.diagramLayout&&(c.diagramLayout=c.createDiagramLayout()),d=c.diagramLayout));return d};return b};
-mxEditor.prototype.setGraphContainer=function(a){null==this.graph.container&&(this.graph.init(a),this.rubberband=new mxRubberband(this.graph),this.disableContextMenu&&mxEvent.disableContextMenu(a),mxClient.IS_QUIRKS&&new mxDivResizer(a))};mxEditor.prototype.installDblClickHandler=function(a){a.addListener(mxEvent.DOUBLE_CLICK,mxUtils.bind(this,function(b,c){var d=c.getProperty("cell");null!=d&&a.isEnabled()&&null!=this.dblClickAction&&(this.execute(this.dblClickAction,d),c.consume())}))};
-mxEditor.prototype.installUndoHandler=function(a){var b=mxUtils.bind(this,function(a,b){var c=b.getProperty("edit");this.undoManager.undoableEditHappened(c)});a.getModel().addListener(mxEvent.UNDO,b);a.getView().addListener(mxEvent.UNDO,b);b=function(b,d){var c=d.getProperty("edit").changes;a.setSelectionCells(a.getSelectionCellsForChanges(c))};this.undoManager.addListener(mxEvent.UNDO,b);this.undoManager.addListener(mxEvent.REDO,b)};
-mxEditor.prototype.installDrillHandler=function(a){var b=mxUtils.bind(this,function(a){this.fireEvent(new mxEventObject(mxEvent.ROOT))});a.getView().addListener(mxEvent.DOWN,b);a.getView().addListener(mxEvent.UP,b)};
-mxEditor.prototype.installChangeHandler=function(a){var b=mxUtils.bind(this,function(b,d){this.setModified(!0);1==this.validating&&a.validateGraph();for(var c=d.getProperty("edit").changes,f=0;f<c.length;f++){var g=c[f];if(g instanceof mxRootChange||g instanceof mxValueChange&&g.cell==this.graph.model.root||g instanceof mxCellAttributeChange&&g.cell==this.graph.model.root){this.fireEvent(new mxEventObject(mxEvent.ROOT));break}}});a.getModel().addListener(mxEvent.CHANGE,b)};
-mxEditor.prototype.installInsertHandler=function(a){var b=this;a.addMouseListener({mouseDown:function(a,d){null==b.insertFunction||d.isPopupTrigger()||!b.forcedInserting&&null!=d.getState()||(b.graph.clearSelection(),b.insertFunction(d.getEvent(),d.getCell()),this.isActive=!0,d.consume())},mouseMove:function(a,b){this.isActive&&b.consume()},mouseUp:function(a,b){this.isActive&&(this.isActive=!1,b.consume())}})};
-mxEditor.prototype.createDiagramLayout=function(){var a=this.graph.gridSize,b=new mxStackLayout(this.graph,!this.horizontalFlow,this.swimlaneSpacing,2*a,2*a);b.isVertexIgnored=function(a){return!b.graph.isSwimlane(a)};return b};mxEditor.prototype.createSwimlaneLayout=function(){return new mxCompactTreeLayout(this.graph,this.horizontalFlow)};mxEditor.prototype.createToolbar=function(){return new mxDefaultToolbar(null,this)};
-mxEditor.prototype.setToolbarContainer=function(a){this.toolbar.init(a);mxClient.IS_QUIRKS&&new mxDivResizer(a)};
-mxEditor.prototype.setStatusContainer=function(a){null==this.status&&(this.status=a,this.addListener(mxEvent.SAVE,mxUtils.bind(this,function(){var a=(new Date).toLocaleString();this.setStatus((mxResources.get(this.lastSavedResource)||this.lastSavedResource)+": "+a)})),this.addListener(mxEvent.OPEN,mxUtils.bind(this,function(){this.setStatus((mxResources.get(this.currentFileResource)||this.currentFileResource)+": "+this.filename)})),mxClient.IS_QUIRKS&&new mxDivResizer(a))};
-mxEditor.prototype.setStatus=function(a){null!=this.status&&null!=a&&(this.status.innerHTML=a)};mxEditor.prototype.setTitleContainer=function(a){this.addListener(mxEvent.ROOT,mxUtils.bind(this,function(b){a.innerHTML=this.getTitle()}));mxClient.IS_QUIRKS&&new mxDivResizer(a)};mxEditor.prototype.treeLayout=function(a,b){null!=a&&(new mxCompactTreeLayout(this.graph,b)).execute(a)};
-mxEditor.prototype.getTitle=function(){for(var a="",b=this.graph,c=b.getCurrentRoot();null!=c&&null!=b.getModel().getParent(b.getModel().getParent(c));)b.isValidRoot(c)&&(a=" > "+b.convertValueToString(c)+a),c=b.getModel().getParent(c);return this.getRootTitle()+a};mxEditor.prototype.getRootTitle=function(){var a=this.graph.getModel().getRoot();return this.graph.convertValueToString(a)};mxEditor.prototype.undo=function(){this.undoManager.undo()};mxEditor.prototype.redo=function(){this.undoManager.redo()};
-mxEditor.prototype.groupCells=function(){var a=null!=this.groupBorderSize?this.groupBorderSize:this.graph.gridSize;return this.graph.groupCells(this.createGroup(),a)};mxEditor.prototype.createGroup=function(){return this.graph.getModel().cloneCell(this.defaultGroup)};mxEditor.prototype.open=function(a){if(null!=a){var b=mxUtils.load(a).getXml();this.readGraphModel(b.documentElement);this.filename=a;this.fireEvent(new mxEventObject(mxEvent.OPEN,"filename",a))}};
-mxEditor.prototype.readGraphModel=function(a){(new mxCodec(a.ownerDocument)).decode(a,this.graph.getModel());this.resetHistory()};mxEditor.prototype.save=function(a,b){a=a||this.getUrlPost();if(null!=a&&0<a.length){var c=this.writeGraphModel(b);this.postDiagram(a,c);this.setModified(!1)}this.fireEvent(new mxEventObject(mxEvent.SAVE,"url",a))};
-mxEditor.prototype.postDiagram=function(a,b){this.escapePostData&&(b=encodeURIComponent(b));mxUtils.post(a,this.postParameterName+"="+b,mxUtils.bind(this,function(c){this.fireEvent(new mxEventObject(mxEvent.POST,"request",c,"url",a,"data",b))}))};mxEditor.prototype.writeGraphModel=function(a){a=null!=a?a:this.linefeed;var b=(new mxCodec).encode(this.graph.getModel());return mxUtils.getXml(b,a)};mxEditor.prototype.getUrlPost=function(){return this.urlPost};mxEditor.prototype.getUrlImage=function(){return this.urlImage};
-mxEditor.prototype.swapStyles=function(a,b){var c=this.graph.getStylesheet().styles[b];this.graph.getView().getStylesheet().putCellStyle(b,this.graph.getStylesheet().styles[a]);this.graph.getStylesheet().putCellStyle(a,c);this.graph.refresh()};
-mxEditor.prototype.showProperties=function(a){a=a||this.graph.getSelectionCell();null==a&&(a=this.graph.getCurrentRoot(),null==a&&(a=this.graph.getModel().getRoot()));if(null!=a){this.graph.stopEditing(!0);var b=mxUtils.getOffset(this.graph.container),c=b.x+10,b=b.y;if(null==this.properties||this.movePropertiesDialog){var d=this.graph.getCellBounds(a);null!=d&&(c+=d.x+Math.min(200,d.width),b+=d.y)}else c=this.properties.getX(),b=this.properties.getY();this.hideProperties();a=this.createProperties(a);
-null!=a&&(this.properties=new mxWindow(mxResources.get(this.propertiesResource)||this.propertiesResource,a,c,b,this.propertiesWidth,this.propertiesHeight,!1),this.properties.setVisible(!0))}};mxEditor.prototype.isPropertiesVisible=function(){return null!=this.properties};
-mxEditor.prototype.createProperties=function(a){var b=this.graph.getModel(),c=b.getValue(a);if(mxUtils.isNode(c)){var d=new mxForm("properties");d.addText("ID",a.getId()).setAttribute("readonly","true");var e=null,f=null,g=null,k=null,l=null;b.isVertex(a)&&(e=b.getGeometry(a),null!=e&&(f=d.addText("top",e.y),g=d.addText("left",e.x),k=d.addText("width",e.width),l=d.addText("height",e.height)));for(var m=b.getStyle(a),n=d.addText("Style",m||""),p=c.attributes,q=[],c=0;c<p.length;c++)q[c]=d.addTextarea(p[c].nodeName,
-p[c].value,"label"==p[c].nodeName?4:2);c=mxUtils.bind(this,function(){this.hideProperties();b.beginUpdate();try{null!=e&&(e=e.clone(),e.x=parseFloat(g.value),e.y=parseFloat(f.value),e.width=parseFloat(k.value),e.height=parseFloat(l.value),b.setGeometry(a,e));0<n.value.length?b.setStyle(a,n.value):b.setStyle(a,null);for(var c=0;c<p.length;c++){var d=new mxCellAttributeChange(a,p[c].nodeName,q[c].value);b.execute(d)}this.graph.isAutoSizeCell(a)&&this.graph.updateCellSize(a)}finally{b.endUpdate()}});
-m=mxUtils.bind(this,function(){this.hideProperties()});d.addButtons(c,m);return d.table}return null};mxEditor.prototype.hideProperties=function(){null!=this.properties&&(this.properties.destroy(),this.properties=null)};
-mxEditor.prototype.showTasks=function(){if(null==this.tasks){var a=document.createElement("div");a.style.padding="4px";a.style.paddingLeft="20px";var b=document.body.clientWidth,b=new mxWindow(mxResources.get(this.tasksResource)||this.tasksResource,a,b-220,this.tasksTop,200);b.setClosable(!0);b.destroyOnClose=!1;var c=mxUtils.bind(this,function(b){mxEvent.release(a);a.innerHTML="";this.createTasks(a)});this.graph.getModel().addListener(mxEvent.CHANGE,c);this.graph.getSelectionModel().addListener(mxEvent.CHANGE,
-c);this.graph.addListener(mxEvent.ROOT,c);null!=this.tasksWindowImage&&b.setImage(this.tasksWindowImage);this.tasks=b;this.createTasks(a)}this.tasks.setVisible(!0)};mxEditor.prototype.refreshTasks=function(a){null!=this.tasks&&(a=this.tasks.content,mxEvent.release(a),a.innerHTML="",this.createTasks(a))};mxEditor.prototype.createTasks=function(a){};
-mxEditor.prototype.showHelp=function(a){if(null==this.help){var b=document.createElement("iframe");b.setAttribute("src",mxResources.get("urlHelp")||this.urlHelp);b.setAttribute("height","100%");b.setAttribute("width","100%");b.setAttribute("frameBorder","0");b.style.backgroundColor="white";a=document.body.clientWidth;var c=document.body.clientHeight||document.documentElement.clientHeight,d=new mxWindow(mxResources.get(this.helpResource)||this.helpResource,b,(a-this.helpWidth)/2,(c-this.helpHeight)/
-3,this.helpWidth,this.helpHeight);d.setMaximizable(!0);d.setClosable(!0);d.destroyOnClose=!1;d.setResizable(!0);null!=this.helpWindowImage&&d.setImage(this.helpWindowImage);mxClient.IS_NS&&(a=function(a){b.setAttribute("height",d.div.offsetHeight-26+"px")},d.addListener(mxEvent.RESIZE_END,a),d.addListener(mxEvent.MAXIMIZE,a),d.addListener(mxEvent.NORMALIZE,a),d.addListener(mxEvent.SHOW,a));this.help=d}this.help.setVisible(!0)};
-mxEditor.prototype.showOutline=function(){if(null==this.outline){var a=document.createElement("div");a.style.overflow="hidden";a.style.position="relative";a.style.width="100%";a.style.height="100%";a.style.background="white";a.style.cursor="move";8==document.documentMode&&(a.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=100)");var b=new mxWindow(mxResources.get(this.outlineResource)||this.outlineResource,a,600,480,200,200,!1),c=new mxOutline(this.graph,a);b.setClosable(!0);b.setResizable(!0);
-b.destroyOnClose=!1;b.addListener(mxEvent.RESIZE_END,function(){c.update()});this.outline=b;this.outline.outline=c}this.outline.setVisible(!0);this.outline.outline.update(!0)};mxEditor.prototype.setMode=function(a){"select"==a?(this.graph.panningHandler.useLeftButtonForPanning=!1,this.graph.setConnectable(!1)):"connect"==a?(this.graph.panningHandler.useLeftButtonForPanning=!1,this.graph.setConnectable(!0)):"pan"==a&&(this.graph.panningHandler.useLeftButtonForPanning=!0,this.graph.setConnectable(!1))};
-mxEditor.prototype.createPopupMenu=function(a,b,c){this.popupHandler.createMenu(this,a,b,c)};mxEditor.prototype.createEdge=function(a,b){var c;if(null!=this.defaultEdge)c=this.graph.getModel().cloneCell(this.defaultEdge);else{c=new mxCell("");c.setEdge(!0);var d=new mxGeometry;d.relative=!0;c.setGeometry(d)}d=this.getEdgeStyle();null!=d&&c.setStyle(d);return c};mxEditor.prototype.getEdgeStyle=function(){return this.defaultEdgeStyle};
-mxEditor.prototype.consumeCycleAttribute=function(a){return null!=this.cycleAttributeValues&&0<this.cycleAttributeValues.length&&this.graph.isSwimlane(a)?this.cycleAttributeValues[this.cycleAttributeIndex++%this.cycleAttributeValues.length]:null};mxEditor.prototype.cycleAttribute=function(a){if(null!=this.cycleAttributeName){var b=this.consumeCycleAttribute(a);null!=b&&a.setStyle(a.getStyle()+";"+this.cycleAttributeName+"="+b)}};
-mxEditor.prototype.addVertex=function(a,b,c,d){for(var e=this.graph.getModel();null!=a&&!this.graph.isValidDropTarget(a);)a=e.getParent(a);a=null!=a?a:this.graph.getSwimlaneAt(c,d);var f=this.graph.getView().scale,g=e.getGeometry(b),k=e.getGeometry(a);if(this.graph.isSwimlane(b)&&!this.graph.swimlaneNesting)a=null;else{if(null==a&&this.swimlaneRequired)return null;if(null!=a&&null!=k){var l=this.graph.getView().getState(a);if(null!=l){if(c-=l.origin.x*f,d-=l.origin.y*f,this.graph.isConstrainedMoving){var k=
-g.width,m=g.height,n=l.x+l.width;c+k>n&&(c-=c+k-n);n=l.y+l.height;d+m>n&&(d-=d+m-n)}}else null!=k&&(c-=k.x*f,d-=k.y*f)}}g=g.clone();g.x=this.graph.snap(c/f-this.graph.getView().translate.x-this.graph.gridSize/2);g.y=this.graph.snap(d/f-this.graph.getView().translate.y-this.graph.gridSize/2);b.setGeometry(g);null==a&&(a=this.graph.getDefaultParent());this.cycleAttribute(b);this.fireEvent(new mxEventObject(mxEvent.BEFORE_ADD_VERTEX,"vertex",b,"parent",a));e.beginUpdate();try{b=this.graph.addCell(b,
-a),null!=b&&(this.graph.constrainChild(b),this.fireEvent(new mxEventObject(mxEvent.ADD_VERTEX,"vertex",b)))}finally{e.endUpdate()}null!=b&&(this.graph.setSelectionCell(b),this.graph.scrollCellToVisible(b),this.fireEvent(new mxEventObject(mxEvent.AFTER_ADD_VERTEX,"vertex",b)));return b};
-mxEditor.prototype.destroy=function(){this.destroyed||(this.destroyed=!0,null!=this.tasks&&this.tasks.destroy(),null!=this.outline&&this.outline.destroy(),null!=this.properties&&this.properties.destroy(),null!=this.keyHandler&&this.keyHandler.destroy(),null!=this.rubberband&&this.rubberband.destroy(),null!=this.toolbar&&this.toolbar.destroy(),null!=this.graph&&this.graph.destroy(),this.templates=this.status=null)};
-var mxCodecRegistry={codecs:[],aliases:[],register:function(a){if(null!=a){var b=a.getName();mxCodecRegistry.codecs[b]=a;var c=mxUtils.getFunctionName(a.template.constructor);c!=b&&mxCodecRegistry.addAlias(c,b)}return a},addAlias:function(a,b){mxCodecRegistry.aliases[a]=b},getCodec:function(a){var b=null;if(null!=a){var b=mxUtils.getFunctionName(a),c=mxCodecRegistry.aliases[b];null!=c&&(b=c);b=mxCodecRegistry.codecs[b];if(null==b)try{b=new mxObjectCodec(new a),mxCodecRegistry.register(b)}catch(d){}}return b}};
-function mxCodec(a){this.document=a||mxUtils.createXmlDocument();this.objects=[]}mxCodec.prototype.document=null;mxCodec.prototype.objects=null;mxCodec.prototype.elements=null;mxCodec.prototype.encodeDefaults=!1;mxCodec.prototype.putObject=function(a,b){return this.objects[a]=b};mxCodec.prototype.getObject=function(a){var b=null;null!=a&&(b=this.objects[a],null==b&&(b=this.lookup(a),null==b&&(a=this.getElementById(a),null!=a&&(b=this.decode(a)))));return b};mxCodec.prototype.lookup=function(a){return null};
-mxCodec.prototype.getElementById=function(a){if(null==this.elements){if(null==this.document.documentElement)throw Error("mxCodec constructor needs document parameter");this.elements={};this.addElement(this.document.documentElement)}return this.elements[a]};mxCodec.prototype.addElement=function(a){if(a.nodeType==mxConstants.NODETYPE_ELEMENT){var b=a.getAttribute("id");null!=b&&null==this.elements[b]&&(this.elements[b]=a)}for(a=a.firstChild;null!=a;)this.addElement(a),a=a.nextSibling};
-mxCodec.prototype.getId=function(a){var b=null;null!=a&&(b=this.reference(a),null==b&&a instanceof mxCell&&(b=a.getId(),null==b&&(b=mxCellPath.create(a),0==b.length&&(b="root"))));return b};mxCodec.prototype.reference=function(a){return null};mxCodec.prototype.encode=function(a){var b=null;if(null!=a&&null!=a.constructor){var c=mxCodecRegistry.getCodec(a.constructor);null!=c?b=c.encode(this,a):mxUtils.isNode(a)?b=mxUtils.importNode(this.document,a,!0):mxLog.warn("mxCodec.encode: No codec for "+mxUtils.getFunctionName(a.constructor))}return b};
-mxCodec.prototype.decode=function(a,b){var c=null;if(null!=a&&a.nodeType==mxConstants.NODETYPE_ELEMENT){c=null;try{c=window[a.nodeName]}catch(d){}c=mxCodecRegistry.getCodec(c);null!=c?c=c.decode(this,a,b):(c=a.cloneNode(!0),c.removeAttribute("as"))}return c};mxCodec.prototype.encodeCell=function(a,b,c){b.appendChild(this.encode(a));if(null==c||c){c=a.getChildCount();for(var d=0;d<c;d++)this.encodeCell(a.getChildAt(d),b)}};
-mxCodec.prototype.isCellCodec=function(a){return null!=a&&"function"==typeof a.isCellCodec?a.isCellCodec():!1};mxCodec.prototype.decodeCell=function(a,b){b=null!=b?b:!0;var c=null;if(null!=a&&a.nodeType==mxConstants.NODETYPE_ELEMENT){c=mxCodecRegistry.getCodec(a.nodeName);if(!this.isCellCodec(c))for(var d=a.firstChild;null!=d&&!this.isCellCodec(c);)c=mxCodecRegistry.getCodec(d.nodeName),d=d.nextSibling;this.isCellCodec(c)||(c=mxCodecRegistry.getCodec(mxCell));c=c.decode(this,a);b&&this.insertIntoGraph(c)}return c};
-mxCodec.prototype.insertIntoGraph=function(a){var b=a.parent,c=a.getTerminal(!0),d=a.getTerminal(!1);a.setTerminal(null,!1);a.setTerminal(null,!0);a.parent=null;null!=b&&b.insert(a);null!=c&&c.insertEdge(a,!0);null!=d&&d.insertEdge(a,!1)};mxCodec.prototype.setAttribute=function(a,b,c){null!=b&&null!=c&&a.setAttribute(b,c)};
-function mxObjectCodec(a,b,c,d){this.template=a;this.exclude=null!=b?b:[];this.idrefs=null!=c?c:[];this.mapping=null!=d?d:[];this.reverse={};for(var e in this.mapping)this.reverse[this.mapping[e]]=e}mxObjectCodec.allowEval=!1;mxObjectCodec.prototype.template=null;mxObjectCodec.prototype.exclude=null;mxObjectCodec.prototype.idrefs=null;mxObjectCodec.prototype.mapping=null;mxObjectCodec.prototype.reverse=null;mxObjectCodec.prototype.getName=function(){return mxUtils.getFunctionName(this.template.constructor)};
-mxObjectCodec.prototype.cloneTemplate=function(){return new this.template.constructor};mxObjectCodec.prototype.getFieldName=function(a){if(null!=a){var b=this.reverse[a];null!=b&&(a=b)}return a};mxObjectCodec.prototype.getAttributeName=function(a){if(null!=a){var b=this.mapping[a];null!=b&&(a=b)}return a};mxObjectCodec.prototype.isExcluded=function(a,b,c,d){return b==mxObjectIdentity.FIELD_NAME||0<=mxUtils.indexOf(this.exclude,b)};
-mxObjectCodec.prototype.isReference=function(a,b,c,d){return 0<=mxUtils.indexOf(this.idrefs,b)};mxObjectCodec.prototype.encode=function(a,b){var c=a.document.createElement(this.getName());b=this.beforeEncode(a,b,c);this.encodeObject(a,b,c);return this.afterEncode(a,b,c)};mxObjectCodec.prototype.encodeObject=function(a,b,c){a.setAttribute(c,"id",a.getId(b));for(var d in b){var e=d,f=b[e];null==f||this.isExcluded(b,e,f,!0)||(mxUtils.isInteger(e)&&(e=null),this.encodeValue(a,b,e,f,c))}};
-mxObjectCodec.prototype.encodeValue=function(a,b,c,d,e){if(null!=d){if(this.isReference(b,c,d,!0)){var f=a.getId(d);if(null==f){mxLog.warn("mxObjectCodec.encode: No ID for "+this.getName()+"."+c+"="+d);return}d=f}f=this.template[c];if(null==c||a.encodeDefaults||f!=d)c=this.getAttributeName(c),this.writeAttribute(a,b,c,d,e)}};mxObjectCodec.prototype.writeAttribute=function(a,b,c,d,e){"object"!=typeof d?this.writePrimitiveAttribute(a,b,c,d,e):this.writeComplexAttribute(a,b,c,d,e)};
-mxObjectCodec.prototype.writePrimitiveAttribute=function(a,b,c,d,e){d=this.convertAttributeToXml(a,b,c,d,e);null==c?(b=a.document.createElement("add"),"function"==typeof d?b.appendChild(a.document.createTextNode(d)):a.setAttribute(b,"value",d),e.appendChild(b)):"function"!=typeof d&&a.setAttribute(e,c,d)};
-mxObjectCodec.prototype.writeComplexAttribute=function(a,b,c,d,e){a=a.encode(d);null!=a?(null!=c&&a.setAttribute("as",c),e.appendChild(a)):mxLog.warn("mxObjectCodec.encode: No node for "+this.getName()+"."+c+": "+d)};mxObjectCodec.prototype.convertAttributeToXml=function(a,b,c,d){this.isBooleanAttribute(a,b,c,d)&&(d=1==d?"1":"0");return d};mxObjectCodec.prototype.isBooleanAttribute=function(a,b,c,d){return"undefined"==typeof d.length&&(1==d||0==d)};
-mxObjectCodec.prototype.convertAttributeFromXml=function(a,b,c){var d=b.value;this.isNumericAttribute(a,b,c)&&(d=parseFloat(d));return d};mxObjectCodec.prototype.isNumericAttribute=function(a,b,c){return mxUtils.isNumeric(b.value)};mxObjectCodec.prototype.beforeEncode=function(a,b,c){return b};mxObjectCodec.prototype.afterEncode=function(a,b,c){return c};
-mxObjectCodec.prototype.decode=function(a,b,c){var d=b.getAttribute("id"),e=a.objects[d];null==e&&(e=c||this.cloneTemplate(),null!=d&&a.putObject(d,e));b=this.beforeDecode(a,b,e);this.decodeNode(a,b,e);return this.afterDecode(a,b,e)};mxObjectCodec.prototype.decodeNode=function(a,b,c){null!=b&&(this.decodeAttributes(a,b,c),this.decodeChildren(a,b,c))};mxObjectCodec.prototype.decodeAttributes=function(a,b,c){b=b.attributes;if(null!=b)for(var d=0;d<b.length;d++)this.decodeAttribute(a,b[d],c)};
-mxObjectCodec.prototype.isIgnoredAttribute=function(a,b,c){return"as"==b.nodeName||"id"==b.nodeName};mxObjectCodec.prototype.decodeAttribute=function(a,b,c){if(!this.isIgnoredAttribute(a,b,c)){var d=b.nodeName;b=this.convertAttributeFromXml(a,b,c);var e=this.getFieldName(d);if(this.isReference(c,e,b,!1)){a=a.getObject(b);if(null==a){mxLog.warn("mxObjectCodec.decode: No object for "+this.getName()+"."+d+"="+b);return}b=a}this.isExcluded(c,d,b,!1)||(c[d]=b)}};
-mxObjectCodec.prototype.decodeChildren=function(a,b,c){for(b=b.firstChild;null!=b;){var d=b.nextSibling;b.nodeType!=mxConstants.NODETYPE_ELEMENT||this.processInclude(a,b,c)||this.decodeChild(a,b,c);b=d}};
-mxObjectCodec.prototype.decodeChild=function(a,b,c){var d=this.getFieldName(b.getAttribute("as"));if(null==d||!this.isExcluded(c,d,b,!1)){var e=this.getFieldTemplate(c,d,b);"add"==b.nodeName?(a=b.getAttribute("value"),null==a&&mxObjectCodec.allowEval&&(a=mxUtils.eval(mxUtils.getTextContent(b)))):a=a.decode(b,e);this.addObjectValue(c,d,a,e)}};mxObjectCodec.prototype.getFieldTemplate=function(a,b,c){a=a[b];a instanceof Array&&0<a.length&&(a=null);return a};
-mxObjectCodec.prototype.addObjectValue=function(a,b,c,d){null!=c&&c!=d&&(null!=b&&0<b.length?a[b]=c:a.push(c))};mxObjectCodec.prototype.processInclude=function(a,b,c){if("include"==b.nodeName){b=b.getAttribute("name");if(null!=b)try{var d=mxUtils.load(b).getDocumentElement();null!=d&&a.decode(d,c)}catch(e){}return!0}return!1};mxObjectCodec.prototype.beforeDecode=function(a,b,c){return b};mxObjectCodec.prototype.afterDecode=function(a,b,c){return c};
-mxCodecRegistry.register(function(){var a=new mxObjectCodec(new mxCell,["children","edges","overlays","mxTransient"],["parent","source","target"]);a.isCellCodec=function(){return!0};a.isNumericAttribute=function(a,c,d){return"value"!==c.nodeName&&mxObjectCodec.prototype.isNumericAttribute.apply(this,arguments)};a.isExcluded=function(a,c,d,e){return mxObjectCodec.prototype.isExcluded.apply(this,arguments)||e&&"value"==c&&d.nodeType==mxConstants.NODETYPE_ELEMENT};a.afterEncode=function(a,c,d){if(null!=
-c.value&&c.value.nodeType==mxConstants.NODETYPE_ELEMENT){var b=d;d=mxUtils.importNode(a.document,c.value,!0);d.appendChild(b);a=b.getAttribute("id");d.setAttribute("id",a);b.removeAttribute("id")}return d};a.beforeDecode=function(a,c,d){var b=c.cloneNode(!0),f=this.getName();c.nodeName!=f?(b=c.getElementsByTagName(f)[0],null!=b&&b.parentNode==c?(mxUtils.removeWhitespace(b,!0),mxUtils.removeWhitespace(b,!1),b.parentNode.removeChild(b)):b=null,d.value=c.cloneNode(!0),c=d.value.getAttribute("id"),null!=
-c&&(d.setId(c),d.value.removeAttribute("id"))):d.setId(c.getAttribute("id"));if(null!=b)for(c=0;c<this.idrefs.length;c++){var f=this.idrefs[c],g=b.getAttribute(f);if(null!=g){b.removeAttribute(f);var k=a.objects[g]||a.lookup(g);null==k&&(g=a.getElementById(g),null!=g&&(k=(mxCodecRegistry.codecs[g.nodeName]||this).decode(a,g)));d[f]=k}}return b};return a}());
-mxCodecRegistry.register(function(){var a=new mxObjectCodec(new mxGraphModel);a.encodeObject=function(a,c,d){var b=a.document.createElement("root");a.encodeCell(c.getRoot(),b);d.appendChild(b)};a.decodeChild=function(a,c,d){"root"==c.nodeName?this.decodeRoot(a,c,d):mxObjectCodec.prototype.decodeChild.apply(this,arguments)};a.decodeRoot=function(a,c,d){var b=null;for(c=c.firstChild;null!=c;){var f=a.decodeCell(c);null!=f&&null==f.getParent()&&(b=f);c=c.nextSibling}null!=b&&d.setRoot(b)};return a}());
-mxCodecRegistry.register(function(){var a=new mxObjectCodec(new mxRootChange,["model","previous","root"]);a.afterEncode=function(a,c,d){a.encodeCell(c.root,d);return d};a.beforeDecode=function(a,c,d){if(null!=c.firstChild&&c.firstChild.nodeType==mxConstants.NODETYPE_ELEMENT){c=c.cloneNode(!0);var b=c.firstChild;d.root=a.decodeCell(b,!1);d=b.nextSibling;b.parentNode.removeChild(b);for(b=d;null!=b;)d=b.nextSibling,a.decodeCell(b),b.parentNode.removeChild(b),b=d}return c};a.afterDecode=function(a,c,
-d){d.previous=d.root;return d};return a}());
-mxCodecRegistry.register(function(){var a=new mxObjectCodec(new mxChildChange,["model","child","previousIndex"],["parent","previous"]);a.isReference=function(a,c,d,e){return"child"!=c||null==a.previous&&e?0<=mxUtils.indexOf(this.idrefs,c):!0};a.afterEncode=function(a,c,d){this.isReference(c,"child",c.child,!0)?d.setAttribute("child",a.getId(c.child)):a.encodeCell(c.child,d);return d};a.beforeDecode=function(a,c,d){if(null!=c.firstChild&&c.firstChild.nodeType==mxConstants.NODETYPE_ELEMENT){c=c.cloneNode(!0);
-var b=c.firstChild;d.child=a.decodeCell(b,!1);d=b.nextSibling;b.parentNode.removeChild(b);for(b=d;null!=b;){d=b.nextSibling;if(b.nodeType==mxConstants.NODETYPE_ELEMENT){var f=b.getAttribute("id");null==a.lookup(f)&&a.decodeCell(b)}b.parentNode.removeChild(b);b=d}}else b=c.getAttribute("child"),d.child=a.getObject(b);return c};a.afterDecode=function(a,c,d){null!=d.child&&(null!=d.child.parent&&null!=d.previous&&d.child.parent!=d.previous&&(d.previous=d.child.parent),d.child.parent=d.previous,d.previous=
-d.parent,d.previousIndex=d.index);return d};return a}());mxCodecRegistry.register(function(){var a=new mxObjectCodec(new mxTerminalChange,["model","previous"],["cell","terminal"]);a.afterDecode=function(a,c,d){d.previous=d.terminal;return d};return a}());var mxGenericChangeCodec=function(a,b){var c=new mxObjectCodec(a,["model","previous"],["cell"]);c.afterDecode=function(a,c,f){mxUtils.isNode(f.cell)&&(f.cell=a.decodeCell(f.cell,!1));f.previous=f[b];return f};return c};
-mxCodecRegistry.register(mxGenericChangeCodec(new mxValueChange,"value"));mxCodecRegistry.register(mxGenericChangeCodec(new mxStyleChange,"style"));mxCodecRegistry.register(mxGenericChangeCodec(new mxGeometryChange,"geometry"));mxCodecRegistry.register(mxGenericChangeCodec(new mxCollapseChange,"collapsed"));mxCodecRegistry.register(mxGenericChangeCodec(new mxVisibleChange,"visible"));mxCodecRegistry.register(mxGenericChangeCodec(new mxCellAttributeChange,"value"));
-mxCodecRegistry.register(function(){return new mxObjectCodec(new mxGraph,"graphListeners eventListeners view container cellRenderer editor selection".split(" "))}());
-mxCodecRegistry.register(function(){var a=new mxObjectCodec(new mxGraphView);a.encode=function(a,c){return this.encodeCell(a,c,c.graph.getModel().getRoot())};a.encodeCell=function(a,c,d){var b=c.graph.getModel(),f=c.getState(d),g=b.getParent(d);if(null==g||null!=f){var k=b.getChildCount(d),l=c.graph.getCellGeometry(d),m=null;g==b.getRoot()?m="layer":null==g?m="graph":b.isEdge(d)?m="edge":0<k&&null!=l?m="group":b.isVertex(d)&&(m="vertex");if(null!=m){var n=a.document.createElement(m);null!=c.graph.getLabel(d)&&
-(n.setAttribute("label",c.graph.getLabel(d)),c.graph.isHtmlLabel(d)&&n.setAttribute("html",!0));if(null==g){var p=c.getGraphBounds();null!=p&&(n.setAttribute("x",Math.round(p.x)),n.setAttribute("y",Math.round(p.y)),n.setAttribute("width",Math.round(p.width)),n.setAttribute("height",Math.round(p.height)));n.setAttribute("scale",c.scale)}else if(null!=f&&null!=l){for(p in f.style)g=f.style[p],"function"==typeof g&&"object"==typeof g&&(g=mxStyleRegistry.getName(g)),null!=g&&"function"!=typeof g&&"object"!=
-typeof g&&n.setAttribute(p,g);g=f.absolutePoints;if(null!=g&&0<g.length){l=Math.round(g[0].x)+","+Math.round(g[0].y);for(p=1;p<g.length;p++)l+=" "+Math.round(g[p].x)+","+Math.round(g[p].y);n.setAttribute("points",l)}else n.setAttribute("x",Math.round(f.x)),n.setAttribute("y",Math.round(f.y)),n.setAttribute("width",Math.round(f.width)),n.setAttribute("height",Math.round(f.height));p=f.absoluteOffset;null!=p&&(0!=p.x&&n.setAttribute("dx",Math.round(p.x)),0!=p.y&&n.setAttribute("dy",Math.round(p.y)))}for(p=
-0;p<k;p++)f=this.encodeCell(a,c,b.getChildAt(d,p)),null!=f&&n.appendChild(f)}}return n};return a}());
-var mxStylesheetCodec=mxCodecRegistry.register(function(){var a=new mxObjectCodec(new mxStylesheet);a.encode=function(a,c){var b=a.document.createElement(this.getName()),e;for(e in c.styles){var f=c.styles[e],g=a.document.createElement("add");if(null!=e){g.setAttribute("as",e);for(var k in f){var l=this.getStringValue(k,f[k]);if(null!=l){var m=a.document.createElement("add");m.setAttribute("value",l);m.setAttribute("as",k);g.appendChild(m)}}0<g.childNodes.length&&b.appendChild(g)}}return b};a.getStringValue=
-function(a,c){var b=typeof c;"function"==b?c=mxStyleRegistry.getName(style[j]):"object"==b&&(c=null);return c};a.decode=function(a,c,d){d=d||new this.template.constructor;var b=c.getAttribute("id");null!=b&&(a.objects[b]=d);for(c=c.firstChild;null!=c;){if(!this.processInclude(a,c,d)&&"add"==c.nodeName&&(b=c.getAttribute("as"),null!=b)){var f=c.getAttribute("extend"),g=null!=f?mxUtils.clone(d.styles[f]):null;null==g&&(null!=f&&mxLog.warn("mxStylesheetCodec.decode: stylesheet "+f+" not found to extend"),
-g={});for(f=c.firstChild;null!=f;){if(f.nodeType==mxConstants.NODETYPE_ELEMENT){var k=f.getAttribute("as");if("add"==f.nodeName){var l=mxUtils.getTextContent(f);null!=l&&0<l.length&&mxStylesheetCodec.allowEval?l=mxUtils.eval(l):(l=f.getAttribute("value"),mxUtils.isNumeric(l)&&(l=parseFloat(l)));null!=l&&(g[k]=l)}else"remove"==f.nodeName&&delete g[k]}f=f.nextSibling}d.putCellStyle(b,g)}c=c.nextSibling}return d};return a}());mxStylesheetCodec.allowEval=!0;
-mxCodecRegistry.register(function(){var a=new mxObjectCodec(new mxDefaultKeyHandler);a.encode=function(a,c){return null};a.decode=function(a,c,d){if(null!=d)for(c=c.firstChild;null!=c;){if(!this.processInclude(a,c,d)&&"add"==c.nodeName){var b=c.getAttribute("as"),f=c.getAttribute("action"),g=c.getAttribute("control");d.bindAction(b,f,g)}c=c.nextSibling}return d};return a}());
-var mxDefaultToolbarCodec=mxCodecRegistry.register(function(){var a=new mxObjectCodec(new mxDefaultToolbar);a.encode=function(a,c){return null};a.decode=function(a,c,d){if(null!=d){var b=d.editor;for(c=c.firstChild;null!=c;){if(c.nodeType==mxConstants.NODETYPE_ELEMENT&&!this.processInclude(a,c,d))if("separator"==c.nodeName)d.addSeparator();else if("br"==c.nodeName)d.toolbar.addBreak();else if("hr"==c.nodeName)d.toolbar.addLine();else if("add"==c.nodeName){var f=c.getAttribute("as"),f=mxResources.get(f)||
-f,g=c.getAttribute("icon"),k=c.getAttribute("pressedIcon"),l=c.getAttribute("action"),m=c.getAttribute("mode"),n=c.getAttribute("template"),p="0"!=c.getAttribute("toggle"),q=mxUtils.getTextContent(c),r=null;if(null!=l)r=d.addItem(f,g,l,k);else if(null!=m)var t=mxDefaultToolbarCodec.allowEval?mxUtils.eval(q):null,r=d.addMode(f,g,m,k,t);else if(null!=n||null!=q&&0<q.length)r=b.templates[n],n=c.getAttribute("style"),null!=r&&null!=n&&(r=b.graph.cloneCells([r])[0],r.setStyle(n)),n=null,null!=q&&0<q.length&&
-mxDefaultToolbarCodec.allowEval&&(n=mxUtils.eval(q)),r=d.addPrototype(f,g,r,k,n,p);else if(k=mxUtils.getChildNodes(c),0<k.length)if(null==g)for(n=d.addActionCombo(f),f=0;f<k.length;f++)p=k[f],"separator"==p.nodeName?d.addOption(n,"---"):"add"==p.nodeName&&(g=p.getAttribute("as"),p=p.getAttribute("action"),d.addActionOption(n,g,p));else{var u=null,x=d.addPrototype(f,g,function(){var a=b.templates[u.value];if(null!=a){var a=a.clone(),c=u.options[u.selectedIndex].cellStyle;null!=c&&a.setStyle(c);return a}mxLog.warn("Template "+
-a+" not found");return null},null,null,p),u=d.addCombo();mxEvent.addListener(u,"change",function(){d.toolbar.selectMode(x,function(a){a=mxUtils.convertPoint(b.graph.container,mxEvent.getClientX(a),mxEvent.getClientY(a));return b.addVertex(null,t(),a.x,a.y)});d.toolbar.noReset=!1});for(f=0;f<k.length;f++)p=k[f],"separator"==p.nodeName?d.addOption(u,"---"):"add"==p.nodeName&&(g=p.getAttribute("as"),q=p.getAttribute("template"),d.addOption(u,g,q||n).cellStyle=p.getAttribute("style"))}null!=r&&(n=c.getAttribute("id"),
-null!=n&&0<n.length&&r.setAttribute("id",n))}c=c.nextSibling}}return d};return a}());mxDefaultToolbarCodec.allowEval=!0;mxCodecRegistry.register(function(){var a=new mxObjectCodec(new mxDefaultPopupMenu);a.encode=function(a,c){return null};a.decode=function(a,c,d){var b=c.getElementsByTagName("include")[0];null!=b?this.processInclude(a,b,d):null!=d&&(d.config=c);return d};return a}());
-mxCodecRegistry.register(function(){var a=new mxObjectCodec(new mxEditor,"modified lastSnapshot ignoredChanges undoManager graphContainer toolbarContainer".split(" "));a.afterDecode=function(a,c,d){a=c.getAttribute("defaultEdge");null!=a&&(c.removeAttribute("defaultEdge"),d.defaultEdge=d.templates[a]);a=c.getAttribute("defaultGroup");null!=a&&(c.removeAttribute("defaultGroup"),d.defaultGroup=d.templates[a]);return d};a.decodeChild=function(a,c,d){if("Array"==c.nodeName){if("templates"==c.getAttribute("as")){this.decodeTemplates(a,
-c,d);return}}else if("ui"==c.nodeName){this.decodeUi(a,c,d);return}mxObjectCodec.prototype.decodeChild.apply(this,arguments)};a.decodeUi=function(a,c,d){for(a=c.firstChild;null!=a;){if("add"==a.nodeName){c=a.getAttribute("as");var b=a.getAttribute("element"),f=a.getAttribute("style");if(null!=b)b=document.getElementById(b),null!=b&&null!=f&&(b.style.cssText+=";"+f);else{var g=parseInt(a.getAttribute("x")),k=parseInt(a.getAttribute("y")),l=a.getAttribute("width"),m=a.getAttribute("height"),b=document.createElement("div");
-b.style.cssText=f;(new mxWindow(mxResources.get(c)||c,b,g,k,l,m,!1,!0)).setVisible(!0)}"graph"==c?d.setGraphContainer(b):"toolbar"==c?d.setToolbarContainer(b):"title"==c?d.setTitleContainer(b):"status"==c?d.setStatusContainer(b):"map"==c&&d.setMapContainer(b)}else"resource"==a.nodeName?mxResources.add(a.getAttribute("basename")):"stylesheet"==a.nodeName&&mxClient.link("stylesheet",a.getAttribute("name"));a=a.nextSibling}};a.decodeTemplates=function(a,c,d){null==d.templates&&(d.templates=[]);c=mxUtils.getChildNodes(c);
-for(var b=0;b<c.length;b++){for(var f=c[b].getAttribute("as"),g=c[b].firstChild;null!=g&&1!=g.nodeType;)g=g.nextSibling;null!=g&&(d.templates[f]=a.decodeCell(g))}};return a}()); \ No newline at end of file
diff --git a/src/templates/base/resource/steps/define_hardware.html b/src/templates/base/resource/steps/define_hardware.html
deleted file mode 100644
index 573b996..0000000
--- a/src/templates/base/resource/steps/define_hardware.html
+++ /dev/null
@@ -1,17 +0,0 @@
-{% extends "workflow/viewport-element.html" %}
-{% load staticfiles %}
-
-{% load bootstrap4 %}
-
-{% block content %}
-<p>Note that not all labs host every kind of machine.
-As you make your selections, labs and hosts that are not compatible
-with your current configuration will become unavailable.</p>
-<h4>NOTE: Only PTL's are able to create multi-node PODs. See
- <a href="https://wiki.opnfv.org/display/INF/Lab-as-a-Service+at+the+UNH-IOL">here</a>
- for more details</h4>
-<form id="step_form" method="post">
- {% csrf_token %}
- {{form.filter_field|default:"<p>No Form</p>"}}
-</form>
-{% endblock content %}
diff --git a/src/templates/base/resource/steps/host_info.html b/src/templates/base/resource/steps/host_info.html
deleted file mode 100644
index 3230d8f..0000000
--- a/src/templates/base/resource/steps/host_info.html
+++ /dev/null
@@ -1,34 +0,0 @@
-{% extends "workflow/viewport-element.html" %}
-{% load staticfiles %}
-
-{% load bootstrap4 %}
-
-{% block content %}
-
-{% if error %}
-<p>{{error}}</p>
-{% else %}
-
-
-<form id="step_form" method="post">
- {% csrf_token %}
- <table>
- <thead>
- <tr>
- <th>Type</th>
- <th>Name</th>
- </tr>
- </thead>
- <tbody>
- {% for form in formset %}
- <tr>
- {% for field in form %}
- <td>{{field}}</td>
- {% endfor %}
- </tr>
- {% endfor %}
- </table>
- {{formset.management_form}}
-</form>
-{% endif %}
-{% endblock content %}
diff --git a/src/templates/base/resource/steps/meta_info.html b/src/templates/base/resource/steps/meta_info.html
deleted file mode 100644
index 6fef065..0000000
--- a/src/templates/base/resource/steps/meta_info.html
+++ /dev/null
@@ -1,14 +0,0 @@
-{% extends "workflow/viewport-element.html" %}
-{% load staticfiles %}
-
-{% load bootstrap4 %}
-
-{% block content %}
-
-<form id="step_form" method="post">
- {% csrf_token %}
- <table class="px-4">
- {% bootstrap_form form %}
- </table>
-</form>
-{% endblock content %}
diff --git a/src/templates/base/resource/steps/pod_definition.html b/src/templates/base/resource/steps/pod_definition.html
deleted file mode 100644
index 233d995..0000000
--- a/src/templates/base/resource/steps/pod_definition.html
+++ /dev/null
@@ -1,72 +0,0 @@
-{% extends "workflow/viewport-element.html" %}
-{% block extrahead %}
-<title>Pod Definition Prototype</title>
-
-<!-- Loads and initializes the library -->
-<script>
- var mxLoadStylesheets = false;
-</script>
-{% endblock extrahead %}
-
-<!-- Calls the main function after the page has loaded. Container is dynamically created. -->
-{% block content %}
-<div class="h-100 w-100 position-relative">
- <div class="h-100 w-100 position-absolute overflow-hidden">
- <div class="row h-100">
- <div id="graphParent" class="col h-100">
- <div class="d-flex bg-light border align-items-center">
- <div id="toolbarContainer"></div>
- <div class="ml-4 text-info">Hold right click to drag</div>
- </div>
- <!-- Creates a container for the sidebar -->
- <div id="graphContainer" class="border h-100"></div>
- </div>
-
- <div id="network_select" class="col-2">
- <!-- Creates a container for the outline -->
- <div id="outlineContainer" class="border mb-2"></div>
- <button id="btn_add_network" type="button" class="btn btn-primary w-100 mb-2" onclick="network_step.newNetworkWindow();">Add Network</button>
- <ul id="network_list" class="list-group">
- </ul>
- <button type="button" class="d-none" onclick="network_step.submitForm();">Submit</button>
- </div>
- </div>
- </div>
-</div>
-<form id="step_form" method="post">
- {% csrf_token %}
- <input type="hidden" id="hidden_xml_input" name="xml">
-</form>
-<script>
- //gather context data
- try {
- let debug = false;
- {% if debug %}
- debug = true;
- {% endif %}
-
- const False = false;
- const True = true;
-
- let resources = {{resources|safe}};
- let networks = {{networks|safe}};
-
- network_step = new NetworkStep(
- debug,
- resources,
- networks,
- document.getElementById('graphContainer'),
- document.getElementById('outlineContainer'),
- document.getElementById('toolbarContainer'),
- document.getElementById('sidebarContainer')
- );
- form_submission_callbacks.push(() => network_step.prepareForm());
- } catch (e) {
- console.log(e)
- }
-
-</script>
-{% endblock content %}
-{% block onleave %}
-network_step.submitForm();
-{% endblock %}
diff --git a/src/templates/base/resource/uncommon.css b/src/templates/base/resource/uncommon.css
deleted file mode 100644
index 1ea9f35..0000000
--- a/src/templates/base/resource/uncommon.css
+++ /dev/null
@@ -1,162 +0,0 @@
-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('static/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/src/templates/base/rest_framework/api.html b/src/templates/base/rest_framework/api.html
deleted file mode 100644
index a62c8f5..0000000
--- a/src/templates/base/rest_framework/api.html
+++ /dev/null
@@ -1,9 +0,0 @@
-{% extends "rest_framework/base.html" %}
-
-{% block title %}Laas Dashboard API{% endblock %}
-
-{% block branding %}
- <a class='navbar-brand' rel="nofollow" href=#>
- Laas Dashboard API
- </a>
-{% endblock %} \ No newline at end of file
diff --git a/src/templates/base/snapshot_workflow/steps/meta.html b/src/templates/base/snapshot_workflow/steps/meta.html
deleted file mode 100644
index 88136d2..0000000
--- a/src/templates/base/snapshot_workflow/steps/meta.html
+++ /dev/null
@@ -1,17 +0,0 @@
-{% extends "workflow/viewport-element.html" %}
-{% load staticfiles %}
-
-{% load bootstrap4 %}
-
-{% block content %}
-{% bootstrap_form_errors form type='non_fields' %}
-<div class="p-4">
- <form id="step_form" method="POST" class="form">
- {% csrf_token %}
- <div class="form-group">
- {% bootstrap_field form.name %}
- {% bootstrap_field form.description %}
- </div>
- </form>
-</div>
-{% endblock content %}
diff --git a/src/templates/base/snapshot_workflow/steps/select_host.html b/src/templates/base/snapshot_workflow/steps/select_host.html
deleted file mode 100644
index 4243145..0000000
--- a/src/templates/base/snapshot_workflow/steps/select_host.html
+++ /dev/null
@@ -1,83 +0,0 @@
-{% extends "workflow/viewport-element.html" %}
-{% load staticfiles %}
-
-{% load bootstrap4 %}
-
-{% block content %}
-
-{% bootstrap_form_errors form type='non_fields' %}
-<form id="step_form" method="POST" class="form">
-{% csrf_token %}
-<input type="hidden" id="hidden_json_input", name="host"/>
-</form>
-<div class="container-fluid">
- <div class="row" id="host_select_container">
- </div>
-</div>
-<script>
-var selected_host = null;
-var initial = {{chosen|safe|default:'null'}};
-
-function select(obj){
- var booking_id = $(obj).attr("booking");
- var host_name = $(obj).attr("hostname");
- var input = document.getElementById("hidden_json_input");
- input.value = JSON.stringify({"booking": booking_id, "name": host_name});
- // clear out and highlist host
- if(selected_host != null){
- selected_host.classList.remove("active");
- }
- selected_host = document.getElementById("booking_" + booking_id + "_host_" + host_name);
- selected_host.classList.add("active");
-}
-
-function draw_bookings(){
- var booking_hosts = {{booking_hosts|safe}};
- var bookings = [];
- var container = document.getElementById("host_select_container");
- for(var booking_id in booking_hosts){
- // Create a column with a card
- var column = $("<div/>", {
- class: "col-12 col-md-6 col-lg-3 col-xl-2 my-2"
- }).appendTo(container);
- var booking = $("<div/>", {
- class: "card"
- }).appendTo(column);
- var heading = $("<div/>", {
- class: "card-header"
- }).text(`Booking ${booking_id}`).appendTo(booking);
- var body = $("<ul/>", {
- class: "list-group list-group-flush"
- }).appendTo(booking);
- var footer = $("<div/>", {
- text: "Hosts:",
- class: "card-footer d-flex flex-column"
- }).appendTo(booking);
-
- // Append information to the card body
- $(`<li class="list-group-item">Start: ${booking_hosts[booking_id].start}</li>`).appendTo(body);
- $(`<li class="list-group-item">End: ${booking_hosts[booking_id].end}</li>`).appendTo(body);
- $(`<li class="list-group-item">Purpose: ${booking_hosts[booking_id].purpose}</li>`).appendTo(body);
-
- // Append hosts to footer
- var hosts = booking_hosts[booking_id].hosts;
- for (const host of hosts) {
- $("<button/>", {
- class: "btn btn-outline-primary w-100 mt-1 hostbtn",
- id: `booking_${booking_id}_host_${host.name}`,
- text: host.name,
- booking: booking_id,
- hostname: host.name,
- click: function() {
- select(this);
- }
- }).appendTo(footer);
- }
- }
-}
-draw_bookings();
-if(initial){
- select(initial.booking_id, initial.hostname);
-}
-</script>
-{% endblock content %}
diff --git a/src/templates/base/workflow/confirm.html b/src/templates/base/workflow/confirm.html
deleted file mode 100644
index bc8e4e3..0000000
--- a/src/templates/base/workflow/confirm.html
+++ /dev/null
@@ -1,56 +0,0 @@
-{% extends "workflow/viewport-element.html" %}
-{% load staticfiles %}
-
-{% load bootstrap4 %}
-
-{% block content %}
-
-<div class="text-center">
- <h3>Confirm Session</h3>
-</div>
-<div class="container">
- <div class="row justify-content-center">
- <div class="col-auto">
- <pre>{{confirmation_info|escape}}</pre>
- </div>
- </div>
- <div class="row">
- <div class="col">
- <div id="form_div" class="text-center p-4">
- <form id="step_form" action="/workflow/manager/" method="post">
- {% csrf_token %}
- <div class="d-none">
- {{form|default:"<p> No Form Loaded</p>"}}
- </div>
- </form>
- <div class="cform_buttons mx-auto">
- <button id="confirm_button" class="btn btn-success" onclick="formconfirm()">Confirm</button>
- <button id="cancel_button" class="btn btn-danger" onclick="formcancel()">Cancel</button>
- </div>
- <div class="d-none">
- <form id="manager_delete_form" action="/workflow/finish/" method="post">
- {% csrf_token %}
- </form>
- </div>
- </div>
- </div>
- </div>
-</div>
-<script>
- var select = document.getElementById("id_confirm");
-
- function formconfirm()
- {
- select.value = "True";
- submitStepForm();
- pop_workflow();
- }
- function formcancel()
- {
- pop_workflow();
- }
-</script>
-{% block element_messages %}
-
-{% endblock element_messages %}
-{% endblock content %}
diff --git a/src/templates/base/workflow/no_workflow.html b/src/templates/base/workflow/no_workflow.html
deleted file mode 100644
index 0ac6549..0000000
--- a/src/templates/base/workflow/no_workflow.html
+++ /dev/null
@@ -1,3 +0,0 @@
-<script>
- top.window.location.href='/';
-</script>
diff --git a/src/templates/base/workflow/viewport-base.html b/src/templates/base/workflow/viewport-base.html
deleted file mode 100644
index 88229ca..0000000
--- a/src/templates/base/workflow/viewport-base.html
+++ /dev/null
@@ -1,82 +0,0 @@
-{% extends "base.html" %}
-{% load staticfiles %}
-
-{% load bootstrap4 %}
-
-{% block content %}
-
-<!-- Pagination -->
-<div class="row mt-3">
- <div class="col">
- <nav>
- <ul class="pagination d-flex flex-row" id="topPagination">
- <li class="page-item flex-shrink-1 page-control" id="workflow-nav-back">
- <a class="page-link" href="#" id="gob" onclick="submit_and_go('prev')">
- <i class="fas fa-backward"></i> Back
- </a>
- </li>
- <li class="page-item flex-grow-1">
- <a class="page-link disabled" href="#">
- <i class="far"></i>
- </a>
- </li>
- <li class="page-item flex-shrink-1 page-control" id="workflow-nav-next">
- <a class="page-link text-right" href="#" id="gof" onclick="submit_and_go('next')">
- Next <i class="fas fa-forward"></i>
- </a>
- </li>
- </ul>
- </nav>
- </div>
-</div>
- <div class=”row”>
- <div class=”col-xs-6 col-md-4”>
- Is something not working right? Let us know <a href="mailto::{{contact_email}}"> here! </a>
- </div>
-</div>
-<!-- Top header -->
-<div class="row">
- <div class="col">
- <div id="iframe_header" class="row view-header">
- <div class="col-lg-12">
- <h1 class="d-inline-block" id="view_title"></h1>
- <span class="description text-muted" id="view_desc"></span>
- <p id="view_message"></p>
- </div>
- </div>
- </div>
- <div class="col-auto align-self-center d-flex">
- <button id="cancel_btn" class="btn btn-danger ml-auto" onclick="pop_workflow()">Cancel</button>
- </div>
-</div>
-<div class="row d-flex flex-column flex-grow-1 mt-3">
- <div class="col flex-grow-1">
- <div id="formContainer" class="h-100 w-100"></div>
- </div>
-</div>
-{% csrf_token %}
-<script type="text/javascript">
- function submit_and_go(to) {
- submitStepForm(to);
- }
-
- $(document).ready(function(){
- $.ajax({
- url: "/workflow/manager/",
- dataType: "json",
- success: update_page
- });
- });
-
- // global variable required for mxgraph to load its css and images
- mxBasePath = '{% static "node_modules/mxgraph/javascript/src" %}';
-</script>
-<!-- lazy load scripts -->
-<script type="text/javascript" src="{% static "node_modules/mxgraph/javascript/mxClient.js" %}" ></script>
-<!-- end lazy load scripts -->
-<div class="d-none" id="workflow_pop_form_div">
- <form id="workflow_pop_form" action="/workflow/finish/" method="post">
- {% csrf_token %}
- </form>
-</div>
-{% endblock content %}
diff --git a/src/templates/base/workflow/viewport-element.html b/src/templates/base/workflow/viewport-element.html
deleted file mode 100644
index db4da54..0000000
--- a/src/templates/base/workflow/viewport-element.html
+++ /dev/null
@@ -1,17 +0,0 @@
-{% load bootstrap4 %}
-{% load staticfiles %}
-
-{% block basecontent %}
-
- {% block content %}
- {% endblock content %}
-
- <div class="messages">
- {% block element_messages %}
- {% bootstrap_messages %}
- {% endblock %}
- </div>
-{% endblock basecontent %}
-
-{% block extrajs %}
-{% endblock extrajs %}
diff --git a/src/templates/laas/base.html b/src/templates/laas/base.html
deleted file mode 100644
index f980268..0000000
--- a/src/templates/laas/base.html
+++ /dev/null
@@ -1,89 +0,0 @@
-{% extends "base/base.html" %}
-{% load staticfiles %}
-{% block logo %}
-
-<style>
- nav ,body{
- background-color:#fff !important;
- color:#343a40 !important;
- }
-
- header{
- background-color:#f8f9fa !important;
- color:#343a40 !important;
- }
-
- .nav-bg{
- background-color:#fff !important;
- color:#343a40 !important;
- }
-
- .nav-bg:hover{
- background-color:#f8f9fa !important;
- transition-duration:0.2s;
- }
-
- .dropDown-bg{
- background-color:#d6d8db !important;
- color:#343a40 !important;
- }
-
- .btnAnuket {
- color: #343a40;
- background-color: #6BDAD5;
- transition-duration:0.2s;
- border:0px
- }
- .btnAnuket:hover{
- color: #f8f9fa;
- background-color: #007473;
- border:0px
- }
-
- .btnAnuket:focus{
- color: #f8f9fa !important;
- background-color: #007473 !important;
- border:0px
- }
-
- .alertAnuket{
- background-color: #e6b3c1;
- color:#820c2c;
- border:0px;
- }
- .inTextLink{
- text-decoration: underline;
- }
-
- .Anuket-Text{
- color:#343a40 !important;
- }
-
- h1, h2{
- color:#343a40 !important;
- }
-
- p, h3, h4, h5{
- color:#343a40 !important;
- }
-
- ::selection {
- background: #BCE194;
- color:#343a40;
- }
- ::-moz-selection {
- background: #BCE194;
- color:#343a40;
- }
- </style>
-
-<div class="col-12 col-sm order-1 order-sm-2 text-center text-lg-left">
- <a href="https://anuket.io/" class="navbar-brand">
- <img src="{% static "img/Anuket-logo.svg" %}" width="134.2" height="50" style="vertical-align:middle; margin:12px 12px 12px -20px;">
- </a>
- <a class="navbar-brand d-none d-lg-inline Anuket-Text" href={% url 'dashboard:index' %} style="margin-left:10px; font-size:26px; vertical-align:middle;">
- LaaS Dashboard
- </a>
-</div>
-{% endblock logo %}
-
diff --git a/src/templates/laas/dashboard/landing.html b/src/templates/laas/dashboard/landing.html
deleted file mode 100644
index 12d8924..0000000
--- a/src/templates/laas/dashboard/landing.html
+++ /dev/null
@@ -1,12 +0,0 @@
-{% extends "base/dashboard/landing.html" %}
-
-{% block about_us %}
-<p>The Lab as a Service (LaaS) project aims to help in the development and testing of LFN projects such as
- Anuket
- by hosting hardware and providing access to the community. Currently, the only participating lab is the
- University of New Hampshire Interoperability Lab (UNH-IOL).</p>
-<p>To get started, you can request access to a server at the right. PTL's have the ability to design and
- book a
- whole block of servers with customized layer2 networks (e.g. a Pharos Pod). Read more here:
- <a href="https://wiki.anuket.io/display/HOME/Lab+as+a+Service" target="_blank" class="inTextLink">LaaS Wiki</a></p>
-{% endblock %} \ No newline at end of file
diff --git a/src/templates/laas/layout.html b/src/templates/laas/layout.html
deleted file mode 100644
index f9b1d99..0000000
--- a/src/templates/laas/layout.html
+++ /dev/null
@@ -1,5 +0,0 @@
-{% extends "base/layout.html" %}
-
-{% block head-title %}
-<title>LaaS Dashboard</title>
-{% endblock head-title %}
diff --git a/src/templates/lfedge/base.html b/src/templates/lfedge/base.html
deleted file mode 100644
index 4413340..0000000
--- a/src/templates/lfedge/base.html
+++ /dev/null
@@ -1,31 +0,0 @@
-{% extends "base/base.html" %}
-{% load staticfiles %}
-{% block bgColor %}
-<link rel="stylesheet" href="{% static "css/lfedge.css" %}">
-
-<nav class="navbar navbar-light LFEdge navbar-fixed-top border-bottom py-0 mb-0" role="navigation">
-{% endblock bgColor %}
-
-{% block logo %}
-<div class="barClamp col-12 col-sm order-1 order-sm-2 text-center text-lg-left">
- <a href="https://www.lfedge.org/" class="navbar-brand">
- <img src="{% static "img/lfedge-logo.png" %}" alt="lfedge logo">
- </a>
-
- <a class="wtext d-none d-lg-inline" href={% url 'dashboard:index' %}>
- Dashboard
- </a>
-</div>
-{% endblock logo %}
-{% block dropDown %}
-{% endblock dropDown %}
-{% block userDropDownText %}
-<a class="nav-link p-0 wtext p-2" data-toggle="dropdown" href="#">
- {% if request.user.username %}
- {{request.user.username}}
- {% else %}
- <i class="fas fa-user"></i>
- {% endif %}
- <i class="fas fa-caret-down rotate"></i>
-</a>
-{% endblock userDropDownText %}
diff --git a/src/templates/lfedge/booking/booking_table.html b/src/templates/lfedge/booking/booking_table.html
deleted file mode 100644
index 4020b5e..0000000
--- a/src/templates/lfedge/booking/booking_table.html
+++ /dev/null
@@ -1,41 +0,0 @@
-
-
-
-<thead>
-<tr>
- <th>Owner</th>
- <th>Purpose</th>
- <th>Project</th>
- <th>Start</th>
- <th>End</th>
- <th>Operating System</th>
- <th>Pod</th>
-</tr>
-</thead>
-<tbody>
-{% for booking in bookings %}
- <tr>
- <td>
- {{ booking.owner.username }}
- </td>
- <td>
- {{ booking.purpose }}
- </td>
- <td>
- {{ booking.project }}
- </td>
- <td>
- {{ booking.start }}
- </td>
- <td>
- {{ booking.end }}
- </td>
- <td>
- {{ booking.resource.get_head_node.config.image.os.name }}
- </td>
- <td>
- {{ booking.resource.get_template_name }}
- </td>
- </tr>
-{% endfor %}
-</tbody>
diff --git a/src/templates/lfedge/booking/quick_deploy.html b/src/templates/lfedge/booking/quick_deploy.html
deleted file mode 100644
index ccafd90..0000000
--- a/src/templates/lfedge/booking/quick_deploy.html
+++ /dev/null
@@ -1,28 +0,0 @@
-{% extends "base/booking/quick_deploy.html" %}
-{% block opnfv %}
-{% endblock opnfv %}
-{% block form-text %}
-<p class="my-0">
- Please select a host type you wish to book.
- Only available types are shown.
- More information can be found here:
- <a href="https://wiki.lfedge.org/display/LE/Shared+Community+Lab">LF Edge Wiki</a>.
- If something isn't working right, let us know <a href="mailto:{{contact_email}}"> here! </a>
-</p>
-{% endblock form-text %}
-{% block collab %}
-<div class="col-12 col-lg-6 my-2">
- <div class="col border rounded py-2 h-100">
- <label>Collaborators</label>
- {{ form.users }}
- </div>
-</div>
-{% endblock collab %}
-
-{% block image_script %}
- <script type="text/javascript">
-// document.getElementById("id_image").disabled = true;
-// document.getElementById("id_image").style.display = 'none';
-// document.getElementById("id_image").previousElementSibling.style.display = 'none';
- </script>
-{% endblock image_script %}
diff --git a/src/templates/lfedge/dashboard/landing.html b/src/templates/lfedge/dashboard/landing.html
deleted file mode 100644
index 9a776dc..0000000
--- a/src/templates/lfedge/dashboard/landing.html
+++ /dev/null
@@ -1,23 +0,0 @@
-{% extends "base/dashboard/landing.html" %}
-{% block about_us %}
- <p>The Shared Community Lab at the IOL aims to help development and testing of LF Edge projects by hosting hardware and providing access to the community.</p>
- <p>To get started, you can request access to a pod at the right.</p>
-{% endblock about_us %}
-
-{% block btnGrp %}
-<style>
-.btnLFEdge {
- color: #fff;
- background-color: #0049b0;
-}
-.btnLFEdge:hover{
- color: #fff;
- background-color: #001776;
-}
-</style>
-<p>To get started, book a pod below:</p>
-<a class="btn btnLFEdge btn-lg d-flex flex-column justify-content-center align-content-center border text-white p-4" href="/booking/quick/">Book a Pod</a>
-{% endblock btnGrp %}
-
-{% block returningUsers %}
-{% endblock returningUsers %}
diff --git a/src/templates/lfedge/layout.html b/src/templates/lfedge/layout.html
deleted file mode 100644
index 217060c..0000000
--- a/src/templates/lfedge/layout.html
+++ /dev/null
@@ -1,5 +0,0 @@
-{% extends "base/layout.html" %}
-
-{% block head-title %}
-<title>LF Edge Dashboard</title>
-{% endblock head-title %}
diff --git a/src/workflow/README b/src/workflow/README
deleted file mode 100644
index fb4b949..0000000
--- a/src/workflow/README
+++ /dev/null
@@ -1,31 +0,0 @@
-This app creates "workflows", which are long and complex interactions from the user.
-Workflows are composed of multiple steps. At each step the user inputs some information.
-The content of one step may impact following steps.
-
-The WorkflowStep object is the abstract type for all the workflow steps.
-Important attributes and methods:
-
-template - the django template to use when rendering this step
-valid - the status code from WorkflowStepStatus
-
-get_context() - returns a dictionary that is used when rendering this step's template
- You should always call super's get_context and add / overwrite any data into that
- dictionary
-
-post(data, user) - this method is called when the step is POST'd to.
- data is from the request object, suitable for a Form's constructor
-
-
-Repository
-Each step has a reference to a shared repository (self.repo).
-The repo is a key-value store that allows the steps to share data
-
-Steps render based on the current state of the repo. For example, a step
-may get information about each host the user said they want and ask for additional
-input for each machine.
-Because the steps render based on what is in the repo, a user can easily go back to
-a previous step and change some data. This data will change in the repo and
-affect later steps accordingly.
-
-Everything stored in the repo is temporary. After a workflow has been completed, the repo
-is translated into Django models and saved to the database.
diff --git a/src/workflow/__init__.py b/src/workflow/__init__.py
deleted file mode 100644
index e0408fa..0000000
--- a/src/workflow/__init__.py
+++ /dev/null
@@ -1,8 +0,0 @@
-##############################################################################
-# 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/src/workflow/apps.py b/src/workflow/apps.py
deleted file mode 100644
index adc2738..0000000
--- a/src/workflow/apps.py
+++ /dev/null
@@ -1,15 +0,0 @@
-##############################################################################
-# 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.apps import AppConfig
-
-
-class WorkflowConfig(AppConfig):
- name = 'workflow'
diff --git a/src/workflow/booking_workflow.py b/src/workflow/booking_workflow.py
deleted file mode 100644
index ef89804..0000000
--- a/src/workflow/booking_workflow.py
+++ /dev/null
@@ -1,182 +0,0 @@
-##############################################################################
-# Copyright (c) 2018 Sawyer Bergeron, Parker Berberian, and others.
-# Copyright (c) 2020 Sawyer Bergeron, Sean Smith, 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.utils import timezone
-
-from datetime import timedelta
-
-from booking.models import Booking
-from workflow.models import WorkflowStep, AbstractSelectOrCreate
-from workflow.forms import ResourceSelectorForm, BookingMetaForm, OPNFVSelectForm
-from resource_inventory.models import OPNFVConfig, ResourceTemplate
-from django.db.models import Q
-
-
-"""
-subclassing notes:
- subclasses have to define the following class attributes:
- self.repo_key: main output of step, where the selected/created single selector
- result is placed at the end
- self.confirm_key:
-"""
-
-
-class Abstract_Resource_Select(AbstractSelectOrCreate):
- form = ResourceSelectorForm
- template = 'dashboard/genericselect.html'
- title = "Select Resource"
- description = "Select a resource template to use for your deployment"
- short_title = "pod select"
-
- def __init__(self, *args, **kwargs):
- super().__init__(*args, **kwargs)
- self.select_repo_key = self.repo.SELECTED_RESOURCE_TEMPLATE
- self.confirm_key = self.workflow_type
-
- def alert_bundle_missing(self):
- self.set_invalid("Please select a valid resource template")
-
- def get_form_queryset(self):
- user = self.repo_get(self.repo.SESSION_USER)
- return ResourceTemplate.objects.filter((Q(owner=user) | Q(public=True)))
-
- def get_page_context(self):
- return {
- 'select_type': 'resource',
- 'select_type_title': 'Resource template',
- 'addable_type_num': 1
- }
-
- def put_confirm_info(self, bundle):
- confirm_dict = self.repo_get(self.repo.CONFIRMATION)
- if self.confirm_key not in confirm_dict:
- confirm_dict[self.confirm_key] = {}
- confirm_dict[self.confirm_key]["Resource Template"] = bundle.name
- self.repo_put(self.repo.CONFIRMATION, confirm_dict)
-
-
-class Booking_Resource_Select(Abstract_Resource_Select):
- workflow_type = "booking"
-
-
-class OPNFV_EnablePicker(object):
- pass
-
-
-class OPNFV_Select(AbstractSelectOrCreate, OPNFV_EnablePicker):
- title = "Choose an OPNFV Config"
- description = "Choose or create a description of how you want to deploy OPNFV"
- short_title = "opnfv config"
- form = OPNFVSelectForm
- enabled = False
-
- def __init__(self, *args, **kwargs):
- super().__init__(*args, **kwargs)
- self.select_repo_key = self.repo.SELECTED_OPNFV_CONFIG
- self.confirm_key = "booking"
-
- def alert_bundle_missing(self):
- self.set_invalid("Please select a valid OPNFV config")
-
- def get_form_queryset(self):
- cb = self.repo_get(self.repo.SELECTED_CONFIG_BUNDLE)
- qs = OPNFVConfig.objects.filter(bundle=cb)
- return qs
-
- def put_confirm_info(self, config):
- confirm_dict = self.repo_get(self.repo.CONFIRMATION)
- if self.confirm_key not in confirm_dict:
- confirm_dict[self.confirm_key] = {}
- confirm_dict[self.confirm_key]["OPNFV Configuration"] = config.name
- self.repo_put(self.repo.CONFIRMATION, confirm_dict)
-
- def get_page_context(self):
- return {
- 'select_type': 'opnfv',
- 'select_type_title': 'OPNFV Config',
- 'addable_type_num': 4
- }
-
-
-class Booking_Meta(WorkflowStep):
- template = 'booking/steps/booking_meta.html'
- title = "Extra Info"
- description = "Tell us how long you want your booking, what it is for, and who else should have access to it"
- short_title = "booking info"
-
- def get_context(self):
- context = super(Booking_Meta, self).get_context()
- initial = {}
- default = []
- try:
- models = self.repo_get(self.repo.BOOKING_MODELS, {})
- booking = models.get("booking")
- if booking:
- initial['purpose'] = booking.purpose
- initial['project'] = booking.project
- initial['length'] = (booking.end - booking.start).days
- info = self.repo_get(self.repo.BOOKING_INFO_FILE, False)
- if info:
- initial['info_file'] = info
- users = models.get("collaborators", [])
- for user in users:
- default.append(user.userprofile)
- except Exception:
- pass
-
- owner = self.repo_get(self.repo.SESSION_USER)
-
- context['form'] = BookingMetaForm(initial=initial, user_initial=default, owner=owner)
- return context
-
- def post(self, post_data, user):
- form = BookingMetaForm(data=post_data, owner=user)
-
- forms = self.repo_get(self.repo.BOOKING_FORMS, {})
-
- forms["meta_form"] = form
- self.repo_put(self.repo.BOOKING_FORMS, forms)
-
- if form.is_valid():
- models = self.repo_get(self.repo.BOOKING_MODELS, {})
- if "booking" not in models:
- models['booking'] = Booking()
- models['collaborators'] = []
- confirm = self.repo_get(self.repo.CONFIRMATION)
- if "booking" not in confirm:
- confirm['booking'] = {}
-
- models['booking'].start = timezone.now()
- models['booking'].end = timezone.now() + timedelta(days=int(form.cleaned_data['length']))
- models['booking'].purpose = form.cleaned_data['purpose']
- models['booking'].project = form.cleaned_data['project']
- for key in ['length', 'project', 'purpose']:
- confirm['booking'][key] = form.cleaned_data[key]
-
- if form.cleaned_data["deploy_opnfv"]:
- self.repo_get(self.repo.SESSION_MANAGER).set_step_statuses(OPNFV_EnablePicker, desired_enabled=True)
- else:
- self.repo_get(self.repo.SESSION_MANAGER).set_step_statuses(OPNFV_EnablePicker, desired_enabled=False)
-
- userprofile_list = form.cleaned_data['users']
- confirm['booking']['collaborators'] = []
- for userprofile in userprofile_list:
- models['collaborators'].append(userprofile.user)
- confirm['booking']['collaborators'].append(userprofile.user.username)
-
- info_file = form.cleaned_data.get("info_file", False)
- if info_file:
- self.repo_put(self.repo.BOOKING_INFO_FILE, info_file)
-
- self.repo_put(self.repo.BOOKING_MODELS, models)
- self.repo_put(self.repo.CONFIRMATION, confirm)
- self.set_valid("Step Completed")
- else:
- self.set_invalid("Please complete the fields highlighted in red to continue")
diff --git a/src/workflow/forms.py b/src/workflow/forms.py
deleted file mode 100644
index 62abad6..0000000
--- a/src/workflow/forms.py
+++ /dev/null
@@ -1,489 +0,0 @@
-##############################################################################
-# Copyright (c) 2018 Sawyer Bergeron, Parker Berberian, and others.
-# Copyright (c) 2020 Sawyer Bergeron, Sean Smith, 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 django.forms import widgets, ValidationError
-from django.utils.safestring import mark_safe
-from django.template.loader import render_to_string
-from django.forms.widgets import NumberInput
-
-import json
-import urllib
-
-from account.models import Lab
-from account.models import UserProfile
-from resource_inventory.models import (
- OPNFVRole,
- Installer,
- Scenario
-)
-from resource_inventory.resource_manager import ResourceManager
-from booking.lib import get_user_items, get_user_field_opts
-
-
-class SearchableSelectMultipleWidget(widgets.SelectMultiple):
- template_name = 'dashboard/searchable_select_multiple.html'
-
- def __init__(self, attrs=None):
- self.items = attrs['items']
- self.show_from_noentry = attrs['show_from_noentry']
- self.show_x_results = attrs['show_x_results']
- self.results_scrollable = attrs['results_scrollable']
- self.selectable_limit = attrs['selectable_limit']
- self.placeholder = attrs['placeholder']
- self.name = attrs['name']
- self.initial = attrs.get("initial", [])
-
- super(SearchableSelectMultipleWidget, self).__init__()
-
- def render(self, name, value, attrs=None, renderer=None):
-
- context = self.get_context(attrs)
- return mark_safe(render_to_string(self.template_name, context))
-
- def get_context(self, attrs):
- return {
- 'items': self.items,
- 'name': self.name,
- 'show_from_noentry': self.show_from_noentry,
- 'show_x_results': self.show_x_results,
- 'results_scrollable': self.results_scrollable,
- 'selectable_limit': self.selectable_limit,
- 'placeholder': self.placeholder,
- 'initial': self.initial,
- }
-
-
-class SearchableSelectMultipleField(forms.Field):
- def __init__(self, *args, required=True, widget=None, label=None, disabled=False,
- items=None, queryset=None, show_from_noentry=True, show_x_results=-1,
- results_scrollable=False, selectable_limit=-1, placeholder="search here",
- name="searchable_select", initial=[], **kwargs):
- """
- From the documentation.
-
- # required -- Boolean that specifies whether the field is required.
- # True by default.
- # widget -- A Widget class, or instance of a Widget class, that should
- # be used for this Field when displaying it. Each Field has a
- # default Widget that it'll use if you don't specify this. In
- # most cases, the default widget is TextInput.
- # label -- A verbose name for this field, for use in displaying this
- # field in a form. By default, Django will use a "pretty"
- # version of the form field name, if the Field is part of a
- # Form.
- # initial -- A value to use in this Field's initial display. This value
- # is *not* used as a fallback if data isn't given.
- # help_text -- An optional string to use as "help text" for this Field.
- # error_messages -- An optional dictionary to override the default
- # messages that the field will raise.
- # show_hidden_initial -- Boolean that specifies if it is needed to render a
- # hidden widget with initial value after widget.
- # validators -- List of additional validators to use
- # localize -- Boolean that specifies if the field should be localized.
- # disabled -- Boolean that specifies whether the field is disabled, that
- # is its widget is shown in the form but not editable.
- # label_suffix -- Suffix to be added to the label. Overrides
- # form's label_suffix.
- """
- self.widget = widget
- if self.widget is None:
- self.widget = SearchableSelectMultipleWidget(
- attrs={
- 'items': items,
- 'initial': [obj.id for obj in initial],
- 'show_from_noentry': show_from_noentry,
- 'show_x_results': show_x_results,
- 'results_scrollable': results_scrollable,
- 'selectable_limit': selectable_limit,
- 'placeholder': placeholder,
- 'name': name,
- 'disabled': disabled
- }
- )
- self.disabled = disabled
- self.queryset = queryset
- self.selectable_limit = selectable_limit
-
- super().__init__(disabled=disabled, **kwargs)
-
- self.required = required
-
- def clean(self, data):
- data = data[0]
- if not data:
- if self.required:
- raise ValidationError("Nothing was selected")
- else:
- return []
- try:
- data_as_list = json.loads(data)
- except json.decoder.JSONDecodeError:
- data_as_list = None
- if not data_as_list:
- raise ValidationError("Contents Not JSON")
- if self.selectable_limit != -1:
- if len(data_as_list) > self.selectable_limit:
- raise ValidationError("Too many items were selected")
-
- items = []
- for elem in data_as_list:
- items.append(self.queryset.get(id=elem))
-
- return items
-
-
-class SearchableSelectAbstractForm(forms.Form):
- def __init__(self, *args, queryset=None, initial=[], **kwargs):
- self.queryset = queryset
- items = self.generate_items(self.queryset)
- options = self.generate_options()
-
- super(SearchableSelectAbstractForm, self).__init__(*args, **kwargs)
- self.fields['searchable_select'] = SearchableSelectMultipleField(
- initial=initial,
- items=items,
- queryset=self.queryset,
- **options
- )
-
- def get_validated_bundle(self):
- bundles = self.cleaned_data['searchable_select']
- if len(bundles) < 1: # don't need to check for >1, as field does that for us
- raise ValidationError("No bundle was selected")
- return bundles[0]
-
- def generate_items(self, queryset):
- raise Exception("SearchableSelectAbstractForm does not implement concrete generate_items()")
-
- def generate_options(self, disabled=False):
- return {
- 'show_from_noentry': True,
- 'show_x_results': -1,
- 'results_scrollable': True,
- 'selectable_limit': 1,
- 'placeholder': 'Search for a Bundle',
- 'name': 'searchable_select',
- 'disabled': False
- }
-
-
-class SWConfigSelectorForm(SearchableSelectAbstractForm):
- def generate_items(self, queryset):
- items = {}
-
- for bundle in queryset:
- items[bundle.id] = {
- 'expanded_name': bundle.name,
- 'small_name': bundle.owner.username,
- 'string': bundle.description,
- 'id': bundle.id
- }
-
- return items
-
-
-class OPNFVSelectForm(SearchableSelectAbstractForm):
- def generate_items(self, queryset):
- items = {}
-
- for config in queryset:
- items[config.id] = {
- 'expanded_name': config.name,
- 'small_name': config.bundle.owner.username,
- 'string': config.description,
- 'id': config.id
- }
-
- return items
-
-
-class ResourceSelectorForm(SearchableSelectAbstractForm):
- def generate_items(self, queryset):
- items = {}
-
- for bundle in queryset:
- items[bundle.id] = {
- 'expanded_name': bundle.name,
- 'small_name': bundle.owner.username,
- 'string': bundle.description,
- 'id': bundle.id
- }
-
- return items
-
-
-class BookingMetaForm(forms.Form):
- # Django Form class for Book a Pod
- length = forms.IntegerField(
- widget=NumberInput(
- attrs={
- "type": "range",
- 'min': "1",
- "max": "21",
- "value": "1"
- }
- )
- )
- purpose = forms.CharField(max_length=1000)
- project = forms.CharField(max_length=400)
- info_file = forms.CharField(max_length=1000, required=False)
- deploy_opnfv = forms.BooleanField(required=False)
-
- def __init__(self, *args, user_initial=[], owner=None, **kwargs):
- super(BookingMetaForm, self).__init__(**kwargs)
-
- self.fields['users'] = SearchableSelectMultipleField(
- queryset=UserProfile.objects.select_related('user').exclude(user=owner),
- initial=user_initial,
- items=get_user_items(exclude=owner),
- required=False,
- **get_user_field_opts()
- )
-
-
-class MultipleSelectFilterWidget(forms.Widget):
- def __init__(self, *args, display_objects=None, filter_items=None, neighbors=None, **kwargs):
- super(MultipleSelectFilterWidget, self).__init__(*args, **kwargs)
- self.display_objects = display_objects
- self.filter_items = filter_items
- self.neighbors = neighbors
- self.template_name = "dashboard/multiple_select_filter_widget.html"
-
- def render(self, name, value, attrs=None, renderer=None):
- context = self.get_context(name, value, attrs)
- html = render_to_string(self.template_name, context=context)
- return mark_safe(html)
-
- def get_context(self, name, value, attrs):
- return {
- 'display_objects': self.display_objects,
- 'neighbors': self.neighbors,
- 'filter_items': self.filter_items,
- 'initial_value': value
- }
-
-
-class MultipleSelectFilterField(forms.Field):
-
- def __init__(self, **kwargs):
- self.initial = kwargs.get("initial")
- super().__init__(**kwargs)
-
- def to_python(self, value):
- try:
- return json.loads(value)
- except json.decoder.JSONDecodeError:
- pass
- raise ValidationError("content is not valid JSON")
-
-
-class FormUtils:
- @staticmethod
- def getLabData(multiple_hosts=False, user=None):
- """
- Get all labs and thier host profiles, returns a serialized version the form can understand.
-
- Could be rewritten with a related query to make it faster
- """
- # javascript truthy variables
- true = 1
- false = 0
- if multiple_hosts:
- multiple_hosts = true
- else:
- multiple_hosts = false
- labs = {}
- resources = {}
- items = {}
- neighbors = {}
- for lab in Lab.objects.all():
- lab_node = {
- 'id': "lab_" + str(lab.lab_user.id),
- 'model_id': lab.lab_user.id,
- 'name': lab.name,
- 'description': lab.description,
- 'selected': false,
- 'selectable': true,
- 'follow': multiple_hosts,
- 'multiple': false,
- 'class': 'lab',
- 'available_resources': json.dumps(lab.get_available_resources())
- }
-
- items[lab_node['id']] = lab_node
- neighbors[lab_node['id']] = []
- labs[lab_node['id']] = lab_node
-
- for template in ResourceManager.getInstance().getAvailableResourceTemplates(lab, user):
- resource_node = {
- 'form': {"name": "host_name", "type": "text", "placeholder": "hostname"},
- 'id': "resource_" + str(template.id),
- 'model_id': template.id,
- 'name': template.name,
- 'description': template.description,
- 'selected': false,
- 'selectable': true,
- 'follow': false,
- 'multiple': multiple_hosts,
- 'class': 'resource',
- 'required_resources': json.dumps(template.get_required_resources())
- }
-
- if multiple_hosts:
- resource_node['values'] = [] # place to store multiple values
-
- items[resource_node['id']] = resource_node
- neighbors[lab_node['id']].append(resource_node['id'])
-
- if resource_node['id'] not in neighbors:
- neighbors[resource_node['id']] = []
-
- neighbors[resource_node['id']].append(lab_node['id'])
- resources[resource_node['id']] = resource_node
-
- display_objects = [("lab", labs.values()), ("resource", resources.values())]
-
- context = {
- 'display_objects': display_objects,
- 'neighbors': neighbors,
- 'filter_items': items
- }
-
- return context
-
-
-class HardwareDefinitionForm(forms.Form):
-
- def __init__(self, user, *args, **kwargs):
- super(HardwareDefinitionForm, self).__init__(*args, **kwargs)
- attrs = FormUtils.getLabData(multiple_hosts=True, user=user)
- self.fields['filter_field'] = MultipleSelectFilterField(
- widget=MultipleSelectFilterWidget(**attrs)
- )
-
-
-class PodDefinitionForm(forms.Form):
-
- fields = ["xml"]
- xml = forms.CharField()
-
-
-class ResourceMetaForm(forms.Form):
-
- bundle_name = forms.CharField(label="POD Name")
- bundle_description = forms.CharField(label="POD Description", widget=forms.Textarea, max_length=1000)
-
-
-class GenericHostMetaForm(forms.Form):
-
- host_profile = forms.CharField(label="Host Type", disabled=True, required=False)
- host_name = forms.CharField(label="Host Name")
-
-
-class NetworkDefinitionForm(forms.Form):
- def __init__(self, *args, **kwargs):
- super(NetworkDefinitionForm, self).__init__(**kwargs)
-
-
-class NetworkConfigurationForm(forms.Form):
- def __init__(self, *args, **kwargs):
- super(NetworkConfigurationForm).__init__(**kwargs)
-
-
-class HostSoftwareDefinitionForm(forms.Form):
- # Django Form class for Design a Pod
- host_name = forms.CharField(
- max_length=200,
- disabled=False,
- required=True
- )
- headnode = forms.BooleanField(required=False, widget=forms.HiddenInput)
-
- def __init__(self, *args, **kwargs):
- imageQS = kwargs.pop("imageQS")
- super(HostSoftwareDefinitionForm, self).__init__(*args, **kwargs)
- self.fields['image'] = forms.ModelChoiceField(queryset=imageQS)
-
-
-class WorkflowSelectionForm(forms.Form):
- fields = ['workflow']
-
- empty_permitted = False
-
- workflow = forms.ChoiceField(
- choices=(
- (0, 'Booking'),
- (1, 'Resource Bundle'),
- (2, 'Software Configuration')
- ),
- label="Choose Workflow",
- initial='booking',
- required=True
- )
-
-
-class SnapshotHostSelectForm(forms.Form):
- host = forms.CharField()
-
-
-class BasicMetaForm(forms.Form):
- name = forms.CharField()
- description = forms.CharField(widget=forms.Textarea)
-
-
-class ConfirmationForm(forms.Form):
- fields = ['confirm']
-
- confirm = forms.ChoiceField(
- choices=(
- (False, "Cancel"),
- (True, "Confirm")
- )
- )
-
-
-def validate_step(value):
- if value not in ["prev", "next", "current"]:
- raise ValidationError(str(value) + " is not allowed")
-
-
-def validate_step_form(value):
- try:
- urllib.parse.unquote_plus(value)
- except Exception:
- raise ValidationError("Value is not url encoded data")
-
-
-class ManagerForm(forms.Form):
- step = forms.CharField(widget=forms.widgets.HiddenInput, validators=[validate_step])
- step_form = forms.CharField(widget=forms.widgets.HiddenInput, validators=[validate_step_form])
- # other fields?
-
-
-class OPNFVSelectionForm(forms.Form):
- installer = forms.ModelChoiceField(queryset=Installer.objects.all(), required=True)
- scenario = forms.ModelChoiceField(queryset=Scenario.objects.all(), required=True)
-
-
-class OPNFVNetworkRoleForm(forms.Form):
- role = forms.CharField(max_length=200, disabled=True, required=False)
-
- def __init__(self, *args, config_bundle, **kwargs):
- super(OPNFVNetworkRoleForm, self).__init__(*args, **kwargs)
- self.fields['network'] = forms.ModelChoiceField(
- queryset=config_bundle.bundle.networks.all()
- )
-
-
-class OPNFVHostRoleForm(forms.Form):
- host_name = forms.CharField(max_length=200, disabled=True, required=False)
- role = forms.ModelChoiceField(queryset=OPNFVRole.objects.all().order_by("name").distinct("name"))
diff --git a/src/workflow/models.py b/src/workflow/models.py
deleted file mode 100644
index e065202..0000000
--- a/src/workflow/models.py
+++ /dev/null
@@ -1,693 +0,0 @@
-##############################################################################
-# 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.template.loader import get_template
-from django.http import HttpResponse
-from django.utils import timezone
-from django.db import transaction
-
-import yaml
-import requests
-
-from workflow.forms import ConfirmationForm
-from api.models import JobFactory
-from dashboard.exceptions import ResourceAvailabilityException, ModelValidationException
-from resource_inventory.models import Image, OPNFVConfig, ResourceOPNFVConfig, NetworkRole
-from resource_inventory.resource_manager import ResourceManager
-from resource_inventory.pdf_templater import PDFTemplater
-from notifier.manager import NotificationHandler
-from booking.models import Booking
-
-
-class BookingAuthManager():
- """
- Verifies Booking Authorization.
-
- Class to verify that the user is allowed to book the requested resource
- The user must input a url to the INFO.yaml file to prove that they are the ptl of
- an approved project if they are booking a multi-node pod.
- This class parses the url and checks the logged in user against the info file.
- """
-
- LFN_PROJECTS = ["opnfv"] # TODO
-
- def parse_github_url(self, url):
- project_leads = []
- try:
- parts = url.split("/")
- if "http" in parts[0]: # the url include http(s)://
- parts = parts[2:]
- if parts[-1] != "INFO.yaml":
- return None
- if parts[0] not in ["github.com", "raw.githubusercontent.com"]:
- return None
- if parts[1] not in self.LFN_PROJECTS:
- return None
- # now to download and parse file
- if parts[3] == "blob":
- parts[3] = "raw"
- url = "https://" + "/".join(parts)
- info_file = requests.get(url, timeout=15).text
- info_parsed = yaml.load(info_file)
- ptl = info_parsed.get('project_lead')
- if ptl:
- project_leads.append(ptl)
- sub_ptl = info_parsed.get("subproject_lead")
- if sub_ptl:
- project_leads.append(sub_ptl)
-
- except Exception:
- pass
-
- return project_leads
-
- def parse_gerrit_url(self, url):
- project_leads = []
- try:
- halfs = url.split("?")
- parts = halfs[0].split("/")
- args = halfs[1].split(";")
- if "http" in parts[0]: # the url include http(s)://
- parts = parts[2:]
- if "f=INFO.yaml" not in args:
- return None
- if "gerrit.opnfv.org" not in parts[0]:
- return None
- try:
- i = args.index("a=blob")
- args[i] = "a=blob_plain"
- except ValueError:
- pass
- # recreate url
- halfs[1] = ";".join(args)
- halfs[0] = "/".join(parts)
- # now to download and parse file
- url = "https://" + "?".join(halfs)
- info_file = requests.get(url, timeout=15).text
- info_parsed = yaml.load(info_file)
- ptl = info_parsed.get('project_lead')
- if ptl:
- project_leads.append(ptl)
- sub_ptl = info_parsed.get("subproject_lead")
- if sub_ptl:
- project_leads.append(sub_ptl)
-
- except Exception:
- return None
-
- return project_leads
-
- def parse_opnfv_git_url(self, url):
- project_leads = []
- try:
- parts = url.split("/")
- if "http" in parts[0]: # the url include http(s)://
- parts = parts[2:]
- if "INFO.yaml" not in parts[-1]:
- return None
- if "git.opnfv.org" not in parts[0]:
- return None
- if parts[-2] == "tree":
- parts[-2] = "plain"
- # now to download and parse file
- url = "https://" + "/".join(parts)
- info_file = requests.get(url, timeout=15).text
- info_parsed = yaml.load(info_file)
- ptl = info_parsed.get('project_lead')
- if ptl:
- project_leads.append(ptl)
- sub_ptl = info_parsed.get("subproject_lead")
- if sub_ptl:
- project_leads.append(sub_ptl)
-
- except Exception:
- return None
-
- return project_leads
-
- def parse_url(self, info_url):
- """
- Parse the project URL.
-
- Gets the INFO.yaml file from the project and returns the PTL info.
- """
- if "github" in info_url:
- return self.parse_github_url(info_url)
-
- if "gerrit.opnfv.org" in info_url:
- return self.parse_gerrit_url(info_url)
-
- if "git.opnfv.org" in info_url:
- return self.parse_opnfv_git_url(info_url)
-
- def booking_allowed(self, booking, repo):
- """
- Assert the current Booking Policy.
-
- This is the method that will have to change whenever the booking policy changes in the Infra
- group / LFN. This is a nice isolation of that administration crap
- currently checks if the booking uses multiple servers. if it does, then the owner must be a PTL,
- which is checked using the provided info file
- """
- if booking.owner.userprofile.booking_privledge:
- return True # admin override for this user
- if Booking.objects.filter(owner=booking.owner, end__gt=timezone.now()).count() >= 3:
- return False
- if len(booking.resource.template.get_required_resources()) < 2:
- return True # if they only have one server, we dont care
- if repo.BOOKING_INFO_FILE not in repo.el:
- return False # INFO file not provided
- ptl_info = self.parse_url(repo.el.get(repo.BOOKING_INFO_FILE))
- for ptl in ptl_info:
- if ptl['email'] == booking.owner.userprofile.email_addr:
- return True
- return False
-
-
-class WorkflowStepStatus(object):
- """
- Poor man's enum for the status of a workflow step.
-
- The steps in a workflow are not completed (UNTOUCHED)
- or they have been completed correctly (VALID) or they were filled out
- incorrectly (INVALID)
- """
-
- UNTOUCHED = 0
- INVALID = 100
- VALID = 200
-
-
-class WorkflowStep(object):
- template = 'bad_request.html'
- title = "Generic Step"
- description = "You were led here by mistake"
- short_title = "error"
- metastep = None
- # phasing out metastep:
-
- valid = WorkflowStepStatus.UNTOUCHED
- message = ""
-
- enabled = True
-
- def cleanup(self):
- raise Exception("WorkflowStep subclass of type " + str(type(self)) + " has no concrete implemented cleanup() method")
-
- def enable(self):
- if not self.enabled:
- self.enabled = True
-
- def disable(self):
- if self.enabled:
- self.cleanup()
- self.enabled = False
-
- def set_invalid(self, message, code=WorkflowStepStatus.INVALID):
- self.valid = code
- self.message = message
-
- def set_valid(self, message, code=WorkflowStepStatus.VALID):
- self.valid = code
- self.message = message
-
- def to_json(self):
- return {
- 'title': self.short_title,
- 'enabled': self.enabled,
- 'valid': self.valid,
- 'message': self.message,
- }
-
- def __init__(self, id, repo=None):
- self.repo = repo
- self.id = id
-
- def get_context(self):
- context = {}
- context['step_number'] = self.repo_get('steps')
- context['active_step'] = self.repo_get('active_step')
- context['render_correct'] = "true"
- context['step_title'] = self.title
- context['description'] = self.description
- return context
-
- def render(self, request):
- return HttpResponse(self.render_to_string(request))
-
- def render_to_string(self, request):
- template = get_template(self.template)
- return template.render(self.get_context(), request)
-
- def post(self, post_content, user):
- raise Exception("WorkflowStep subclass of type " + str(type(self)) + " has no concrete post() method")
-
- def validate(self, request):
- pass
-
- def repo_get(self, key, default=None):
- return self.repo.get(key, default, self.id)
-
- def repo_put(self, key, value):
- return self.repo.put(key, value, self.id)
-
-
-"""
-subclassing notes:
- subclasses have to define the following class attributes:
- self.select_repo_key: where the selected "object" or "bundle" is to be placed in the repo
- self.form: the form to be used
- alert_bundle_missing(): what message to display if a user does not select/selects an invalid object
- get_form_queryset(): generate a queryset to be used to filter available items for the field
- get_page_context(): return simple context such as page header and other info
-"""
-
-
-class AbstractSelectOrCreate(WorkflowStep):
- template = 'dashboard/genericselect.html'
- title = "Select a Bundle"
- short_title = "select"
- description = "Generic bundle selector step"
-
- select_repo_key = None
- form = None # subclasses are expected to use a form that is a subclass of SearchableSelectGenericForm
-
- def alert_bundle_missing(self): # override in subclasses to change message if field isn't filled out
- self.set_invalid("Please select a valid bundle")
-
- def post(self, post_data, user):
- form = self.form(post_data, queryset=self.get_form_queryset())
- if form.is_valid():
- bundle = form.get_validated_bundle()
- if not bundle:
- self.alert_bundle_missing()
- return
- self.repo_put(self.select_repo_key, bundle)
- self.put_confirm_info(bundle)
- self.set_valid("Step Completed")
- else:
- self.alert_bundle_missing()
-
- def get_context(self):
- default = []
-
- bundle = self.repo_get(self.select_repo_key, False)
- if bundle:
- default.append(bundle)
-
- form = self.form(queryset=self.get_form_queryset(), initial=default)
-
- context = {'form': form, **self.get_page_context()}
- context.update(super().get_context())
-
- return context
-
- def get_page_context():
- return {
- 'select_type': 'generic',
- 'select_type_title': 'Generic Bundle'
- }
-
-
-class Confirmation_Step(WorkflowStep):
- template = 'workflow/confirm.html'
- title = "Confirm Changes"
- description = "Does this all look right?"
-
- short_title = "confirm"
-
- def get_context(self):
- context = super(Confirmation_Step, self).get_context()
- context['form'] = ConfirmationForm()
- # Summary of submitted form data shown on the 'confirm' step of the workflow
- confirm_details = "\nPod:\n Name: '{name}'\n Description: '{desc}'\nLab: '{lab}'".format(
- name=self.repo_get(self.repo.CONFIRMATION)['resource']['name'],
- desc=self.repo_get(self.repo.CONFIRMATION)['resource']['description'],
- lab=self.repo_get(self.repo.CONFIRMATION)['template']['lab'])
- confirm_details += "\nResources:"
- for i, device in enumerate(self.repo_get(self.repo.RESOURCE_TEMPLATE_MODELS)['resources']):
- confirm_details += "\n " + str(device) + ": " + str(self.repo_get(self.repo.CONFIRMATION)['template']['resources'][i]['profile'])
- context['confirmation_info'] = confirm_details
- if self.valid == WorkflowStepStatus.VALID:
- context["confirm_succeeded"] = "true"
-
- return context
-
- def flush_to_db(self):
- errors = self.repo.make_models()
- if errors:
- return errors
-
- def post(self, post_data, user):
- form = ConfirmationForm(post_data)
- if form.is_valid():
- data = form.cleaned_data['confirm']
- if data == "True":
- errors = self.flush_to_db()
- if errors:
- self.set_invalid("ERROR OCCURRED: " + errors)
- else:
- self.set_valid("Confirmed")
-
- elif data == "False":
- self.repo.cancel()
- self.set_valid("Canceled")
- else:
- self.set_invalid("Bad Form Contents")
-
- else:
- self.set_invalid("Bad Form Contents")
-
-
-class Repository():
-
- EDIT = "editing"
- MODELS = "models"
- RESOURCE_SELECT = "resource_select"
- CONFIRMATION = "confirmation"
- SELECTED_RESOURCE_TEMPLATE = "selected resource template pk"
- SELECTED_OPNFV_CONFIG = "selected opnfv deployment config"
- RESOURCE_TEMPLATE_MODELS = "generic_resource_template_models"
- RESOURCE_TEMPLATE_INFO = "generic_resource_template_info"
- BOOKING = "booking"
- LAB = "lab"
- RCONFIG_LAST_HOSTLIST = "resource_configuration_network_previous_hostlist"
- BOOKING_FORMS = "booking_forms"
- SWCONF_HOSTS = "swconf_hosts"
- BOOKING_MODELS = "booking models"
- CONFIG_MODELS = "configuration bundle models"
- OPNFV_MODELS = "opnfv configuration models"
- SESSION_USER = "session owner user account"
- SESSION_MANAGER = "session manager for current session"
- VALIDATED_MODEL_GRB = "valid grb config model instance in db"
- VALIDATED_MODEL_CONFIG = "valid config model instance in db"
- VALIDATED_MODEL_BOOKING = "valid booking model instance in db"
- VLANS = "a list of vlans"
- SNAPSHOT_MODELS = "the models for snapshotting"
- SNAPSHOT_BOOKING_ID = "the booking id for snapshotting"
- SNAPSHOT_NAME = "the name of the snapshot"
- SNAPSHOT_DESC = "description of the snapshot"
- BOOKING_INFO_FILE = "the INFO.yaml file for this user's booking"
-
- # new keys for migration to using ResourceTemplates:
- RESOURCE_TEMPLATE_MODELS = "current working model of resource template"
-
- # migratory elements of segmented workflow
- # each of these is the end result of a different workflow.
- HAS_RESULT = "whether or not workflow has a result"
- RESULT_KEY = "key for target index that result will be put into in parent"
- RESULT = "result object from workflow"
-
- def get_child_defaults(self):
- return_tuples = []
- for key in [self.SELECTED_RESOURCE_TEMPLATE, self.SESSION_USER]:
- return_tuples.append((key, self.el.get(key)))
- return return_tuples
-
- def set_defaults(self, defaults):
- for key, value in defaults:
- self.el[key] = value
-
- def get(self, key, default, id):
-
- self.add_get_history(key, id)
- return self.el.get(key, default)
-
- def put(self, key, val, id):
- self.add_put_history(key, id)
- self.el[key] = val
-
- def add_get_history(self, key, id):
- self.add_history(key, id, self.get_history)
-
- def add_put_history(self, key, id):
- self.add_history(key, id, self.put_history)
-
- def add_history(self, key, id, history):
- if key not in history:
- history[key] = [id]
- else:
- history[key].append(id)
-
- def cancel(self):
- if self.RESOURCE_TEMPLATE_MODELS in self.el:
- models = self.el[self.RESOURCE_TEMPLATE_MODELS]
- if models['template'].temporary:
- models['template'].delete()
- # deleting current template should cascade delete all
- # necessary related models
-
- def make_models(self):
- if self.SNAPSHOT_MODELS in self.el:
- errors = self.make_snapshot()
- if errors:
- return errors
-
- # if GRB WF, create it
- if self.RESOURCE_TEMPLATE_MODELS in self.el:
- errors = self.make_generic_resource_bundle()
- if errors:
- return errors
- else:
- self.el[self.HAS_RESULT] = True
- self.el[self.RESULT_KEY] = self.SELECTED_RESOURCE_TEMPLATE
- return
-
- if self.OPNFV_MODELS in self.el:
- errors = self.make_opnfv_config()
- if errors:
- return errors
- else:
- self.el[self.HAS_RESULT] = True
- self.el[self.RESULT_KEY] = self.SELECTED_OPNFV_CONFIG
-
- if self.BOOKING_MODELS in self.el:
- errors = self.make_booking()
- if errors:
- return errors
- # create notification
- booking = self.el[self.BOOKING_MODELS]['booking']
- NotificationHandler.notify_new_booking(booking)
-
- def make_snapshot(self):
- owner = self.el[self.SESSION_USER]
- models = self.el[self.SNAPSHOT_MODELS]
- image = models.get('snapshot', Image())
- booking_id = self.el.get(self.SNAPSHOT_BOOKING_ID)
- if not booking_id:
- return "SNAP, No booking ID provided"
- booking = Booking.objects.get(pk=booking_id)
- if booking.start > timezone.now() or booking.end < timezone.now():
- return "Booking is not active"
- name = self.el.get(self.SNAPSHOT_NAME)
- if not name:
- return "SNAP, no name provided"
- host = models.get('host')
- if not host:
- return "SNAP, no host provided"
- description = self.el.get(self.SNAPSHOT_DESC, "")
- image.from_lab = booking.lab
- image.name = name
- image.description = description
- image.public = False
- image.lab_id = -1
- image.owner = owner
- image.host_type = host.profile
- image.save()
- try:
- current_image = host.config.image
- image.os = current_image.os
- image.save()
- except Exception:
- pass
- JobFactory.makeSnapshotTask(image, booking, host)
-
- self.el[self.RESULT] = image
- self.el[self.HAS_RESULT] = True
-
- def make_generic_resource_bundle(self):
- owner = self.el[self.SESSION_USER]
- if self.RESOURCE_TEMPLATE_MODELS in self.el:
- models = self.el[self.RESOURCE_TEMPLATE_MODELS]
- models['template'].owner = owner
- models['template'].temporary = False
- models['template'].save()
- self.el[self.RESULT] = models['template']
- self.el[self.HAS_RESULT] = True
- return False
-
- else:
- return "GRB no models given. CODE:0x0001"
-
- def make_software_config_bundle(self):
- models = self.el[self.CONFIG_MODELS]
- if 'bundle' in models:
- bundle = models['bundle']
- bundle.bundle = self.el[self.SELECTED_RESOURCE_TEMPLATE]
- try:
- bundle.save()
- except Exception as e:
- return "SWC, saving bundle generated exception: " + str(e) + "CODE:0x0007"
-
- else:
- return "SWC, no bundle in models. CODE:0x0006"
- if 'host_configs' in models:
- host_configs = models['host_configs']
- for host_config in host_configs:
- host_config.template = host_config.template
- host_config.profile = host_config.profile
- try:
- host_config.save()
- except Exception as e:
- return "SWC, saving host configs generated exception: " + str(e) + "CODE:0x0009"
- else:
- return "SWC, no host configs in models. CODE:0x0008"
- if 'opnfv' in models:
- opnfvconfig = models['opnfv']
- opnfvconfig.bundle = opnfvconfig.bundle
- if opnfvconfig.scenario not in opnfvconfig.installer.sup_scenarios.all():
- return "SWC, scenario not supported by installer. CODE:0x000d"
- try:
- opnfvconfig.save()
- except Exception as e:
- return "SWC, saving opnfv config generated exception: " + str(e) + "CODE:0x000b"
- else:
- pass
-
- self.el[self.RESULT] = bundle
- return False
-
- @transaction.atomic # TODO: Rewrite transactions with savepoints at user level for all workflows
- def make_booking(self):
- models = self.el[self.BOOKING_MODELS]
- owner = self.el[self.SESSION_USER]
-
- if 'booking' in models:
- booking = models['booking']
- else:
- return "BOOK, no booking model exists. CODE:0x000f"
-
- selected_grb = None
-
- if self.SELECTED_RESOURCE_TEMPLATE in self.el:
- selected_grb = self.el[self.SELECTED_RESOURCE_TEMPLATE]
- else:
- return "BOOK, no selected resource. CODE:0x000e"
-
- if not booking.start:
- return "BOOK, booking has no start. CODE:0x0010"
- if not booking.end:
- return "BOOK, booking has no end. CODE:0x0011"
- if booking.end <= booking.start:
- return "BOOK, end before/same time as start. CODE:0x0012"
-
- if 'collaborators' in models:
- collaborators = models['collaborators']
- else:
- return "BOOK, collaborators not defined. CODE:0x0013"
- try:
- res_manager = ResourceManager.getInstance()
- resource_bundle = res_manager.instantiateTemplate(selected_grb)
- except ResourceAvailabilityException as e:
- return "BOOK, requested resources are not available. Exception: " + str(e) + " CODE:0x0014"
- except ModelValidationException as e:
- return "Error encountered when saving bundle. " + str(e) + " CODE: 0x001b"
-
- booking.resource = resource_bundle
- booking.owner = owner
- booking.lab = selected_grb.lab
-
- is_allowed = BookingAuthManager().booking_allowed(booking, self)
- if not is_allowed:
- return "BOOK, you are not allowed to book the requested resources"
-
- try:
- booking.save()
- except Exception as e:
- return "BOOK, saving booking generated exception: " + str(e) + " CODE:0x0015"
-
- for collaborator in collaborators:
- booking.collaborators.add(collaborator)
-
- try:
- booking.pdf = PDFTemplater.makePDF(booking)
- booking.save()
- except Exception as e:
- return "BOOK, failed to create Pod Desriptor File: " + str(e)
-
- try:
- JobFactory.makeCompleteJob(booking)
- except Exception as e:
- return "BOOK, serializing for api generated exception: " + str(e) + " CODE:0xFFFF"
-
- try:
- booking.save()
- except Exception as e:
- return "BOOK, saving booking generated exception: " + str(e) + " CODE:0x0016"
-
- self.el[self.RESULT] = booking
- self.el[self.HAS_RESULT] = True
-
- def make_opnfv_config(self):
- opnfv_models = self.el[self.OPNFV_MODELS]
- config_bundle = self.el[self.SELECTED_CONFIG_BUNDLE]
- if not config_bundle:
- return "No Configuration bundle selected"
- info = opnfv_models.get("meta", {})
- name = info.get("name", False)
- desc = info.get("description", False)
- if not (name and desc):
- return "No name or description given"
- installer = opnfv_models['installer_chosen']
- if not installer:
- return "No OPNFV Installer chosen"
- scenario = opnfv_models['scenario_chosen']
- if not scenario:
- return "No OPNFV Scenario chosen"
-
- opnfv_config = OPNFVConfig.objects.create(
- bundle=config_bundle,
- name=name,
- description=desc,
- installer=installer,
- scenario=scenario
- )
-
- network_roles = opnfv_models['network_roles']
- for net_role in network_roles:
- opnfv_config.networks.add(
- NetworkRole.objects.create(
- name=net_role['role'],
- network=net_role['network']
- )
- )
-
- host_roles = opnfv_models['host_roles']
- for host_role in host_roles:
- config = config_bundle.hostConfigurations.get(
- host__resource__name=host_role['host_name']
- )
- ResourceOPNFVConfig.objects.create(
- role=host_role['role'],
- host_config=config,
- opnfv_config=opnfv_config
- )
-
- self.el[self.RESULT] = opnfv_config
- self.el[self.HAS_RESULT] = True
-
- def __init__(self):
- self.el = {}
- self.el[self.CONFIRMATION] = {}
- self.el["active_step"] = 0
- self.el[self.HAS_RESULT] = False
- self.el[self.RESULT] = None
- self.get_history = {}
- self.put_history = {}
diff --git a/src/workflow/opnfv_workflow.py b/src/workflow/opnfv_workflow.py
deleted file mode 100644
index 6ffc91d..0000000
--- a/src/workflow/opnfv_workflow.py
+++ /dev/null
@@ -1,292 +0,0 @@
-##############################################################################
-# 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.forms import formset_factory
-
-from workflow.models import WorkflowStep, AbstractSelectOrCreate
-from resource_inventory.models import ResourceTemplate, OPNFV_SETTINGS
-from workflow.forms import OPNFVSelectionForm, OPNFVNetworkRoleForm, OPNFVHostRoleForm, SWConfigSelectorForm, BasicMetaForm
-
-
-class OPNFV_Resource_Select(AbstractSelectOrCreate):
- title = "Select Software Configuration"
- description = "Choose the software bundle you wish to use as a base for your OPNFV configuration"
- short_title = "software config"
- form = SWConfigSelectorForm
-
- def __init__(self, *args, **kwargs):
- super().__init__(*args, **kwargs)
- self.select_repo_key = self.repo.SELECTED_CONFIG_BUNDLE
-
- def get_form_queryset(self):
- user = self.repo_get(self.repo.SESSION_USER)
- qs = ResourceTemplate.objects.filter(owner=user)
- return qs
-
- def put_confirm_info(self, bundle):
- confirm_dict = self.repo_get(self.repo.CONFIRMATION)
- confirm_dict['software bundle'] = bundle.name
- confirm_dict['hardware POD'] = bundle.bundle.name
- self.repo_put(self.repo.CONFIRMATION, confirm_dict)
-
- def get_page_context(self):
- return {
- 'select_type': 'swconfig',
- 'select_type_title': 'Software Config',
- 'addable_type_num': 2
- }
-
-
-class Pick_Installer(WorkflowStep):
- template = 'config_bundle/steps/pick_installer.html'
- title = 'Pick OPNFV Installer'
- description = 'Choose which OPNFV installer to use'
- short_title = "opnfv installer"
- modified_key = "installer_step"
-
- def update_confirmation(self):
- confirm = self.repo_get(self.repo.CONFIRMATION, {})
- models = self.repo_get(self.repo.OPNFV_MODELS, {})
- installer = models.get("installer_chosen")
- scenario = models.get("scenario_chosen")
- if not (installer and scenario):
- return
- confirm['installer'] = installer.name
- confirm['scenario'] = scenario.name
- self.repo_put(self.repo.CONFIRMATION, confirm)
-
- def get_context(self):
- context = super(Pick_Installer, self).get_context()
-
- models = self.repo_get(self.repo.OPNFV_MODELS, {})
- initial = {
- "installer": models.get("installer_chosen"),
- "scenario": models.get("scenario_chosen")
- }
-
- context["form"] = OPNFVSelectionForm(initial=initial)
- return context
-
- def post(self, post_data, user):
- form = OPNFVSelectionForm(post_data)
- if form.is_valid():
- installer = form.cleaned_data['installer']
- scenario = form.cleaned_data['scenario']
- models = self.repo_get(self.repo.OPNFV_MODELS, {})
- models['installer_chosen'] = installer
- models['scenario_chosen'] = scenario
- self.repo_put(self.repo.OPNFV_MODELS, models)
- self.update_confirmation()
- self.set_valid("Step Completed")
- else:
- self.set_invalid("Please select an Installer and Scenario")
-
-
-class Assign_Network_Roles(WorkflowStep):
- template = 'config_bundle/steps/assign_network_roles.html'
- title = 'Pick Network Roles'
- description = 'Choose what role each network should get'
- short_title = "network roles"
- modified_key = "net_roles_step"
-
- """
- to do initial filling, repo should have a "network_roles" array with the following structure for each element:
- {
- "role": <NetworkRole object ref>,
- "network": <Network object ref>
- }
- """
- def create_netformset(self, roles, config_bundle, data=None):
- roles_initial = []
- set_roles = self.repo_get(self.repo.OPNFV_MODELS, {}).get("network_roles")
- if set_roles:
- roles_initial = set_roles
- else:
- for role in OPNFV_SETTINGS.NETWORK_ROLES:
- roles_initial.append({"role": role})
-
- Formset = formset_factory(OPNFVNetworkRoleForm, extra=0)
- kwargs = {
- "initial": roles_initial,
- "form_kwargs": {"config_bundle": config_bundle}
- }
- formset = None
- if data:
- formset = Formset(data, **kwargs)
- else:
- formset = Formset(**kwargs)
- return formset
-
- def get_context(self):
- context = super(Assign_Network_Roles, self).get_context()
- config_bundle = self.repo_get(self.repo.SELECTED_CONFIG_BUNDLE)
- if config_bundle is None:
- context["unavailable"] = True
- return context
-
- roles = OPNFV_SETTINGS.NETWORK_ROLES
- formset = self.create_netformset(roles, config_bundle)
- context['formset'] = formset
-
- return context
-
- def update_confirmation(self):
- confirm = self.repo_get(self.repo.CONFIRMATION, {})
- models = self.repo_get(self.repo.OPNFV_MODELS, {})
- roles = models.get("network_roles")
- if not roles:
- return
- confirm['network roles'] = {}
- for role in roles:
- confirm['network roles'][role['role']] = role['network'].name
- self.repo_put(self.repo.CONFIRMATION, confirm)
-
- def post(self, post_data, user):
- models = self.repo_get(self.repo.OPNFV_MODELS, {})
- config_bundle = self.repo_get(self.repo.SELECTED_CONFIG_BUNDLE)
- roles = OPNFV_SETTINGS.NETWORK_ROLES
- net_role_formset = self.create_netformset(roles, config_bundle, data=post_data)
- if net_role_formset.is_valid():
- results = []
- for form in net_role_formset:
- results.append({
- "role": form.cleaned_data['role'],
- "network": form.cleaned_data['network']
- })
- models['network_roles'] = results
- self.set_valid("Completed")
- self.repo_put(self.repo.OPNFV_MODELS, models)
- self.update_confirmation()
- else:
- self.set_invalid("Please complete all fields")
-
-
-class Assign_Host_Roles(WorkflowStep): # taken verbatim from Define_Software in sw workflow, merge the two?
- template = 'config_bundle/steps/assign_host_roles.html'
- title = 'Pick Host Roles'
- description = "Choose the role each machine will have in your OPNFV pod"
- short_title = "host roles"
- modified_key = "host_roles_step"
-
- def create_host_role_formset(self, hostlist=[], data=None):
- models = self.repo_get(self.repo.OPNFV_MODELS, {})
- host_roles = models.get("host_roles", [])
- if not host_roles:
- for host in hostlist:
- initial = {"host_name": host.resource.name}
- host_roles.append(initial)
- models['host_roles'] = host_roles
- self.repo_put(self.repo.OPNFV_MODELS, models)
-
- HostFormset = formset_factory(OPNFVHostRoleForm, extra=0)
-
- kwargs = {"initial": host_roles}
- formset = None
- if data:
- formset = HostFormset(data, **kwargs)
- else:
- formset = HostFormset(**kwargs)
-
- return formset
-
- def get_context(self):
- context = super(Assign_Host_Roles, self).get_context()
- config = self.repo_get(self.repo.SELECTED_CONFIG_BUNDLE)
- if config is None:
- context['error'] = "Please select a Configuration on the first step"
-
- formset = self.create_host_role_formset(hostlist=config.bundle.getResources())
- context['formset'] = formset
-
- return context
-
- def get_host_role_mapping(self, host_roles, hostname):
- for obj in host_roles:
- if hostname == obj['host_name']:
- return obj
- return None
-
- def update_confirmation(self):
- confirm = self.repo_get(self.repo.CONFIRMATION, {})
- models = self.repo_get(self.repo.OPNFV_MODELS, {})
- roles = models.get("host_roles")
- if not roles:
- return
- confirm['host roles'] = {}
- for role in roles:
- confirm['host roles'][role['host_name']] = role['role'].name
- self.repo_put(self.repo.CONFIRMATION, confirm)
-
- def post(self, post_data, user):
- formset = self.create_host_role_formset(data=post_data)
-
- models = self.repo_get(self.repo.OPNFV_MODELS, {})
- host_roles = models.get("host_roles", [])
-
- has_jumphost = False
- if formset.is_valid():
- for form in formset:
- hostname = form.cleaned_data['host_name']
- role = form.cleaned_data['role']
- mapping = self.get_host_role_mapping(host_roles, hostname)
- mapping['role'] = role
- if "jumphost" in role.name.lower():
- has_jumphost = True
-
- models['host_roles'] = host_roles
- self.repo_put(self.repo.OPNFV_MODELS, models)
- self.update_confirmation()
-
- if not has_jumphost:
- self.set_invalid('Must have at least one "Jumphost" per POD')
- else:
- self.set_valid("Completed")
- else:
- self.set_invalid("Please complete all fields")
-
-
-class MetaInfo(WorkflowStep):
- template = 'config_bundle/steps/config_software.html'
- title = "Other Info"
- description = "Give your software config a name, description, and other stuff"
- short_title = "config info"
-
- def get_context(self):
- context = super(MetaInfo, self).get_context()
-
- initial = self.repo_get(self.repo.OPNFV_MODELS, {}).get("meta", {})
- context["form"] = BasicMetaForm(initial=initial)
- return context
-
- def update_confirmation(self):
- confirm = self.repo_get(self.repo.CONFIRMATION, {})
- models = self.repo_get(self.repo.OPNFV_MODELS, {})
- meta = models.get("meta")
- if not meta:
- return
- confirm['name'] = meta['name']
- confirm['description'] = meta['description']
- self.repo_put(self.repo.CONFIRMATION, confirm)
-
- def post(self, post_data, user):
- models = self.repo_get(self.repo.OPNFV_MODELS, {})
- info = models.get("meta", {})
-
- form = BasicMetaForm(post_data)
- if form.is_valid():
- info['name'] = form.cleaned_data['name']
- info['description'] = form.cleaned_data['description']
- models['meta'] = info
- self.repo_put(self.repo.OPNFV_MODELS, models)
- self.update_confirmation()
- self.set_valid("Complete")
- else:
- self.set_invalid("Please correct the errors shown below")
- self.repo_put(self.repo.OPNFV_MODELS, models)
diff --git a/src/workflow/resource_bundle_workflow.py b/src/workflow/resource_bundle_workflow.py
deleted file mode 100644
index 4e288b5..0000000
--- a/src/workflow/resource_bundle_workflow.py
+++ /dev/null
@@ -1,614 +0,0 @@
-##############################################################################
-# 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.conf import settings
-from django.forms import formset_factory
-from django.core.exceptions import ValidationError
-
-from typing import List
-
-import re
-import json
-from xml.dom import minidom
-import traceback
-
-from workflow.models import WorkflowStep
-from account.models import Lab
-from workflow.forms import (
- HardwareDefinitionForm,
- NetworkDefinitionForm,
- ResourceMetaForm,
- HostSoftwareDefinitionForm,
-)
-from resource_inventory.models import (
- ResourceTemplate,
- ResourceConfiguration,
- InterfaceConfiguration,
- Network,
- NetworkConnection,
- Image,
-)
-from dashboard.exceptions import (
- InvalidVlanConfigurationException,
- NetworkExistsException,
- ResourceAvailabilityException
-)
-
-import logging
-logger = logging.getLogger(__name__)
-
-
-class Define_Hardware(WorkflowStep):
-
- template = 'resource/steps/define_hardware.html'
- title = "Define Hardware"
- description = "Choose the type and amount of machines you want"
- short_title = "hosts"
-
- def __init__(self, *args, **kwargs):
- self.form = None
- super().__init__(*args, **kwargs)
-
- def get_context(self):
- context = super(Define_Hardware, self).get_context()
- user = self.repo_get(self.repo.SESSION_USER)
- context['form'] = self.form or HardwareDefinitionForm(user)
- return context
-
- def update_models(self, data):
- data = data['filter_field']
- models = self.repo_get(self.repo.RESOURCE_TEMPLATE_MODELS, {})
- models['resources'] = [] # This will always clear existing data when this step changes
- models['connections'] = []
- models['interfaces'] = {}
- if "template" not in models:
- template = ResourceTemplate.objects.create(temporary=True)
- models['template'] = template
-
- resource_data = data['resource']
-
- new_template = models['template']
-
- public_network = Network.objects.create(name="public", bundle=new_template, is_public=True)
-
- all_networks = {public_network.id: public_network}
-
- for resource_template_dict in resource_data.values():
- id = resource_template_dict['id']
- old_template = ResourceTemplate.objects.get(id=id)
-
- # instantiate genericHost and store in repo
- for _ in range(0, resource_template_dict['count']):
- resource_configs = old_template.resourceConfigurations.all()
- for config in resource_configs:
- # need to save now for connections to refer to it later
- new_config = ResourceConfiguration.objects.create(
- profile=config.profile,
- image=config.image,
- name=config.name,
- template=new_template)
-
- for interface_config in config.interface_configs.all():
- new_interface_config = InterfaceConfiguration.objects.create(
- profile=interface_config.profile,
- resource_config=new_config)
-
- for connection in interface_config.connections.all():
- network = None
- if connection.network.is_public:
- network = public_network
- else:
- # check if network is known
- if connection.network.id not in all_networks:
- # create matching one
- new_network = Network(
- name=connection.network.name + "_" + str(new_config.id),
- bundle=new_template,
- is_public=False)
- new_network.save()
-
- all_networks[connection.network.id] = new_network
-
- network = all_networks[connection.network.id]
-
- new_connection = NetworkConnection(
- network=network,
- vlan_is_tagged=connection.vlan_is_tagged)
-
- new_interface_config.save() # can't do later because M2M on next line
- new_connection.save()
-
- new_interface_config.connections.add(new_connection)
-
- unique_resource_ref = new_config.name + "_" + str(new_config.id)
- if unique_resource_ref not in models['interfaces']:
- models['interfaces'][unique_resource_ref] = []
- models['interfaces'][unique_resource_ref].append(interface_config)
-
- models['resources'].append(new_config)
-
- models['networks'] = all_networks
-
- # add selected lab to models
- for lab_dict in data['lab'].values():
- if lab_dict['selected']:
- models['template'].lab = Lab.objects.get(lab_user__id=lab_dict['id'])
- models['template'].save()
- break # if somehow we get two 'true' labs, we only use one
-
- # return to repo
- self.repo_put(self.repo.RESOURCE_TEMPLATE_MODELS, models)
-
- def update_confirmation(self):
- confirm = self.repo_get(self.repo.CONFIRMATION, {})
- if "template" not in confirm:
- confirm['template'] = {}
- confirm['template']['resources'] = []
- models = self.repo_get(self.repo.RESOURCE_TEMPLATE_MODELS, {})
- if 'template' in models:
- for resource in models['template'].getConfigs():
- host_dict = {"name": resource.name, "profile": resource.profile.name}
- confirm['template']['resources'].append(host_dict)
- if "template" in models:
- confirm['template']['lab'] = models['template'].lab.lab_user.username
- self.repo_put(self.repo.CONFIRMATION, confirm)
-
- def post(self, post_data, user):
- try:
- user = self.repo_get(self.repo.SESSION_USER)
- self.form = HardwareDefinitionForm(user, post_data)
- if self.form.is_valid():
- self.update_models(self.form.cleaned_data)
- self.update_confirmation()
- self.set_valid("Step Completed")
- else:
- self.set_invalid("Please complete the fields highlighted in red to continue")
- except Exception as e:
- print("Caught exception: " + str(e))
- traceback.print_exc()
- self.form = None
- self.set_invalid("Please select a lab.")
-
-
-class Define_Software(WorkflowStep):
- template = 'config_bundle/steps/define_software.html'
- title = "Pick Software"
- description = "Choose the opnfv and image of your machines"
- short_title = "host config"
-
- def build_filter_data(self, hosts_data):
- """
- Build list of Images to filter out.
-
- returns a 2D array of images to exclude
- based on the ordering of the passed
- hosts_data
- """
-
- filter_data = []
- user = self.repo_get(self.repo.SESSION_USER)
- lab = self.repo_get(self.repo.RESOURCE_TEMPLATE_MODELS)['template'].lab
- for i, host_data in enumerate(hosts_data):
- host = ResourceConfiguration.objects.get(pk=host_data['host_id'])
- wrong_owner = Image.objects.exclude(owner=user).exclude(public=True)
- wrong_host = Image.objects.exclude(architecture=host.profile.architecture)
- wrong_lab = Image.objects.exclude(from_lab=lab)
- excluded_images = wrong_owner | wrong_host | wrong_lab
- filter_data.append([])
- for image in excluded_images:
- filter_data[i].append(image.pk)
- return filter_data
-
- def create_hostformset(self, hostlist, data=None):
- hosts_initial = []
- configs = self.repo_get(self.repo.RESOURCE_TEMPLATE_MODELS, {}).get("resources")
- if configs:
- for i in range(len(configs)):
- default_name = 'laas-node'
- if i > 0:
- default_name = default_name + "-" + str(i + 1)
- hosts_initial.append({
- 'host_id': configs[i].id,
- 'host_name': default_name,
- 'headnode': False,
- 'image': configs[i].image
- })
- else:
- for host in hostlist:
- hosts_initial.append({
- 'host_id': host.id,
- 'host_name': host.name
- })
-
- HostFormset = formset_factory(HostSoftwareDefinitionForm, extra=0)
- filter_data = self.build_filter_data(hosts_initial)
-
- class SpecialHostFormset(HostFormset):
- def get_form_kwargs(self, index):
- kwargs = super(SpecialHostFormset, self).get_form_kwargs(index)
- if index is not None:
- kwargs['imageQS'] = Image.objects.exclude(pk__in=filter_data[index])
- return kwargs
-
- if data:
- return SpecialHostFormset(data, initial=hosts_initial)
- return SpecialHostFormset(initial=hosts_initial)
-
- def get_host_list(self, grb=None):
- return self.repo_get(self.repo.RESOURCE_TEMPLATE_MODELS).get("resources")
-
- def get_context(self):
- context = super(Define_Software, self).get_context()
-
- context["formset"] = self.create_hostformset(self.get_host_list())
-
- return context
-
- def post(self, post_data, user):
- hosts = self.get_host_list()
- formset = self.create_hostformset(hosts, data=post_data)
- has_headnode = False
- if formset.is_valid():
- for i, form in enumerate(formset):
- host = hosts[i]
- image = form.cleaned_data['image']
- hostname = form.cleaned_data['host_name']
- headnode = form.cleaned_data['headnode']
- if headnode:
- has_headnode = True
- host.is_head_node = headnode
- host.name = hostname
- host.image = image
- # RFC921: They must start with a letter, end with a letter or digit and have only letters or digits or hyphen as interior characters
- if bool(re.match("^[A-Za-z0-9-]*$", hostname)) is False:
- self.set_invalid("Device names must only contain alphanumeric characters and dashes.")
- return
- if not hostname[0].isalpha() or not hostname[-1].isalnum():
- self.set_invalid("Device names must start with a letter and end with a letter or digit.")
- return
- for j in range(i):
- if j != i and hostname == hosts[j].name:
- self.set_invalid("Devices must have unique names. Please try again.")
- return
- host.save()
-
- if not has_headnode and len(hosts) > 0:
- self.set_invalid("No headnode. Please set a headnode.")
- return
-
- self.set_valid("Completed")
- else:
- self.set_invalid("Please complete all fields.")
-
-
-class Define_Nets(WorkflowStep):
- template = 'resource/steps/pod_definition.html'
- title = "Define Networks"
- description = "Use the tool below to draw the network topology of your POD"
- short_title = "networking"
- form = NetworkDefinitionForm
-
- def get_vlans(self):
- vlans = self.repo_get(self.repo.VLANS)
- if vlans:
- return vlans
- # try to grab some vlans from lab
- models = self.repo_get(self.repo.RESOURCE_TEMPLATE_MODELS, {})
- if "bundle" not in models:
- return None
- lab = models['bundle'].lab
- if lab is None or lab.vlan_manager is None:
- return None
- try:
- vlans = lab.vlan_manager.get_vlans(count=lab.vlan_manager.block_size)
- self.repo_put(self.repo.VLANS, vlans)
- return vlans
- except Exception:
- return None
-
- def make_mx_network_dict(self, network):
- return {
- 'id': network.id,
- 'name': network.name,
- 'public': network.is_public
- }
-
- def make_mx_resource_dict(self, resource_config):
- resource_dict = {
- 'id': resource_config.id,
- 'interfaces': [],
- 'value': {
- 'name': resource_config.name,
- 'id': resource_config.id,
- 'description': resource_config.profile.description
- }
- }
-
- for interface_config in resource_config.interface_configs.all():
- connections = []
- for connection in interface_config.connections.all():
- connections.append({'tagged': connection.vlan_is_tagged, 'network': connection.network.id})
-
- interface_dict = {
- "id": interface_config.id,
- "name": interface_config.profile.name,
- "description": "speed: " + str(interface_config.profile.speed) + "M\ntype: " + interface_config.profile.nic_type,
- "connections": connections
- }
-
- resource_dict['interfaces'].append(interface_dict)
-
- return resource_dict
-
- def make_mx_host_dict(self, generic_host):
- host = {
- 'id': generic_host.profile.name,
- 'interfaces': [],
- 'value': {
- "name": generic_host.profile.name,
- "description": generic_host.profile.description
- }
- }
- for iface in generic_host.profile.interfaceprofile.all():
- host['interfaces'].append({
- "name": iface.name,
- "description": "speed: " + str(iface.speed) + "M\ntype: " + iface.nic_type
- })
- return host
-
- # first step guards this one, so can't get here without at least empty
- # models being populated by step one
- def get_context(self):
- context = super(Define_Nets, self).get_context()
- context.update({
- 'form': NetworkDefinitionForm(),
- 'debug': settings.DEBUG,
- 'resources': {},
- 'networks': {},
- 'vlans': [],
- # remove others
- 'hosts': [],
- 'added_hosts': [],
- 'removed_hosts': []
- })
-
- models = self.repo_get(self.repo.RESOURCE_TEMPLATE_MODELS) # infallible, guarded by prior step
- for resource in models['resources']:
- d = self.make_mx_resource_dict(resource)
- context['resources'][d['id']] = d
-
- for network in models['networks'].values():
- d = self.make_mx_network_dict(network)
- context['networks'][d['id']] = d
-
- return context
-
- def post(self, post_data, user):
- try:
- xmlData = post_data.get("xml")
- self.updateModels(xmlData)
- # update model with xml
- self.set_valid("Networks applied successfully")
- except ResourceAvailabilityException:
- self.set_invalid("Public network not availble")
- except Exception as e:
- traceback.print_exc()
- self.set_invalid("An error occurred when applying networks: " + str(e))
-
- def resetNetworks(self, networks: List[Network]): # potentially just pass template here?
- for network in networks:
- network.delete()
-
- def updateModels(self, xmlData):
- models = self.repo_get(self.repo.RESOURCE_TEMPLATE_MODELS, {})
- given_hosts = None
- interfaces = None
- networks = None
- try:
- given_hosts, interfaces, networks = self.parseXml(xmlData)
- except Exception as e:
- print("tried to parse Xml, got exception instead:")
- print(e)
-
- existing_rconfig_list = models.get("resources", [])
- existing_rconfigs = {} # maps id to host
- for rconfig in existing_rconfig_list:
- existing_rconfigs["host_" + str(rconfig.id)] = rconfig
-
- bundle = models.get("template") # hard fail if not in repo
-
- self.resetNetworks(models['networks'].values())
- models['networks'] = {}
-
- for net_id, net in networks.items():
- network = Network.objects.create(
- name=net['name'],
- bundle=bundle,
- is_public=net['public'])
-
- models['networks'][net_id] = network
- network.save()
-
- for hostid, given_host in given_hosts.items():
- for ifaceId in given_host['interfaces']:
- iface = interfaces[ifaceId]
-
- iface_config = InterfaceConfiguration.objects.get(id=iface['config_id'])
- if iface_config.resource_config.template.id != bundle.id:
- raise ValidationError("User does not own the template they are editing")
-
- for connection in iface['connections']:
- network_id = connection['network']
- net = models['networks'][network_id]
- connection = NetworkConnection(vlan_is_tagged=connection['tagged'], network=net)
- connection.save()
- iface_config.connections.add(connection)
- iface_config.save()
- self.repo_put(self.repo.RESOURCE_TEMPLATE_MODELS, models)
-
- def decomposeXml(self, xmlString):
- """
- Translate XML into useable data.
-
- This function takes in an xml doc from our front end
- and returns dictionaries that map cellIds to the xml
- nodes themselves. There is no unpacking of the
- xml objects, just grouping and organizing
- """
- connections = {}
- networks = {}
- hosts = {}
- interfaces = {}
- network_ports = {}
-
- xmlDom = minidom.parseString(xmlString)
- root = xmlDom.documentElement.firstChild
- for cell in root.childNodes:
- cellId = cell.getAttribute('id')
- group = cellId.split("_")[0]
- parentGroup = cell.getAttribute("parent").split("_")[0]
- # place cell into correct group
-
- if cell.getAttribute("edge"):
- connections[cellId] = cell
-
- elif "network" in group:
- networks[cellId] = cell
-
- elif "host" in group:
- hosts[cellId] = cell
-
- elif "host" in parentGroup:
- interfaces[cellId] = cell
-
- # make network ports also map to thier network
- elif "network" in parentGroup:
- network_ports[cellId] = cell.getAttribute("parent") # maps port ID to net ID
-
- return connections, networks, hosts, interfaces, network_ports
-
- # serialize and deserialize xml from mxGraph
- def parseXml(self, xmlString):
- networks = {} # maps net name to network object
- hosts = {} # cotains id -> hosts, each containing interfaces, referencing networks
- interfaces = {} # maps id -> interface
- untagged_ifaces = set() # used to check vlan config
- network_names = set() # used to check network names
- xml_connections, xml_nets, xml_hosts, xml_ifaces, xml_ports = self.decomposeXml(xmlString)
-
- # parse Hosts
- for cellId, cell in xml_hosts.items():
- cell_json_str = cell.getAttribute("value")
- cell_json = json.loads(cell_json_str)
- host = {"interfaces": [], "name": cellId, "hostname": cell_json['name']}
- hosts[cellId] = host
-
- # parse networks
- for cellId, cell in xml_nets.items():
- escaped_json_str = cell.getAttribute("value")
- json_str = escaped_json_str.replace('&quot;', '"')
- net_info = json.loads(json_str)
- net_name = net_info['name']
- public = net_info['public']
- if net_name in network_names:
- raise NetworkExistsException("Non unique network name found")
- network = {"name": net_name, "public": public, "id": cellId}
- networks[cellId] = network
- network_names.add(net_name)
-
- # parse interfaces
- for cellId, cell in xml_ifaces.items():
- parentId = cell.getAttribute('parent')
- cell_json_str = cell.getAttribute("value")
- cell_json = json.loads(cell_json_str)
- iface = {"graph_id": cellId, "connections": [], "config_id": cell_json['id'], "profile_name": cell_json['name']}
- hosts[parentId]['interfaces'].append(cellId)
- interfaces[cellId] = iface
-
- # parse connections
- for cellId, cell in xml_connections.items():
- escaped_json_str = cell.getAttribute("value")
- json_str = escaped_json_str.replace('&quot;', '"')
- attributes = json.loads(json_str)
- tagged = attributes['tagged']
- interface = None
- network = None
- src = cell.getAttribute("source")
- tgt = cell.getAttribute("target")
- if src in interfaces:
- interface = interfaces[src]
- network = networks[xml_ports[tgt]]
- else:
- interface = interfaces[tgt]
- network = networks[xml_ports[src]]
-
- if not tagged:
- if interface['config_id'] in untagged_ifaces:
- raise InvalidVlanConfigurationException("More than one untagged vlan on an interface")
- untagged_ifaces.add(interface['config_id'])
-
- # add connection to interface
- interface['connections'].append({"tagged": tagged, "network": network['id']})
-
- return hosts, interfaces, networks
-
-
-class Resource_Meta_Info(WorkflowStep):
- template = 'resource/steps/meta_info.html'
- title = "Extra Info"
- description = "Please fill out the rest of the information about your resource"
- short_title = "pod info"
-
- def update_confirmation(self):
- confirm = self.repo_get(self.repo.CONFIRMATION, {})
- if "template" not in confirm:
- confirm['template'] = {}
- models = self.repo_get(self.repo.RESOURCE_TEMPLATE_MODELS, {})
- if "template" in models:
- confirm['template']['description'] = models['template'].description
- confirm['template']['name'] = models['template'].name
- self.repo_put(self.repo.CONFIRMATION, confirm)
-
- def get_context(self):
- context = super(Resource_Meta_Info, self).get_context()
- name = ""
- desc = ""
- models = self.repo_get(self.repo.RESOURCE_TEMPLATE_MODELS, None)
- bundle = models['template']
- if bundle:
- name = bundle.name
- desc = bundle.description
- context['form'] = ResourceMetaForm(initial={"bundle_name": name, "bundle_description": desc})
- return context
-
- def post(self, post_data, user):
- form = ResourceMetaForm(post_data)
- if form.is_valid():
- models = self.repo_get(self.repo.RESOURCE_TEMPLATE_MODELS, {})
- name = form.cleaned_data['bundle_name']
- desc = form.cleaned_data['bundle_description']
- bundle = models['template'] # infallible
- bundle.name = name
- bundle.description = desc
- bundle.save()
- self.repo_put(self.repo.RESOURCE_TEMPLATE_MODELS, models)
- confirm = self.repo_get(self.repo.CONFIRMATION)
- if "resource" not in confirm:
- confirm['resource'] = {}
- confirm_info = confirm['resource']
- confirm_info["name"] = name
- tmp = desc
- if len(tmp) > 60:
- tmp = tmp[:60] + "..."
- confirm_info["description"] = tmp
- self.repo_put(self.repo.CONFIRMATION, confirm)
- self.set_valid("Step Completed")
- else:
- self.set_invalid("Please complete all fields.")
diff --git a/src/workflow/snapshot_workflow.py b/src/workflow/snapshot_workflow.py
deleted file mode 100644
index c0e2052..0000000
--- a/src/workflow/snapshot_workflow.py
+++ /dev/null
@@ -1,116 +0,0 @@
-##############################################################################
-# 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.utils import timezone
-import json
-
-from booking.models import Booking
-from resource_inventory.models import ResourceQuery, Image
-from workflow.models import WorkflowStep
-from workflow.forms import BasicMetaForm, SnapshotHostSelectForm
-
-
-class Select_Host_Step(WorkflowStep):
- template = "snapshot_workflow/steps/select_host.html"
- title = "Select Host"
- description = "Choose which machine you want to snapshot"
- short_title = "host"
-
- def get_context(self):
- context = super(Select_Host_Step, self).get_context()
- context['form'] = SnapshotHostSelectForm()
- booking_hosts = {}
- now = timezone.now()
- user = self.repo_get(self.repo.SESSION_USER)
- bookings = Booking.objects.filter(start__lt=now, end__gt=now, owner=user)
- for booking in bookings:
- booking_hosts[booking.id] = {}
- booking_hosts[booking.id]['purpose'] = booking.purpose
- booking_hosts[booking.id]['start'] = booking.start.strftime("%Y-%m-%d")
- booking_hosts[booking.id]['end'] = booking.end.strftime("%Y-%m-%d")
- booking_hosts[booking.id]['hosts'] = []
- for genericHost in booking.resource.template.getResources():
- booking_hosts[booking.id]['hosts'].append({"name": genericHost.resource.name})
-
- context['booking_hosts'] = booking_hosts
-
- chosen_host = self.repo_get(self.repo.SNAPSHOT_MODELS, {}).get("host")
- if chosen_host:
- chosen = {}
- chosen['booking_id'] = self.repo_get(self.repo.SNAPSHOT_BOOKING_ID)
- chosen['hostname'] = chosen_host.template.resource.name
- context['chosen'] = chosen
- return context
-
- def post(self, post_data, user):
- host_data = post_data.get("host")
- if not host_data:
- self.set_invalid("Please select a host")
- return
- host = json.loads(host_data)
- if 'name' not in host or 'booking' not in host:
- self.set_invalid("Invalid host selected")
- return
- name = host['name']
- booking_id = host['booking']
- booking = Booking.objects.get(pk=booking_id)
- host = ResourceQuery.get(bundle=booking.resource, template__resource__name=name)
- models = self.repo_get(self.repo.SNAPSHOT_MODELS, {})
- if "host" not in models:
- models['host'] = host
- if 'snapshot' not in models:
- models['snapshot'] = Image()
- self.repo_put(self.repo.SNAPSHOT_MODELS, models)
- self.repo_put(self.repo.SNAPSHOT_BOOKING_ID, booking_id)
-
- confirm = self.repo_get(self.repo.CONFIRMATION, {})
- snap_confirm = confirm.get("snapshot", {})
- snap_confirm['host'] = name
- confirm['snapshot'] = snap_confirm
- self.repo_put(self.repo.CONFIRMATION, confirm)
- self.set_valid("Success")
-
-
-class Image_Meta_Step(WorkflowStep):
- template = "snapshot_workflow/steps/meta.html"
- title = "Additional Information"
- description = "We need some more info"
- short_title = "info"
-
- def get_context(self):
- context = super(Image_Meta_Step, self).get_context()
- name = self.repo_get(self.repo.SNAPSHOT_NAME, False)
- desc = self.repo_get(self.repo.SNAPSHOT_DESC, False)
- form = None
- if name and desc:
- form = BasicMetaForm(initial={"name": name, "description": desc})
- else:
- form = BasicMetaForm()
- context['form'] = form
- return context
-
- def post(self, post_data, user):
- form = BasicMetaForm(post_data)
- if form.is_valid():
- name = form.cleaned_data['name']
- self.repo_put(self.repo.SNAPSHOT_NAME, name)
- description = form.cleaned_data['description']
- self.repo_put(self.repo.SNAPSHOT_DESC, description)
-
- confirm = self.repo_get(self.repo.CONFIRMATION, {})
- snap_confirm = confirm.get("snapshot", {})
- snap_confirm['name'] = name
- snap_confirm['description'] = description
- confirm['snapshot'] = snap_confirm
- self.repo_put(self.repo.CONFIRMATION, confirm)
-
- self.set_valid("Success")
- else:
- self.set_invalid("Please Fill out the Form")
diff --git a/src/workflow/tests/__init__.py b/src/workflow/tests/__init__.py
deleted file mode 100644
index 4f0437d..0000000
--- a/src/workflow/tests/__init__.py
+++ /dev/null
@@ -1,8 +0,0 @@
-##############################################################################
-# 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/src/workflow/tests/constants.py b/src/workflow/tests/constants.py
deleted file mode 100644
index f94a949..0000000
--- a/src/workflow/tests/constants.py
+++ /dev/null
@@ -1,198 +0,0 @@
-##############################################################################
-# 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
-##############################################################################
-POD_XML = """<mxGraphModel>
-<root>
-<mxCell id="0"/>
-<mxCell id="1" parent="0"/>
-<mxCell id="host_null" value="Test profile 0" style="editable=0" vertex="1" connectable="0" parent="1">
-<mxGeometry x="75" y="150" width="110" height="90" as="geometry"/>
-</mxCell>
-<mxCell id="2" value="eno0" style="fillColor=blue;editable=0" vertex="1" parent="host_null">
-<mxGeometry x="90" y="5" width="20" height="20" as="geometry"/>
-</mxCell>
-<mxCell id="3" value="eno1" style="fillColor=blue;editable=0" vertex="1" parent="host_null">
-<mxGeometry x="90" y="30" width="20" height="20" as="geometry"/>
-</mxCell>
-<mxCell id="4" value="eno2" style="fillColor=blue;editable=0" vertex="1" parent="host_null">
-<mxGeometry x="90" y="55" width="20" height="20" as="geometry"/>
-</mxCell>
-<mxCell id="5" value="Test profile 3" style="editable=0" vertex="1" connectable="0" parent="1">
-<mxGeometry x="75" y="290" width="110" height="90" as="geometry"/>
-</mxCell>
-<mxCell id="6" value="eno0" style="fillColor=blue;editable=0" vertex="1" parent="5">
-<mxGeometry x="90" y="5" width="20" height="20" as="geometry"/>
-</mxCell>
-<mxCell id="7" value="eno1" style="fillColor=blue;editable=0" vertex="1" parent="5">
-<mxGeometry x="90" y="30" width="20" height="20" as="geometry"/>
-</mxCell>
-<mxCell id="8" value="eno2" style="fillColor=blue;editable=0" vertex="1" parent="5">
-<mxGeometry x="90" y="55" width="20" height="20" as="geometry"/>
-</mxCell>
-<mxCell id="network_0" value="{&quot;vlan_id&quot;:&quot;500&quot;,&quot;name&quot;:&quot;net&quot;}" style="fillColor=red" vertex="1" parent="1">
-<mxGeometry x="400" y="-20" width="10" height="2000" as="geometry"/>
-</mxCell>
-<mxCell id="9" value="" style="fillColor=black;opacity=0" vertex="1" parent="network_0">
-<mxGeometry width="10" height="40" relative="1" as="geometry"/>
-</mxCell>
-<mxCell id="10" value="" style="fillColor=black;opacity=0" vertex="1" parent="network_0">
-<mxGeometry y="0.02" width="10" height="40" relative="1" as="geometry"/>
-</mxCell>
-<mxCell id="11" value="" style="fillColor=black;opacity=0" vertex="1" parent="network_0">
-<mxGeometry y="0.04" width="10" height="40" relative="1" as="geometry"/>
-</mxCell>
-<mxCell id="12" value="" style="fillColor=black;opacity=0" vertex="1" parent="network_0">
-<mxGeometry y="0.06" width="10" height="40" relative="1" as="geometry"/>
-</mxCell>
-<mxCell id="13" value="" style="fillColor=black;opacity=0" vertex="1" parent="network_0">
-<mxGeometry y="0.08" width="10" height="40" relative="1" as="geometry"/>
-</mxCell>
-<mxCell id="14" value="" style="fillColor=black;opacity=0" vertex="1" parent="network_0">
-<mxGeometry y="0.1" width="10" height="40" relative="1" as="geometry"/>
-</mxCell>
-<mxCell id="15" value="" style="fillColor=black;opacity=0" vertex="1" parent="network_0">
-<mxGeometry y="0.12" width="10" height="40" relative="1" as="geometry"/>
-</mxCell>
-<mxCell id="16" value="" style="fillColor=black;opacity=0" vertex="1" parent="network_0">
-<mxGeometry y="0.14" width="10" height="40" relative="1" as="geometry"/>
-</mxCell>
-<mxCell id="17" value="" style="fillColor=black;opacity=0" vertex="1" parent="network_0">
-<mxGeometry y="0.16" width="10" height="40" relative="1" as="geometry"/>
-</mxCell>
-<mxCell id="18" value="" style="fillColor=black;opacity=0" vertex="1" parent="network_0">
-<mxGeometry y="0.18" width="10" height="40" relative="1" as="geometry"/>
-</mxCell>
-<mxCell id="19" value="" style="fillColor=black;opacity=0" vertex="1" parent="network_0">
-<mxGeometry y="0.2" width="10" height="40" relative="1" as="geometry"/>
-</mxCell>
-<mxCell id="20" value="" style="fillColor=black;opacity=0" vertex="1" parent="network_0">
-<mxGeometry y="0.22" width="10" height="40" relative="1" as="geometry"/>
-</mxCell>
-<mxCell id="21" value="" style="fillColor=black;opacity=0" vertex="1" parent="network_0">
-<mxGeometry y="0.24" width="10" height="40" relative="1" as="geometry"/>
-</mxCell>
-<mxCell id="22" value="" style="fillColor=black;opacity=0" vertex="1" parent="network_0">
-<mxGeometry y="0.26" width="10" height="40" relative="1" as="geometry"/>
-</mxCell>
-<mxCell id="23" value="" style="fillColor=black;opacity=0" vertex="1" parent="network_0">
-<mxGeometry y="0.28" width="10" height="40" relative="1" as="geometry"/>
-</mxCell>
-<mxCell id="24" value="" style="fillColor=black;opacity=0" vertex="1" parent="network_0">
-<mxGeometry y="0.3" width="10" height="40" relative="1" as="geometry"/>
-</mxCell>
-<mxCell id="25" value="" style="fillColor=black;opacity=0" vertex="1" parent="network_0">
-<mxGeometry y="0.32" width="10" height="40" relative="1" as="geometry"/>
-</mxCell>
-<mxCell id="26" value="" style="fillColor=black;opacity=0" vertex="1" parent="network_0">
-<mxGeometry y="0.34" width="10" height="40" relative="1" as="geometry"/>
-</mxCell>
-<mxCell id="27" value="" style="fillColor=black;opacity=0" vertex="1" parent="network_0">
-<mxGeometry y="0.36" width="10" height="40" relative="1" as="geometry"/>
-</mxCell>
-<mxCell id="28" value="" style="fillColor=black;opacity=0" vertex="1" parent="network_0">
-<mxGeometry y="0.38" width="10" height="40" relative="1" as="geometry"/>
-</mxCell>
-<mxCell id="29" value="" style="fillColor=black;opacity=0" vertex="1" parent="network_0">
-<mxGeometry y="0.4" width="10" height="40" relative="1" as="geometry"/>
-</mxCell>
-<mxCell id="30" value="" style="fillColor=black;opacity=0" vertex="1" parent="network_0">
-<mxGeometry y="0.42" width="10" height="40" relative="1" as="geometry"/>
-</mxCell>
-<mxCell id="31" value="" style="fillColor=black;opacity=0" vertex="1" parent="network_0">
-<mxGeometry y="0.44" width="10" height="40" relative="1" as="geometry"/>
-</mxCell>
-<mxCell id="32" value="" style="fillColor=black;opacity=0" vertex="1" parent="network_0">
-<mxGeometry y="0.46" width="10" height="40" relative="1" as="geometry"/>
-</mxCell>
-<mxCell id="33" value="" style="fillColor=black;opacity=0" vertex="1" parent="network_0">
-<mxGeometry y="0.48" width="10" height="40" relative="1" as="geometry"/>
-</mxCell>
-<mxCell id="34" value="" style="fillColor=black;opacity=0" vertex="1" parent="network_0">
-<mxGeometry y="0.5" width="10" height="40" relative="1" as="geometry"/>
-</mxCell>
-<mxCell id="35" value="" style="fillColor=black;opacity=0" vertex="1" parent="network_0">
-<mxGeometry y="0.52" width="10" height="40" relative="1" as="geometry"/>
-</mxCell>
-<mxCell id="36" value="" style="fillColor=black;opacity=0" vertex="1" parent="network_0">
-<mxGeometry y="0.54" width="10" height="40" relative="1" as="geometry"/>
-</mxCell>
-<mxCell id="37" value="" style="fillColor=black;opacity=0" vertex="1" parent="network_0">
-<mxGeometry y="0.56" width="10" height="40" relative="1" as="geometry"/>
-</mxCell>
-<mxCell id="38" value="" style="fillColor=black;opacity=0" vertex="1" parent="network_0">
-<mxGeometry y="0.58" width="10" height="40" relative="1" as="geometry"/>
-</mxCell>
-<mxCell id="39" value="" style="fillColor=black;opacity=0" vertex="1" parent="network_0">
-<mxGeometry y="0.6" width="10" height="40" relative="1" as="geometry"/>
-</mxCell>
-<mxCell id="40" value="" style="fillColor=black;opacity=0" vertex="1" parent="network_0">
-<mxGeometry y="0.62" width="10" height="40" relative="1" as="geometry"/>
-</mxCell>
-<mxCell id="41" value="" style="fillColor=black;opacity=0" vertex="1" parent="network_0">
-<mxGeometry y="0.64" width="10" height="40" relative="1" as="geometry"/>
-</mxCell>
-<mxCell id="42" value="" style="fillColor=black;opacity=0" vertex="1" parent="network_0">
-<mxGeometry y="0.66" width="10" height="40" relative="1" as="geometry"/>
-</mxCell>
-<mxCell id="43" value="" style="fillColor=black;opacity=0" vertex="1" parent="network_0">
-<mxGeometry y="0.68" width="10" height="40" relative="1" as="geometry"/>
-</mxCell>
-<mxCell id="44" value="" style="fillColor=black;opacity=0" vertex="1" parent="network_0">
-<mxGeometry y="0.7000000000000001" width="10" height="40" relative="1" as="geometry"/>
-</mxCell>
-<mxCell id="45" value="" style="fillColor=black;opacity=0" vertex="1" parent="network_0">
-<mxGeometry y="0.72" width="10" height="40" relative="1" as="geometry"/>
-</mxCell>
-<mxCell id="46" value="" style="fillColor=black;opacity=0" vertex="1" parent="network_0">
-<mxGeometry y="0.74" width="10" height="40" relative="1" as="geometry"/>
-</mxCell>
-<mxCell id="47" value="" style="fillColor=black;opacity=0" vertex="1" parent="network_0">
-<mxGeometry y="0.76" width="10" height="40" relative="1" as="geometry"/>
-</mxCell>
-<mxCell id="48" value="" style="fillColor=black;opacity=0" vertex="1" parent="network_0">
-<mxGeometry y="0.78" width="10" height="40" relative="1" as="geometry"/>
-</mxCell>
-<mxCell id="49" value="" style="fillColor=black;opacity=0" vertex="1" parent="network_0">
-<mxGeometry y="0.8" width="10" height="40" relative="1" as="geometry"/>
-</mxCell>
-<mxCell id="50" value="" style="fillColor=black;opacity=0" vertex="1" parent="network_0">
-<mxGeometry y="0.8200000000000001" width="10" height="40" relative="1" as="geometry"/>
-</mxCell>
-<mxCell id="51" value="" style="fillColor=black;opacity=0" vertex="1" parent="network_0">
-<mxGeometry y="0.84" width="10" height="40" relative="1" as="geometry"/>
-</mxCell>
-<mxCell id="52" value="" style="fillColor=black;opacity=0" vertex="1" parent="network_0">
-<mxGeometry y="0.86" width="10" height="40" relative="1" as="geometry"/>
-</mxCell>
-<mxCell id="53" value="" style="fillColor=black;opacity=0" vertex="1" parent="network_0">
-<mxGeometry y="0.88" width="10" height="40" relative="1" as="geometry"/>
-</mxCell>
-<mxCell id="54" value="" style="fillColor=black;opacity=0" vertex="1" parent="network_0">
-<mxGeometry y="0.9" width="10" height="40" relative="1" as="geometry"/>
-</mxCell>
-<mxCell id="55" value="" style="fillColor=black;opacity=0" vertex="1" parent="network_0">
-<mxGeometry y="0.92" width="10" height="40" relative="1" as="geometry"/>
-</mxCell>
-<mxCell id="56" value="" style="fillColor=black;opacity=0" vertex="1" parent="network_0">
-<mxGeometry y="0.9400000000000001" width="10" height="40" relative="1" as="geometry"/>
-</mxCell>
-<mxCell id="57" value="" style="fillColor=black;opacity=0" vertex="1" parent="network_0">
-<mxGeometry y="0.96" width="10" height="40" relative="1" as="geometry"/>
-</mxCell>
-<mxCell id="58" value="" style="fillColor=black;opacity=0" vertex="1" parent="network_0">
-<mxGeometry y="0.98" width="10" height="40" relative="1" as="geometry"/>
-</mxCell>
-<mxCell id="59" value="{&quot;tagged&quot;:true}" style="strokeColor=red" edge="1" parent="1" source="2" target="13">
-<mxGeometry relative="1" as="geometry"/>
-</mxCell>
-<mxCell id="60" value="{&quot;tagged&quot;:false}" style="strokeColor=red" edge="1" parent="1" source="7" target="17">
-<mxGeometry relative="1" as="geometry"/>
-</mxCell>
-</root>
-</mxGraphModel>
-"""
diff --git a/src/workflow/tests/test_fixtures.py b/src/workflow/tests/test_fixtures.py
deleted file mode 100644
index fe16be7..0000000
--- a/src/workflow/tests/test_fixtures.py
+++ /dev/null
@@ -1,2 +0,0 @@
-
-MX_GRAPH_MODEL = '<mxGraphModel><root><mxCell id="0"/><mxCell id="1" parent="0"/><mxCell id="host_c" value="{&quot;name&quot;:&quot;c&quot;,&quot;description&quot;:&quot;Intel based ProLiant server from HPE&quot;}" style="editable=0" parent="1" vertex="1" connectable="0"><mxGeometry x="75" y="150" width="110" height="175" as="geometry"><mxPoint x="-50" as="offset"/></mxGeometry></mxCell><mxCell id="2" value="{&quot;name&quot;:&quot;ens4f1&quot;,&quot;description&quot;:&quot;speed: 10000M type: onboard&quot;}" style="fillColor=blue;editable=0" parent="host_c" vertex="1"><mxGeometry x="90" y="12" width="20" height="20" as="geometry"><mxPoint x="-26" as="offset"/></mxGeometry></mxCell><mxCell id="3" value="{&quot;name&quot;:&quot;ens4f0&quot;,&quot;description&quot;:&quot;speed: 10000M type: onboard&quot;}" style="fillColor=blue;editable=0" parent="host_c" vertex="1"><mxGeometry x="90" y="37" width="20" height="20" as="geometry"><mxPoint x="-26" as="offset"/></mxGeometry></mxCell><mxCell id="4" value="{&quot;name&quot;:&quot;ens1f2&quot;,&quot;description&quot;:&quot;speed: 10000M type: onboard&quot;}" style="fillColor=blue;editable=0" parent="host_c" vertex="1"><mxGeometry x="90" y="62" width="20" height="20" as="geometry"><mxPoint x="-26" as="offset"/></mxGeometry></mxCell><mxCell id="5" value="{&quot;name&quot;:&quot;ens1f1&quot;,&quot;description&quot;:&quot;speed: 10000M type: onboard&quot;}" style="fillColor=blue;editable=0" parent="host_c" vertex="1"><mxGeometry x="90" y="87" width="20" height="20" as="geometry"><mxPoint x="-26" as="offset"/></mxGeometry></mxCell><mxCell id="6" value="{&quot;name&quot;:&quot;ens1f0&quot;,&quot;description&quot;:&quot;speed: 10000M type: onboard&quot;}" style="fillColor=blue;editable=0" parent="host_c" vertex="1"><mxGeometry x="90" y="112" width="20" height="20" as="geometry"><mxPoint x="-26" as="offset"/></mxGeometry></mxCell><mxCell id="7" value="{&quot;name&quot;:&quot;eno49&quot;,&quot;description&quot;:&quot;speed: 10000M type: onboard&quot;}" style="fillColor=blue;editable=0" parent="host_c" vertex="1"><mxGeometry x="90" y="137" width="20" height="20" as="geometry"><mxPoint x="-22" as="offset"/></mxGeometry></mxCell><mxCell id="network_0" value="{&quot;name&quot;:&quot;public&quot;,&quot;public&quot;:true}" style="fillColor=red" parent="1" vertex="1"><mxGeometry x="400" y="-10" width="10" height="1700" as="geometry"/></mxCell><mxCell id="8" value="" style="fillColor=black;opacity=0" parent="network_0" vertex="1"><mxGeometry width="10" height="37.77777777777778" relative="1" as="geometry"/></mxCell><mxCell id="9" value="" style="fillColor=black;opacity=0" parent="network_0" vertex="1"><mxGeometry y="0.022222222222222223" width="10" height="37.77777777777778" relative="1" as="geometry"/></mxCell><mxCell id="10" value="" style="fillColor=black;opacity=0" parent="network_0" vertex="1"><mxGeometry y="0.044444444444444446" width="10" height="37.77777777777778" relative="1" as="geometry"/></mxCell><mxCell id="11" value="" style="fillColor=black;opacity=0" parent="network_0" vertex="1"><mxGeometry y="0.06666666666666667" width="10" height="37.77777777777778" relative="1" as="geometry"/></mxCell><mxCell id="12" value="" style="fillColor=black;opacity=0" parent="network_0" vertex="1"><mxGeometry y="0.08888888888888889" width="10" height="37.77777777777778" relative="1" as="geometry"/></mxCell><mxCell id="13" value="" style="fillColor=black;opacity=0" parent="network_0" vertex="1"><mxGeometry y="0.11111111111111112" width="10" height="37.77777777777778" relative="1" as="geometry"/></mxCell><mxCell id="14" value="" style="fillColor=black;opacity=0" parent="network_0" vertex="1"><mxGeometry y="0.13333333333333333" width="10" height="37.77777777777778" relative="1" as="geometry"/></mxCell><mxCell id="15" value="" style="fillColor=black;opacity=0" parent="network_0" vertex="1"><mxGeometry y="0.15555555555555556" width="10" height="37.77777777777778" relative="1" as="geometry"/></mxCell><mxCell id="16" value="" style="fillColor=black;opacity=0" parent="network_0" vertex="1"><mxGeometry y="0.17777777777777778" width="10" height="37.77777777777778" relative="1" as="geometry"/></mxCell><mxCell id="17" value="" style="fillColor=black;opacity=0" parent="network_0" vertex="1"><mxGeometry y="0.2" width="10" height="37.77777777777778" relative="1" as="geometry"/></mxCell><mxCell id="18" value="" style="fillColor=black;opacity=0" parent="network_0" vertex="1"><mxGeometry y="0.22222222222222224" width="10" height="37.77777777777778" relative="1" as="geometry"/></mxCell><mxCell id="19" value="" style="fillColor=black;opacity=0" parent="network_0" vertex="1"><mxGeometry y="0.24444444444444446" width="10" height="37.77777777777778" relative="1" as="geometry"/></mxCell><mxCell id="20" value="" style="fillColor=black;opacity=0" parent="network_0" vertex="1"><mxGeometry y="0.26666666666666666" width="10" height="37.77777777777778" relative="1" as="geometry"/></mxCell><mxCell id="21" value="" style="fillColor=black;opacity=0" parent="network_0" vertex="1"><mxGeometry y="0.2888888888888889" width="10" height="37.77777777777778" relative="1" as="geometry"/></mxCell><mxCell id="22" value="" style="fillColor=black;opacity=0" parent="network_0" vertex="1"><mxGeometry y="0.3111111111111111" width="10" height="37.77777777777778" relative="1" as="geometry"/></mxCell><mxCell id="23" value="" style="fillColor=black;opacity=0" parent="network_0" vertex="1"><mxGeometry y="0.33333333333333337" width="10" height="37.77777777777778" relative="1" as="geometry"/></mxCell><mxCell id="24" value="" style="fillColor=black;opacity=0" parent="network_0" vertex="1"><mxGeometry y="0.35555555555555557" width="10" height="37.77777777777778" relative="1" as="geometry"/></mxCell><mxCell id="25" value="" style="fillColor=black;opacity=0" parent="network_0" vertex="1"><mxGeometry y="0.37777777777777777" width="10" height="37.77777777777778" relative="1" as="geometry"/></mxCell><mxCell id="26" value="" style="fillColor=black;opacity=0" parent="network_0" vertex="1"><mxGeometry y="0.4" width="10" height="37.77777777777778" relative="1" as="geometry"/></mxCell><mxCell id="27" value="" style="fillColor=black;opacity=0" parent="network_0" vertex="1"><mxGeometry y="0.4222222222222222" width="10" height="37.77777777777778" relative="1" as="geometry"/></mxCell><mxCell id="28" value="" style="fillColor=black;opacity=0" parent="network_0" vertex="1"><mxGeometry y="0.4444444444444445" width="10" height="37.77777777777778" relative="1" as="geometry"/></mxCell><mxCell id="29" value="" style="fillColor=black;opacity=0" parent="network_0" vertex="1"><mxGeometry y="0.4666666666666667" width="10" height="37.77777777777778" relative="1" as="geometry"/></mxCell><mxCell id="30" value="" style="fillColor=black;opacity=0" parent="network_0" vertex="1"><mxGeometry y="0.48888888888888893" width="10" height="37.77777777777778" relative="1" as="geometry"/></mxCell><mxCell id="31" value="" style="fillColor=black;opacity=0" parent="network_0" vertex="1"><mxGeometry y="0.5111111111111112" width="10" height="37.77777777777778" relative="1" as="geometry"/></mxCell><mxCell id="32" value="" style="fillColor=black;opacity=0" parent="network_0" vertex="1"><mxGeometry y="0.5333333333333333" width="10" height="37.77777777777778" relative="1" as="geometry"/></mxCell><mxCell id="33" value="" style="fillColor=black;opacity=0" parent="network_0" vertex="1"><mxGeometry y="0.5555555555555556" width="10" height="37.77777777777778" relative="1" as="geometry"/></mxCell><mxCell id="34" value="" style="fillColor=black;opacity=0" parent="network_0" vertex="1"><mxGeometry y="0.5777777777777778" width="10" height="37.77777777777778" relative="1" as="geometry"/></mxCell><mxCell id="35" value="" style="fillColor=black;opacity=0" parent="network_0" vertex="1"><mxGeometry y="0.6" width="10" height="37.77777777777778" relative="1" as="geometry"/></mxCell><mxCell id="36" value="" style="fillColor=black;opacity=0" parent="network_0" vertex="1"><mxGeometry y="0.6222222222222222" width="10" height="37.77777777777778" relative="1" as="geometry"/></mxCell><mxCell id="37" value="" style="fillColor=black;opacity=0" parent="network_0" vertex="1"><mxGeometry y="0.6444444444444445" width="10" height="37.77777777777778" relative="1" as="geometry"/></mxCell><mxCell id="38" value="" style="fillColor=black;opacity=0" parent="network_0" vertex="1"><mxGeometry y="0.6666666666666667" width="10" height="37.77777777777778" relative="1" as="geometry"/></mxCell><mxCell id="39" value="" style="fillColor=black;opacity=0" parent="network_0" vertex="1"><mxGeometry y="0.6888888888888889" width="10" height="37.77777777777778" relative="1" as="geometry"/></mxCell><mxCell id="40" value="" style="fillColor=black;opacity=0" parent="network_0" vertex="1"><mxGeometry y="0.7111111111111111" width="10" height="37.77777777777778" relative="1" as="geometry"/></mxCell><mxCell id="41" value="" style="fillColor=black;opacity=0" parent="network_0" vertex="1"><mxGeometry y="0.7333333333333334" width="10" height="37.77777777777778" relative="1" as="geometry"/></mxCell><mxCell id="42" value="" style="fillColor=black;opacity=0" parent="network_0" vertex="1"><mxGeometry y="0.7555555555555555" width="10" height="37.77777777777778" relative="1" as="geometry"/></mxCell><mxCell id="43" value="" style="fillColor=black;opacity=0" parent="network_0" vertex="1"><mxGeometry y="0.7777777777777778" width="10" height="37.77777777777778" relative="1" as="geometry"/></mxCell><mxCell id="44" value="" style="fillColor=black;opacity=0" parent="network_0" vertex="1"><mxGeometry y="0.8" width="10" height="37.77777777777778" relative="1" as="geometry"/></mxCell><mxCell id="45" value="" style="fillColor=black;opacity=0" parent="network_0" vertex="1"><mxGeometry y="0.8222222222222223" width="10" height="37.77777777777778" relative="1" as="geometry"/></mxCell><mxCell id="46" value="" style="fillColor=black;opacity=0" parent="network_0" vertex="1"><mxGeometry y="0.8444444444444444" width="10" height="37.77777777777778" relative="1" as="geometry"/></mxCell><mxCell id="47" value="" style="fillColor=black;opacity=0" parent="network_0" vertex="1"><mxGeometry y="0.8666666666666667" width="10" height="37.77777777777778" relative="1" as="geometry"/></mxCell><mxCell id="48" value="" style="fillColor=black;opacity=0" parent="network_0" vertex="1"><mxGeometry y="0.888888888888889" width="10" height="37.77777777777778" relative="1" as="geometry"/></mxCell><mxCell id="49" value="" style="fillColor=black;opacity=0" parent="network_0" vertex="1"><mxGeometry y="0.9111111111111111" width="10" height="37.77777777777778" relative="1" as="geometry"/></mxCell><mxCell id="50" value="" style="fillColor=black;opacity=0" parent="network_0" vertex="1"><mxGeometry y="0.9333333333333333" width="10" height="37.77777777777778" relative="1" as="geometry"/></mxCell><mxCell id="51" value="" style="fillColor=black;opacity=0" parent="network_0" vertex="1"><mxGeometry y="0.9555555555555556" width="10" height="37.77777777777778" relative="1" as="geometry"/></mxCell><mxCell id="52" value="" style="fillColor=black;opacity=0" parent="network_0" vertex="1"><mxGeometry y="0.9777777777777779" width="10" height="37.77777777777778" relative="1" as="geometry"/></mxCell><mxCell id="53" value="{&quot;tagged&quot;:false}" style="strokeColor=red" parent="1" source="2" target="13" edge="1"><mxGeometry relative="1" as="geometry"/></mxCell></root></mxGraphModel>'
diff --git a/src/workflow/tests/test_steps.py b/src/workflow/tests/test_steps.py
deleted file mode 100644
index ba27313..0000000
--- a/src/workflow/tests/test_steps.py
+++ /dev/null
@@ -1,269 +0,0 @@
-##############################################################################
-# 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
-##############################################################################
-
-"""
-This file tests basic functionality of each step class.
-
-More in depth case coverage of WorkflowStep.post() must happen elsewhere.
-"""
-
-import json
-from unittest import SkipTest, mock
-
-from django.test import TestCase, RequestFactory
-from dashboard.testing_utils import make_lab, make_user, make_os, \
- make_complete_host_profile, make_opnfv_role, make_image, make_grb, \
- make_config_bundle, make_host, make_user_profile, make_generic_host
-from workflow import resource_bundle_workflow
-from workflow import booking_workflow
-from workflow import sw_bundle_workflow
-from workflow.models import Repository
-from workflow.tests import test_fixtures
-
-
-class TestConfig:
- """
- Basic class to instantiate and hold reference.
-
- to models we will need often
- """
-
- def __init__(self, usr=None):
- self.lab = make_lab()
- self.user = usr or make_user()
- self.os = make_os()
- self.host_prof = make_complete_host_profile(self.lab)
- self.host = make_host(self.host_prof, self.lab, name="host1")
-
- # pod description as required by testing lib
- self.topology = {
- "host1": {
- "type": self.host_prof,
- "role": make_opnfv_role(),
- "image": make_image(self.lab, 3, self.user, self.os, self.host_prof),
- "nets": [
- [{"name": "public", "tagged": True, "public": True}]
- ]
- }
- }
- self.grb = make_grb(self.topology, self.user, self.lab)[0]
- self.generic_host = make_generic_host(self.grb, self.host_prof, "host1")
-
-
-class StepTestCase(TestCase):
-
- # after setUp is called, this should be an instance of a step
- step = None
-
- post_data = {} # subclasses will set this
-
- @classmethod
- def setUpTestData(cls):
- super().setUpTestData()
- cls.factory = RequestFactory()
- cls.user_prof = make_user_profile()
- cls.user = cls.user_prof.user
-
- def setUp(self):
- super().setUp()
- if self.step is None:
- raise SkipTest("Step instance not given")
- repo = Repository()
- self.add_to_repo(repo)
- self.step = self.step(1, repo)
-
- def assertCorrectPostBehavior(self, post_data):
- """
- Stub for validating step behavior on POST request.
-
- allows subclasses to override and make assertions about
- the side effects of self.step.post()
- post_data is the data passed into post()
- """
- return
-
- def add_to_repo(self, repo):
- """
- Stub for modifying the step's repo.
-
- This method is a hook that allows subclasses to modify
- the contents of the repo before the step is created.
- """
- return
-
- def assertValidHtml(self, html_str):
- """
- Assert that html_str is a valid html fragment.
-
- However, I know of no good way of doing this in python
- """
- self.assertTrue(isinstance(html_str, str))
- self.assertGreater(len(html_str), 0)
-
- def test_render_to_string(self):
- request = self.factory.get("/workflow/manager/")
- request.user = self.user
- response_html = self.step.render_to_string(request)
- self.assertValidHtml(response_html)
-
- def test_post(self, data=None):
- post_data = data or self.post_data
- self.step.post(post_data, self.user)
- self.assertCorrectPostBehavior(data)
-
-
-class SelectStepTestCase(StepTestCase):
- # ID of model to be sent to the step's form
- # can be an int or a list of ints
- obj_id = -1
-
- def setUp(self):
- super().setUp()
-
- try:
- iter(self.obj_id)
- except TypeError:
- self.obj_id = [self.obj_id]
-
- field_data = json.dumps(self.obj_id)
- self.post_data = {
- "searchable_select": [field_data]
- }
-
-
-class DefineHardwareTestCase(StepTestCase):
- step = resource_bundle_workflow.Define_Hardware
- post_data = {
- "filter_field": {
- "lab": {
- "lab_35": {"selected": True, "id": 35}},
- "host": {
- "host_1": {"selected": True, "id": 1}}
- }
- }
-
-
-class DefineNetworkTestCase(StepTestCase):
- step = resource_bundle_workflow.Define_Nets
- post_data = {"xml": test_fixtures.MX_GRAPH_MODEL}
-
-
-class ResourceMetaTestCase(StepTestCase):
- step = resource_bundle_workflow.Resource_Meta_Info
- post_data = {
- "bundle_name": "my_bundle",
- "bundle_description": "My Bundle"
- }
-
-
-class BookingResourceTestCase(SelectStepTestCase):
- step = booking_workflow.Booking_Resource_Select
-
- def add_to_repo(self, repo):
- repo.el[repo.SESSION_USER] = self.user
-
- @classmethod
- def setUpTestData(cls):
- super().setUpTestData()
- conf = TestConfig(usr=cls.user)
- cls.obj_id = conf.grb.id
-
-
-class SoftwareSelectTestCase(SelectStepTestCase):
- step = booking_workflow.SWConfig_Select
-
- def add_to_repo(self, repo):
- repo.el[repo.SESSION_USER] = self.user
- repo.el[repo.SELECTED_RESOURCE_TEMPLATE] = self.conf.grb
-
- @classmethod
- def setUpTestData(cls):
- super().setUpTestData()
- cls.conf = TestConfig(usr=cls.user)
- host_map = {"host1": cls.conf.generic_host}
- config_bundle = make_config_bundle(cls.conf.grb, cls.conf.user, cls.conf.topology, host_map)[0]
- cls.obj_id = config_bundle.id
-
-
-class OPNFVSelectTestCase(SelectStepTestCase):
- step = booking_workflow.OPNFV_Select
-
- def add_to_repo(self, repo):
- repo.el[repo.SELECTED_CONFIG_BUNDLE] = self.config_bundle
-
- @classmethod
- def setUpTestData(cls):
- super().setUpTestData()
- conf = TestConfig(usr=cls.user)
- host_map = {"host1": conf.generic_host}
- cls.config_bundle, opnfv_config = make_config_bundle(conf.grb, conf.user, conf.topology, host_map)
- cls.obj_id = opnfv_config.id
-
-
-class BookingMetaTestCase(StepTestCase):
- step = booking_workflow.Booking_Meta
- post_data = {
- "length": 14,
- "purpose": "Testing",
- "project": "Lab as a Service",
- "users": ["[-1]"]
- }
-
- def add_to_repo(self, repo):
- repo.el[repo.SESSION_MANAGER] = mock.MagicMock()
-
- @classmethod
- def setUpTestData(cls):
- super().setUpTestData()
- new_user = make_user(username="collaborator", email="different@mail.com")
- new_user_prof = make_user_profile(user=new_user)
- data = "[" + str(new_user_prof.id) + "]" # list of IDs
- cls.post_data['users'] = [data]
-
-
-class ConfigResourceSelectTestCase(SelectStepTestCase):
- step = sw_bundle_workflow.SWConf_Resource_Select
-
- def add_to_repo(self, repo):
- repo.el[repo.SESSION_USER] = self.user
-
- @classmethod
- def setUpTestData(cls):
- super().setUpTestData()
- conf = TestConfig(usr=cls.user)
- cls.obj_id = conf.grb.id
-
-
-class DefineSoftwareTestCase(StepTestCase):
- step = sw_bundle_workflow.Define_Software
- post_data = {
- "form-0-image": 1,
- "headnode": 1,
- "form-0-headnode": "",
- "form-TOTAL_FORMS": 1,
- "form-INITIAL_FORMS": 1,
- "form-MIN_NUM_FORMS": 0,
- "form-MAX_NUM_FORMS": 1000,
- }
-
- def add_to_repo(self, repo):
- repo.el[repo.SELECTED_RESOURCE_TEMPLATE] = self.conf.grb
-
- @classmethod
- def setUpTestData(cls):
- super().setUpTestData()
- cls.conf = TestConfig(usr=cls.user)
-
-
-class ConfigSoftwareTestCase(StepTestCase):
- step = sw_bundle_workflow.Config_Software
- post_data = {
- "name": "config_bundle",
- "description": "My Config Bundle"
- }
diff --git a/src/workflow/tests/test_workflows.py b/src/workflow/tests/test_workflows.py
deleted file mode 100644
index 995d699..0000000
--- a/src/workflow/tests/test_workflows.py
+++ /dev/null
@@ -1,99 +0,0 @@
-##############################################################################
-# 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 unittest import SkipTest
-from django.test import TestCase
-from workflow.workflow_factory import WorkflowFactory
-
-
-"""
-To start a workflow:
- POST to /wf/workflow {"add": <wf_type_int>
-
- types:
- 0 - Booking
- 1 - Resource
- 2 - Config
-
-To remove a workflow:
- POST to /wf/workflow {"cancel": ""}
-"""
-
-
-class WorkflowTestCase(TestCase):
-
- @classmethod
- def setUpClass(cls):
- super().setUpClass()
- raise SkipTest("These tests are no good")
-
- def setUp(self):
- self.clear_workflow()
- self.create_workflow(self.wf_type)
-
- def create_workflow(self, wf_type):
- self.clear_workflow()
-
- # creates workflow on backend
- self.client.post("/", {"create": int(wf_type)}) # TODO: verify content type, etc
-
- def clear_workflow(self):
- session = self.client.session
- for k in session.keys():
- del session[k]
- session.save()
-
- def render_steps(self):
- """Retrieve each step individually at /wf/workflow/step=<index>."""
- for i in range(self.step_count):
- # renders the step itself, not in an iframe
- exception = None
- try:
- response = self.client.get("/wf/workflow/", {"step": str(i)})
- self.assertLess(response.status_code, 300)
- except Exception as e:
- exception = e
-
- self.assertIsNone(exception)
-
-
-class BookingWorkflowTestCase(WorkflowTestCase):
-
- @classmethod
- def setUpClass(cls):
- super(BookingWorkflowTestCase, cls).setUpClass()
- cls.step_count = len(WorkflowFactory.booking_steps)
- cls.wf_type = 0
-
- def test_steps_render(self):
- super(BookingWorkflowTestCase, self).render_steps()
-
-
-class ResourceWorkflowTestCase(WorkflowTestCase):
-
- @classmethod
- def setUpClass(cls):
- super(ResourceWorkflowTestCase, cls).setUpClass()
- cls.step_count = len(WorkflowFactory.resource_steps)
- cls.wf_type = 1
-
- def test_steps_render(self):
- super(ResourceWorkflowTestCase, self).render_steps()
-
-
-class ConfigWorkflowTestCase(WorkflowTestCase):
-
- @classmethod
- def setUpClass(cls):
- super(ConfigWorkflowTestCase, cls).setUpClass()
- cls.step_count = len(WorkflowFactory.config_steps)
- cls.wf_type = 2
-
- def test_steps_render(self):
- super(ConfigWorkflowTestCase, self).render_steps()
diff --git a/src/workflow/urls.py b/src/workflow/urls.py
deleted file mode 100644
index b1b95a7..0000000
--- a/src/workflow/urls.py
+++ /dev/null
@@ -1,23 +0,0 @@
-##############################################################################
-# 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.conf.urls import url
-
-from workflow.views import manager_view, viewport_view, add_workflow, remove_workflow, create_workflow
-
-app_name = 'workflow'
-urlpatterns = [
-
- url(r'^manager/$', manager_view, name='manager'),
- url(r'^add/$', add_workflow, name='add_workflow'),
- url(r'^create/$', create_workflow, name='create_workflow'),
- url(r'^pop/$', remove_workflow, name='remove_workflow'),
- url(r'^$', viewport_view, name='viewport')
-]
diff --git a/src/workflow/views.py b/src/workflow/views.py
deleted file mode 100644
index fb311b7..0000000
--- a/src/workflow/views.py
+++ /dev/null
@@ -1,112 +0,0 @@
-##############################################################################
-# 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.http import HttpResponse
-from django.shortcuts import render
-from account.models import Lab
-
-import uuid
-
-from workflow.workflow_manager import ManagerTracker, SessionManager
-
-import logging
-logger = logging.getLogger(__name__)
-
-
-def attempt_auth(request):
- try:
- manager = ManagerTracker.managers[request.session['manager_session']]
-
- return manager
-
- except KeyError:
- return None
-
-
-def remove_workflow(request):
- manager = attempt_auth(request)
-
- if not manager:
- return no_workflow(request)
-
- has_more_workflows, result = manager.pop_workflow(discard=True)
-
- if not has_more_workflows: # this was the last workflow, so delete the reference to it in the tracker
- del ManagerTracker.managers[request.session['manager_session']]
- return manager.render(request)
-
-
-def add_workflow(request):
- manager = attempt_auth(request)
- if not manager:
- return no_workflow(request)
- try:
- workflow_type = int(request.POST.get('workflow_type'))
- except ValueError:
- return HttpResponse(status=400)
-
- manager.add_workflow(workflow_type=workflow_type)
- return manager.render(request) # do we want this?
-
-
-def manager_view(request):
- manager = attempt_auth(request)
- if not manager:
- return no_workflow(request)
-
- return manager.handle_request(request)
-
-
-def viewport_view(request):
- if not request.user.is_authenticated:
- return login(request)
-
- manager = attempt_auth(request)
- if manager is None:
- return no_workflow(request)
-
- if request.method != 'GET':
- return HttpResponse(status=405)
-
- context = {
- 'contact_email': Lab.objects.get(name="UNH_IOL").contact_email
- }
-
- return render(request, 'workflow/viewport-base.html', context)
-
-
-def create_workflow(request):
- if request.method != 'POST':
- return HttpResponse(status=405)
- workflow_type = request.POST.get('workflow_type')
- try:
- workflow_type = int(workflow_type)
- except Exception:
- return HttpResponse(status=400)
- mgr_uuid = create_session(workflow_type, request=request,)
- request.session['manager_session'] = mgr_uuid
- return HttpResponse()
-
-
-def create_session(wf_type, request):
- smgr = SessionManager(request=request)
- smgr.add_workflow(workflow_type=wf_type, target_id=request.POST.get("target"))
- manager_uuid = uuid.uuid4().hex
- ManagerTracker.getInstance().managers[manager_uuid] = smgr
-
- return manager_uuid
-
-
-def no_workflow(request):
- return render(request, 'workflow/no_workflow.html', {'title': "Not Found"}, status=404)
-
-
-def login(request):
- return render(request, "dashboard/login.html", {'title': 'Authentication Required'})
diff --git a/src/workflow/workflow_factory.py b/src/workflow/workflow_factory.py
deleted file mode 100644
index e688510..0000000
--- a/src/workflow/workflow_factory.py
+++ /dev/null
@@ -1,126 +0,0 @@
-##############################################################################
-# Copyright (c) 2018 Sawyer Bergeron, Parker Berberian, Sean Smith, 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 workflow.booking_workflow import Booking_Resource_Select, Booking_Meta, OPNFV_Select
-from workflow.resource_bundle_workflow import Define_Hardware, Define_Nets, Resource_Meta_Info, Define_Software
-from workflow.snapshot_workflow import Select_Host_Step, Image_Meta_Step
-from workflow.opnfv_workflow import Pick_Installer, Assign_Network_Roles, Assign_Host_Roles, OPNFV_Resource_Select, MetaInfo
-from workflow.models import Confirmation_Step
-
-import uuid
-
-import logging
-logger = logging.getLogger(__name__)
-
-
-class MetaStep(object):
-
- UNTOUCHED = 0
- INVALID = 100
- VALID = 200
-
- def set_invalid(self, message, code=100):
- self.valid = code
- self.message = message
-
- def set_valid(self, message, code=200):
- self.valid = code
- self.message = message
-
- def __init__(self, *args, **kwargs):
- self.short_title = "error"
- self.skip_step = 0
- self.valid = 0
- self.hidden = False
- self.message = ""
- self.id = uuid.uuid4()
-
- def to_json(self):
- return {
- 'title': self.short_title,
- 'skip': self.skip_step,
- 'valid': self.valid,
- 'message': self.message,
- }
-
- def __str__(self):
- return "metastep: " + str(self.short_title)
-
- def __hash__(self):
- return hash(self.id)
-
- def __eq__(self, other):
- return self.id.int == other.id.int
-
- def __ne__(self, other):
- return self.id.int != other.id.int
-
-
-class Workflow(object):
- def __init__(self, steps, repository):
- self.repository = repository
- self.steps = steps
- self.active_index = 0
-
-
-class WorkflowFactory():
- booking_steps = [
- Booking_Resource_Select,
- Booking_Meta,
- OPNFV_Select,
- ]
-
- resource_steps = [
- Define_Hardware,
- Define_Software,
- Define_Nets,
- Resource_Meta_Info,
- ]
-
- snapshot_steps = [
- Select_Host_Step,
- Image_Meta_Step,
- ]
-
- opnfv_steps = [
- OPNFV_Resource_Select,
- Pick_Installer,
- Assign_Network_Roles,
- Assign_Host_Roles,
- MetaInfo
- ]
-
- def conjure(self, workflow_type=None, repo=None):
- workflow_types = [
- self.booking_steps,
- self.resource_steps,
- self.snapshot_steps,
- self.opnfv_steps,
- ]
-
- steps = self.make_steps(workflow_types[workflow_type], repository=repo)
- return steps
-
- def create_workflow(self, workflow_type=None, repo=None):
- steps = self.conjure(workflow_type, repo)
- c_step = self.make_step(Confirmation_Step, repo)
- steps.append(c_step)
- return Workflow(steps, repo)
-
- def make_steps(self, step_types, repository):
- steps = []
- for step_type in step_types:
- steps.append(self.make_step(step_type, repository))
-
- return steps
-
- def make_step(self, step_type, repository):
- iden = step_type.description + step_type.title + step_type.template
- return step_type(iden, repository)
diff --git a/src/workflow/workflow_manager.py b/src/workflow/workflow_manager.py
deleted file mode 100644
index 40be9d6..0000000
--- a/src/workflow/workflow_manager.py
+++ /dev/null
@@ -1,270 +0,0 @@
-##############################################################################
-# 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.http import JsonResponse
-from django.http.request import QueryDict
-from django.urls import reverse
-
-from booking.models import Booking
-from workflow.workflow_factory import WorkflowFactory
-from workflow.models import Repository
-from resource_inventory.models import (
- ResourceTemplate,
- ResourceConfiguration,
- OPNFVConfig
-)
-from workflow.forms import ManagerForm
-
-import logging
-logger = logging.getLogger(__name__)
-
-
-class SessionManager():
- def active_workflow(self):
- return self.workflows[-1]
-
- def __init__(self, request=None):
- self.workflows = []
- self.owner = request.user
- self.factory = WorkflowFactory()
- self.result = None
-
- def set_step_statuses(self, superclass_type, desired_enabled=True):
- workflow = self.active_workflow()
- steps = workflow.steps
- for step in steps:
- if isinstance(step, superclass_type):
- if desired_enabled:
- step.enable()
- else:
- step.disable()
-
- def add_workflow(self, workflow_type=None, **kwargs):
- repo = Repository()
- if (len(self.workflows) >= 1):
- defaults = self.workflows[-1].repository.get_child_defaults()
- repo.set_defaults(defaults)
- repo.el[repo.HAS_RESULT] = False
- repo.el[repo.SESSION_USER] = self.owner
- repo.el[repo.SESSION_MANAGER] = self
- self.workflows.append(
- self.factory.create_workflow(
- workflow_type=workflow_type,
- repo=repo
- )
- )
-
- def get_redirect(self):
- if isinstance(self.result, Booking):
- return reverse('booking:booking_detail', kwargs={'booking_id': self.result.id})
- return "/"
-
- def pop_workflow(self, discard=False):
- multiple_wfs = len(self.workflows) > 1
- if multiple_wfs:
- if self.workflows[-1].repository.el[Repository.RESULT]: # move result
- key = self.workflows[-1].repository.el[Repository.RESULT_KEY]
- result = self.workflows[-1].repository.el[Repository.RESULT]
- self.workflows[-2].repository.el[key] = result
- prev_workflow = self.workflows.pop()
- if self.workflows:
- current_repo = self.workflows[-1].repository
- else:
- current_repo = prev_workflow.repository
- self.result = current_repo.el[current_repo.RESULT]
- if discard:
- current_repo.cancel()
- return multiple_wfs, self.result
-
- def status(self, request):
- return {
- "steps": [step.to_json() for step in self.active_workflow().steps],
- "active": self.active_workflow().repository.el['active_step'],
- "workflow_count": len(self.workflows)
- }
-
- def handle_post(self, request):
- form = ManagerForm(request.POST)
- if form.is_valid():
- self.get_active_step().post(
- QueryDict(form.cleaned_data['step_form']),
- user=request.user
- )
- # change step
- if form.cleaned_data['step'] == 'prev':
- self.go_prev()
- if form.cleaned_data['step'] == 'next':
- self.go_next()
- else:
- pass # Exception?
-
- def handle_request(self, request):
- if request.method == 'POST':
- self.handle_post(request)
- return self.render(request)
-
- def render(self, request, **kwargs):
- if self.workflows:
- return JsonResponse({
- "meta": self.status(request),
- "content": self.get_active_step().render_to_string(request),
- })
- else:
- return JsonResponse({
- "redirect": self.get_redirect()
- })
-
- def post_render(self, request):
- return self.active_workflow().steps[self.active_workflow().active_index].post_render(request)
-
- def get_active_step(self):
- return self.active_workflow().steps[self.active_workflow().active_index]
-
- def go_next(self, **kwargs):
- # need to verify current step is valid to allow this
- if self.get_active_step().valid < 200:
- return
- next_step = self.active_workflow().active_index + 1
- if next_step >= len(self.active_workflow().steps):
- raise Exception("Out of bounds request for step")
- while not self.active_workflow().steps[next_step].enabled:
- next_step += 1
- self.active_workflow().repository.el['active_step'] = next_step
- self.active_workflow().active_index = next_step
-
- def go_prev(self, **kwargs):
- prev_step = self.active_workflow().active_index - 1
- if prev_step < 0:
- raise Exception("Out of bounds request for step")
- while not self.active_workflow().steps[prev_step].enabled:
- prev_step -= 1
- self.active_workflow().repository.el['active_step'] = prev_step
- self.active_workflow().active_index = prev_step
-
- def prefill_repo(self, target_id, workflow_type):
- self.repository.el[self.repository.EDIT] = True
- edit_object = None
- if workflow_type == 0:
- edit_object = Booking.objects.get(pk=target_id)
- self.prefill_booking(edit_object)
- elif workflow_type == 1:
- edit_object = ResourceTemplate.objects.get(pk=target_id)
- self.prefill_resource(edit_object)
- elif workflow_type == 2:
- edit_object = ResourceTemplate.objects.get(pk=target_id)
- self.prefill_config(edit_object)
-
- def prefill_booking(self, booking):
- models = self.make_booking_models(booking)
- confirmation = self.make_booking_confirm(booking)
- self.active_workflow().repository.el[self.active_workflow().repository.BOOKING_MODELS] = models
- self.active_workflow().repository.el[self.active_workflow().repository.CONFIRMATION] = confirmation
- self.active_workflow().repository.el[self.active_workflow().repository.RESOURCE_TEMPLATE_MODELS] = self.make_grb_models(booking.resource.template)
- self.active_workflow().repository.el[self.active_workflow().repository.SELECTED_RESOURCE_TEMPLATE] = self.make_grb_models(booking.resource.template)['bundle']
- self.active_workflow().repository.el[self.active_workflow().repository.CONFIG_MODELS] = self.make_config_models(booking.config_bundle)
-
- def prefill_resource(self, resource):
- models = self.make_grb_models(resource)
- confirm = self.make_grb_confirm(resource)
- self.active_workflow().repository.el[self.active_workflow().repository.RESOURCE_TEMPLATE_MODELS] = models
- self.active_workflow().repository.el[self.active_workflow().repository.CONFIRMATION] = confirm
-
- def prefill_config(self, config):
- models = self.make_config_models(config)
- confirm = self.make_config_confirm(config)
- self.active_workflow().repository.el[self.active_workflow().repository.CONFIG_MODELS] = models
- self.active_workflow().repository.el[self.active_workflow().repository.CONFIRMATION] = confirm
- grb_models = self.make_grb_models(config.bundle)
- self.active_workflow().repository.el[self.active_workflow().repository.RESOURCE_TEMPLATE_MODELS] = grb_models
-
- def make_grb_models(self, resource):
- models = self.active_workflow().repository.el.get(self.active_workflow().repository.RESOURCE_TEMPLATE_MODELS, {})
- models['hosts'] = []
- models['bundle'] = resource
- models['interfaces'] = {}
- models['vlans'] = {}
- for host in resource.getResources():
- models['hosts'].append(host)
- models['interfaces'][host.resource.name] = []
- models['vlans'][host.resource.name] = {}
- for interface in host.generic_interfaces.all():
- models['interfaces'][host.resource.name].append(interface)
- models['vlans'][host.resource.name][interface.profile.name] = []
- for vlan in interface.vlans.all():
- models['vlans'][host.resource.name][interface.profile.name].append(vlan)
- return models
-
- def make_grb_confirm(self, resource):
- confirm = self.active_workflow().repository.el.get(self.active_workflow().repository.CONFIRMATION, {})
- confirm['resource'] = {}
- confirm['resource']['hosts'] = []
- confirm['resource']['lab'] = resource.lab.lab_user.username
- for host in resource.getResources():
- confirm['resource']['hosts'].append({"name": host.resource.name, "profile": host.profile.name})
- return confirm
-
- def make_config_models(self, config):
- models = self.active_workflow().repository.el.get(self.active_workflow().repository.CONFIG_MODELS, {})
- models['bundle'] = config
- models['host_configs'] = []
- for host_conf in ResourceConfiguration.objects.filter(bundle=config):
- models['host_configs'].append(host_conf)
- models['opnfv'] = OPNFVConfig.objects.filter(bundle=config).last()
- return models
-
- def make_config_confirm(self, config):
- confirm = self.active_workflow().repository.el.get(self.active_workflow().repository.CONFIRMATION, {})
- confirm['configuration'] = {}
- confirm['configuration']['hosts'] = []
- confirm['configuration']['name'] = config.name
- confirm['configuration']['description'] = config.description
- opnfv = OPNFVConfig.objects.filter(bundle=config).last()
- confirm['configuration']['installer'] = opnfv.installer.name
- confirm['configuration']['scenario'] = opnfv.scenario.name
- for host_conf in ResourceConfiguration.objects.filter(bundle=config):
- h = {"name": host_conf.host.resource.name, "image": host_conf.image.name, "role": host_conf.opnfvRole.name}
- confirm['configuration']['hosts'].append(h)
- return confirm
-
- def make_booking_models(self, booking):
- models = self.active_workflow().repository.el.get(self.active_workflow().repository.BOOKING_MODELS, {})
- models['booking'] = booking
- models['collaborators'] = []
- for user in booking.collaborators.all():
- models['collaborators'].append(user)
- return models
-
- def make_booking_confirm(self, booking):
- confirm = self.active_workflow().repository.el.get(self.active_workflow().repository.CONFIRMATION, {})
- confirm['booking'] = {}
- confirm['booking']['length'] = (booking.end - booking.start).days
- confirm['booking']['project'] = booking.project
- confirm['booking']['purpose'] = booking.purpose
- confirm['booking']['resource name'] = booking.resource.template.name
- confirm['booking']['configuration name'] = booking.config_bundle.name
- confirm['booking']['collaborators'] = []
- for user in booking.collaborators.all():
- confirm['booking']['collaborators'].append(user.username)
- return confirm
-
-
-class ManagerTracker():
- instance = None
-
- managers = {}
-
- def __init__(self):
- pass
-
- @staticmethod
- def getInstance():
- if ManagerTracker.instance is None:
- ManagerTracker.instance = ManagerTracker()
- return ManagerTracker.instance