summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--dashboard/Makefile15
-rw-r--r--dashboard/__init__.py8
-rw-r--r--dashboard/booking_communication_agent.py42
-rw-r--r--dashboard/config.env.sample12
-rw-r--r--dashboard/config/nginx/pharos_dashboard.conf26
-rw-r--r--dashboard/dashboard_api/__init__.py8
-rw-r--r--dashboard/dashboard_api/api.py91
-rw-r--r--dashboard/dashboard_notification/notification.py120
-rw-r--r--dashboard/data/UNH_IOL/HPE_Proliant/hpe13.yaml53
-rw-r--r--dashboard/data/UNH_IOL/HPE_Proliant/hpe14.yaml41
-rw-r--r--dashboard/data/UNH_IOL/HPE_Proliant/hpe15.yaml41
-rw-r--r--dashboard/data/UNH_IOL/HPE_Proliant/hpe16.yaml41
-rw-r--r--dashboard/data/UNH_IOL/HPE_Proliant/hpe17.yaml41
-rw-r--r--dashboard/data/UNH_IOL/HPE_Proliant/hpe18.yaml41
-rw-r--r--dashboard/data/UNH_IOL/HPE_Proliant/hpe19.yaml41
-rw-r--r--dashboard/data/UNH_IOL/HPE_Proliant/hpe20.yaml41
-rw-r--r--dashboard/data/UNH_IOL/hostlist.json14
-rw-r--r--dashboard/docker-compose.override-dev.yml25
-rw-r--r--dashboard/docker-compose.yml33
-rw-r--r--dashboard/open-api-spec.yaml523
-rw-r--r--dashboard/rabbitmq/Dockerfile4
-rwxr-xr-xdashboard/rabbitmq/init.sh10
-rw-r--r--dashboard/readme.txt17
-rw-r--r--dashboard/requirements.txt (renamed from dashboard/web/requirements.txt)14
-rw-r--r--dashboard/src/__init__.py8
-rw-r--r--dashboard/src/account/__init__.py2
-rw-r--r--dashboard/src/account/admin.py5
-rw-r--r--dashboard/src/account/jira_util.py2
-rw-r--r--dashboard/src/account/migrations/0001_initial.py33
-rw-r--r--dashboard/src/account/migrations/0002_auto_20180110_1636.py33
-rw-r--r--dashboard/src/account/migrations/0002_lab_description.py19
-rw-r--r--dashboard/src/account/migrations/0003_auto_20180110_1639.py24
-rw-r--r--dashboard/src/account/migrations/0003_publicnetwork.py25
-rw-r--r--dashboard/src/account/migrations/__init__.py10
-rw-r--r--dashboard/src/account/models.py125
-rw-r--r--dashboard/src/account/tasks.py4
-rw-r--r--dashboard/src/account/tests/__init__.py2
-rw-r--r--dashboard/src/account/tests/test_general.py6
-rw-r--r--dashboard/src/account/urls.py29
-rw-r--r--dashboard/src/account/views.py145
-rw-r--r--dashboard/src/api/__init__.py2
-rw-r--r--dashboard/src/api/admin.py41
-rw-r--r--dashboard/src/api/migrations/0001_initial.py185
-rw-r--r--dashboard/src/api/migrations/0002_remove_job_delta.py17
-rw-r--r--dashboard/src/api/migrations/0003_auto_20190102_1956.py18
-rw-r--r--dashboard/src/api/migrations/0004_snapshotconfig_snapshotrelation.py42
-rw-r--r--dashboard/src/api/migrations/0005_snapshotconfig_delta.py18
-rw-r--r--dashboard/src/api/migrations/0006_auto_20190313_1729.py23
-rw-r--r--dashboard/src/api/migrations/0007_auto_20190417_1511.py25
-rw-r--r--dashboard/src/api/migrations/0007_opnfvapiconfig_opnfv_config.py20
-rw-r--r--dashboard/src/api/migrations/0008_auto_20190419_1414.py28
-rw-r--r--dashboard/src/api/migrations/0009_merge_20190508_1317.py14
-rw-r--r--dashboard/src/api/migrations/__init__.py4
-rw-r--r--dashboard/src/api/models.py1015
-rw-r--r--dashboard/src/api/serializers.py53
-rw-r--r--dashboard/src/api/serializers/__init__.py (renamed from dashboard/src/jenkins/__init__.py)4
-rw-r--r--dashboard/src/api/serializers/booking_serializer.py175
-rw-r--r--[-rwxr-xr-x]dashboard/src/api/serializers/old_serializers.py (renamed from dashboard/config/postgres/docker-entrypoint-initdb.d/pharos_dashboard.sh)13
-rw-r--r--dashboard/src/api/tests/__init__.py (renamed from dashboard/src/dashboard/migrations/__init__.py)4
-rw-r--r--dashboard/src/api/tests/test_models_unittest.py269
-rw-r--r--dashboard/src/api/urls.py36
-rw-r--r--dashboard/src/api/views.py139
-rw-r--r--dashboard/src/booking/__init__.py2
-rw-r--r--dashboard/src/booking/admin.py6
-rw-r--r--dashboard/src/booking/forms.py135
-rw-r--r--dashboard/src/booking/lib.py36
-rw-r--r--dashboard/src/booking/migrations/0001_initial.py48
-rw-r--r--dashboard/src/booking/migrations/0002_booking_changeid.py38
-rw-r--r--dashboard/src/booking/migrations/0002_booking_pdf.py18
-rw-r--r--dashboard/src/booking/migrations/0003_auto_20180108_2024.py25
-rw-r--r--dashboard/src/booking/migrations/0003_auto_20190115_1733.py30
-rw-r--r--dashboard/src/booking/migrations/0004_auto_20190124_1700.py20
-rw-r--r--dashboard/src/booking/migrations/0005_booking_idf.py18
-rw-r--r--dashboard/src/booking/migrations/0006_booking_opnfv_config.py20
-rw-r--r--dashboard/src/booking/migrations/__init__.py10
-rw-r--r--dashboard/src/booking/models.py75
-rw-r--r--dashboard/src/booking/quick_deployer.py355
-rw-r--r--dashboard/src/booking/stats.py58
-rw-r--r--dashboard/src/booking/tests/__init__.py2
-rw-r--r--dashboard/src/booking/tests/test_models.py228
-rw-r--r--dashboard/src/booking/tests/test_quick_booking.py150
-rw-r--r--dashboard/src/booking/tests/test_views.py106
-rw-r--r--dashboard/src/booking/urls.py34
-rw-r--r--dashboard/src/booking/views.py283
-rw-r--r--dashboard/src/dashboard/__init__.py2
-rw-r--r--dashboard/src/dashboard/actions.py47
-rw-r--r--dashboard/src/dashboard/admin.py6
-rw-r--r--dashboard/src/dashboard/context_processors.py13
-rw-r--r--dashboard/src/dashboard/exceptions.py56
-rw-r--r--dashboard/src/dashboard/migrations/0001_initial.py64
-rw-r--r--dashboard/src/dashboard/migrations/0002_auto_20170505_0815.py42
-rw-r--r--dashboard/src/dashboard/migrations/0003_resource_resource_lab.py22
-rw-r--r--dashboard/src/dashboard/models.py90
-rw-r--r--dashboard/src/dashboard/populate_db_iol.py350
-rw-r--r--dashboard/src/dashboard/tasks.py114
-rw-r--r--dashboard/src/dashboard/templatetags/__init__.py2
-rw-r--r--dashboard/src/dashboard/templatetags/jenkins_filters.py38
-rw-r--r--dashboard/src/dashboard/testing_utils.py396
-rw-r--r--dashboard/src/dashboard/tests/__init__.py2
-rw-r--r--dashboard/src/dashboard/tests/test_models.py69
-rw-r--r--dashboard/src/dashboard/tests/test_views.py75
-rw-r--r--dashboard/src/dashboard/urls.py24
-rw-r--r--dashboard/src/dashboard/views.py205
-rw-r--r--dashboard/src/jenkins/adapter.py137
-rw-r--r--dashboard/src/jenkins/migrations/0001_initial.py53
-rw-r--r--dashboard/src/jenkins/models.py62
-rw-r--r--dashboard/src/jenkins/tasks.py64
-rw-r--r--dashboard/src/jenkins/tests.py129
-rwxr-xr-x[-rw-r--r--]dashboard/src/manage.py3
-rw-r--r--dashboard/src/notifier/__init__.py8
-rw-r--r--dashboard/src/notifier/admin.py6
-rw-r--r--dashboard/src/notifier/apps.py3
-rw-r--r--dashboard/src/notifier/dispatchers.py33
-rw-r--r--dashboard/src/notifier/manager.py133
-rw-r--r--dashboard/src/notifier/migrations/0001_initial.py25
-rw-r--r--dashboard/src/notifier/migrations/0002_auto_20181102_1631.py44
-rw-r--r--dashboard/src/notifier/migrations/0003_auto_20190123_1741.py23
-rw-r--r--dashboard/src/notifier/migrations/0004_auto_20190124_2115.py23
-rw-r--r--dashboard/src/notifier/migrations/0005_auto_20190306_1616.py18
-rw-r--r--dashboard/src/notifier/models.py31
-rw-r--r--dashboard/src/notifier/tests/test_dispatcher.py (renamed from dashboard/src/jenkins/admin.py)12
-rw-r--r--dashboard/src/notifier/tests/test_models.py30
-rw-r--r--dashboard/src/notifier/urls.py19
-rw-r--r--dashboard/src/notifier/views.py58
-rw-r--r--dashboard/src/pharos_dashboard/celery.py2
-rw-r--r--dashboard/src/pharos_dashboard/settings.py63
-rw-r--r--dashboard/src/pharos_dashboard/urls.py11
-rw-r--r--dashboard/src/resource_inventory/__init__.py (renamed from dashboard/src/jenkins/migrations/__init__.py)4
-rw-r--r--dashboard/src/resource_inventory/admin.py53
-rw-r--r--dashboard/src/resource_inventory/apps.py (renamed from dashboard/src/jenkins/apps.py)5
-rw-r--r--dashboard/src/resource_inventory/idf_templater.py151
-rw-r--r--dashboard/src/resource_inventory/migrations/0001_initial.py328
-rw-r--r--dashboard/src/resource_inventory/migrations/0002_auto_20180919_1459.py18
-rw-r--r--dashboard/src/resource_inventory/migrations/0003_vlan_public.py18
-rw-r--r--dashboard/src/resource_inventory/migrations/0004_auto_20181017_1532.py28
-rw-r--r--dashboard/src/resource_inventory/migrations/0005_image_os.py19
-rw-r--r--dashboard/src/resource_inventory/migrations/0006_auto_20190124_1700.py76
-rw-r--r--dashboard/src/resource_inventory/migrations/0007_auto_20190306_1616.py31
-rw-r--r--dashboard/src/resource_inventory/migrations/0008_host_remote_management.py19
-rw-r--r--dashboard/src/resource_inventory/migrations/0009_auto_20190315_1757.py73
-rw-r--r--dashboard/src/resource_inventory/migrations/0010_auto_20190430_1405.py54
-rw-r--r--dashboard/src/resource_inventory/migrations/__init__.py0
-rw-r--r--dashboard/src/resource_inventory/models.py371
-rw-r--r--dashboard/src/resource_inventory/pdf_templater.py193
-rw-r--r--dashboard/src/resource_inventory/resource_manager.py174
-rw-r--r--dashboard/src/resource_inventory/tests/test_managers.py249
-rw-r--r--dashboard/src/resource_inventory/tests/test_models.py173
-rw-r--r--dashboard/src/resource_inventory/urls.py35
-rw-r--r--dashboard/src/resource_inventory/views.py38
-rw-r--r--dashboard/src/static/bower.json9
-rw-r--r--dashboard/src/static/css/base.css8
-rw-r--r--dashboard/src/static/css/detail_view.css39
-rw-r--r--dashboard/src/static/css/graph_common.css172
-rw-r--r--dashboard/src/static/css/theme.css6
-rw-r--r--dashboard/src/static/img/mxgraph/add.pngbin0 -> 1564 bytes
-rw-r--r--dashboard/src/static/img/mxgraph/button.gifbin0 -> 137 bytes
-rw-r--r--dashboard/src/static/img/mxgraph/camera.pngbin0 -> 887 bytes
-rw-r--r--dashboard/src/static/img/mxgraph/check.pngbin0 -> 253 bytes
-rw-r--r--dashboard/src/static/img/mxgraph/close.gifbin0 -> 70 bytes
-rw-r--r--dashboard/src/static/img/mxgraph/close.pngbin0 -> 1910 bytes
-rw-r--r--dashboard/src/static/img/mxgraph/collapsed.gifbin0 -> 877 bytes
-rw-r--r--dashboard/src/static/img/mxgraph/connector.gifbin0 -> 954 bytes
-rw-r--r--dashboard/src/static/img/mxgraph/copy.pngbin0 -> 728 bytes
-rw-r--r--dashboard/src/static/img/mxgraph/cut.pngbin0 -> 781 bytes
-rw-r--r--dashboard/src/static/img/mxgraph/delete2.pngbin0 -> 914 bytes
-rw-r--r--dashboard/src/static/img/mxgraph/dot.gifbin0 -> 517 bytes
-rw-r--r--dashboard/src/static/img/mxgraph/error.gifbin0 -> 907 bytes
-rw-r--r--dashboard/src/static/img/mxgraph/expanded.gifbin0 -> 878 bytes
-rw-r--r--dashboard/src/static/img/mxgraph/export1.pngbin0 -> 857 bytes
-rw-r--r--dashboard/src/static/img/mxgraph/fit_to_size.pngbin0 -> 529 bytes
-rw-r--r--dashboard/src/static/img/mxgraph/gradient_background.jpgbin0 -> 6164 bytes
-rw-r--r--dashboard/src/static/img/mxgraph/green-dot.gifbin0 -> 326 bytes
-rw-r--r--dashboard/src/static/img/mxgraph/group.pngbin0 -> 899 bytes
-rw-r--r--dashboard/src/static/img/mxgraph/handle-connect.pngbin0 -> 1300 bytes
-rw-r--r--dashboard/src/static/img/mxgraph/handle-main.pngbin0 -> 379 bytes
-rw-r--r--dashboard/src/static/img/mxgraph/icons48/column.pngbin0 -> 1787 bytes
-rw-r--r--dashboard/src/static/img/mxgraph/icons48/earth.pngbin0 -> 4520 bytes
-rw-r--r--dashboard/src/static/img/mxgraph/icons48/gear.pngbin0 -> 4418 bytes
-rw-r--r--dashboard/src/static/img/mxgraph/icons48/keys.pngbin0 -> 4295 bytes
-rw-r--r--dashboard/src/static/img/mxgraph/icons48/mail_new.pngbin0 -> 3944 bytes
-rw-r--r--dashboard/src/static/img/mxgraph/icons48/server.pngbin0 -> 3556 bytes
-rw-r--r--dashboard/src/static/img/mxgraph/icons48/table.pngbin0 -> 1574 bytes
-rw-r--r--dashboard/src/static/img/mxgraph/key.pngbin0 -> 300 bytes
-rw-r--r--dashboard/src/static/img/mxgraph/loading.gifbin0 -> 7517 bytes
-rw-r--r--dashboard/src/static/img/mxgraph/maximize.gifbin0 -> 843 bytes
-rw-r--r--dashboard/src/static/img/mxgraph/minimize.gifbin0 -> 64 bytes
-rw-r--r--dashboard/src/static/img/mxgraph/navigate_minus.pngbin0 -> 485 bytes
-rw-r--r--dashboard/src/static/img/mxgraph/navigate_plus.pngbin0 -> 709 bytes
-rw-r--r--dashboard/src/static/img/mxgraph/normalize.gifbin0 -> 845 bytes
-rw-r--r--dashboard/src/static/img/mxgraph/paste.pngbin0 -> 783 bytes
-rw-r--r--dashboard/src/static/img/mxgraph/plus.pngbin0 -> 236 bytes
-rw-r--r--dashboard/src/static/img/mxgraph/point.gifbin0 -> 55 bytes
-rw-r--r--dashboard/src/static/img/mxgraph/press32.pngbin0 -> 2261 bytes
-rw-r--r--dashboard/src/static/img/mxgraph/print32.pngbin0 -> 2111 bytes
-rw-r--r--dashboard/src/static/img/mxgraph/printer.pngbin0 -> 896 bytes
-rw-r--r--dashboard/src/static/img/mxgraph/redo.pngbin0 -> 895 bytes
-rw-r--r--dashboard/src/static/img/mxgraph/resize.gifbin0 -> 74 bytes
-rw-r--r--dashboard/src/static/img/mxgraph/separator.gifbin0 -> 146 bytes
-rw-r--r--dashboard/src/static/img/mxgraph/sidebar_bg.gifbin0 -> 80 bytes
-rw-r--r--dashboard/src/static/img/mxgraph/spacer.gifbin0 -> 43 bytes
-rw-r--r--dashboard/src/static/img/mxgraph/submenu.gifbin0 -> 56 bytes
-rw-r--r--dashboard/src/static/img/mxgraph/toolbar_bg.gifbin0 -> 155 bytes
-rw-r--r--dashboard/src/static/img/mxgraph/transparent.gifbin0 -> 90 bytes
-rw-r--r--dashboard/src/static/img/mxgraph/undo.pngbin0 -> 879 bytes
-rw-r--r--dashboard/src/static/img/mxgraph/view_1_1.pngbin0 -> 849 bytes
-rw-r--r--dashboard/src/static/img/mxgraph/view_1_132.pngbin0 -> 2199 bytes
-rw-r--r--dashboard/src/static/img/mxgraph/view_next.pngbin0 -> 918 bytes
-rw-r--r--dashboard/src/static/img/mxgraph/view_previous.pngbin0 -> 912 bytes
-rw-r--r--dashboard/src/static/img/mxgraph/warning.gifbin0 -> 276 bytes
-rw-r--r--dashboard/src/static/img/mxgraph/warning.pngbin0 -> 425 bytes
-rw-r--r--dashboard/src/static/img/mxgraph/window-title.gifbin0 -> 275 bytes
-rw-r--r--dashboard/src/static/img/mxgraph/window.gifbin0 -> 75 bytes
-rw-r--r--dashboard/src/static/img/mxgraph/wires-grid.gifbin0 -> 50 bytes
-rw-r--r--dashboard/src/static/img/mxgraph/zoom_in.pngbin0 -> 858 bytes
-rw-r--r--dashboard/src/static/img/mxgraph/zoom_in32.pngbin0 -> 2184 bytes
-rw-r--r--dashboard/src/static/img/mxgraph/zoom_out.pngbin0 -> 847 bytes
-rw-r--r--dashboard/src/static/img/mxgraph/zoom_out32.pngbin0 -> 2150 bytes
-rw-r--r--dashboard/src/static/js/dashboard.js1134
-rw-r--r--dashboard/src/static/js/mxClient.min.js1808
-rw-r--r--dashboard/src/templates/account/booking_list.html136
-rw-r--r--dashboard/src/templates/account/configuration_list.html72
-rw-r--r--dashboard/src/templates/account/details.html9
-rw-r--r--dashboard/src/templates/account/image_list.html123
-rw-r--r--dashboard/src/templates/account/resource_list.html118
-rw-r--r--dashboard/src/templates/account/user_list.html2
-rw-r--r--dashboard/src/templates/account/userprofile_update_form.html19
-rw-r--r--dashboard/src/templates/base.html308
-rw-r--r--dashboard/src/templates/booking/booking_calendar.html84
-rw-r--r--dashboard/src/templates/booking/booking_delete.html2
-rw-r--r--dashboard/src/templates/booking/booking_detail.html421
-rw-r--r--dashboard/src/templates/booking/booking_grid_item.html11
-rw-r--r--dashboard/src/templates/booking/booking_list.html40
-rw-r--r--dashboard/src/templates/booking/booking_table.html18
-rw-r--r--dashboard/src/templates/booking/quick_deploy.html187
-rw-r--r--dashboard/src/templates/booking/stats.html73
-rw-r--r--dashboard/src/templates/booking/steps/booking_confirm.html25
-rw-r--r--dashboard/src/templates/booking/steps/booking_meta.html87
-rw-r--r--dashboard/src/templates/booking/steps/resource_select.html73
-rw-r--r--dashboard/src/templates/booking/steps/swconfig_select.html73
-rw-r--r--dashboard/src/templates/config_bundle/steps/assign_host_roles.html22
-rw-r--r--dashboard/src/templates/config_bundle/steps/assign_network_roles.html22
-rw-r--r--dashboard/src/templates/config_bundle/steps/config_software.html28
-rw-r--r--dashboard/src/templates/config_bundle/steps/define_software.html55
-rw-r--r--dashboard/src/templates/config_bundle/steps/pick_installer.html32
-rw-r--r--dashboard/src/templates/config_bundle/steps/table_formset.html64
-rw-r--r--dashboard/src/templates/dashboard/ci_pods.html61
-rw-r--r--dashboard/src/templates/dashboard/dev_pods.html70
-rw-r--r--dashboard/src/templates/dashboard/genericselect.html104
-rw-r--r--dashboard/src/templates/dashboard/grid.html10
-rw-r--r--dashboard/src/templates/dashboard/host_profile_detail.html71
-rw-r--r--dashboard/src/templates/dashboard/idf.yaml46
-rw-r--r--dashboard/src/templates/dashboard/jenkins_slaves.html46
-rw-r--r--dashboard/src/templates/dashboard/lab_detail.html154
-rw-r--r--dashboard/src/templates/dashboard/lab_list.html28
-rw-r--r--dashboard/src/templates/dashboard/landing.html172
-rw-r--r--dashboard/src/templates/dashboard/login.html8
-rw-r--r--dashboard/src/templates/dashboard/multiple_select_filter_widget.html149
-rw-r--r--dashboard/src/templates/dashboard/pdf.yaml92
-rw-r--r--dashboard/src/templates/dashboard/resource.html13
-rw-r--r--dashboard/src/templates/dashboard/resource_all.html15
-rw-r--r--dashboard/src/templates/dashboard/resource_detail.html58
-rw-r--r--dashboard/src/templates/dashboard/searchable_select_multiple.html209
-rw-r--r--dashboard/src/templates/dashboard/table.html10
-rw-r--r--dashboard/src/templates/layout.html21
-rw-r--r--dashboard/src/templates/notifier/email_ended.txt21
-rw-r--r--dashboard/src/templates/notifier/email_fulfilled.txt17
-rw-r--r--dashboard/src/templates/notifier/end_booking.html36
-rw-r--r--dashboard/src/templates/notifier/inbox.html129
-rw-r--r--dashboard/src/templates/notifier/new_booking.html34
-rw-r--r--dashboard/src/templates/notifier/notification.html83
-rw-r--r--dashboard/src/templates/resource/hostprofile_detail.html102
-rw-r--r--dashboard/src/templates/resource/hosts.html42
-rw-r--r--dashboard/src/templates/resource/mxClient.min.js1808
-rw-r--r--dashboard/src/templates/resource/steps/define_hardware.html24
-rw-r--r--dashboard/src/templates/resource/steps/host_info.html43
-rw-r--r--dashboard/src/templates/resource/steps/meta_info.html46
-rw-r--r--dashboard/src/templates/resource/steps/pod_definition.html140
-rw-r--r--dashboard/src/templates/resource/uncommon.css162
-rw-r--r--dashboard/src/templates/snapshot_workflow/steps/meta.html32
-rw-r--r--dashboard/src/templates/snapshot_workflow/steps/select_host.html128
-rw-r--r--dashboard/src/templates/workflow/confirm.html163
-rw-r--r--dashboard/src/templates/workflow/no_workflow.html3
-rw-r--r--dashboard/src/templates/workflow/resource_select.html50
-rw-r--r--dashboard/src/templates/workflow/viewport-base.html486
-rw-r--r--dashboard/src/templates/workflow/viewport-element.html69
-rw-r--r--dashboard/src/workflow/__init__.py8
-rw-r--r--dashboard/src/workflow/apps.py15
-rw-r--r--dashboard/src/workflow/booking_workflow.py220
-rw-r--r--dashboard/src/workflow/forms.py448
-rw-r--r--dashboard/src/workflow/models.py732
-rw-r--r--dashboard/src/workflow/opnfv_workflow.py299
-rw-r--r--dashboard/src/workflow/resource_bundle_workflow.py463
-rw-r--r--dashboard/src/workflow/snapshot_workflow.py119
-rw-r--r--dashboard/src/workflow/sw_bundle_workflow.py198
-rw-r--r--dashboard/src/workflow/tests/__init__.py (renamed from dashboard/dashboard_notification/__init__.py)4
-rw-r--r--dashboard/src/workflow/tests/constants.py198
-rw-r--r--dashboard/src/workflow/tests/test_steps.py281
-rw-r--r--dashboard/src/workflow/tests/test_steps_render.py43
-rw-r--r--dashboard/src/workflow/tests/test_workflows.py100
-rw-r--r--dashboard/src/workflow/urls.py34
-rw-r--r--dashboard/src/workflow/views.py139
-rw-r--r--dashboard/src/workflow/workflow_factory.py134
-rw-r--r--dashboard/src/workflow/workflow_manager.py243
-rwxr-xr-xdashboard/test.sh16
-rw-r--r--dashboard/tox.ini21
-rw-r--r--dashboard/web/Dockerfile29
-rwxr-xr-xdashboard/web/init.sh12
-rw-r--r--dashboard/worker/Dockerfile22
-rwxr-xr-xdashboard/worker/init.sh10
-rw-r--r--dashboard/worker/requirements.txt18
310 files changed, 21711 insertions, 2663 deletions
diff --git a/dashboard/Makefile b/dashboard/Makefile
index 9070917..31243b0 100644
--- a/dashboard/Makefile
+++ b/dashboard/Makefile
@@ -1,5 +1,14 @@
build:
- docker-compose build
+ docker-compose -f docker-compose.yml -f docker-compose.override-dev.yml build
+
+dev-up:
+ docker-compose -f docker-compose.yml -f docker-compose.override-dev.yml up -d
+
+dev-start:
+ docker-compose -f docker-compose.yml -f docker-compose.override-dev.yml start
+
+dev-stop:
+ docker-compose -f docker-compose.yml -f docker-compose.override-dev.yml stop
up:
docker-compose up -d
@@ -23,10 +32,10 @@ shell-db:
docker exec -ti ps01 bash
log-nginx:
- docker-compose logs nginx
+ docker-compose logs nginx
log-web:
- docker-compose logs web
+ docker-compose logs web
log-ps:
docker-compose logs postgres
diff --git a/dashboard/__init__.py b/dashboard/__init__.py
deleted file mode 100644
index ce1acf3..0000000
--- a/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
-############################################################################## \ No newline at end of file
diff --git a/dashboard/booking_communication_agent.py b/dashboard/booking_communication_agent.py
deleted file mode 100644
index c52e98b..0000000
--- a/dashboard/booking_communication_agent.py
+++ /dev/null
@@ -1,42 +0,0 @@
-from dashboard_notification.notification import Notification
-from dashboard_api.api import DashboardAPI
-
-CONFIG = {
- 'dashboard_ip': '127.0.0.1',
- 'dashboard_url': 'http://127.0.0.1',
- 'api_token': 'f33ff43c85ecb13f5d0632c05dbb0a7d85a5a8d1',
- 'user': 'opnfv',
- 'password': 'opnfvopnfv'
-}
-
-api = DashboardAPI(CONFIG['dashboard_url'], api_token=CONFIG['api_token'], verbose=True)
-
-
-def booking_start(message):
- content = message.content
- booking = api.get_booking(id=content['booking_id'])
-
- # do something here...
-
- # notify dashboard
- api.post_resource_status(resource_id=booking['resource_id'], type='info', title='pod setup',
- content='details')
-
-
-def booking_end(message):
- # do something here...
-
- # notify dashboard
- api.post_resource_status(resource_id=message.content['resource_id'], type='info',
- title='booking end', content='details')
-
-
-def main():
- with Notification(CONFIG['dashboard_ip'], CONFIG['user'], CONFIG['password']) as notification:
- notification.register(booking_start, 'Arm POD 2', 'booking_start')
- notification.register(booking_end, 'Arm POD 2', 'booking_end')
- notification.receive() # wait for notifications
-
-
-if __name__ == "__main__":
- main()
diff --git a/dashboard/config.env.sample b/dashboard/config.env.sample
index 8d0afd6..7677c2f 100644
--- a/dashboard/config.env.sample
+++ b/dashboard/config.env.sample
@@ -2,7 +2,11 @@ DASHBOARD_URL=http://labs.opnfv.org
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG=False
+TEST=False
+POSTGRES_DB=sample_name
+POSTGRES_USER=sample_user
+POSTGRES_PASSWORD=sample_pass
DB_NAME=sample_name
DB_USER=sample_user
DB_PASS=sample_pass
@@ -20,12 +24,8 @@ JIRA_USER_NAME=sample_jira_user
JIRA_USER_PASSWORD=sample_jira_pass
# Rabbitmq
-RABBITMQ_USER=opnfv
-RABBITMQ_PASSWORD=opnfvopnfv
-
-# Cleanup: time is in days
-BOOKING_EXPIRE_TIME=30
-BOOKING_MAXIMUM_NUMBER=10
+RABBITMQ_DEFAULT_USER=opnfv
+RABBITMQ_DEFAULT_PASS=opnfvopnfv
#Jenkins Build Server
JENKINS_URL=https://build.opnfv.org/ci
diff --git a/dashboard/config/nginx/pharos_dashboard.conf b/dashboard/config/nginx/pharos_dashboard.conf
index 87b6f8e..6f32979 100644
--- a/dashboard/config/nginx/pharos_dashboard.conf
+++ b/dashboard/config/nginx/pharos_dashboard.conf
@@ -1,24 +1,24 @@
upstream web {
- ip_hash;
- server web:8000;
+ ip_hash;
+ server web:8000;
}
# portal
-server {
- listen 80;
- server_name localhost;
- charset utf-8;
+server {
+ listen 80;
+ server_name localhost;
+ charset utf-8;
- location /static {
+ location /static {
alias /static;
- }
+ }
- location /media {
+ location /media {
alias /media;
- }
+ }
- location / {
+ location / {
proxy_set_header Host $host;
- proxy_pass http://web/;
- }
+ proxy_pass http://web/;
+ }
}
diff --git a/dashboard/dashboard_api/__init__.py b/dashboard/dashboard_api/__init__.py
deleted file mode 100644
index ce1acf3..0000000
--- a/dashboard/dashboard_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
-############################################################################## \ No newline at end of file
diff --git a/dashboard/dashboard_api/api.py b/dashboard/dashboard_api/api.py
deleted file mode 100644
index d40e0aa..0000000
--- a/dashboard/dashboard_api/api.py
+++ /dev/null
@@ -1,91 +0,0 @@
-##############################################################################
-# Copyright (c) 2016 Max Breitenfeldt and others.
-#
-# All rights reserved. This program and the accompanying materials
-# are made available under the terms of the Apache License, Version 2.0
-# which accompanies this distribution, and is available at
-# http://www.apache.org/licenses/LICENSE-2.0
-##############################################################################
-
-
-import logging
-
-import requests
-
-URLS = {
- 'resources': '/api/resources/',
- 'servers': '/api/servers/',
- 'bookings': '/api/bookings',
- 'resource_status': '/api/resource_status/',
-}
-
-class DashboardAPI(object):
- def __init__(self, dashboard_url, api_token='', verbose=False):
- self._api_token = api_token
- self._verbose = verbose
- self._resources_url = dashboard_url + URLS['resources']
- self._servers_url = dashboard_url + URLS['servers']
- self._bookings_url = dashboard_url + URLS['bookings']
- self._resources_status_url = dashboard_url + URLS['resource_status']
- self._logger = logging.getLogger(__name__)
-
- def get_all_resources(self):
- return self._get_json(self._resources_url)
-
- def get_resource(self, id='', name='', url=''):
- if url != '':
- return self._get_json(url)[0]
- url = self._resources_url + self._url_parameter(id=id, name=name)
- return self._get_json(url)[0]
-
- def get_all_bookings(self):
- return self._get_json(self._bookings_url)
-
- def get_resource_bookings(self, resource_id):
- url = self._bookings_url + self._url_parameter(resource_id=resource_id)
- return self._get_json(url)
-
- def get_booking(self, id):
- url = self._bookings_url + self._url_parameter(id=id)
- return self._get_json(url)[0]
-
- def post_resource_status(self, resource_id, type, title, content):
- data = {
- 'resource': resource_id,
- 'type': type,
- 'title': title,
- 'content': content
- }
- return self._post_json(self._resources_status_url, data)
-
- def get_url(self, url):
- return self._get_json(url)
-
- def _url_parameter(self, **kwargs):
- res = ''
- prefix = '?'
- for key, val in kwargs.items():
- res += prefix + key + '=' + str(val)
- prefix = '&'
- return res
-
- def _get_json(self, url):
- try:
- response = requests.get(url)
- if self._verbose:
- print('Get JSON: ' + url)
- print(response.status_code, response.content)
- return response.json()
- except requests.exceptions.RequestException as e:
- self._logger.exception(e)
- except ValueError as e:
- self._logger.exception(e)
-
- def _post_json(self, url, json):
- if self._api_token == '':
- raise Exception('Need api token to POST data.')
- response = requests.post(url, json, headers={'Authorization': 'Token ' + self._api_token})
- if self._verbose:
- print('Post JSON: ' + url)
- print(response.status_code, response.content)
- return response.status_code
diff --git a/dashboard/dashboard_notification/notification.py b/dashboard/dashboard_notification/notification.py
deleted file mode 100644
index 6843c76..0000000
--- a/dashboard/dashboard_notification/notification.py
+++ /dev/null
@@ -1,120 +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 jsonpickle
-import pika
-
-
-class Message(object):
- def __init__(self, type, topic, content):
- self.type = type
- self.topic = topic
- self.content = content
-
-
-class Notification(object):
- """
- This class can be used by the dashboard and the labs to exchange notifications about booking
- events and pod status. It utilizes rabbitmq to communicate.
-
- Notifications are associated to an event and to a topic.
- Events are:
- [ 'booking_start', 'booking_end']
- The topic is usually a POD name, ie:
- 'Intel POD 2'
- """
-
- def __init__(self, dashboard_url, user=None, password=None, verbose=False):
- self.rabbitmq_broker = dashboard_url
- self.verbose = verbose
- if user is None and password is None:
- self._connection = pika.BlockingConnection(pika.ConnectionParameters(
- host=self.rabbitmq_broker))
- else:
- self.credentials = pika.PlainCredentials(user, password)
- self._connection = pika.BlockingConnection(pika.ConnectionParameters(
- credentials=self.credentials,
- host=self.rabbitmq_broker))
- self._registry = {}
- self._channel = self._connection.channel()
- self._channel.exchange_declare(exchange='notifications', type='topic')
- self._result = self._channel.queue_declare(exclusive=True, durable=True)
- self._queue_name = self._result.method.queue
-
- def __enter__(self):
- return self
-
- def __exit__(self, exc_type, exc_val, exc_tb):
- self._connection.close()
-
- def register(self, function, topic, type='all'):
- """
- Registers a function to be called for the specified event.
- :param function: the function to register
- :param event: the event type
- :param regex: a regex to specify for wich topics the function will be called. Some
- possible Expressions can be:
- 'Intel POD 2' : Intel POD 2
- """
-
- if topic not in self._registry:
- self._registry[topic] = [(function, type)]
- else:
- self._registry[topic].append((function, type))
-
- def receive(self):
- """
- Start receiving notifications. This is a blocking operation, if a notification is received,
- the registered functions will be called.
- """
- if self.verbose:
- print('Start receiving Notifications. Keys: ', self._registry.keys())
- self._receive_message(self._registry.keys())
-
- def send(self, message):
- """
- Send an event notification.
- :param event: the event type
- :param topic: the pod name
- :param content: a JSON-serializable dictionary
- """
- self._send_message(message)
-
- def _send_message(self, message):
- routing_key = message.topic
- message_json = jsonpickle.encode(message)
- self._channel.basic_publish(exchange='notifications',
- routing_key=routing_key,
- body=message_json,
- properties=pika.BasicProperties(
- content_type='application/json',
- delivery_mode=2, # make message persistent
- ))
- if self.verbose:
- print(" [x] Sent %r:%r" % (routing_key, message_json))
-
- def _receive_message(self, binding_keys):
- for key in binding_keys:
- self._channel.queue_bind(exchange='notifications',
- queue=self._queue_name,
- routing_key=key)
- self._channel.basic_consume(self._message_callback,
- queue=self._queue_name)
- self._channel.start_consuming()
-
- def _message_callback(self, ch, method, properties, body):
- if self.verbose:
- print(" [x] Got %r:%r" % (method.routing_key, body))
- if method.routing_key not in self._registry:
- return
- for func, type in self._registry[method.routing_key]:
- message = jsonpickle.decode(body.decode())
- if message.type == type:
- func(message)
- ch.basic_ack(delivery_tag=method.delivery_tag)
diff --git a/dashboard/data/UNH_IOL/HPE_Proliant/hpe13.yaml b/dashboard/data/UNH_IOL/HPE_Proliant/hpe13.yaml
new file mode 100644
index 0000000..fd40aff
--- /dev/null
+++ b/dashboard/data/UNH_IOL/HPE_Proliant/hpe13.yaml
@@ -0,0 +1,53 @@
+---
+disk:
+ -
+ name: sda
+ size: 894.2G
+ -
+ name: loop0
+ size: 100G
+ -
+ name: loop1
+ size: 2G
+ -
+ name: loop2
+ size: 792M
+ -
+ name: loop3
+ size: 825M
+cpu:
+ arch: x86_64
+ cpus: 2
+ cores: 88
+memory: 503G
+interface:
+ -
+ name: eno49
+ mac: '48:df:37:1d:54:00'
+ busaddr: '0000:04:00.0'
+ speed: 10000
+ -
+ name: ens1f0
+ mac: '3c:fd:fe:b2:c3:70'
+ busaddr: '0000:05:00.0'
+ speed: 10000
+ -
+ name: ens1f1
+ mac: '3c:fd:fe:b2:c3:71'
+ busaddr: '0000:05:00.1'
+ speed: 10000
+ -
+ name: ens1f2
+ mac: '3c:fd:fe:b2:c3:72'
+ busaddr: '0000:05:00.2'
+ speed: 10000
+ -
+ name: ens4f0
+ mac: '3c:fd:fe:b2:ae:d0'
+ busaddr: '0000:88:00.0'
+ speed: 10000
+ -
+ name: ens4f1
+ mac: '3c:fd:fe:b2:ae:d1'
+ busaddr: '0000:88:00.1'
+ speed: 10000
diff --git a/dashboard/data/UNH_IOL/HPE_Proliant/hpe14.yaml b/dashboard/data/UNH_IOL/HPE_Proliant/hpe14.yaml
new file mode 100644
index 0000000..612f4d0
--- /dev/null
+++ b/dashboard/data/UNH_IOL/HPE_Proliant/hpe14.yaml
@@ -0,0 +1,41 @@
+---
+disk:
+ -
+ name: sda
+ size: 894.2G
+cpu:
+ arch: x86_64
+ cpus: 2
+ cores: 88
+memory: 503G
+interface:
+ -
+ name: eno49
+ mac: '48:df:37:1d:4d:60'
+ busaddr: '0000:04:00.0'
+ speed: 10000
+ -
+ name: ens1f0
+ mac: '3c:fd:fe:b2:b2:00'
+ busaddr: '0000:05:00.0'
+ speed: 10000
+ -
+ name: ens1f1
+ mac: '3c:fd:fe:b2:b2:01'
+ busaddr: '0000:05:00.1'
+ speed: 10000
+ -
+ name: ens1f2
+ mac: '3c:fd:fe:b2:b2:02'
+ busaddr: '0000:05:00.2'
+ speed: 10000
+ -
+ name: ens4f0
+ mac: '3c:fd:fe:b2:b1:d8'
+ busaddr: '0000:88:00.0'
+ speed: 10000
+ -
+ name: ens4f1
+ mac: '3c:fd:fe:b2:b1:d9'
+ busaddr: '0000:88:00.1'
+ speed: 10000
diff --git a/dashboard/data/UNH_IOL/HPE_Proliant/hpe15.yaml b/dashboard/data/UNH_IOL/HPE_Proliant/hpe15.yaml
new file mode 100644
index 0000000..c6799ac
--- /dev/null
+++ b/dashboard/data/UNH_IOL/HPE_Proliant/hpe15.yaml
@@ -0,0 +1,41 @@
+---
+disk:
+ -
+ name: sda
+ size: 894.2G
+cpu:
+ arch: x86_64
+ cpus: 2
+ cores: 88
+memory: 503G
+interface:
+ -
+ name: eno49
+ mac: '48:df:37:22:c5:c0'
+ busaddr: '0000:04:00.0'
+ speed: 10000
+ -
+ name: ens1f0
+ mac: '3c:fd:fe:b2:ae:68'
+ busaddr: '0000:05:00.0'
+ speed: 10000
+ -
+ name: ens1f1
+ mac: '3c:fd:fe:b2:ae:69'
+ busaddr: '0000:05:00.1'
+ speed: 10000
+ -
+ name: ens1f2
+ mac: '3c:fd:fe:b2:ae:6a'
+ busaddr: '0000:05:00.2'
+ speed: 10000
+ -
+ name: ens4f0
+ mac: '3c:fd:fe:b2:ad:90'
+ busaddr: '0000:88:00.0'
+ speed: 10000
+ -
+ name: ens4f1
+ mac: '3c:fd:fe:b2:ad:91'
+ busaddr: '0000:88:00.1'
+ speed: 10000
diff --git a/dashboard/data/UNH_IOL/HPE_Proliant/hpe16.yaml b/dashboard/data/UNH_IOL/HPE_Proliant/hpe16.yaml
new file mode 100644
index 0000000..3148532
--- /dev/null
+++ b/dashboard/data/UNH_IOL/HPE_Proliant/hpe16.yaml
@@ -0,0 +1,41 @@
+---
+disk:
+ -
+ name: sda
+ size: 894.2G
+cpu:
+ arch: x86_64
+ cpus: 2
+ cores: 88
+memory: 503G
+interface:
+ -
+ name: eth0
+ mac: '48:df:37:22:c5:30'
+ busaddr: '0000:04:00.0'
+ speed: 10000
+ -
+ name: eth2
+ mac: '3c:fd:fe:b2:b1:60'
+ busaddr: '0000:05:00.0'
+ speed: 10000
+ -
+ name: eth3
+ mac: '3c:fd:fe:b2:b1:61'
+ busaddr: '0000:05:00.1'
+ speed: 10000
+ -
+ name: eth4
+ mac: '3c:fd:fe:b2:b1:62'
+ busaddr: '0000:05:00.2'
+ speed: 10000
+ -
+ name: eth6
+ mac: '3c:fd:fe:b2:b3:b8'
+ busaddr: '0000:88:00.0'
+ speed: 10000
+ -
+ name: eth7
+ mac: '3c:fd:fe:b2:b3:b9'
+ busaddr: '0000:88:00.1'
+ speed: 10000
diff --git a/dashboard/data/UNH_IOL/HPE_Proliant/hpe17.yaml b/dashboard/data/UNH_IOL/HPE_Proliant/hpe17.yaml
new file mode 100644
index 0000000..4b13ae2
--- /dev/null
+++ b/dashboard/data/UNH_IOL/HPE_Proliant/hpe17.yaml
@@ -0,0 +1,41 @@
+---
+disk:
+ -
+ name: sda
+ size: 894.2G
+cpu:
+ arch: x86_64
+ cpus: 2
+ cores: 88
+memory: 503G
+interface:
+ -
+ name: eno49
+ mac: '48:df:37:1d:48:e0'
+ busaddr: '0000:04:00.0'
+ speed: 10000
+ -
+ name: ens1f0
+ mac: '3c:fd:fe:b2:ad:80'
+ busaddr: '0000:05:00.0'
+ speed: 10000
+ -
+ name: ens1f1
+ mac: '3c:fd:fe:b2:ad:81'
+ busaddr: '0000:05:00.1'
+ speed: 10000
+ -
+ name: ens1f2
+ mac: '3c:fd:fe:b2:ad:82'
+ busaddr: '0000:05:00.2'
+ speed: 10000
+ -
+ name: ens4f0
+ mac: '3c:fd:fe:b2:b2:d0'
+ busaddr: '0000:88:00.0'
+ speed: 10000
+ -
+ name: ens4f1
+ mac: '3c:fd:fe:b2:b2:d1'
+ busaddr: '0000:88:00.1'
+ speed: 10000
diff --git a/dashboard/data/UNH_IOL/HPE_Proliant/hpe18.yaml b/dashboard/data/UNH_IOL/HPE_Proliant/hpe18.yaml
new file mode 100644
index 0000000..aa411f1
--- /dev/null
+++ b/dashboard/data/UNH_IOL/HPE_Proliant/hpe18.yaml
@@ -0,0 +1,41 @@
+---
+disk:
+ -
+ name: sda
+ size: 894.2G
+cpu:
+ arch: x86_64
+ cpus: 2
+ cores: 88
+memory: 503G
+interface:
+ -
+ name: eno49
+ mac: '48:df:37:19:a9:b0'
+ busaddr: '0000:04:00.0'
+ speed: 10000
+ -
+ name: ens1f0
+ mac: '3c:fd:fe:b2:ae:b8'
+ busaddr: '0000:05:00.0'
+ speed: 10000
+ -
+ name: ens1f1
+ mac: '3c:fd:fe:b2:ae:b9'
+ busaddr: '0000:05:00.1'
+ speed: 10000
+ -
+ name: ens1f2
+ mac: '3c:fd:fe:b2:ae:ba'
+ busaddr: '0000:05:00.2'
+ speed: 10000
+ -
+ name: ens4f0
+ mac: '3c:fd:fe:b2:af:50'
+ busaddr: '0000:88:00.0'
+ speed: 10000
+ -
+ name: ens4f1
+ mac: '3c:fd:fe:b2:af:51'
+ busaddr: '0000:88:00.1'
+ speed: 10000
diff --git a/dashboard/data/UNH_IOL/HPE_Proliant/hpe19.yaml b/dashboard/data/UNH_IOL/HPE_Proliant/hpe19.yaml
new file mode 100644
index 0000000..40e538c
--- /dev/null
+++ b/dashboard/data/UNH_IOL/HPE_Proliant/hpe19.yaml
@@ -0,0 +1,41 @@
+---
+disk:
+ -
+ name: sda
+ size: 894.2G
+cpu:
+ arch: x86_64
+ cpus: 2
+ cores: 88
+memory: 503G
+interface:
+ -
+ name: eno49
+ mac: '48:df:37:22:b5:90'
+ busaddr: '0000:04:00.0'
+ speed: 10000
+ -
+ name: ens1f0
+ mac: '3c:fd:fe:b2:ad:20'
+ busaddr: '0000:05:00.0'
+ speed: 10000
+ -
+ name: ens1f1
+ mac: '3c:fd:fe:b2:ad:21'
+ busaddr: '0000:05:00.1'
+ speed: 10000
+ -
+ name: ens1f2
+ mac: '3c:fd:fe:b2:ad:22'
+ busaddr: '0000:05:00.2'
+ speed: 10000
+ -
+ name: ens4f0
+ mac: '3c:fd:fe:b2:b3:10'
+ busaddr: '0000:88:00.0'
+ speed: 10000
+ -
+ name: ens4f1
+ mac: '3c:fd:fe:b2:b3:11'
+ busaddr: '0000:88:00.1'
+ speed: 10000
diff --git a/dashboard/data/UNH_IOL/HPE_Proliant/hpe20.yaml b/dashboard/data/UNH_IOL/HPE_Proliant/hpe20.yaml
new file mode 100644
index 0000000..6c83e79
--- /dev/null
+++ b/dashboard/data/UNH_IOL/HPE_Proliant/hpe20.yaml
@@ -0,0 +1,41 @@
+---
+disk:
+ -
+ name: sda
+ size: 894.2G
+cpu:
+ arch: x86_64
+ cpus: 2
+ cores: 88
+memory: 503G
+interface:
+ -
+ name: eno49
+ mac: '48:df:37:1d:49:a0'
+ busaddr: '0000:04:00.0'
+ speed: 10000
+ -
+ name: ens1f0
+ mac: '3c:fd:fe:b2:b1:c8'
+ busaddr: '0000:05:00.0'
+ speed: 10000
+ -
+ name: ens1f1
+ mac: '3c:fd:fe:b2:b1:c9'
+ busaddr: '0000:05:00.1'
+ speed: 10000
+ -
+ name: ens1f2
+ mac: '3c:fd:fe:b2:b1:ca'
+ busaddr: '0000:05:00.2'
+ speed: 10000
+ -
+ name: ens4f0
+ mac: '3c:fd:fe:b2:ae:a0'
+ busaddr: '0000:88:00.0'
+ speed: 10000
+ -
+ name: ens4f1
+ mac: '3c:fd:fe:b2:ae:a1'
+ busaddr: '0000:88:00.1'
+ speed: 10000
diff --git a/dashboard/data/UNH_IOL/hostlist.json b/dashboard/data/UNH_IOL/hostlist.json
new file mode 100644
index 0000000..cd03859
--- /dev/null
+++ b/dashboard/data/UNH_IOL/hostlist.json
@@ -0,0 +1,14 @@
+{
+ "profiles": {
+ "HPE_Proliant": [
+ "hpe13",
+ "hpe14",
+ "hpe15",
+ "hpe16",
+ "hpe17",
+ "hpe18",
+ "hpe19",
+ "hpe20"
+ ]
+ }
+}
diff --git a/dashboard/docker-compose.override-dev.yml b/dashboard/docker-compose.override-dev.yml
new file mode 100644
index 0000000..e1e1632
--- /dev/null
+++ b/dashboard/docker-compose.override-dev.yml
@@ -0,0 +1,25 @@
+---
+##############################################################################
+# Copyright (c) 2018 Trevor Bramwell 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
+##############################################################################
+version: '3'
+services:
+ web:
+ image: opnfv/pharos-tools-laas-dashboard:dev
+ build:
+ context: .
+ dockerfile: web/Dockerfile
+ volumes:
+ - ./src:/pharos_dashboard
+ worker:
+ image: opnfv/pharos-tools-laas-celery:dev
+ build:
+ context: .
+ dockerfile: worker/Dockerfile
+ volumes:
+ - ./src:/pharos_dashboard
diff --git a/dashboard/docker-compose.yml b/dashboard/docker-compose.yml
index 44a263f..eac84e6 100644
--- a/dashboard/docker-compose.yml
+++ b/dashboard/docker-compose.yml
@@ -1,15 +1,14 @@
---
##############################################################################
# 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
##############################################################################
-
-
-version: '2'
+version: '3'
services:
nginx:
restart: always
@@ -19,59 +18,51 @@ services:
- "80:80"
volumes:
- ./config/nginx:/etc/nginx/conf.d
- - /var/lib/pharos_dashboard/static:/static
- - /var/lib/pharos_dashboard/media:/media
+ - pharos-static:/static:ro
+ - pharos-media:/media
depends_on:
- web
web:
+ image: opnfv/pharos-tools-laas-dashboard:latest
restart: always
- build: ./web/
container_name: dg01
- # yamllint disable rule:line-length
- command: bash -c "python manage.py migrate && python manage.py collectstatic --no-input && gunicorn pharos_dashboard.wsgi -b 0.0.0.0:8000"
- # yamllint enable rule:line-length
depends_on:
- postgres
links:
- postgres
env_file: config.env
volumes:
- - ./:/pharos_dashboard
- - /var/lib/pharos_dashboard/static:/static
- - /var/lib/pharos_dashboard/media:/media
+ - pharos-static:/static
+ - pharos-media:/media
expose:
- "8000"
postgres:
restart: always
- image: postgres:latest
+ image: postgres:10
container_name: ps01
env_file: config.env
volumes:
- - ./config/postgres/docker-entrypoint-initdb.d:/docker-entrypoint-initdb.d
- pharos-data:/var/lib/postgresql/data
rabbitmq:
restart: always
- build: ./rabbitmq/
+ image: rabbitmq
container_name: rm01
env_file: config.env
ports:
- "5672:5672"
worker:
+ image: opnfv/pharos-tools-laas-celery:latest
restart: always
- build: ./worker/
- # yamllint disable rule:line-length
- command: bash -c "celery -A pharos_dashboard worker -l info -B --schedule=~/celerybeat-schedule"
- # yamllint enable rule:line-length
env_file: config.env
links:
- postgres
- rabbitmq
- volumes:
- - ./:/pharos_dashboard
volumes:
+ pharos-media:
+ pharos-static:
pharos-data:
external: true
diff --git a/dashboard/open-api-spec.yaml b/dashboard/open-api-spec.yaml
new file mode 100644
index 0000000..2e8dfd6
--- /dev/null
+++ b/dashboard/open-api-spec.yaml
@@ -0,0 +1,523 @@
+---
+swagger: "2.0"
+info:
+ description: This is the Lab as a Service API
+ version: 2.0.1
+ title: LaaS API
+ contact:
+ email: nfvlab@iol.unh.edu
+ license:
+ name: Apache 2.0
+ url: http://www.apache.org/licenses/LICENSE-2.0.html
+host: virtserver.swaggerhub.com
+basePath: /IOL-OPNFV-LaaS/Labs/1.0.0
+tags:
+- name: admin
+ description: Secured Admin-only calls
+- name: developers
+ description: Operations available to regular developers
+schemes:
+- https
+paths:
+ /api/labs/{lab-name}/jobs/new:
+ get:
+ summary: list of new, unstarted jobs for the lab
+ description: |
+ List of jobs for <lab-name> to start. These jobs all must have a status of `new`,
+ meaning they are unstarted.
+ produces:
+ - application/json
+ parameters:
+ - name: lab-name
+ in: path
+ required: true
+ type: string
+ responses:
+ 200:
+ description: search results matching criteria
+ schema:
+ type: array
+ items:
+ $ref: '#/definitions/Job'
+ /api/labs/{lab-name}/jobs/current:
+ get:
+ summary: list of unfinished jobs
+ description: |
+ List of jobs for <lab-name> that are still in progress. A job is in progress if
+ it has been started but has not finished.
+ produces:
+ - application/json
+ parameters:
+ - name: lab-name
+ in: path
+ required: true
+ type: string
+ responses:
+ 200:
+ description: search results matching criteria
+ schema:
+ type: array
+ items:
+ $ref: '#/definitions/Job'
+ /api/labs/{lab-name}/jobs/done:
+ get:
+ summary: list of done jobs
+ description: |
+ List of jobs for <lab-name> that were started and are no longer in progress.
+ A job can be marked 'done' with a succesful or error status.
+ produces:
+ - application/json
+ parameters:
+ - name: lab-name
+ in: path
+ required: true
+ type: string
+ responses:
+ 200:
+ description: search results matching criteria
+ schema:
+ type: array
+ items:
+ $ref: '#/definitions/Job'
+ /api/labs/{lab-name}/jobs/{job_id}/{task_id}>:
+ post:
+ summary: update job information
+ produces:
+ - application/json
+ parameters:
+ - name: lab-name
+ in: path
+ required: true
+ type: string
+ - name: job_id
+ in: path
+ required: true
+ type: integer
+ - name: task_id
+ in: path
+ required: true
+ type: string
+ - in: body
+ name: payload
+ description: payload, schema based on job type
+ required: true
+ schema:
+ $ref: '#/definitions/JobUpdate'
+ responses:
+ 200:
+ description: success
+ /api/labs/{lab-name}/inventory:
+ get:
+ summary: lab inventory
+ produces:
+ - application/json
+ parameters:
+ - name: lab-name
+ in: path
+ required: true
+ type: string
+ responses:
+ 200:
+ description: lab inventory
+ schema:
+ $ref: '#/definitions/Inventory'
+ post:
+ summary: updates lab inventory
+ parameters:
+ - name: lab-name
+ in: path
+ required: true
+ type: string
+ - in: body
+ name: inventory
+ required: true
+ schema:
+ $ref: '#/definitions/Inventory'
+ responses:
+ 200:
+ description: success
+ /api/labs/{lab-name}/profile:
+ get:
+ summary: lab profile
+ produces:
+ - application/json
+ parameters:
+ - name: lab-name
+ in: path
+ required: true
+ type: string
+ responses:
+ 200:
+ description: lab profile
+ schema:
+ $ref: '#/definitions/Profile'
+ post:
+ summary: updates lab profile
+ parameters:
+ - name: lab-name
+ in: path
+ required: true
+ type: string
+ - in: body
+ name: profile
+ required: true
+ schema:
+ $ref: '#/definitions/Profile'
+ responses:
+ 200:
+ description: success
+definitions:
+ Host_Interface:
+ properties:
+ mac:
+ type: string
+ example: 00:11:22:33:44:55
+ description: mac address
+ busaddr:
+ type: string
+ example: 0000:02:00.1
+ description: bus address reported by `ethtool -i <ifname>`
+ switchport:
+ $ref: '#/definitions/Switchport'
+ Generic_Interface:
+ properties:
+ speed:
+ type: string
+ example: 10G
+ description: speed in M or G
+ name:
+ type: string
+ example: eno3
+ description: interface name
+ Generic_Disk:
+ properties:
+ size:
+ type: string
+ example: 500G
+ description: size in M, G, or T
+ type:
+ type: string
+ example: SSD
+ description: must be SSD or HDD
+ name:
+ type: string
+ example: sda
+ description: name of root block device
+ CPU:
+ properties:
+ cores:
+ type: integer
+ format: int32
+ example: 64
+ description: how many CPU cores the host has (across all physical cpus)
+ minimum: 1
+ arch:
+ type: string
+ example: x86_64
+ description: must be x86_64 or aarch64
+ cpus:
+ type: integer
+ example: 2
+ description: Number of different physical CPU chips
+ minimum: 1
+ Image:
+ properties:
+ name:
+ type: string
+ description:
+ type: string
+ lab_id:
+ type: string
+ description: identifier provided by lab
+ dashboard_id:
+ type: string
+ description: identifier provided by dashboard
+ Inventory_Host:
+ properties:
+ interfaces:
+ type: array
+ items:
+ $ref: '#/definitions/Host_Interface'
+ hostname:
+ type: string
+ example: hpe3.opnfv.iol.unh.edu
+ description: globally unique fqdn
+ host_type:
+ type: string
+ description: name of host type this host belongs to
+ Inventory_Network:
+ properties:
+ cidr:
+ type: string
+ example: 174.0.5.0/24
+ description: subnet description
+ gateway:
+ type: string
+ example: 174.0.5.1
+ description: ip of gateway
+ vlan:
+ type: integer
+ example: 100
+ description: vlan tag of this network
+ Inventory:
+ properties:
+ hosts:
+ type: array
+ description: all hosts
+ items:
+ $ref: '#/definitions/Inventory_Host'
+ networks:
+ type: array
+ description: all networks
+ items:
+ $ref: '#/definitions/Inventory_Network'
+ images:
+ type: array
+ description: available images
+ items:
+ $ref: '#/definitions/Image'
+ host_types:
+ type: array
+ description: all host types hosted by a lab
+ items:
+ $ref: '#/definitions/Host_Type'
+ Host_Type:
+ properties:
+ cpu:
+ $ref: '#/definitions/CPU'
+ disks:
+ type: array
+ items:
+ $ref: '#/definitions/Generic_Disk'
+ description:
+ type: string
+ description: human readable description of host type
+ interface:
+ type: array
+ items:
+ $ref: '#/definitions/Generic_Interface'
+ ram:
+ $ref: '#/definitions/Ram'
+ name:
+ type: string
+ description: lab-unique name
+ Ram:
+ properties:
+ amount:
+ type: integer
+ example: 16
+ description: amount of ram in Gibibytes (GiB)
+ Switchport:
+ properties:
+ switch_name:
+ type: string
+ example: Cisco-9
+ description: name of switch owning this switchport
+ port_name:
+ type: string
+ example: Ethernet1/34
+ description: name of port on switch
+ invariant_config:
+ type: array
+ description: list of vlans that may not be modified on this port
+ items:
+ $ref: '#/definitions/Vlan'
+ current_config:
+ type: array
+ description: list of current vlan configuration
+ items:
+ $ref: '#/definitions/Vlan'
+ Vlan:
+ properties:
+ vlan_id:
+ type: integer
+ example: 100
+ description: vlan id
+ minimum: 1
+ maximum: 4098
+ tagged:
+ type: boolean
+ example: true
+ description: whether this vlan is tagged or untagged
+ Job:
+ properties:
+ id:
+ type: integer
+ description: globally unique job identifier
+ payload:
+ $ref: '#/definitions/JobPayload'
+ JobPayload:
+ properties:
+ hardware:
+ $ref: '#/definitions/HardwareTask'
+ software:
+ $ref: '#/definitions/SoftwareTask'
+ network:
+ $ref: '#/definitions/NetworkTask'
+ access:
+ $ref: '#/definitions/AccessTask'
+ snapshot:
+ $ref: '#/definitions/SnapshotTask'
+ HardwareTask:
+ properties:
+ taskId:
+ $ref: '#/definitions/HardwareConfig'
+ SoftwareTask:
+ properties:
+ taskId:
+ $ref: '#/definitions/SoftwarePayload'
+ NetworkTask:
+ properties:
+ taskId:
+ $ref: '#/definitions/NetworkPayload'
+ AccessTask:
+ properties:
+ taskId:
+ $ref: '#/definitions/AccessPayload'
+ SnapshotTask:
+ properties:
+ taskId:
+ $ref: '#/definitions/SnapshotPayload'
+ SnapshotPayload:
+ properties:
+ host:
+ type: string
+ example: hpe3
+ description: how the lab identifies the host
+ image:
+ type: string
+ example: "4"
+ description: lab id of existing image, if updating an existing image. if this key does not exist, the lab must create a new image
+ dashboard_id:
+ type: string
+ description: how the dashboard identifies this image / snapshot
+ AccessPayload:
+ properties:
+ revoke:
+ type: boolean
+ description: whether to revoke key during completion of job
+ user:
+ type: string
+ description: PK/ID of user access is being given to
+ access_type:
+ type: string
+ example: ssh
+ description: type of access key to be generated. Options include "vpn and ssh"
+ hosts:
+ type: array
+ description: hosts to grant access to if applicable
+ items:
+ type: string
+ description: id of host
+ lab_token:
+ type: string
+ description: identifier provided by lab to this task
+ HardwareConfig:
+ properties:
+ id:
+ type: string
+ description: ID of host
+ image:
+ type: integer
+ example: 42
+ description: lab provided ID of the request image
+ power:
+ type: string
+ example: on
+ description: desired power state, either on or off
+ hostname:
+ type: string
+ example: my_new_machine
+ description: user-defined hostname
+ ipmi_create:
+ type: boolean
+ description: whether or not to create an ipmi account
+ lab_token:
+ type: string
+ description: identifier provided by lab to this task
+ SoftwarePayload:
+ properties:
+ opnfv:
+ $ref: '#/definitions/OpnfvConfiguration'
+ lab_token:
+ type: string
+ description: identifier provided by lab to this task
+ OpnfvHost:
+ properties:
+ hostname:
+ type: string
+ example: Jumphost
+ description: maps hostname to OPNFV role
+ OpnfvConfiguration:
+ properties:
+ installer:
+ type: string
+ description: Installer user wants
+ scenario:
+ type: string
+ description: scenario of OPNFV to deploy
+ pdf:
+ type: string
+ example: LaaS.com/api/my_job/pdf
+ description: URL to find the Pod Descriptor File contents
+ idf:
+ type: string
+ example: LaaS.com/api/my_job/idf
+ description: URL to find the Installer Descriptor File contents
+ roles:
+ type: array
+ description: role the host will play in OPNFV
+ items:
+ $ref: '#/definitions/OpnfvHost'
+ NetworkPayload:
+ properties:
+ hostId:
+ $ref: '#/definitions/NetworkConfig'
+ lab_token:
+ type: string
+ description: identifier provided by lab to this task
+ NetworkConfig:
+ properties:
+ interface_name:
+ type: array
+ description: list of vlans on this interface
+ items:
+ $ref: '#/definitions/Vlan'
+ JobUpdate:
+ properties:
+ status:
+ type: integer
+ description: status type, see status enum
+ message:
+ type: string
+ description: message from lab for user
+ lab_token:
+ type: string
+ description: identifier provided by lab to this task
+ Profile:
+ properties:
+ name:
+ type: string
+ description: proper expanded lab name
+ contact:
+ $ref: '#/definitions/Contact'
+ description:
+ type: string
+ host_count:
+ type: array
+ items:
+ $ref: '#/definitions/Host_Number'
+ Host_Number:
+ properties:
+ type:
+ type: string
+ count:
+ type: integer
+ Contact:
+ properties:
+ phone:
+ type: string
+ description: phone number at which a lab can be reached
+ email:
+ type: string
+ description: email at which a lab can be reached
diff --git a/dashboard/rabbitmq/Dockerfile b/dashboard/rabbitmq/Dockerfile
deleted file mode 100644
index 71162a4..0000000
--- a/dashboard/rabbitmq/Dockerfile
+++ /dev/null
@@ -1,4 +0,0 @@
-FROM rabbitmq
-
-ADD init.sh /init.sh
-CMD ["/init.sh"] \ No newline at end of file
diff --git a/dashboard/rabbitmq/init.sh b/dashboard/rabbitmq/init.sh
deleted file mode 100755
index 9d04dd1..0000000
--- a/dashboard/rabbitmq/init.sh
+++ /dev/null
@@ -1,10 +0,0 @@
-#!/bin/sh
-
-# Create Rabbitmq user
-( sleep 10 ; \
-rabbitmqctl add_user $RABBITMQ_USER $RABBITMQ_PASSWORD 2>/dev/null ; \
-rabbitmqctl set_user_tags $RABBITMQ_USER administrator ; \
-rabbitmqctl set_permissions -p / $RABBITMQ_USER ".*" ".*" ".*" ; \
-echo "*** User '$RABBITMQ_USER' with password '$RABBITMQ_PASSWORD' completed. ***") &
-
-rabbitmq-server $@
diff --git a/dashboard/readme.txt b/dashboard/readme.txt
index 2a25912..96ece92 100644
--- a/dashboard/readme.txt
+++ b/dashboard/readme.txt
@@ -9,23 +9,24 @@
The dashboard is deployed using docker-compose.
-Application / database files are saved in /var/lib/pharos_dashboard/.
+
+Application / database files are saved in the 'pharos-data' container
+which needs to be pre-built before bringing up the dashboard.
Deployment:
- clone the repository
- complete the config.env.sample file and save it as config.env
-- install docker, docker-compose and bower
-- run 'bower install' in ./src/static/ to fetch javascript dependencies
-- run 'make build' to build the containers
+- install docker, docker-compose
- run 'make data'
- run 'make up' to run the dashboard
Updating:
+- run 'docker-compose pull'
+- run 'docker-compose up -d'
- make stop
- git pull
-- run 'bower install' if javascript dependencies changed
- make build
- make start
@@ -34,3 +35,9 @@ If there is migrations that need user input (like renaming a field), they need t
Logs / Shell access:
- there is some shortcuts in the makefile
+
+Development:
+
+- Install dependencies listed in 'Deployment'
+- run 'make build'
+- run 'make dev-up'
diff --git a/dashboard/web/requirements.txt b/dashboard/requirements.txt
index 0d864d3..55e5fc9 100644
--- a/dashboard/web/requirements.txt
+++ b/dashboard/requirements.txt
@@ -1,11 +1,11 @@
celery==3.1.23
-cryptography==1.4
-Django==1.10
-django-bootstrap3==7.0.1
-django-crispy-forms==1.6.0
-django-filter==0.14.0
+cryptography==2.3.1
+Django==2.1
+django-bootstrap4==0.0.8
+django-crispy-forms==1.7.2
+django-filter==2.0.0
django-registration==2.1.2
-djangorestframework==3.4.6
+djangorestframework==3.8.2
gunicorn==19.6.0
jira==1.0.7
jsonpickle==0.9.3
@@ -16,3 +16,5 @@ psycopg2==2.6.2
PyJWT==1.4.2
requests==2.11.0
django-fernet-fields==0.5
+pyyaml==3.13
+pytz==2018.5
diff --git a/dashboard/src/__init__.py b/dashboard/src/__init__.py
deleted file mode 100644
index ce1acf3..0000000
--- a/dashboard/src/__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
-############################################################################## \ No newline at end of file
diff --git a/dashboard/src/account/__init__.py b/dashboard/src/account/__init__.py
index b5914ce..b6fef6c 100644
--- a/dashboard/src/account/__init__.py
+++ b/dashboard/src/account/__init__.py
@@ -6,5 +6,3 @@
# which accompanies this distribution, and is available at
# http://www.apache.org/licenses/LICENSE-2.0
##############################################################################
-
-
diff --git a/dashboard/src/account/admin.py b/dashboard/src/account/admin.py
index 6f77122..b4c142c 100644
--- a/dashboard/src/account/admin.py
+++ b/dashboard/src/account/admin.py
@@ -1,5 +1,6 @@
##############################################################################
# Copyright (c) 2016 Max Breitenfeldt and others.
+# Copyright (c) 2018 Parker Berberian, Sawyer Bergeron, and others.
#
# All rights reserved. This program and the accompanying materials
# are made available under the terms of the Apache License, Version 2.0
@@ -10,7 +11,9 @@
from django.contrib import admin
-from account.models import UserProfile, Lab
+from account.models import UserProfile, Lab, VlanManager, PublicNetwork
admin.site.register(UserProfile)
admin.site.register(Lab)
+admin.site.register(VlanManager)
+admin.site.register(PublicNetwork)
diff --git a/dashboard/src/account/jira_util.py b/dashboard/src/account/jira_util.py
index fdb87f7..18b0e26 100644
--- a/dashboard/src/account/jira_util.py
+++ b/dashboard/src/account/jira_util.py
@@ -62,4 +62,4 @@ def get_jira(user):
'key_cert': key_cert
}
- return JIRA(server=settings.JIRA_URL, oauth=oauth_dict) \ No newline at end of file
+ return JIRA(server=settings.JIRA_URL, oauth=oauth_dict)
diff --git a/dashboard/src/account/migrations/0001_initial.py b/dashboard/src/account/migrations/0001_initial.py
index 591f702..c8b5bdc 100644
--- a/dashboard/src/account/migrations/0001_initial.py
+++ b/dashboard/src/account/migrations/0001_initial.py
@@ -1,6 +1,4 @@
-# -*- coding: utf-8 -*-
-# Generated by Django 1.10 on 2016-11-03 13:33
-from __future__ import unicode_literals
+# Generated by Django 2.1 on 2018-09-14 14:48
import account.models
from django.conf import settings
@@ -18,21 +16,50 @@ class Migration(migrations.Migration):
operations = [
migrations.CreateModel(
+ name='Lab',
+ fields=[
+ ('name', models.CharField(max_length=200, primary_key=True, serialize=False, unique=True)),
+ ('contact_email', models.EmailField(blank=True, max_length=200, null=True)),
+ ('contact_phone', models.CharField(blank=True, max_length=20, null=True)),
+ ('status', models.IntegerField(default=0)),
+ ('location', models.TextField(default='unknown')),
+ ('api_token', models.CharField(max_length=50)),
+ ('lab_user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
+ ],
+ ),
+ migrations.CreateModel(
name='UserProfile',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('timezone', models.CharField(default='UTC', max_length=100)),
('ssh_public_key', models.FileField(blank=True, null=True, upload_to=account.models.upload_to)),
('pgp_public_key', models.FileField(blank=True, null=True, upload_to=account.models.upload_to)),
+ ('email_addr', models.CharField(default='email@mail.com', max_length=300)),
('company', models.CharField(max_length=200)),
('oauth_token', models.CharField(max_length=1024)),
('oauth_secret', models.CharField(max_length=1024)),
('jira_url', models.CharField(default='', max_length=100)),
('full_name', models.CharField(default='', max_length=100)),
+ ('booking_privledge', models.BooleanField(default=False)),
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
options={
'db_table': 'user_profile',
},
),
+ migrations.CreateModel(
+ name='VlanManager',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('vlans', models.TextField()),
+ ('block_size', models.IntegerField()),
+ ('allow_overlapping', models.BooleanField()),
+ ('reserved_vlans', models.TextField()),
+ ],
+ ),
+ migrations.AddField(
+ model_name='lab',
+ name='vlan_manager',
+ field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='account.VlanManager'),
+ ),
]
diff --git a/dashboard/src/account/migrations/0002_auto_20180110_1636.py b/dashboard/src/account/migrations/0002_auto_20180110_1636.py
deleted file mode 100644
index 6170acb..0000000
--- a/dashboard/src/account/migrations/0002_auto_20180110_1636.py
+++ /dev/null
@@ -1,33 +0,0 @@
-# -*- coding: utf-8 -*-
-# Generated by Django 1.10 on 2018-01-10 16:36
-from __future__ import unicode_literals
-
-from django.conf import settings
-from django.db import migrations, models
-import django.db.models.deletion
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- migrations.swappable_dependency(settings.AUTH_USER_MODEL),
- ('account', '0001_initial'),
- ]
-
- operations = [
- migrations.CreateModel(
- name='Lab',
- fields=[
- ('id', models.CharField(max_length=200, primary_key=True, serialize=False)),
- ('name', models.CharField(max_length=200, unique=True)),
- ('contact_email', models.EmailField(blank=True, max_length=200, null=True)),
- ('contact_phone', models.CharField(blank=True, max_length=20, null=True)),
- ('lab_user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
- ],
- ),
- migrations.AddField(
- model_name='userprofile',
- name='email_addr',
- field=models.CharField(default='email@mail.com', max_length=300),
- ),
- ]
diff --git a/dashboard/src/account/migrations/0002_lab_description.py b/dashboard/src/account/migrations/0002_lab_description.py
new file mode 100644
index 0000000..445501a
--- /dev/null
+++ b/dashboard/src/account/migrations/0002_lab_description.py
@@ -0,0 +1,19 @@
+# Generated by Django 2.1 on 2018-09-14 20:22
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('account', '0001_initial'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='lab',
+ name='description',
+ field=models.CharField(default='Lab description default', max_length=240),
+ preserve_default=False,
+ ),
+ ]
diff --git a/dashboard/src/account/migrations/0003_auto_20180110_1639.py b/dashboard/src/account/migrations/0003_auto_20180110_1639.py
deleted file mode 100644
index d0bc4d6..0000000
--- a/dashboard/src/account/migrations/0003_auto_20180110_1639.py
+++ /dev/null
@@ -1,24 +0,0 @@
-# -*- coding: utf-8 -*-
-# Generated by Django 1.10 on 2018-01-10 16:39
-from __future__ import unicode_literals
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('account', '0002_auto_20180110_1636'),
- ]
-
- operations = [
- migrations.RemoveField(
- model_name='lab',
- name='id',
- ),
- migrations.AlterField(
- model_name='lab',
- name='name',
- field=models.CharField(max_length=200, primary_key=True, serialize=False, unique=True),
- ),
- ]
diff --git a/dashboard/src/account/migrations/0003_publicnetwork.py b/dashboard/src/account/migrations/0003_publicnetwork.py
new file mode 100644
index 0000000..71e5caa
--- /dev/null
+++ b/dashboard/src/account/migrations/0003_publicnetwork.py
@@ -0,0 +1,25 @@
+# Generated by Django 2.1 on 2018-09-26 14:41
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('account', '0002_lab_description'),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='PublicNetwork',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('vlan', models.IntegerField()),
+ ('in_use', models.BooleanField(default=False)),
+ ('cidr', models.CharField(default='0.0.0.0/0', max_length=50)),
+ ('gateway', models.CharField(default='0.0.0.0', max_length=50)),
+ ('lab', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='account.Lab')),
+ ],
+ ),
+ ]
diff --git a/dashboard/src/account/migrations/__init__.py b/dashboard/src/account/migrations/__init__.py
index b5914ce..e69de29 100644
--- a/dashboard/src/account/migrations/__init__.py
+++ b/dashboard/src/account/migrations/__init__.py
@@ -1,10 +0,0 @@
-##############################################################################
-# Copyright (c) 2016 Max Breitenfeldt and others.
-#
-# All rights reserved. This program and the accompanying materials
-# are made available under the terms of the Apache License, Version 2.0
-# which accompanies this distribution, and is available at
-# http://www.apache.org/licenses/LICENSE-2.0
-##############################################################################
-
-
diff --git a/dashboard/src/account/models.py b/dashboard/src/account/models.py
index aad4c50..4fc7c40 100644
--- a/dashboard/src/account/models.py
+++ b/dashboard/src/account/models.py
@@ -10,11 +10,20 @@
from django.contrib.auth.models import User
from django.db import models
+import json
+import random
+
+
+class LabStatus(object):
+ UP = 0
+ TEMP_DOWN = 100
+ DOWN = 200
def upload_to(object, filename):
return object.user.username + '/' + filename
+
class UserProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
timezone = models.CharField(max_length=100, blank=False, default='UTC')
@@ -28,6 +37,7 @@ class UserProfile(models.Model):
jira_url = models.CharField(max_length=100, default='')
full_name = models.CharField(max_length=100, default='')
+ booking_privledge = models.BooleanField(default=False)
class Meta:
db_table = 'user_profile'
@@ -35,11 +45,126 @@ class UserProfile(models.Model):
def __str__(self):
return self.user.username
+
+class VlanManager(models.Model):
+ # list of length 4096 containing either 0 (not available) or 1 (available)
+ vlans = models.TextField()
+ block_size = models.IntegerField()
+ allow_overlapping = models.BooleanField()
+ # list of length 4096 containing either 0 (not rexerved) or 1 (reserved)
+ reserved_vlans = models.TextField()
+
+ def get_vlan(self, count=1):
+ allocated = []
+ vlans = json.loads(self.vlans)
+ for i in range(count):
+ new_vlan = vlans.index(1) # will throw if none available
+ vlans[new_vlan] = 0
+ allocated.append(new_vlan)
+ if count == 1:
+ return allocated[0]
+ return allocated
+
+ def get_public_vlan(self):
+ return PublicNetwork.objects.filter(lab=self.lab_set.first(), in_use=False).first()
+
+ def reserve_public_vlan(self, vlan):
+ net = PublicNetwork.objects.get(lab=self.lab_set.first(), vlan=vlan, in_use=False)
+ net.in_use = True
+ net.save()
+
+ def release_public_vlan(self, vlan):
+ net = PublicNetwork.objects.get(lab=self.lab_set.first(), vlan=vlan, in_use=True)
+ net.in_use = False
+ net.save()
+
+ def public_vlan_is_available(self, vlan):
+ net = PublicNetwork.objects.get(lab=self.lab_set.first(), vlan=vlan)
+ return not net.in_use
+
+ def is_available(self, vlans):
+ """
+ 'vlans' is either a single vlan id integer or a list of integers
+ will return true (available) or false
+ """
+ if self.allow_overlapping:
+ return True
+
+ reserved = json.loads(self.reserved_vlans)
+ vlan_master_list = json.loads(self.vlans)
+ try:
+ iter(vlans)
+ except 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):
+ """
+ '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):
+ my_vlans = json.loads(self.vlans)
+
+ try:
+ iter(vlans)
+ except Exception:
+ vlans = [vlans]
+
+ vlans = set(vlans)
+
+ for vlan in vlans:
+ if my_vlans[vlan] == 0:
+ raise ValueError("vlan " + str(vlan) + " is not available")
+
+ my_vlans[vlan] = 0
+ self.vlans = json.dumps(my_vlans)
+ self.save()
+
+
class Lab(models.Model):
lab_user = models.OneToOneField(User, on_delete=models.CASCADE)
name = models.CharField(max_length=200, primary_key=True, unique=True, null=False, blank=False)
contact_email = models.EmailField(max_length=200, null=True, blank=True)
contact_phone = models.CharField(max_length=20, null=True, blank=True)
+ status = models.IntegerField(default=LabStatus.UP)
+ vlan_manager = models.ForeignKey(VlanManager, on_delete=models.CASCADE, null=True)
+ location = models.TextField(default="unknown")
+ api_token = models.CharField(max_length=50)
+ description = models.CharField(max_length=240)
+
+ @staticmethod
+ def make_api_token():
+ alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
+ key = ""
+ for i in range(45):
+ key += random.choice(alphabet)
+ return key
def __str__(self):
return self.name
+
+
+class PublicNetwork(models.Model):
+ vlan = models.IntegerField()
+ lab = models.ForeignKey(Lab, on_delete=models.CASCADE)
+ in_use = models.BooleanField(default=False)
+ cidr = models.CharField(max_length=50, default="0.0.0.0/0")
+ gateway = models.CharField(max_length=50, default="0.0.0.0")
diff --git a/dashboard/src/account/tasks.py b/dashboard/src/account/tasks.py
index bfb865d..fe51974 100644
--- a/dashboard/src/account/tasks.py
+++ b/dashboard/src/account/tasks.py
@@ -1,5 +1,6 @@
##############################################################################
# Copyright (c) 2016 Max Breitenfeldt and others.
+# Copyright (c) 2018 Parker Berberian, Sawyer Bergeron, and others.
#
# All rights reserved. This program and the accompanying materials
# are made available under the terms of the Apache License, Version 2.0
@@ -28,7 +29,6 @@ def sync_jira_accounts():
user.email = user_dict['emailAddress']
user.userprofile.url = user_dict['self']
user.userprofile.full_name = user_dict['displayName']
- print(user_dict)
user.userprofile.save()
- user.save() \ No newline at end of file
+ user.save()
diff --git a/dashboard/src/account/tests/__init__.py b/dashboard/src/account/tests/__init__.py
index b5914ce..b6fef6c 100644
--- a/dashboard/src/account/tests/__init__.py
+++ b/dashboard/src/account/tests/__init__.py
@@ -6,5 +6,3 @@
# which accompanies this distribution, and is available at
# http://www.apache.org/licenses/LICENSE-2.0
##############################################################################
-
-
diff --git a/dashboard/src/account/tests/test_general.py b/dashboard/src/account/tests/test_general.py
index e8f483b..3fb52b0 100644
--- a/dashboard/src/account/tests/test_general.py
+++ b/dashboard/src/account/tests/test_general.py
@@ -30,7 +30,7 @@ class AccountMiddlewareTestCase(TestCase):
The timezone should be UTC for anonymous users, for authenticated users it should be set
to user.userprofile.timezone
"""
- #default
+ # default
self.assertEqual(timezone.get_current_timezone_name(), 'UTC')
url = reverse('account:settings')
@@ -47,7 +47,7 @@ class AccountMiddlewareTestCase(TestCase):
self.user1profile.timezone = 'Etc/Greenwich'
self.user1profile.save()
self.client.get(url)
- self.assertEqual(timezone.get_current_timezone_name(), 'Etc/Greenwich')
+ 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')
@@ -56,5 +56,3 @@ class AccountMiddlewareTestCase(TestCase):
self.client.login(username='user2', password='user2')
self.client.get(url)
self.assertTrue(user2.userprofile)
-
-
diff --git a/dashboard/src/account/urls.py b/dashboard/src/account/urls.py
index 3962a0c..8aad80c 100644
--- a/dashboard/src/account/urls.py
+++ b/dashboard/src/account/urls.py
@@ -1,5 +1,6 @@
##############################################################################
# Copyright (c) 2016 Max Breitenfeldt and others.
+# Copyright (c) 2018 Parker Berberian, Sawyer Bergeron, and others.
#
# All rights reserved. This program and the accompanying materials
# are made available under the terms of the Apache License, Version 2.0
@@ -24,13 +25,39 @@ Including another URLconf
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 *
+from account.views import (
+ AccountSettingsView,
+ JiraAuthenticatedView,
+ JiraLoginView,
+ JiraLogoutView,
+ UserListView,
+ account_resource_view,
+ account_booking_view,
+ account_images_view,
+ account_configuration_view,
+ account_detail_view,
+ resource_delete_view,
+ booking_cancel_view,
+ image_delete_view,
+ configuration_delete_view
+)
+app_name = "account"
urlpatterns = [
url(r'^settings/', AccountSettingsView.as_view(), name='settings'),
url(r'^authenticated/$', JiraAuthenticatedView.as_view(), name='authenticated'),
url(r'^login/$', JiraLoginView.as_view(), name='login'),
url(r'^logout/$', JiraLogoutView.as_view(), name='logout'),
url(r'^users/$', UserListView.as_view(), name='users'),
+ url(r'^my/resources/$', account_resource_view, name="my-resources"),
+ path('my/resources/delete/<int:resource_id>', resource_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/configurations/$', account_configuration_view, name="my-configurations"),
+ path('my/configurations/delete/<int:config_id>', configuration_delete_view),
+ url(r'^my/$', account_detail_view, name="my-account"),
]
diff --git a/dashboard/src/account/views.py b/dashboard/src/account/views.py
index e6a0e5d..2b4eccb 100644
--- a/dashboard/src/account/views.py
+++ b/dashboard/src/account/views.py
@@ -1,5 +1,6 @@
##############################################################################
# Copyright (c) 2016 Max Breitenfeldt and others.
+# Copyright (c) 2018 Parker Berberian, Sawyer Bergeron, and others.
#
# All rights reserved. This program and the accompanying materials
# are made available under the terms of the Apache License, Version 2.0
@@ -13,20 +14,26 @@ import urllib
import oauth2 as oauth
from django.conf import settings
+from django.utils import timezone
from django.contrib import messages
from django.contrib.auth import logout, authenticate, login
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 jira import JIRA
from rest_framework.authtoken.models import Token
from account.forms import AccountSettingsForm
from account.jira_util import SignatureMethod_RSA_SHA1
from account.models import UserProfile
+from booking.models import Booking
+from resource_inventory.models import GenericResourceBundle, ConfigBundle, Image, Host
@method_decorator(login_required, name='dispatch')
@@ -59,7 +66,7 @@ class JiraLoginView(RedirectView):
# Step 1. Get a request token from Jira.
try:
resp, content = client.request(settings.OAUTH_REQUEST_TOKEN_URL, "POST")
- except Exception as e:
+ except Exception:
messages.add_message(self.request, messages.ERROR,
'Error: Connection to Jira failed. Please contact an Administrator')
return '/'
@@ -72,8 +79,8 @@ class JiraLoginView(RedirectView):
self.request.session['request_token'] = dict(urllib.parse.parse_qsl(content.decode()))
# Step 3. Redirect the user to the authentication URL.
url = settings.OAUTH_AUTHORIZE_URL + '?oauth_token=' + \
- self.request.session['request_token']['oauth_token'] + \
- '&oauth_callback=' + settings.OAUTH_CALLBACK_URL
+ self.request.session['request_token']['oauth_token'] + \
+ '&oauth_callback=' + settings.OAUTH_CALLBACK_URL
return url
@@ -95,7 +102,7 @@ class JiraAuthenticatedView(RedirectView):
# Step 2. Request the authorized access token from Jira.
try:
resp, content = client.request(settings.OAUTH_ACCESS_TOKEN_URL, "POST")
- except Exception as e:
+ except Exception:
messages.add_message(self.request, messages.ERROR,
'Error: Connection to Jira failed. Please contact an Administrator')
return '/'
@@ -153,3 +160,133 @@ class UserListView(TemplateView):
context = super(UserListView, self).get_context_data(**kwargs)
context.update({'title': "Dashboard Users", 'users': users})
return context
+
+
+def account_detail_view(request):
+ template = "account/details.html"
+ return render(request, template)
+
+
+def account_resource_view(request):
+ """
+ gathers a users genericResoureBundles and
+ turns them into displayable objects
+ """
+ if not request.user.is_authenticated:
+ return render(request, "dashboard/login.html", {'title': 'Authentication Required'})
+ template = "account/resource_list.html"
+ resources = GenericResourceBundle.objects.filter(
+ owner=request.user).prefetch_related("configbundle_set")
+ mapping = {}
+ resource_list = []
+ booking_mapping = {}
+ for grb in resources:
+ resource_list.append(grb)
+ mapping[grb.id] = [{"id": x.id, "name": x.name} for x in grb.configbundle_set.all()]
+ if Booking.objects.filter(resource__template=grb, end__gt=timezone.now()).exists():
+ booking_mapping[grb.id] = "true"
+ context = {
+ "resources": resource_list,
+ "grb_mapping": mapping,
+ "booking_mapping": booking_mapping,
+ "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_configuration_view(request):
+ if not request.user.is_authenticated:
+ return render(request, "dashboard/login.html", {'title': 'Authentication Required'})
+ template = "account/configuration_list.html"
+ configs = list(ConfigBundle.objects.filter(owner=request.user))
+ context = {"title": "Configuration List", "configurations": configs}
+ return render(request, template, context=context)
+
+
+def account_images_view(request):
+ if not request.user.is_authenticated:
+ return render(request, "dashboard/login.html", {'title': 'Authentication Required'})
+ template = "account/image_list.html"
+ my_images = Image.objects.filter(owner=request.user)
+ public_images = Image.objects.filter(public=True)
+ used_images = {}
+ for image in my_images:
+ if Host.objects.filter(booked=True, config__image=image).exists():
+ 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 resource_delete_view(request, resource_id=None):
+ if not request.user.is_authenticated:
+ return HttpResponse('no') # 403?
+ grb = get_object_or_404(GenericResourceBundle, pk=resource_id)
+ if not request.user.id == grb.owner.id:
+ return HttpResponse('no') # 403?
+ if Booking.objects.filter(resource__template=grb, end__gt=timezone.now()).exists():
+ return HttpResponse('no') # 403?
+ grb.delete()
+ return HttpResponse('')
+
+
+def configuration_delete_view(request, config_id=None):
+ if not request.user.is_authenticated:
+ return HttpResponse('no') # 403?
+ config = get_object_or_404(ConfigBundle, pk=config_id)
+ if not request.user.id == config.owner.id:
+ return HttpResponse('no') # 403?
+ if Booking.objects.filter(config_bundle=config, end__gt=timezone.now()).exists():
+ return HttpResponse('no')
+ config.delete()
+ return HttpResponse('')
+
+
+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 Host.objects.filter(booked=True, config__image=image).exists():
+ return HttpResponse('no') # 403?
+ image.delete()
+ return HttpResponse('')
diff --git a/dashboard/src/api/__init__.py b/dashboard/src/api/__init__.py
index b5914ce..b6fef6c 100644
--- a/dashboard/src/api/__init__.py
+++ b/dashboard/src/api/__init__.py
@@ -6,5 +6,3 @@
# which accompanies this distribution, and is available at
# http://www.apache.org/licenses/LICENSE-2.0
##############################################################################
-
-
diff --git a/dashboard/src/api/admin.py b/dashboard/src/api/admin.py
new file mode 100644
index 0000000..8b2fcb3
--- /dev/null
+++ b/dashboard/src/api/admin.py
@@ -0,0 +1,41 @@
+##############################################################################
+# 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,
+)
+
+
+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)
diff --git a/dashboard/src/api/migrations/0001_initial.py b/dashboard/src/api/migrations/0001_initial.py
new file mode 100644
index 0000000..abe6f5e
--- /dev/null
+++ b/dashboard/src/api/migrations/0001_initial.py
@@ -0,0 +1,185 @@
+##############################################################################
+# Copyright (c) 2018 Parker Berberian, Sawyer Bergeron, and others.
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+##############################################################################
+# Generated by Django 2.1 on 2018-09-14 14:48
+
+import api.models
+from django.conf import settings
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+ initial = True
+
+ dependencies = [
+ migrations.swappable_dependency(settings.AUTH_USER_MODEL),
+ ('booking', '__first__'),
+ ('resource_inventory', '__first__'),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='AccessRelation',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('status', models.IntegerField(default=0)),
+ ('task_id', models.CharField(default=api.models.get_task_uuid, max_length=37)),
+ ('lab_token', models.CharField(default='null', max_length=50)),
+ ('message', models.TextField(default='')),
+ ],
+ options={
+ 'abstract': False,
+ },
+ ),
+ migrations.CreateModel(
+ name='HostHardwareRelation',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('status', models.IntegerField(default=0)),
+ ('task_id', models.CharField(default=api.models.get_task_uuid, max_length=37)),
+ ('lab_token', models.CharField(default='null', max_length=50)),
+ ('message', models.TextField(default='')),
+ ('host', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='resource_inventory.Host')),
+ ],
+ options={
+ 'abstract': False,
+ },
+ ),
+ migrations.CreateModel(
+ name='HostNetworkRelation',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('status', models.IntegerField(default=0)),
+ ('task_id', models.CharField(default=api.models.get_task_uuid, max_length=37)),
+ ('lab_token', models.CharField(default='null', max_length=50)),
+ ('message', models.TextField(default='')),
+ ('host', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='resource_inventory.Host')),
+ ],
+ options={
+ 'abstract': False,
+ },
+ ),
+ migrations.CreateModel(
+ name='Job',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('status', models.IntegerField(default=0)),
+ ('delta', models.TextField()),
+ ('complete', models.BooleanField(default=False)),
+ ('booking', models.OneToOneField(null=True, on_delete=django.db.models.deletion.CASCADE, to='booking.Booking')),
+ ],
+ ),
+ migrations.CreateModel(
+ name='OpnfvApiConfig',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('installer', models.CharField(max_length=100)),
+ ('scenario', models.CharField(max_length=100)),
+ ('delta', models.TextField()),
+ ('roles', models.ManyToManyField(to='resource_inventory.Host')),
+ ],
+ ),
+ migrations.CreateModel(
+ name='SoftwareRelation',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('status', models.IntegerField(default=0)),
+ ('task_id', models.CharField(default=api.models.get_task_uuid, max_length=37)),
+ ('lab_token', models.CharField(default='null', max_length=50)),
+ ('message', models.TextField(default='')),
+ ('job', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='api.Job')),
+ ],
+ options={
+ 'abstract': False,
+ },
+ ),
+ migrations.CreateModel(
+ name='TaskConfig',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ],
+ ),
+ migrations.CreateModel(
+ name='AccessConfig',
+ fields=[
+ ('taskconfig_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='api.TaskConfig')),
+ ('access_type', models.CharField(max_length=50)),
+ ('revoke', models.BooleanField(default=False)),
+ ('context', models.TextField(default='')),
+ ('delta', models.TextField()),
+ ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
+ ],
+ bases=('api.taskconfig',),
+ ),
+ migrations.CreateModel(
+ name='HardwareConfig',
+ fields=[
+ ('taskconfig_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='api.TaskConfig')),
+ ('image', models.CharField(default='defimage', max_length=100)),
+ ('power', models.CharField(default='off', max_length=100)),
+ ('hostname', models.CharField(default='hostname', max_length=100)),
+ ('ipmi_create', models.BooleanField(default=False)),
+ ('delta', models.TextField()),
+ ],
+ bases=('api.taskconfig',),
+ ),
+ migrations.CreateModel(
+ name='NetworkConfig',
+ fields=[
+ ('taskconfig_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='api.TaskConfig')),
+ ('delta', models.TextField()),
+ ('interfaces', models.ManyToManyField(to='resource_inventory.Interface')),
+ ],
+ bases=('api.taskconfig',),
+ ),
+ migrations.CreateModel(
+ name='SoftwareConfig',
+ fields=[
+ ('taskconfig_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='api.TaskConfig')),
+ ('opnfv', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='api.OpnfvApiConfig')),
+ ],
+ bases=('api.taskconfig',),
+ ),
+ migrations.AddField(
+ model_name='hostnetworkrelation',
+ name='job',
+ field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='api.Job'),
+ ),
+ migrations.AddField(
+ model_name='hosthardwarerelation',
+ name='job',
+ field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='api.Job'),
+ ),
+ migrations.AddField(
+ model_name='accessrelation',
+ name='job',
+ field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='api.Job'),
+ ),
+ migrations.AddField(
+ model_name='softwarerelation',
+ name='config',
+ field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='api.SoftwareConfig'),
+ ),
+ migrations.AddField(
+ model_name='hostnetworkrelation',
+ name='config',
+ field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='api.NetworkConfig'),
+ ),
+ migrations.AddField(
+ model_name='hosthardwarerelation',
+ name='config',
+ field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='api.HardwareConfig'),
+ ),
+ migrations.AddField(
+ model_name='accessrelation',
+ name='config',
+ field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='api.AccessConfig'),
+ ),
+ ]
diff --git a/dashboard/src/api/migrations/0002_remove_job_delta.py b/dashboard/src/api/migrations/0002_remove_job_delta.py
new file mode 100644
index 0000000..157a40f
--- /dev/null
+++ b/dashboard/src/api/migrations/0002_remove_job_delta.py
@@ -0,0 +1,17 @@
+# 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/dashboard/src/api/migrations/0003_auto_20190102_1956.py b/dashboard/src/api/migrations/0003_auto_20190102_1956.py
new file mode 100644
index 0000000..2ea5d70
--- /dev/null
+++ b/dashboard/src/api/migrations/0003_auto_20190102_1956.py
@@ -0,0 +1,18 @@
+# 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/dashboard/src/api/migrations/0004_snapshotconfig_snapshotrelation.py b/dashboard/src/api/migrations/0004_snapshotconfig_snapshotrelation.py
new file mode 100644
index 0000000..62bc7af
--- /dev/null
+++ b/dashboard/src/api/migrations/0004_snapshotconfig_snapshotrelation.py
@@ -0,0 +1,42 @@
+# 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/dashboard/src/api/migrations/0005_snapshotconfig_delta.py b/dashboard/src/api/migrations/0005_snapshotconfig_delta.py
new file mode 100644
index 0000000..559af90
--- /dev/null
+++ b/dashboard/src/api/migrations/0005_snapshotconfig_delta.py
@@ -0,0 +1,18 @@
+# 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/dashboard/src/api/migrations/0006_auto_20190313_1729.py b/dashboard/src/api/migrations/0006_auto_20190313_1729.py
new file mode 100644
index 0000000..ec148bd
--- /dev/null
+++ b/dashboard/src/api/migrations/0006_auto_20190313_1729.py
@@ -0,0 +1,23 @@
+# 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/dashboard/src/api/migrations/0007_auto_20190417_1511.py b/dashboard/src/api/migrations/0007_auto_20190417_1511.py
new file mode 100644
index 0000000..e7d2c59
--- /dev/null
+++ b/dashboard/src/api/migrations/0007_auto_20190417_1511.py
@@ -0,0 +1,25 @@
+# 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/dashboard/src/api/migrations/0007_opnfvapiconfig_opnfv_config.py b/dashboard/src/api/migrations/0007_opnfvapiconfig_opnfv_config.py
new file mode 100644
index 0000000..46f3631
--- /dev/null
+++ b/dashboard/src/api/migrations/0007_opnfvapiconfig_opnfv_config.py
@@ -0,0 +1,20 @@
+# 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/dashboard/src/api/migrations/0008_auto_20190419_1414.py b/dashboard/src/api/migrations/0008_auto_20190419_1414.py
new file mode 100644
index 0000000..03c3865
--- /dev/null
+++ b/dashboard/src/api/migrations/0008_auto_20190419_1414.py
@@ -0,0 +1,28 @@
+# 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/dashboard/src/api/migrations/0009_merge_20190508_1317.py b/dashboard/src/api/migrations/0009_merge_20190508_1317.py
new file mode 100644
index 0000000..1a34380
--- /dev/null
+++ b/dashboard/src/api/migrations/0009_merge_20190508_1317.py
@@ -0,0 +1,14 @@
+# 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/dashboard/src/api/migrations/__init__.py b/dashboard/src/api/migrations/__init__.py
index b5914ce..e0408fa 100644
--- a/dashboard/src/api/migrations/__init__.py
+++ b/dashboard/src/api/migrations/__init__.py
@@ -1,10 +1,8 @@
##############################################################################
-# Copyright (c) 2016 Max Breitenfeldt and others.
+# Copyright (c) 2018 Parker Berberian, Sawyer Bergeron, and others.
#
# All rights reserved. This program and the accompanying materials
# are made available under the terms of the Apache License, Version 2.0
# which accompanies this distribution, and is available at
# http://www.apache.org/licenses/LICENSE-2.0
##############################################################################
-
-
diff --git a/dashboard/src/api/models.py b/dashboard/src/api/models.py
new file mode 100644
index 0000000..1f708ae
--- /dev/null
+++ b/dashboard/src/api/models.py
@@ -0,0 +1,1015 @@
+##############################################################################
+# 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
+from django.shortcuts import get_object_or_404
+from django.urls import reverse
+
+import json
+import uuid
+
+from booking.models import Booking
+from resource_inventory.models import (
+ Lab,
+ HostProfile,
+ Host,
+ Image,
+ Interface,
+ HostOPNFVConfig,
+ RemoteInfo,
+ OPNFVConfig
+)
+from resource_inventory.idf_templater import IDFTemplater
+from resource_inventory.pdf_templater import PDFTemplater
+
+
+class JobStatus(object):
+ NEW = 0
+ CURRENT = 100
+ DONE = 200
+ ERROR = 300
+
+
+class LabManagerTracker(object):
+
+ @classmethod
+ def get(cls, lab_name, token):
+ """
+ Takes in a lab name (from a url path)
+ returns a lab manager instance for that lab, if it exists
+ """
+ try:
+ lab = Lab.objects.get(name=lab_name)
+ except Exception:
+ raise PermissionDenied("Lab not found")
+ if lab.api_token == token:
+ return LabManager(lab)
+ raise PermissionDenied("Lab not authorized")
+
+
+class LabManager(object):
+ """
+ This is the class that will ultimately handle all REST calls to
+ lab endpoints.
+ handles jobs, inventory, status, etc
+ may need to create helper classes
+ """
+
+ def __init__(self, lab):
+ self.lab = lab
+
+ def update_host_remote_info(self, data, host_id):
+ host = get_object_or_404(Host, labid=host_id, lab=self.lab)
+ 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 = host.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()
+ host.remote_management = remote_info
+ host.save()
+ booking = Booking.objects.get(resource=host.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'] = []
+ for host in HostProfile.objects.filter(labs=self.lab):
+ count = Host.objects.filter(profile=host, lab=self.lab).count()
+ prof['host_count'].append(
+ {
+ "type": host.name,
+ "count": count
+ }
+ )
+ return prof
+
+ def get_inventory(self):
+ inventory = {}
+ hosts = Host.objects.filter(lab=self.lab)
+ images = Image.objects.filter(from_lab=self.lab)
+ profiles = HostProfile.objects.filter(labs=self.lab)
+ inventory['hosts'] = self.serialize_hosts(hosts)
+ inventory['images'] = self.serialize_images(images)
+ inventory['host_types'] = self.serialize_host_profiles(profiles)
+ return inventory
+
+ def get_host(self, hostname):
+ host = get_object_or_404(Host, labid=hostname, lab=self.lab)
+ return {
+ "booked": host.booked,
+ "working": host.working,
+ "type": host.profile.name
+ }
+
+ def update_host(self, hostname, data):
+ host = get_object_or_404(Host, labid=hostname, lab=self.lab)
+ if "working" in data:
+ working = data['working'] == "true"
+ host.working = working
+ host.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_job(self, jobid):
+ return Job.objects.get(pk=jobid).to_dict()
+
+ def update_job(self, jobid, data):
+ {}
+
+ def serialize_jobs(self, jobs, status=JobStatus.NEW):
+ job_ser = []
+ for job in jobs:
+ jsonized_job = job.get_delta(status)
+ if len(jsonized_job['payload']) < 1:
+ continue
+ job_ser.append(jsonized_job)
+
+ return job_ser
+
+ def serialize_hosts(self, hosts):
+ host_ser = []
+ for host in hosts:
+ h = {}
+ h['interfaces'] = []
+ h['hostname'] = host.name
+ h['host_type'] = host.profile.name
+ for iface in host.interfaces.all():
+ eth = {}
+ eth['mac'] = iface.mac_address
+ eth['busaddr'] = iface.bus_address
+ eth['name'] = iface.name
+ eth['switchport'] = {"switch_name": iface.switch_name, "port_name": iface.port_name}
+ h['interfaces'].append(eth)
+ return host_ser
+
+ def serialize_images(self, images):
+ images_ser = []
+ for image in images:
+ images_ser.append(
+ {
+ "name": image.name,
+ "lab_id": image.lab_id,
+ "dashboard_id": image.id
+ }
+ )
+ return images_ser
+
+ def serialize_host_profiles(self, profiles):
+ profile_ser = []
+ for profile in profiles:
+ p = {}
+ p['cpu'] = {
+ "cores": profile.cpuprofile.first().cores,
+ "arch": profile.cpuprofile.first().architecture,
+ "cpus": profile.cpuprofile.first().cpus,
+ }
+ p['disks'] = []
+ for disk in profile.storageprofile.all():
+ d = {
+ "size": disk.size,
+ "type": disk.media_type,
+ "name": disk.name
+ }
+ p['disks'].append(d)
+ p['description'] = profile.description
+ p['interfaces'] = []
+ for iface in profile.interfaceprofile.all():
+ p['interfaces'].append(
+ {
+ "speed": iface.speed,
+ "name": iface.name
+ }
+ )
+
+ p['ram'] = {"amount": profile.ramprofile.first().amount}
+ p['name'] = profile.name
+ profile_ser.append(p)
+ return profile_ser
+
+
+class Job(models.Model):
+ """
+ This is the class that is serialized and put into the api
+ """
+ booking = models.OneToOneField(Booking, on_delete=models.CASCADE, null=True)
+ status = models.IntegerField(default=JobStatus.NEW)
+ complete = models.BooleanField(default=False)
+
+ def to_dict(self):
+ d = {}
+ j = {}
+ j['id'] = self.id
+ for relation in AccessRelation.objects.filter(job=self):
+ if 'access' not in d:
+ d['access'] = {}
+ d['access'][relation.task_id] = relation.config.to_dict()
+ for relation in SoftwareRelation.objects.filter(job=self):
+ if 'software' not in d:
+ d['software'] = {}
+ d['software'][relation.task_id] = relation.config.to_dict()
+ for relation in HostHardwareRelation.objects.filter(job=self):
+ if 'hardware' not in d:
+ d['hardware'] = {}
+ d['hardware'][relation.task_id] = relation.config.to_dict()
+ for relation in HostNetworkRelation.objects.filter(job=self):
+ if 'network' not in d:
+ d['network'] = {}
+ d['network'][relation.task_id] = relation.config.to_dict()
+ for relation in SnapshotRelation.objects.filter(job=self):
+ if 'snapshot' not in d:
+ d['snapshot'] = {}
+ d['snapshot'][relation.task_id] = relation.config.to_dict()
+
+ j['payload'] = d
+
+ return j
+
+ def get_tasklist(self, status="all"):
+ tasklist = []
+ clist = [
+ HostHardwareRelation,
+ AccessRelation,
+ HostNetworkRelation,
+ SoftwareRelation,
+ SnapshotRelation
+ ]
+ if status == "all":
+ for cls in clist:
+ tasklist += list(cls.objects.filter(job=self))
+ else:
+ for cls in clist:
+ tasklist += list(cls.objects.filter(job=self).filter(status=status))
+ return tasklist
+
+ def is_fulfilled(self):
+ """
+ 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 = {}
+ j = {}
+ j['id'] = self.id
+ for relation in AccessRelation.objects.filter(job=self).filter(status=status):
+ if 'access' not in d:
+ d['access'] = {}
+ d['access'][relation.task_id] = relation.config.get_delta()
+ for relation in SoftwareRelation.objects.filter(job=self).filter(status=status):
+ if 'software' not in d:
+ d['software'] = {}
+ d['software'][relation.task_id] = relation.config.get_delta()
+ for relation in HostHardwareRelation.objects.filter(job=self).filter(status=status):
+ if 'hardware' not in d:
+ d['hardware'] = {}
+ d['hardware'][relation.task_id] = relation.config.get_delta()
+ for relation in HostNetworkRelation.objects.filter(job=self).filter(status=status):
+ if 'network' not in d:
+ d['network'] = {}
+ d['network'][relation.task_id] = relation.config.get_delta()
+ for relation in SnapshotRelation.objects.filter(job=self).filter(status=status):
+ if 'snapshot' not in d:
+ d['snapshot'] = {}
+ d['snapshot'][relation.task_id] = relation.config.get_delta()
+
+ j['payload'] = d
+ return j
+
+ def to_json(self):
+ return json.dumps(self.to_dict())
+
+
+class TaskConfig(models.Model):
+ def to_dict(self):
+ pass
+
+ def get_delta(self):
+ pass
+
+ def to_json(self):
+ return json.dumps(self.to_dict())
+
+ def clear_delta(self):
+ self.delta = '{}'
+
+
+class 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 = self.interfaces.first().host.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 OpnfvApiConfig(models.Model):
+
+ installer = models.CharField(max_length=200)
+ scenario = models.CharField(max_length=300)
+ roles = models.ManyToManyField(Host)
+ # 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):
+ if not self.delta:
+ self.delta = self.to_json()
+ self.save()
+ return json.loads(self.delta)
+
+
+class AccessConfig(TaskConfig):
+ access_type = models.CharField(max_length=50)
+ user = models.ForeignKey(User, on_delete=models.CASCADE)
+ revoke = models.BooleanField(default=False)
+ context = models.TextField(default="")
+ delta = models.TextField(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):
+ if not self.delta:
+ self.delta = self.to_json()
+ self.save()
+ d = json.loads(self.delta)
+ d["lab_token"] = self.accessrelation.lab_token
+
+ return d
+
+ def to_json(self):
+ return json.dumps(self.to_dict())
+
+ def clear_delta(self):
+ d = {}
+ d["lab_token"] = self.accessrelation.lab_token
+ self.delta = json.dumps(d)
+
+ def set_access_type(self, access_type):
+ self.access_type = access_type
+ d = json.loads(self.delta)
+ d['access_type'] = access_type
+ self.delta = json.dumps(d)
+
+ def set_user(self, user):
+ self.user = user
+ d = json.loads(self.delta)
+ d['user'] = self.user.id
+ self.delta = json.dumps(d)
+
+ def set_revoke(self, revoke):
+ self.revoke = revoke
+ d = json.loads(self.delta)
+ d['revoke'] = revoke
+ self.delta = json.dumps(d)
+
+ def set_context(self, context):
+ self.context = json.dumps(context)
+ d = json.loads(self.delta)
+ d['context'] = context
+ self.delta = json.dumps(d)
+
+
+class SoftwareConfig(TaskConfig):
+ """
+ handled opnfv installations, etc
+ """
+ opnfv = models.ForeignKey(OpnfvApiConfig, on_delete=models.CASCADE)
+
+ def to_dict(self):
+ d = {}
+ if self.opnfv:
+ d['opnfv'] = self.opnfv.to_dict()
+
+ d["lab_token"] = self.softwarerelation.lab_token
+ self.delta = json.dumps(d)
+
+ return d
+
+ def get_delta(self):
+ d = {}
+ d['opnfv'] = self.opnfv.get_delta()
+ d['lab_token'] = self.softwarerelation.lab_token
+
+ return d
+
+ def clear_delta(self):
+ self.opnfv.clear_delta()
+
+ def to_json(self):
+ return json.dumps(self.to_dict())
+
+
+class HardwareConfig(TaskConfig):
+ """
+ handles imaging, user accounts, etc
+ """
+ image = models.CharField(max_length=100, default="defimage")
+ power = models.CharField(max_length=100, default="off")
+ hostname = models.CharField(max_length=100, default="hostname")
+ ipmi_create = models.BooleanField(default=False)
+ delta = models.TextField()
+
+ def to_dict(self):
+ d = {}
+ d['image'] = self.image
+ d['power'] = self.power
+ d['hostname'] = self.hostname
+ d['ipmi_create'] = str(self.ipmi_create)
+ d['id'] = self.hosthardwarerelation.host.labid
+ return d
+
+ def to_json(self):
+ return json.dumps(self.to_dict())
+
+ def get_delta(self):
+ if not self.delta:
+ self.delta = self.to_json()
+ self.save()
+ d = json.loads(self.delta)
+ d['lab_token'] = self.hosthardwarerelation.lab_token
+ return d
+
+ def clear_delta(self):
+ d = {}
+ d["id"] = self.hosthardwarerelation.host.labid
+ d["lab_token"] = self.hosthardwarerelation.lab_token
+ self.delta = json.dumps(d)
+
+ def set_image(self, image):
+ self.image = image
+ d = json.loads(self.delta)
+ d['image'] = self.image
+ self.delta = json.dumps(d)
+
+ def set_power(self, power):
+ self.power = power
+ d = json.loads(self.delta)
+ d['power'] = power
+ self.delta = json.dumps(d)
+
+ def set_hostname(self, hostname):
+ self.hostname = hostname
+ d = json.loads(self.delta)
+ d['hostname'] = hostname
+ self.delta = json.dumps(d)
+
+ def set_ipmi_create(self, ipmi_create):
+ self.ipmi_create = ipmi_create
+ d = json.loads(self.delta)
+ d['ipmi_create'] = ipmi_create
+ self.delta = json.dumps(d)
+
+
+class NetworkConfig(TaskConfig):
+ """
+ handles network configuration
+ """
+ interfaces = models.ManyToManyField(Interface)
+ delta = models.TextField()
+
+ def to_dict(self):
+ d = {}
+ hid = self.hostnetworkrelation.host.labid
+ d[hid] = {}
+ for interface in self.interfaces.all():
+ d[hid][interface.mac_address] = []
+ for vlan in interface.config.all():
+ d[hid][interface.mac_address].append({"vlan_id": vlan.vlan_id, "tagged": vlan.tagged})
+
+ return d
+
+ def to_json(self):
+ return json.dumps(self.to_dict())
+
+ def get_delta(self):
+ if not self.delta:
+ self.delta = self.to_json()
+ self.save()
+ d = json.loads(self.delta)
+ d['lab_token'] = self.hostnetworkrelation.lab_token
+ return d
+
+ def clear_delta(self):
+ 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.host.labid
+ if hid not in d:
+ d[hid] = {}
+ d[hid][interface.mac_address] = []
+ for vlan in interface.config.all():
+ d[hid][interface.mac_address].append({"vlan_id": vlan.vlan_id, "tagged": vlan.tagged})
+ self.delta = json.dumps(d)
+
+
+class SnapshotConfig(TaskConfig):
+
+ host = models.ForeignKey(Host, null=True, on_delete=models.DO_NOTHING)
+ image = models.IntegerField(null=True)
+ 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):
+ if not self.delta:
+ self.delta = self.to_json()
+ self.save()
+
+ d = json.loads(self.delta)
+ 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 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):
+ status = models.IntegerField(default=JobStatus.NEW)
+ job = models.ForeignKey(Job, on_delete=models.CASCADE)
+ config = models.OneToOneField(TaskConfig, on_delete=models.CASCADE)
+ task_id = models.CharField(default=get_task_uuid, max_length=37)
+ lab_token = models.CharField(default="null", max_length=50)
+ message = models.TextField(default="")
+
+ def delete(self, *args, **kwargs):
+ self.config.delete()
+ return super(self.__class__, self).delete(*args, **kwargs)
+
+ def type_str(self):
+ return "Generic Task"
+
+ class Meta:
+ abstract = True
+
+
+class AccessRelation(TaskRelation):
+ config = models.OneToOneField(AccessConfig, on_delete=models.CASCADE)
+
+ def type_str(self):
+ return "Access Task"
+
+ def delete(self, *args, **kwargs):
+ self.config.delete()
+ return super(self.__class__, self).delete(*args, **kwargs)
+
+
+class SoftwareRelation(TaskRelation):
+ config = models.OneToOneField(SoftwareConfig, on_delete=models.CASCADE)
+
+ def type_str(self):
+ return "Software Configuration Task"
+
+ def delete(self, *args, **kwargs):
+ self.config.delete()
+ return super(self.__class__, self).delete(*args, **kwargs)
+
+
+class HostHardwareRelation(TaskRelation):
+ host = models.ForeignKey(Host, on_delete=models.CASCADE)
+ config = models.OneToOneField(HardwareConfig, on_delete=models.CASCADE)
+
+ def type_str(self):
+ return "Hardware Configuration Task"
+
+ def get_delta(self):
+ return self.config.to_dict()
+
+ def delete(self, *args, **kwargs):
+ self.config.delete()
+ return super(self.__class__, self).delete(*args, **kwargs)
+
+
+class HostNetworkRelation(TaskRelation):
+ host = models.ForeignKey(Host, on_delete=models.CASCADE)
+ config = models.OneToOneField(NetworkConfig, on_delete=models.CASCADE)
+
+ def type_str(self):
+ return "Network Configuration Task"
+
+ def delete(self, *args, **kwargs):
+ self.config.delete()
+ return super(self.__class__, self).delete(*args, **kwargs)
+
+
+class SnapshotRelation(TaskRelation):
+ snapshot = models.ForeignKey(Image, on_delete=models.CASCADE)
+ config = models.OneToOneField(SnapshotConfig, on_delete=models.CASCADE)
+
+ 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 JobFactory(object):
+
+ @classmethod
+ def reimageHost(cls, new_image, booking, host):
+ """
+ This method will make all necessary changes to make a lab
+ reimage a host.
+ """
+ job = Job.objects.get(booking=booking)
+ # make hardware task new
+ hardware_relation = HostHardwareRelation.objects.get(host=host, job=job)
+ hardware_relation.config.set_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(host=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 makeCompleteJob(cls, booking):
+ hosts = Host.objects.filter(bundle=booking.resource)
+ job = None
+ try:
+ job = Job.objects.get(booking=booking)
+ except Exception:
+ job = Job.objects.create(status=JobStatus.NEW, booking=booking)
+ cls.makeHardwareConfigs(
+ hosts=hosts,
+ job=job
+ )
+ cls.makeNetworkConfigs(
+ hosts=hosts,
+ job=job
+ )
+ cls.makeSoftware(
+ booking=booking,
+ 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": [host.labid for host in hosts]
+ }
+ )
+ except Exception:
+ continue
+
+ @classmethod
+ def makeHardwareConfigs(cls, hosts=[], job=Job()):
+ for host in hosts:
+ hardware_config = None
+ try:
+ hardware_config = HardwareConfig.objects.get(relation__host=host)
+ except Exception:
+ hardware_config = HardwareConfig()
+
+ relation = HostHardwareRelation()
+ relation.host = host
+ relation.job = job
+ relation.config = hardware_config
+ relation.config.save()
+ relation.config = relation.config
+ relation.save()
+
+ hardware_config.clear_delta()
+ hardware_config.set_image(host.config.image.lab_id)
+ hardware_config.set_hostname(host.template.resource.name)
+ hardware_config.set_power("on")
+ hardware_config.set_ipmi_create(True)
+ hardware_config.save()
+
+ @classmethod
+ def makeAccessConfig(cls, users, access_type, revoke=False, job=Job(), context=False):
+ 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, hosts=[], job=Job()):
+ for host in hosts:
+ network_config = None
+ try:
+ network_config = NetworkConfig.objects.get(relation__host=host)
+ except Exception:
+ network_config = NetworkConfig.objects.create()
+
+ relation = HostNetworkRelation()
+ relation.host = host
+ relation.job = job
+ network_config.save()
+ relation.config = network_config
+ relation.save()
+ network_config.clear_delta()
+
+ for interface in host.interfaces.all():
+ network_config.add_interface(interface)
+ network_config.save()
+
+ @classmethod
+ def make_bridge_config(cls, booking):
+ if booking.resource.hosts.count() < 2:
+ return None
+ try:
+ jumphost_config = HostOPNFVConfig.objects.filter(
+ role__name__iexact="jumphost"
+ )
+ jumphost = Host.objects.get(
+ bundle=booking.resource,
+ config=jumphost_config.host_config
+ )
+ 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()):
+
+ 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.hosts.all():
+ 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
diff --git a/dashboard/src/api/serializers.py b/dashboard/src/api/serializers.py
deleted file mode 100644
index 10e1975..0000000
--- a/dashboard/src/api/serializers.py
+++ /dev/null
@@ -1,53 +0,0 @@
-##############################################################################
-# Copyright (c) 2016 Max Breitenfeldt and others.
-#
-# All rights reserved. This program and the accompanying materials
-# are made available under the terms of the Apache License, Version 2.0
-# which accompanies this distribution, and is available at
-# http://www.apache.org/licenses/LICENSE-2.0
-##############################################################################
-
-
-from rest_framework import serializers
-
-from account.models import UserProfile
-from notifier.models import Notifier
-from booking.models import Booking
-from dashboard.models import Server, Resource, ResourceStatus
-
-class BookingSerializer(serializers.ModelSerializer):
- installer_name = serializers.CharField(source='installer.name')
- scenario_name = serializers.CharField(source='scenario.name')
- opsys_name = serializers.CharField(source='opsys.name')
-
- class Meta:
- model = Booking
- fields = ('id', 'changeid', 'reset', 'user', 'resource_id', 'opsys_name', 'start', 'end', 'installer_name', 'scenario_name', 'purpose')
-
-
-class ServerSerializer(serializers.ModelSerializer):
- class Meta:
- model = Server
- fields = ('id', 'resource_id', 'name', 'model', 'cpu', 'ram', 'storage')
-
-
-class ResourceSerializer(serializers.ModelSerializer):
- class Meta:
- model = Resource
- fields = ('id', 'name', 'description', 'resource_lab', 'url', 'server_set', 'dev_pod')
-
-class ResourceStatusSerializer(serializers.ModelSerializer):
- class Meta:
- model = ResourceStatus
- fields = ('id', 'resource', 'timestamp','type', 'title', 'content')
-
-class NotifierSerializer(serializers.ModelSerializer):
- class Meta:
- model = Notifier
- fields = ('id', 'title', 'content', 'user', 'sender', 'message_type', 'msg_sent')
-
-class UserSerializer(serializers.ModelSerializer):
- username = serializers.CharField(source='user.username')
- class Meta:
- model = UserProfile
- fields = ('user', 'username', 'ssh_public_key', 'pgp_public_key', 'email_addr')
diff --git a/dashboard/src/jenkins/__init__.py b/dashboard/src/api/serializers/__init__.py
index b5914ce..e0408fa 100644
--- a/dashboard/src/jenkins/__init__.py
+++ b/dashboard/src/api/serializers/__init__.py
@@ -1,10 +1,8 @@
##############################################################################
-# Copyright (c) 2016 Max Breitenfeldt and others.
+# Copyright (c) 2018 Parker Berberian, Sawyer Bergeron, and others.
#
# All rights reserved. This program and the accompanying materials
# are made available under the terms of the Apache License, Version 2.0
# which accompanies this distribution, and is available at
# http://www.apache.org/licenses/LICENSE-2.0
##############################################################################
-
-
diff --git a/dashboard/src/api/serializers/booking_serializer.py b/dashboard/src/api/serializers/booking_serializer.py
new file mode 100644
index 0000000..9b5c059
--- /dev/null
+++ b/dashboard/src/api/serializers/booking_serializer.py
@@ -0,0 +1,175 @@
+##############################################################################
+# 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 (
+ HostConfiguration,
+ CpuProfile,
+ DiskProfile,
+ InterfaceProfile,
+ RamProfile,
+ Image,
+ Interface
+)
+
+
+class BookingField(serializers.Field):
+
+ def to_representation(self, booking):
+ """
+ Takes in a booking object.
+ Returns a dictionary of primitives representing that booking
+ """
+ ser = {}
+ ser['id'] = booking.id
+ # main loop to grab relevant info out of booking
+ host_configs = {} # mapping hostname -> config
+ networks = {} # mapping vlan id -> network_hosts
+ for host in booking.resource.hosts.all():
+ host_configs[host.name] = HostConfiguration.objects.get(host=host.template)
+ if "jumphost" not in ser and host_configs[host.name].opnfvRole.name.lower() == "jumphost":
+ ser['jumphost'] = host.name
+ # host is a Host model
+ for i in range(len(host.interfaces.all())):
+ interface = host.interfaces.all()[i]
+ # interface is an Interface model
+ for vlan in interface.config.all():
+ # vlan is Vlan model
+ if vlan.id not in networks:
+ networks[vlan.id] = []
+ net_host = {"hostname": host.name, "tagged": vlan.tagged, "interface": i}
+ networks[vlan.id].append(net_host)
+ # creates networking object of proper form
+ networking = []
+ for vlanid in networks:
+ network = {}
+ network['vlan_id'] = vlanid
+ network['hosts'] = networks[vlanid]
+
+ ser['networking'] = networking
+
+ # creates hosts object of correct form
+ hosts = []
+ for hostname in host_configs:
+ host = {"hostname": hostname}
+ host['deploy_image'] = True # TODO?
+ image = host_configs[hostname].image
+ host['image'] = {
+ "name": image.name,
+ "lab_id": image.lab_id,
+ "dashboard_id": image.id
+ }
+ hosts.append(host)
+
+ ser['hosts'] = hosts
+
+ return ser
+
+ def to_internal_value(self, data):
+ """
+ Takes in a dictionary of primitives
+ Returns a booking object
+
+ This is not going to be implemented or allowed.
+ If someone needs to create a booking through the api,
+ they will send a different booking object
+ """
+ return None
+
+
+class BookingSerializer(serializers.Serializer):
+
+ booking = BookingField()
+
+
+# Host Type stuff, for inventory
+class CPUSerializer(serializers.ModelSerializer):
+ class Meta:
+ model = CpuProfile
+ fields = ('cores', 'architecture', 'cpus')
+
+
+class DiskSerializer(serializers.ModelSerializer):
+ class Meta:
+ model = DiskProfile
+ fields = ('size', 'media_type', 'name')
+
+
+class InterfaceProfileSerializer(serializers.ModelSerializer):
+ class Meta:
+ model = InterfaceProfile
+ fields = ('speed', 'name')
+
+
+class RamSerializer(serializers.ModelSerializer):
+ class Meta:
+ model = RamProfile
+ fields = ('amount', 'channels')
+
+
+class HostTypeSerializer(serializers.Serializer):
+ name = serializers.CharField(max_length=200)
+ ram = RamSerializer()
+ interface = InterfaceProfileSerializer()
+ description = serializers.CharField(max_length=1000)
+ disks = DiskSerializer()
+ cpu = CPUSerializer()
+
+
+# the rest of the inventory stuff
+class NetworkSerializer(serializers.Serializer):
+ cidr = serializers.CharField(max_length=200)
+ gateway = serializers.IPAddressField(max_length=200)
+ vlan = serializers.IntegerField()
+
+
+class ImageSerializer(serializers.ModelSerializer):
+ lab_id = serializers.IntegerField()
+ id = serializers.IntegerField(source="dashboard_id")
+ name = serializers.CharField(max_length=50)
+ description = serializers.CharField(max_length=200)
+
+ class Meta:
+ model = Image
+
+
+class InterfaceField(serializers.Field):
+ def to_representation(self, interface):
+ pass
+
+ def to_internal_value(self, data):
+ """
+ takes in a serialized interface and creates an Interface model
+ """
+ mac = data['mac']
+ bus_address = data['busaddr']
+ switch_name = data['switchport']['switch_name']
+ port_name = data['switchport']['port_name']
+ # TODO config??
+ return Interface.objects.create(
+ mac_address=mac,
+ bus_address=bus_address,
+ switch_name=switch_name,
+ port_name=port_name
+ )
+
+
+class InventoryHostSerializer(serializers.Serializer):
+ hostname = serializers.CharField(max_length=100)
+ host_type = serializers.CharField(max_length=100)
+ interfaces = InterfaceField()
+
+
+class InventorySerializer(serializers.Serializer):
+ hosts = InventoryHostSerializer()
+ networks = NetworkSerializer()
+ images = ImageSerializer()
+ host_types = HostTypeSerializer()
diff --git a/dashboard/config/postgres/docker-entrypoint-initdb.d/pharos_dashboard.sh b/dashboard/src/api/serializers/old_serializers.py
index 526228a..0944881 100755..100644
--- a/dashboard/config/postgres/docker-entrypoint-initdb.d/pharos_dashboard.sh
+++ b/dashboard/src/api/serializers/old_serializers.py
@@ -8,7 +8,14 @@
##############################################################################
-#!/bin/env bash
+from rest_framework import serializers
-psql -U postgres -c "CREATE USER $DB_USER PASSWORD '$DB_PASS'"
-psql -U postgres -c "CREATE DATABASE $DB_NAME OWNER $DB_USER"
+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/dashboard/src/dashboard/migrations/__init__.py b/dashboard/src/api/tests/__init__.py
index b5914ce..2435a9f 100644
--- a/dashboard/src/dashboard/migrations/__init__.py
+++ b/dashboard/src/api/tests/__init__.py
@@ -1,10 +1,8 @@
##############################################################################
-# Copyright (c) 2016 Max Breitenfeldt and others.
+# 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/dashboard/src/api/tests/test_models_unittest.py b/dashboard/src/api/tests/test_models_unittest.py
new file mode 100644
index 0000000..e6f97a6
--- /dev/null
+++ b/dashboard/src/api/tests/test_models_unittest.py
@@ -0,0 +1,269 @@
+##############################################################################
+# 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,
+)
+
+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):
+ """
+ distributes 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.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))
+
+ self.assertEqual(relation.config.power, "on")
+ self.assertTrue(relation.config.ipmi_create)
+ # TODO: the rest of hwconf attrs
+
+ self.assertEqual(len(host_set), 0)
diff --git a/dashboard/src/api/urls.py b/dashboard/src/api/urls.py
index c2cd510..7a48425 100644
--- a/dashboard/src/api/urls.py
+++ b/dashboard/src/api/urls.py
@@ -1,5 +1,6 @@
##############################################################################
# Copyright (c) 2016 Max Breitenfeldt and others.
+# Copyright (c) 2018 Sawyer Bergeron, Parker Berberian, and others
#
# All rights reserved. This program and the accompanying materials
# are made available under the terms of the Apache License, Version 2.0
@@ -24,19 +25,44 @@ Including another URLconf
2. Add a URL to urlpatterns: url(r'^blog/', include('blog.urls'))
"""
from django.conf.urls import url, include
+from django.urls import path
from rest_framework import routers
-from api.views import *
+from api.views import (
+ BookingViewSet,
+ UserViewSet,
+ lab_profile,
+ lab_status,
+ lab_inventory,
+ specific_job,
+ specific_task,
+ new_jobs,
+ current_jobs,
+ done_jobs,
+ update_host_bmc,
+ lab_host,
+ get_pdf,
+ get_idf,
+ GenerateTokenView
+)
router = routers.DefaultRouter()
-router.register(r'resources', ResourceViewSet)
-router.register(r'servers', ServerViewSet)
router.register(r'bookings', BookingViewSet)
-router.register(r'resource_status', ResourceStatusViewSet)
-router.register(r'notifier', NotifierViewSet)
router.register(r'user', UserViewSet)
urlpatterns = [
url(r'^', include(router.urls)),
+ path('labs/<slug:lab_name>/profile', lab_profile),
+ path('labs/<slug:lab_name>/status', lab_status),
+ path('labs/<slug:lab_name>/inventory', lab_inventory),
+ path('labs/<slug:lab_name>/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/new', new_jobs),
+ path('labs/<slug:lab_name>/jobs/current', current_jobs),
+ path('labs/<slug:lab_name>/jobs/done', done_jobs),
url(r'^token$', GenerateTokenView.as_view(), name='generate_token'),
]
diff --git a/dashboard/src/api/views.py b/dashboard/src/api/views.py
index b873ef1..fb28958 100644
--- a/dashboard/src/api/views.py
+++ b/dashboard/src/api/views.py
@@ -1,5 +1,6 @@
##############################################################################
# Copyright (c) 2016 Max Breitenfeldt and others.
+# Copyright (c) 2018 Parker Berberian, Sawyer Bergeron, and others.
#
# All rights reserved. This program and the accompanying materials
# are made available under the terms of the Apache License, Version 2.0
@@ -8,17 +9,21 @@
##############################################################################
-from django.contrib.auth.decorators import login_required, user_passes_test
+from django.contrib.auth.decorators import login_required
from django.shortcuts import redirect
from django.utils.decorators import method_decorator
from django.views import View
+from django.http.response import JsonResponse, HttpResponse
from rest_framework import viewsets
from rest_framework.authtoken.models import Token
+from django.views.decorators.csrf import csrf_exempt
-from api.serializers import *
+from api.serializers.booking_serializer import BookingSerializer
+from api.serializers.old_serializers import UserSerializer
from account.models import UserProfile
from booking.models import Booking
-from dashboard.models import Resource, Server, ResourceStatus
+from api.models import LabManagerTracker, get_task
+from notifier.manager import NotificationHandler
class BookingViewSet(viewsets.ModelViewSet):
@@ -27,29 +32,11 @@ class BookingViewSet(viewsets.ModelViewSet):
filter_fields = ('resource', 'id')
-class ServerViewSet(viewsets.ModelViewSet):
- queryset = Server.objects.all()
- serializer_class = ServerSerializer
- filter_fields = ('resource', 'name')
-
-
-class ResourceViewSet(viewsets.ModelViewSet):
- queryset = Resource.objects.all()
- serializer_class = ResourceSerializer
- filter_fields = ('name', 'id')
-
-class ResourceStatusViewSet(viewsets.ModelViewSet):
- queryset = ResourceStatus.objects.all()
- serializer_class = ResourceStatusSerializer
-
-class NotifierViewSet(viewsets.ModelViewSet):
- queryset = Notifier.objects.none()
- serializer_class = NotifierSerializer
-
class UserViewSet(viewsets.ModelViewSet):
queryset = UserProfile.objects.all()
serializer_class = UserSerializer
+
@method_decorator(login_required, name='dispatch')
class GenerateTokenView(View):
def get(self, request, *args, **kwargs):
@@ -59,3 +46,111 @@ class GenerateTokenView(View):
token.delete()
Token.objects.create(user=user)
return redirect('account:settings')
+
+
+def lab_inventory(request, lab_name=""):
+ lab_token = request.META.get('HTTP_AUTH_TOKEN')
+ lab_manager = LabManagerTracker.get(lab_name, lab_token)
+ return JsonResponse(lab_manager.get_inventory(), safe=False)
+
+
+@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)
+
+
+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)
+
+
+@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)
+
+
+def new_jobs(request, lab_name=""):
+ lab_token = request.META.get('HTTP_AUTH_TOKEN')
+ lab_manager = LabManagerTracker.get(lab_name, lab_token)
+ return JsonResponse(lab_manager.get_new_jobs(), safe=False)
+
+
+def current_jobs(request, lab_name=""):
+ lab_token = request.META.get('HTTP_AUTH_TOKEN')
+ lab_manager = LabManagerTracker.get(lab_name, lab_token)
+ return JsonResponse(lab_manager.get_current_jobs(), safe=False)
+
+
+def done_jobs(request, lab_name=""):
+ lab_token = request.META.get('HTTP_AUTH_TOKEN')
+ lab_manager = LabManagerTracker.get(lab_name, lab_token)
+ return JsonResponse(lab_manager.get_done_jobs(), safe=False)
diff --git a/dashboard/src/booking/__init__.py b/dashboard/src/booking/__init__.py
index b5914ce..b6fef6c 100644
--- a/dashboard/src/booking/__init__.py
+++ b/dashboard/src/booking/__init__.py
@@ -6,5 +6,3 @@
# which accompanies this distribution, and is available at
# http://www.apache.org/licenses/LICENSE-2.0
##############################################################################
-
-
diff --git a/dashboard/src/booking/admin.py b/dashboard/src/booking/admin.py
index 51e1031..162777e 100644
--- a/dashboard/src/booking/admin.py
+++ b/dashboard/src/booking/admin.py
@@ -1,5 +1,6 @@
##############################################################################
# Copyright (c) 2016 Max Breitenfeldt and others.
+# Copyright (c) 2018 Parker Berberian, Sawyer Bergeron, and others.
#
# All rights reserved. This program and the accompanying materials
# are made available under the terms of the Apache License, Version 2.0
@@ -10,9 +11,6 @@
from django.contrib import admin
-from booking.models import *
+from booking.models import Booking
admin.site.register(Booking)
-admin.site.register(Opsys)
-admin.site.register(Installer)
-admin.site.register(Scenario)
diff --git a/dashboard/src/booking/forms.py b/dashboard/src/booking/forms.py
index 9d71b42..df88cc6 100644
--- a/dashboard/src/booking/forms.py
+++ b/dashboard/src/booking/forms.py
@@ -1,65 +1,104 @@
##############################################################################
-# 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 django.forms as forms
+from django.forms.widgets import NumberInput
+
+from workflow.forms import (
+ MultipleSelectFilterField,
+ MultipleSelectFilterWidget,
+ FormUtils)
+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
-from booking.models import Installer, Scenario, Opsys
-from datetime import datetime
-class BookingForm(forms.Form):
- fields = ['start', 'end', 'purpose', 'opsys', 'reset', 'installer', 'scenario']
+class QuickBookingForm(forms.Form):
+ purpose = forms.CharField(max_length=1000)
+ project = forms.CharField(max_length=400)
+ hostname = forms.CharField(max_length=400)
- start = forms.DateTimeField()
- end = forms.DateTimeField()
- reset = forms.ChoiceField(choices = ((True, 'Yes'),(False, 'No')), label="Reset System", initial='False', required=False)
- purpose = forms.CharField(max_length=300)
- opsys = forms.ModelChoiceField(queryset=Opsys.objects.all(), required=False)
- opsys.label = "Operating System"
installer = forms.ModelChoiceField(queryset=Installer.objects.all(), required=False)
scenario = forms.ModelChoiceField(queryset=Scenario.objects.all(), required=False)
-class BookingEditForm(forms.Form):
- fields = ['start', 'end', 'purpose', 'opsys', 'reset', 'installer', 'scenario']
+ def __init__(self, data=None, user=None, *args, **kwargs):
+ if "default_user" in kwargs:
+ default_user = kwargs.pop("default_user")
+ else:
+ default_user = "you"
+ self.default_user = default_user
- start = forms.DateTimeField()
- end = forms.DateTimeField()
- purpose = forms.CharField(max_length=300)
- opsys = forms.ModelChoiceField(queryset=Opsys.objects.all(), required=False)
- installer = forms.ModelChoiceField(queryset=Installer.objects.all(), required=False)
- scenario = forms.ModelChoiceField(queryset=Scenario.objects.all(), required=False)
- reset = forms.ChoiceField(choices = ((True, 'Yes'),(False, 'No')), label="Reset System", initial='False', required=True)
-
-
- def __init__(self, *args, **kwargs ):
- cloned_kwargs = {}
- cloned_kwargs['purpose'] = kwargs.pop('purpose')
- cloned_kwargs['start'] = kwargs.pop('start')
- cloned_kwargs['end'] = kwargs.pop('end')
- if 'installer' in kwargs:
- cloned_kwargs['installer'] = kwargs.pop('installer')
- if 'scenario' in kwargs:
- cloned_kwargs['scenario'] = kwargs.pop('scenario')
- super(BookingEditForm, self).__init__( *args, **kwargs)
-
- self.fields['purpose'].initial = cloned_kwargs['purpose']
- self.fields['start'].initial = cloned_kwargs['start'].strftime('%m/%d/%Y %H:%M')
- self.fields['end'].initial = cloned_kwargs['end'].strftime('%m/%d/%Y %H:%M')
- try:
- self.fields['installer'].initial = cloned_kwargs['installer'].id
- except KeyError:
- pass
- except AttributeError:
- pass
+ super(QuickBookingForm, self).__init__(data=data, **kwargs)
+
+ self.fields["image"] = forms.ModelChoiceField(
+ Image.objects.filter(public=True) | Image.objects.filter(owner=user)
+ )
+
+ self.fields['users'] = SearchableSelectMultipleField(
+ queryset=UserProfile.objects.select_related('user').exclude(user=user),
+ items=get_user_items(exclude=user),
+ required=False,
+ **get_user_field_opts()
+ )
+
+ attrs = FormUtils.getLabData(0)
+ self.fields['filter_field'] = MultipleSelectFilterField(widget=MultipleSelectFilterWidget(**attrs))
+ self.fields['length'] = forms.IntegerField(
+ widget=NumberInput(
+ attrs={
+ "type": "range",
+ 'min': "1",
+ "max": "21",
+ "value": "1"
+ }
+ )
+ )
+
+ def build_user_list(self):
+ """
+ returns a mapping of UserProfile ids to displayable objects expected by
+ searchable multiple select widget
+ """
try:
- self.fields['scenario'].initial = cloned_kwargs['scenario'].id
- except KeyError:
- pass
- except AttributeError:
+ 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/dashboard/src/booking/lib.py b/dashboard/src/booking/lib.py
new file mode 100644
index 0000000..8132c75
--- /dev/null
+++ b/dashboard/src/booking/lib.py
@@ -0,0 +1,36 @@
+##############################################################################
+# 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.select_related('user').exclude(user=exclude)
+ items = {}
+ for up in qs:
+ item = {
+ 'id': up.id,
+ 'expanded_name': up.full_name,
+ 'small_name': up.user.username,
+ 'string': up.email_addr
+ }
+ items[up.id] = item
+ return items
diff --git a/dashboard/src/booking/migrations/0001_initial.py b/dashboard/src/booking/migrations/0001_initial.py
index 6932dae..20415fe 100644
--- a/dashboard/src/booking/migrations/0001_initial.py
+++ b/dashboard/src/booking/migrations/0001_initial.py
@@ -1,6 +1,4 @@
-# -*- coding: utf-8 -*-
-# Generated by Django 1.10 on 2016-11-03 13:33
-from __future__ import unicode_literals
+# Generated by Django 2.1 on 2018-09-14 14:48
from django.conf import settings
from django.db import migrations, models
@@ -12,8 +10,9 @@ class Migration(migrations.Migration):
initial = True
dependencies = [
- ('dashboard', '0001_initial'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
+ ('account', '0001_initial'),
+ ('resource_inventory', '__first__'),
]
operations = [
@@ -23,9 +22,17 @@ class Migration(migrations.Migration):
('id', models.AutoField(primary_key=True, serialize=False)),
('start', models.DateTimeField()),
('end', models.DateTimeField()),
- ('jira_issue_id', models.IntegerField(null=True)),
- ('jira_issue_status', models.CharField(max_length=50)),
+ ('reset', models.BooleanField(default=False)),
+ ('jira_issue_id', models.IntegerField(blank=True, null=True)),
+ ('jira_issue_status', models.CharField(blank=True, max_length=50)),
('purpose', models.CharField(max_length=300)),
+ ('ext_count', models.IntegerField(default=2)),
+ ('project', models.CharField(blank=True, default='', max_length=100, null=True)),
+ ('collaborators', models.ManyToManyField(related_name='collaborators', to=settings.AUTH_USER_MODEL)),
+ ('config_bundle', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='resource_inventory.ConfigBundle')),
+ ('lab', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='account.Lab')),
+ ('owner', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='owner', to=settings.AUTH_USER_MODEL)),
+ ('resource', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='resource_inventory.ResourceBundle')),
],
options={
'db_table': 'booking',
@@ -39,6 +46,14 @@ class Migration(migrations.Migration):
],
),
migrations.CreateModel(
+ name='Opsys',
+ fields=[
+ ('id', models.AutoField(primary_key=True, serialize=False)),
+ ('name', models.CharField(max_length=100)),
+ ('sup_installers', models.ManyToManyField(blank=True, to='booking.Installer')),
+ ],
+ ),
+ migrations.CreateModel(
name='Scenario',
fields=[
('id', models.AutoField(primary_key=True, serialize=False)),
@@ -46,23 +61,8 @@ class Migration(migrations.Migration):
],
),
migrations.AddField(
- model_name='booking',
- name='installer',
- field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.DO_NOTHING, to='booking.Installer'),
- ),
- migrations.AddField(
- model_name='booking',
- name='resource',
- field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='dashboard.Resource'),
- ),
- migrations.AddField(
- model_name='booking',
- name='scenario',
- field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.DO_NOTHING, to='booking.Scenario'),
- ),
- migrations.AddField(
- model_name='booking',
- name='user',
- field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL),
+ model_name='installer',
+ name='sup_scenarios',
+ field=models.ManyToManyField(blank=True, to='booking.Scenario'),
),
]
diff --git a/dashboard/src/booking/migrations/0002_booking_changeid.py b/dashboard/src/booking/migrations/0002_booking_changeid.py
deleted file mode 100644
index 33af8fd..0000000
--- a/dashboard/src/booking/migrations/0002_booking_changeid.py
+++ /dev/null
@@ -1,38 +0,0 @@
-# -*- coding: utf-8 -*-
-# Generated by Django 1.10 on 2017-12-13 15:06
-from __future__ import unicode_literals
-
-from django.db import migrations, models
-import django.db.models.deletion
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('booking', '0001_initial'),
- ]
-
- operations = [
- migrations.CreateModel(
- name='Opsys',
- fields=[
- ('id', models.AutoField(primary_key=True, serialize=False)),
- ('name', models.CharField(max_length=100)),
- ],
- ),
- migrations.AddField(
- model_name='booking',
- name='opsys',
- field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.DO_NOTHING, to='booking.Opsys'),
- ),
- migrations.AddField(
- model_name='booking',
- name='changeid',
- field=models.TextField(default='no change ID'),
- ),
- migrations.AlterField(
- model_name='booking',
- name='changeid',
- field=models.TextField(blank=True, default='no change ID', null=True),
- ),
- ]
diff --git a/dashboard/src/booking/migrations/0002_booking_pdf.py b/dashboard/src/booking/migrations/0002_booking_pdf.py
new file mode 100644
index 0000000..53232c9
--- /dev/null
+++ b/dashboard/src/booking/migrations/0002_booking_pdf.py
@@ -0,0 +1,18 @@
+# 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/dashboard/src/booking/migrations/0003_auto_20180108_2024.py b/dashboard/src/booking/migrations/0003_auto_20180108_2024.py
deleted file mode 100644
index 93cecc2..0000000
--- a/dashboard/src/booking/migrations/0003_auto_20180108_2024.py
+++ /dev/null
@@ -1,25 +0,0 @@
-# -*- coding: utf-8 -*-
-# Generated by Django 1.10 on 2018-01-08 20:24
-from __future__ import unicode_literals
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('booking', '0002_booking_changeid'),
- ]
-
- operations = [
- migrations.AddField(
- model_name='booking',
- name='reset',
- field=models.BooleanField(default=False),
- ),
- migrations.AlterField(
- model_name='booking',
- name='changeid',
- field=models.TextField(blank=True, default='initial', null=True),
- ),
- ] \ No newline at end of file
diff --git a/dashboard/src/booking/migrations/0003_auto_20190115_1733.py b/dashboard/src/booking/migrations/0003_auto_20190115_1733.py
new file mode 100644
index 0000000..70eecfe
--- /dev/null
+++ b/dashboard/src/booking/migrations/0003_auto_20190115_1733.py
@@ -0,0 +1,30 @@
+# 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/dashboard/src/booking/migrations/0004_auto_20190124_1700.py b/dashboard/src/booking/migrations/0004_auto_20190124_1700.py
new file mode 100644
index 0000000..baa32d2
--- /dev/null
+++ b/dashboard/src/booking/migrations/0004_auto_20190124_1700.py
@@ -0,0 +1,20 @@
+# 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/dashboard/src/booking/migrations/0005_booking_idf.py b/dashboard/src/booking/migrations/0005_booking_idf.py
new file mode 100644
index 0000000..31e9170
--- /dev/null
+++ b/dashboard/src/booking/migrations/0005_booking_idf.py
@@ -0,0 +1,18 @@
+# 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/dashboard/src/booking/migrations/0006_booking_opnfv_config.py b/dashboard/src/booking/migrations/0006_booking_opnfv_config.py
new file mode 100644
index 0000000..e5ffc71
--- /dev/null
+++ b/dashboard/src/booking/migrations/0006_booking_opnfv_config.py
@@ -0,0 +1,20 @@
+# 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/dashboard/src/booking/migrations/__init__.py b/dashboard/src/booking/migrations/__init__.py
index b5914ce..e69de29 100644
--- a/dashboard/src/booking/migrations/__init__.py
+++ b/dashboard/src/booking/migrations/__init__.py
@@ -1,10 +0,0 @@
-##############################################################################
-# Copyright (c) 2016 Max Breitenfeldt and others.
-#
-# All rights reserved. This program and the accompanying materials
-# are made available under the terms of the Apache License, Version 2.0
-# which accompanies this distribution, and is available at
-# http://www.apache.org/licenses/LICENSE-2.0
-##############################################################################
-
-
diff --git a/dashboard/src/booking/models.py b/dashboard/src/booking/models.py
index 9156484..9836730 100644
--- a/dashboard/src/booking/models.py
+++ b/dashboard/src/booking/models.py
@@ -1,5 +1,6 @@
##############################################################################
# Copyright (c) 2016 Max Breitenfeldt and others.
+# Copyright (c) 2018 Sawyer Bergeron, Parker Berberian, and others.
#
# All rights reserved. This program and the accompanying materials
# are made available under the terms of the Apache License, Version 2.0
@@ -8,66 +9,35 @@
##############################################################################
-from django.conf import settings
+from resource_inventory.models import ResourceBundle, ConfigBundle, OPNFVConfig
+from account.models import Lab
from django.contrib.auth.models import User
from django.db import models
-from jira import JIRA
-from jira import JIRAError
-from django.utils.crypto import get_random_string
-import hashlib
+import resource_inventory.resource_manager
-from dashboard.models import Resource
-
-
-class Installer(models.Model):
- id = models.AutoField(primary_key=True)
- name = models.CharField(max_length=30)
-
- def __str__(self):
- return self.name
-
-class Scenario(models.Model):
- id = models.AutoField(primary_key=True)
- name = models.CharField(max_length=300)
-
- def __str__(self):
- return self.name
-
-class Opsys(models.Model):
- id = models.AutoField(primary_key=True)
- name = models.CharField(max_length=100)
-
- def __str__(self):
- return self.name
class Booking(models.Model):
id = models.AutoField(primary_key=True)
- changeid = models.TextField(default='initial', blank=True, null=True)
- user = models.ForeignKey(User, models.CASCADE) # delete if user is deleted
- resource = models.ForeignKey(Resource, models.PROTECT)
+ owner = models.ForeignKey(User, on_delete=models.PROTECT, related_name='owner')
+ collaborators = models.ManyToManyField(User, related_name='collaborators')
start = models.DateTimeField()
end = models.DateTimeField()
reset = models.BooleanField(default=False)
- jira_issue_id = models.IntegerField(null=True)
- jira_issue_status = models.CharField(max_length=50)
-
- opsys = models.ForeignKey(Opsys, models.DO_NOTHING, null=True)
- installer = models.ForeignKey(Installer, models.DO_NOTHING, null=True)
- scenario = models.ForeignKey(Scenario, models.DO_NOTHING, null=True)
+ jira_issue_id = models.IntegerField(null=True, blank=True)
+ jira_issue_status = models.CharField(max_length=50, blank=True)
purpose = models.CharField(max_length=300, blank=False)
+ ext_count = models.IntegerField(default=2)
+ resource = models.ForeignKey(ResourceBundle, on_delete=models.SET_NULL, null=True)
+ config_bundle = models.ForeignKey(ConfigBundle, on_delete=models.SET_NULL, null=True)
+ opnfv_config = models.ForeignKey(OPNFVConfig, on_delete=models.SET_NULL, null=True)
+ project = models.CharField(max_length=100, default="", blank=True, null=True)
+ lab = models.ForeignKey(Lab, null=True, on_delete=models.SET_NULL)
+ pdf = models.TextField(blank=True, default="")
+ idf = models.TextField(blank=True, default="")
class Meta:
db_table = 'booking'
- def get_jira_issue(self):
- try:
- jira = JIRA(server=settings.JIRA_URL,
- basic_auth=(settings.JIRA_USER_NAME, settings.JIRA_USER_PASSWORD))
- issue = jira.issue(self.jira_issue_id)
- return issue
- except JIRAError:
- return None
-
def save(self, *args, **kwargs):
"""
Save the booking if self.user is authorized and there is no overlapping booking.
@@ -82,11 +52,14 @@ class Booking(models.Model):
conflicting_dates = conflicting_dates.filter(start__lt=self.end)
if conflicting_dates.count() > 0:
raise ValueError('This booking overlaps with another booking')
- if not self.changeid:
- self.changeid = self.id
- else:
- self.changeid = hashlib.md5(self.changeid.encode() + get_random_string(length=32).encode()).hexdigest()
return super(Booking, self).save(*args, **kwargs)
+ def delete(self, *args, **kwargs):
+ res = self.resource
+ self.resource = None
+ self.save()
+ resource_inventory.resource_manager.ResourceManager.getInstance().deleteResourceBundle(res)
+ return super(self.__class__, self).delete(*args, **kwargs)
+
def __str__(self):
- return str(self.resource) + ' from ' + str(self.start) + ' until ' + str(self.end)
+ return str(self.purpose) + ' from ' + str(self.start) + ' until ' + str(self.end)
diff --git a/dashboard/src/booking/quick_deployer.py b/dashboard/src/booking/quick_deployer.py
new file mode 100644
index 0000000..0e0cc5a
--- /dev/null
+++ b/dashboard/src/booking/quick_deployer.py
@@ -0,0 +1,355 @@
+##############################################################################
+# 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 uuid
+import re
+from django.db.models import Q
+from datetime import timedelta
+from django.utils import timezone
+from account.models import Lab
+
+from resource_inventory.models import (
+ Installer,
+ Image,
+ GenericResourceBundle,
+ ConfigBundle,
+ Host,
+ HostProfile,
+ HostConfiguration,
+ GenericResource,
+ GenericHost,
+ GenericInterface,
+ OPNFVRole,
+ OPNFVConfig,
+ Network,
+ NetworkConnection,
+ NetworkRole,
+ HostOPNFVConfig,
+)
+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 (
+ InvalidHostnameException,
+ ResourceAvailabilityException,
+ ModelValidationException,
+ BookingLengthException
+)
+from api.models import JobFactory
+
+
+# model validity exceptions
+class IncompatibleInstallerForOS(Exception):
+ pass
+
+
+class IncompatibleScenarioForInstaller(Exception):
+ pass
+
+
+class IncompatibleImageForHost(Exception):
+ pass
+
+
+class ImageOwnershipInvalid(Exception):
+ pass
+
+
+class ImageNotAvailableAtLab(Exception):
+ pass
+
+
+class LabDNE(Exception):
+ pass
+
+
+class HostProfileDNE(Exception):
+ pass
+
+
+class HostNotAvailable(Exception):
+ pass
+
+
+class NoLabSelectedError(Exception):
+ pass
+
+
+class OPNFVRoleDNE(Exception):
+ pass
+
+
+class NoRemainingPublicNetwork(Exception):
+ pass
+
+
+class BookingPermissionException(Exception):
+ pass
+
+
+def parse_host_field(host_json):
+ lab, profile = (None, None)
+ lab_dict = host_json['lab']
+ for lab_info in lab_dict.values():
+ if lab_info['selected']:
+ lab = Lab.objects.get(lab_user__id=lab_info['id'])
+
+ host_dict = host_json['host']
+ for host_info in host_dict.values():
+ if host_info['selected']:
+ profile = HostProfile.objects.get(pk=host_info['id'])
+
+ if lab is None:
+ raise NoLabSelectedError("No lab was selected")
+ if profile is None:
+ raise HostProfileDNE("No Host was selected")
+
+ return lab, profile
+
+
+def check_available_matching_host(lab, hostprofile):
+ available_host_types = ResourceManager.getInstance().getAvailableHostTypes(lab)
+ if hostprofile not in available_host_types:
+ # TODO: handle deleting generic resource in this instance along with grb
+ raise HostNotAvailable('Requested host type is not available. Please try again later. Host availability can be viewed in the "Hosts" tab to the left.')
+
+ hostset = Host.objects.filter(lab=lab, profile=hostprofile).filter(booked=False).filter(working=True)
+ if not hostset.exists():
+ raise HostNotAvailable("Couldn't find any matching unbooked hosts")
+
+ return True
+
+
+def generate_grb(owner, lab, common_id):
+ grbundle = GenericResourceBundle(owner=owner)
+ grbundle.lab = lab
+ grbundle.name = "grbundle for quick booking with uid " + common_id
+ grbundle.description = "grbundle created for quick-deploy booking"
+ grbundle.save()
+
+ return grbundle
+
+
+def generate_gresource(bundle, hostname):
+ if not re.match(r"(?=^.{1,253}$)(^([A-Za-z0-9-_]{1,62}\.)*[A-Za-z0-9-_]{1,63})$", hostname):
+ raise InvalidHostnameException("Hostname must comply to RFC 952 and all extensions to it until this point")
+ gresource = GenericResource(bundle=bundle, name=hostname)
+ gresource.save()
+
+ return gresource
+
+
+def generate_ghost(generic_resource, host_profile):
+ ghost = GenericHost()
+ ghost.resource = generic_resource
+ ghost.profile = host_profile
+ ghost.save()
+
+ return ghost
+
+
+def generate_config_bundle(owner, common_id, grbundle):
+ cbundle = ConfigBundle()
+ cbundle.owner = owner
+ cbundle.name = "configbundle for quick booking with uid " + common_id
+ cbundle.description = "configbundle created for quick-deploy booking"
+ cbundle.bundle = grbundle
+ cbundle.save()
+
+ return cbundle
+
+
+def generate_opnfvconfig(scenario, installer, config_bundle):
+ opnfvconfig = OPNFVConfig()
+ opnfvconfig.scenario = scenario
+ opnfvconfig.installer = installer
+ opnfvconfig.bundle = config_bundle
+ opnfvconfig.save()
+
+ return opnfvconfig
+
+
+def generate_hostconfig(generic_host, image, config_bundle):
+ hconf = HostConfiguration()
+ hconf.host = generic_host
+ hconf.image = image
+ hconf.bundle = config_bundle
+ hconf.is_head_node = True
+ hconf.save()
+
+ return hconf
+
+
+def generate_hostopnfv(hostconfig, opnfvconfig):
+ config = HostOPNFVConfig()
+ role = None
+ try:
+ role = OPNFVRole.objects.get(name="Jumphost")
+ except Exception:
+ role = OPNFVRole.objects.create(
+ name="Jumphost",
+ description="Single server jumphost role"
+ )
+ config.role = role
+ config.host_config = hostconfig
+ config.opnfv_config = opnfvconfig
+ config.save()
+ return config
+
+
+def generate_resource_bundle(generic_resource_bundle, config_bundle): # warning: requires cleanup
+ try:
+ resource_manager = ResourceManager.getInstance()
+ resource_bundle = resource_manager.convertResourceBundle(generic_resource_bundle, config=config_bundle)
+ return resource_bundle
+ except ResourceAvailabilityException:
+ raise ResourceAvailabilityException("Requested resources not available")
+ except ModelValidationException:
+ raise ModelValidationException("Encountered error while saving grbundle")
+
+
+def check_invariants(request, **kwargs):
+ installer = kwargs['installer']
+ image = kwargs['image']
+ scenario = kwargs['scenario']
+ lab = kwargs['lab']
+ host_profile = kwargs['host_profile']
+ length = kwargs['length']
+ # check that image os is compatible with installer
+ if installer in image.os.sup_installers.all():
+ # if installer not here, we can omit that and not check for scenario
+ if not scenario:
+ raise IncompatibleScenarioForInstaller("An OPNFV Installer needs a scenario to be chosen to work properly")
+ if scenario not in installer.sup_scenarios.all():
+ raise IncompatibleScenarioForInstaller("The chosen installer does not support the chosen scenario")
+ if image.from_lab != lab:
+ raise ImageNotAvailableAtLab("The chosen image is not available at the chosen hosting lab")
+ if image.host_type != host_profile:
+ raise IncompatibleImageForHost("The chosen image is not available for the chosen host type")
+ if not image.public and image.owner != request.user:
+ raise ImageOwnershipInvalid("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 configure_networking(grb, config):
+ # create network
+ net = Network.objects.create(name="public", bundle=grb, is_public=True)
+ # connect network to generic host
+ grb.getHosts()[0].generic_interfaces.first().connections.add(
+ NetworkConnection.objects.create(network=net, vlan_is_tagged=False)
+ )
+ # asign network role
+ role = NetworkRole.objects.create(name="public", network=net)
+ opnfv_config = config.opnfv_config.first()
+ if opnfv_config:
+ opnfv_config.networks.add(role)
+
+
+def create_from_form(form, request):
+ quick_booking_id = str(uuid.uuid4())
+
+ host_field = form.cleaned_data['filter_field']
+ purpose_field = form.cleaned_data['purpose']
+ project_field = form.cleaned_data['project']
+ users_field = form.cleaned_data['users']
+ hostname = form.cleaned_data['hostname']
+ length = form.cleaned_data['length']
+
+ image = form.cleaned_data['image']
+ scenario = form.cleaned_data['scenario']
+ installer = form.cleaned_data['installer']
+
+ lab, host_profile = parse_host_field(host_field)
+ data = form.cleaned_data
+ data['lab'] = lab
+ data['host_profile'] = host_profile
+ check_invariants(request, **data)
+
+ # check booking privileges
+ if Booking.objects.filter(owner=request.user, end__gt=timezone.now()).count() >= 3 and not request.user.userprofile.booking_privledge:
+ raise BookingPermissionException("You do not have permission to have more than 3 bookings at a time.")
+
+ check_available_matching_host(lab, host_profile) # requires cleanup if failure after this point
+
+ grbundle = generate_grb(request.user, lab, quick_booking_id)
+ gresource = generate_gresource(grbundle, hostname)
+ ghost = generate_ghost(gresource, host_profile)
+ cbundle = generate_config_bundle(request.user, quick_booking_id, grbundle)
+ hconf = generate_hostconfig(ghost, image, cbundle)
+
+ # if no installer provided, just create blank host
+ opnfv_config = None
+ if installer:
+ opnfv_config = generate_opnfvconfig(scenario, installer, cbundle)
+ generate_hostopnfv(hconf, opnfv_config)
+
+ # construct generic interfaces
+ for interface_profile in host_profile.interfaceprofile.all():
+ generic_interface = GenericInterface.objects.create(profile=interface_profile, host=ghost)
+ generic_interface.save()
+
+ configure_networking(grbundle, cbundle)
+
+ # generate resource bundle
+ resource_bundle = generate_resource_bundle(grbundle, cbundle)
+
+ # generate booking
+ booking = Booking.objects.create(
+ purpose=purpose_field,
+ project=project_field,
+ lab=lab,
+ owner=request.user,
+ start=timezone.now(),
+ end=timezone.now() + timedelta(days=int(length)),
+ resource=resource_bundle,
+ config_bundle=cbundle,
+ opnfv_config=opnfv_config
+ )
+ booking.pdf = PDFTemplater.makePDF(booking)
+
+ for collaborator in users_field: # list of UserProfiles
+ booking.collaborators.add(collaborator.user)
+
+ booking.save()
+
+ # generate job
+ JobFactory.makeCompleteJob(booking)
+ NotificationHandler.notify_new_booking(booking)
+
+ return booking
+
+
+def drop_filter(user):
+ installer_filter = {}
+ for image in Image.objects.all():
+ installer_filter[image.id] = {}
+ for installer in image.os.sup_installers.all():
+ installer_filter[image.id][installer.id] = 1
+
+ scenario_filter = {}
+ for installer in Installer.objects.all():
+ scenario_filter[installer.id] = {}
+ for scenario in installer.sup_scenarios.all():
+ scenario_filter[installer.id][scenario.id] = 1
+
+ images = Image.objects.filter(Q(public=True) | Q(owner=user))
+ image_filter = {}
+ for image in images:
+ image_filter[image.id] = {}
+ image_filter[image.id]['lab'] = 'lab_' + str(image.from_lab.lab_user.id)
+ image_filter[image.id]['host_profile'] = 'host_' + str(image.host_type.id)
+ image_filter[image.id]['name'] = image.name
+
+ return {'installer_filter': json.dumps(installer_filter),
+ 'scenario_filter': json.dumps(scenario_filter),
+ 'image_filter': json.dumps(image_filter)}
diff --git a/dashboard/src/booking/stats.py b/dashboard/src/booking/stats.py
new file mode 100644
index 0000000..383723a
--- /dev/null
+++ b/dashboard/src/booking/stats.py
@@ -0,0 +1,58 @@
+##############################################################################
+# Copyright (c) 2018 Parker Berberian, Sawyer Bergeron, and others.
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+##############################################################################
+from booking.models import Booking
+import datetime
+import pytz
+
+
+class StatisticsManager(object):
+
+ @staticmethod
+ def getContinuousBookingTimeSeries(span=28):
+ """
+ Will return a dictionary of names and 2-D array of x and y data points.
+ e.g. {"plot1": [["x1", "x2", "x3"],["y1", "y2", "y3]]}
+ x values will be dates in string
+ every change (booking start / end) will be reflected,
+ instead of one data point per day
+ y values are the integer number of bookings/users active at
+ some point in the given date span is the number of days to plot.
+ The last x value will always be the current time
+ """
+ data = []
+ x = []
+ y = []
+ users = []
+ now = datetime.datetime.now(pytz.utc)
+ delta = datetime.timedelta(days=span)
+ end = now - delta
+ bookings = Booking.objects.filter(start__lte=now, end__gte=end).prefetch_related("collaborators")
+ for booking in bookings: # collect data from each booking
+ user_list = [u.pk for u in booking.collaborators.all()]
+ user_list.append(booking.owner.pk)
+ data.append((booking.start, 1, user_list))
+ data.append((booking.end, -1, user_list))
+
+ # sort based on time
+ data.sort(key=lambda i: i[0])
+
+ # collect data
+ count = 0
+ active_users = {}
+ for datum in data:
+ x.append(str(datum[0])) # time
+ count += datum[1] # booking count
+ y.append(count)
+ for pk in datum[2]: # maintain count of each user's active bookings
+ active_users[pk] = active_users.setdefault(pk, 0) + datum[1]
+ if active_users[pk] == 0:
+ del active_users[pk]
+ users.append(len([x for x in active_users.values() if x > 0]))
+
+ return {"booking": [x, y], "user": [x, users]}
diff --git a/dashboard/src/booking/tests/__init__.py b/dashboard/src/booking/tests/__init__.py
index b5914ce..b6fef6c 100644
--- a/dashboard/src/booking/tests/__init__.py
+++ b/dashboard/src/booking/tests/__init__.py
@@ -6,5 +6,3 @@
# which accompanies this distribution, and is available at
# http://www.apache.org/licenses/LICENSE-2.0
##############################################################################
-
-
diff --git a/dashboard/src/booking/tests/test_models.py b/dashboard/src/booking/tests/test_models.py
index b4cd113..6170295 100644
--- a/dashboard/src/booking/tests/test_models.py
+++ b/dashboard/src/booking/tests/test_models.py
@@ -1,5 +1,6 @@
##############################################################################
# Copyright (c) 2016 Max Breitenfeldt and others.
+# Copyright (c) 2018 Parker Berberian, Sawyer Bergeron, and others.
#
# All rights reserved. This program and the accompanying materials
# are made available under the terms of the Apache License, Version 2.0
@@ -10,85 +11,222 @@
from datetime import timedelta
-from django.contrib.auth.models import Permission
+from django.contrib.auth.models import Permission, User
from django.test import TestCase
from django.utils import timezone
-from booking.models import *
-from dashboard.models import Resource
-from jenkins.models import JenkinsSlave
+# from booking.models import *
+from booking.models import Booking
+from resource_inventory.models import ResourceBundle, GenericResourceBundle, ConfigBundle
class BookingModelTestCase(TestCase):
+
+ count = 0
+
def setUp(self):
- self.slave = JenkinsSlave.objects.create(name='test', url='test')
self.owner = User.objects.create(username='owner')
- self.res1 = Resource.objects.create(name='res1', slave=self.slave, description='x',
- url='x',owner=self.owner)
- self.res2 = Resource.objects.create(name='res2', slave=self.slave, description='x',
- url='x',owner=self.owner)
-
+ self.res1 = ResourceBundle.objects.create(
+ template=GenericResourceBundle.objects.create(
+ name="gbundle" + str(self.count)
+ )
+ )
+ self.count += 1
+ self.res2 = ResourceBundle.objects.create(
+ template=GenericResourceBundle.objects.create(
+ name="gbundle2" + str(self.count)
+ )
+ )
+ self.count += 1
self.user1 = User.objects.create(username='user1')
self.add_booking_perm = Permission.objects.get(codename='add_booking')
self.user1.user_permissions.add(self.add_booking_perm)
self.user1 = User.objects.get(pk=self.user1.id)
-
- self.installer = Installer.objects.create(name='TestInstaller')
- self.scenario = Scenario.objects.create(name='TestScenario')
+ self.config_bundle = ConfigBundle.objects.create(
+ owner=self.user1,
+ name="test config"
+ )
def test_start_end(self):
"""
- if the start of a booking is greater or equal then the end, saving should raise a
- ValueException
+ 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, user=self.user1)
+ self.assertRaises(
+ ValueError,
+ Booking.objects.create,
+ start=start,
+ end=end,
+ resource=self.res1,
+ owner=self.user1,
+ config_bundle=self.config_bundle
+ )
end = start
- self.assertRaises(ValueError, Booking.objects.create, start=start, end=end,
- resource=self.res1, user=self.user1)
+ self.assertRaises(
+ ValueError,
+ Booking.objects.create,
+ start=start,
+ end=end,
+ resource=self.res1,
+ owner=self.user1,
+ config_bundle=self.config_bundle
+ )
def test_conflicts(self):
"""
- saving an overlapping booking on the same resource should raise a ValueException
+ 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, user=self.user1, resource=self.res1))
+ Booking.objects.create(
+ start=start,
+ end=end,
+ owner=self.user1,
+ resource=self.res1,
+ config_bundle=self.config_bundle
+ )
+ )
+
+ self.assertRaises(
+ ValueError,
+ Booking.objects.create,
+ start=start,
+ end=end,
+ resource=self.res1,
+ owner=self.user1,
+ config_bundle=self.config_bundle
+ )
+
+ self.assertRaises(
+ ValueError,
+ Booking.objects.create,
+ start=start + timedelta(days=1),
+ end=end - timedelta(days=1),
+ resource=self.res1,
+ owner=self.user1,
+ config_bundle=self.config_bundle
+ )
+
+ self.assertRaises(
+ ValueError,
+ Booking.objects.create,
+ start=start - timedelta(days=1),
+ end=end,
+ resource=self.res1,
+ owner=self.user1,
+ config_bundle=self.config_bundle
+ )
+
+ self.assertRaises(
+ ValueError,
+ Booking.objects.create,
+ start=start - timedelta(days=1),
+ end=end - timedelta(days=1),
+ resource=self.res1,
+ owner=self.user1,
+ config_bundle=self.config_bundle
+ )
+
+ self.assertRaises(
+ ValueError,
+ Booking.objects.create,
+ start=start,
+ end=end + timedelta(days=1),
+ resource=self.res1,
+ owner=self.user1,
+ config_bundle=self.config_bundle
+ )
+
+ self.assertRaises(
+ ValueError,
+ Booking.objects.create,
+ start=start + timedelta(days=1),
+ end=end + timedelta(days=1),
+ resource=self.res1,
+ owner=self.user1,
+ config_bundle=self.config_bundle
+ )
- self.assertRaises(ValueError, Booking.objects.create, start=start,
- end=end, resource=self.res1, user=self.user1)
- self.assertRaises(ValueError, Booking.objects.create, start=start + timedelta(days=1),
- end=end - timedelta(days=1), resource=self.res1, user=self.user1)
-
- self.assertRaises(ValueError, Booking.objects.create, start=start - timedelta(days=1),
- end=end, resource=self.res1, user=self.user1)
- self.assertRaises(ValueError, Booking.objects.create, start=start - timedelta(days=1),
- end=end - timedelta(days=1), resource=self.res1, user=self.user1)
+ self.assertTrue(
+ Booking.objects.create(
+ start=start - timedelta(days=1),
+ end=start,
+ owner=self.user1,
+ resource=self.res1,
+ config_bundle=self.config_bundle
+ )
+ )
- self.assertRaises(ValueError, Booking.objects.create, start=start,
- end=end + timedelta(days=1), resource=self.res1, user=self.user1)
- self.assertRaises(ValueError, Booking.objects.create, start=start + timedelta(days=1),
- end=end + timedelta(days=1), resource=self.res1, user=self.user1)
+ self.assertTrue(
+ Booking.objects.create(
+ start=end,
+ end=end + timedelta(days=1),
+ owner=self.user1,
+ resource=self.res1,
+ config_bundle=self.config_bundle
+ )
+ )
- self.assertTrue(Booking.objects.create(start=start - timedelta(days=1), end=start,
- user=self.user1, resource=self.res1))
- self.assertTrue(Booking.objects.create(start=end, end=end + timedelta(days=1),
- user=self.user1, resource=self.res1))
+ self.assertTrue(
+ Booking.objects.create(
+ start=start - timedelta(days=2),
+ end=start - timedelta(days=1),
+ owner=self.user1,
+ resource=self.res1,
+ config_bundle=self.config_bundle
+ )
+ )
self.assertTrue(
- Booking.objects.create(start=start - timedelta(days=2), end=start - timedelta(days=1),
- user=self.user1, resource=self.res1))
+ Booking.objects.create(
+ start=end + timedelta(days=1),
+ end=end + timedelta(days=2),
+ owner=self.user1,
+ resource=self.res1,
+ config_bundle=self.config_bundle
+ )
+ )
+
self.assertTrue(
- Booking.objects.create(start=end + timedelta(days=1), end=end + timedelta(days=2),
- user=self.user1, resource=self.res1))
+ Booking.objects.create(
+ start=start,
+ end=end,
+ owner=self.user1,
+ resource=self.res2,
+ config_bundle=self.config_bundle
+ )
+ )
+
+ def test_extensions(self):
+ """
+ saving a booking with an extended end time is allows to happen twice,
+ and each extension must be a maximum of one week long
+ """
+ start = timezone.now()
+ end = start + timedelta(weeks=1)
self.assertTrue(
- Booking.objects.create(start=start, end=end,
- user=self.user1, resource=self.res2, scenario=self.scenario,
- installer=self.installer)) \ No newline at end of file
+ Booking.objects.create(
+ start=start,
+ end=end,
+ owner=self.user1,
+ resource=self.res1,
+ config_bundle=self.config_bundle
+ )
+ )
+
+ booking = Booking.objects.all().first() # should be only thing in db
+
+ self.assertEquals(booking.ext_count, 2)
+ booking.end = booking.end + timedelta(days=3)
+ try:
+ booking.save()
+ except Exception:
+ self.fail("save() threw an exception")
diff --git a/dashboard/src/booking/tests/test_quick_booking.py b/dashboard/src/booking/tests/test_quick_booking.py
new file mode 100644
index 0000000..e445860
--- /dev/null
+++ b/dashboard/src/booking/tests/test_quick_booking.py
@@ -0,0 +1,150 @@
+##############################################################################
+# 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
+
+from django.test import TestCase, Client
+
+from booking.models import Booking
+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_opnfv_role,
+ make_public_net,
+)
+
+
+class QuickBookingValidFormTestCase(TestCase):
+ @classmethod
+ def setUpTestData(cls):
+ cls.user = make_user(False, username="newtestuser", password="testpassword")
+ make_user_profile(cls.user, True)
+
+ lab_user = make_user(True)
+ cls.lab = make_lab(lab_user)
+
+ 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)
+ cls.host = make_host(cls.host_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': '{"hosts":[{"host_' + str(cls.host_profile.id) + '":"true"}], "labs": [{"lab_' + str(cls.lab.lab_user.id) + '":"true"}]}',
+ 'purpose': 'my_purpose',
+ 'project': 'my_project',
+ 'length': '3',
+ 'ignore_this': 1,
+ 'users': '',
+ 'hostname': 'my_host',
+ 'image': str(cls.image.id),
+ 'installer': str(cls.installer.id),
+ 'scenario': str(cls.scenario.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=self.user.username, 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
+ config_bundle = booking.config_bundle
+
+ opnfv_config = config_bundle.opnfv_config.first()
+ self.assertEqual(self.installer, opnfv_config.installer)
+ self.assertEqual(self.scenario, opnfv_config.scenario)
+
+ host = resource_bundle.hosts.first()
+ self.assertEqual(host.profile, self.host_profile)
+ self.assertEqual(host.template.resource.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': '{"hosts":[{"host_' + str(self.host_profile.id + 100) + '":"true"}], "labs": [{"lab_' + str(self.lab.lab_user.id) + '":"true"}]}'})
+
+ self.assertEqual(response.status_code, 200)
+ self.assertIsNone(Booking.objects.first())
+
+ def test_with_invalid_lab_id(self):
+ response = self.post({'filter_field': '{"hosts":[{"host_' + str(self.host_profile.id) + '":"true"}], "labs": [{"lab_' + str(self.lab.lab_user.id + 100) + '":"true"}]}'})
+
+ 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.assertIsNotNone(booking)
+ self.assertValidBooking(booking)
+
+ def test_with_valid_form(self):
+ response = self.post()
+
+ self.assertEqual(response.status_code, 200)
+ booking = Booking.objects.first()
+ self.assertIsNotNone(booking)
+ self.assertValidBooking(booking)
diff --git a/dashboard/src/booking/tests/test_views.py b/dashboard/src/booking/tests/test_views.py
deleted file mode 100644
index c1da013..0000000
--- a/dashboard/src/booking/tests/test_views.py
+++ /dev/null
@@ -1,106 +0,0 @@
-##############################################################################
-# Copyright (c) 2016 Max Breitenfeldt and others.
-#
-# All rights reserved. This program and the accompanying materials
-# are made available under the terms of the Apache License, Version 2.0
-# which accompanies this distribution, and is available at
-# http://www.apache.org/licenses/LICENSE-2.0
-##############################################################################
-
-
-from datetime import timedelta
-
-from django.test import Client
-from django.test import TestCase
-from django.urls import reverse
-from django.utils import timezone
-from django.utils.encoding import force_text
-from registration.forms import User
-
-from account.models import UserProfile
-from booking.models import Booking
-from dashboard.models import Resource
-from jenkins.models import JenkinsSlave
-
-
-class BookingViewTestCase(TestCase):
- def setUp(self):
- self.client = Client()
- self.slave = JenkinsSlave.objects.create(name='test', url='test')
- self.owner = User.objects.create(username='owner')
- self.res1 = Resource.objects.create(name='res1', slave=self.slave, description='x',
- url='x',owner=self.owner)
- self.user1 = User.objects.create(username='user1')
- self.user1.set_password('user1')
- self.user1profile = UserProfile.objects.create(user=self.user1)
- self.user1.save()
-
- self.user1 = User.objects.get(pk=self.user1.id)
-
-
- def test_resource_bookings_json(self):
- url = reverse('booking:bookings_json', kwargs={'resource_id': 0})
- self.assertEqual(self.client.get(url).status_code, 404)
-
- url = reverse('booking:bookings_json', kwargs={'resource_id': self.res1.id})
- response = self.client.get(url)
- self.assertEqual(response.status_code, 200)
- self.assertJSONEqual(force_text(response.content), {"bookings": []})
- booking1 = Booking.objects.create(start=timezone.now(),
- end=timezone.now() + timedelta(weeks=1), user=self.user1,
- resource=self.res1)
- response = self.client.get(url)
- json = response.json()
- self.assertEqual(response.status_code, 200)
- self.assertIn('bookings', json)
- self.assertEqual(len(json['bookings']), 1)
- self.assertIn('start', json['bookings'][0])
- self.assertIn('end', json['bookings'][0])
- self.assertIn('id', json['bookings'][0])
- self.assertIn('purpose', json['bookings'][0])
-
- def test_booking_form_view(self):
- url = reverse('booking:create', kwargs={'resource_id': 0})
- self.assertEqual(self.client.get(url).status_code, 404)
-
- # authenticated user
- url = reverse('booking:create', kwargs={'resource_id': self.res1.id})
- self.client.login(username='user1',password='user1')
- response = self.client.get(url)
- self.assertEqual(response.status_code, 200)
- self.assertTemplateUsed('booking/booking_calendar.html')
- self.assertTemplateUsed('booking/booking_form.html')
- self.assertIn('resource', response.context)
-
-
- def test_booking_view(self):
- start = timezone.now()
- end = start + timedelta(weeks=1)
- booking = Booking.objects.create(start=start, end=end, user=self.user1, resource=self.res1)
-
- url = reverse('booking:detail', kwargs={'booking_id':0})
- response = self.client.get(url)
- self.assertEqual(response.status_code, 404)
-
- url = reverse('booking:detail', kwargs={'booking_id':booking.id})
- response = self.client.get(url)
- self.assertEqual(response.status_code, 200)
- self.assertTemplateUsed('booking/booking_detail.html')
- self.assertIn('booking', response.context)
-
- def test_booking_list_view(self):
- start = timezone.now() - timedelta(weeks=2)
- end = start + timedelta(weeks=1)
- Booking.objects.create(start=start, end=end, user=self.user1, resource=self.res1)
-
- url = reverse('booking:list')
- response = self.client.get(url)
- self.assertEqual(response.status_code, 200)
- self.assertTemplateUsed('booking/booking_list.html')
- self.assertTrue(len(response.context['bookings']) == 0)
-
- start = timezone.now()
- end = start + timedelta(weeks=1)
- Booking.objects.create(start=start, end=end, user=self.user1, resource=self.res1)
- response = self.client.get(url)
- self.assertTrue(len(response.context['bookings']) == 1) \ No newline at end of file
diff --git a/dashboard/src/booking/urls.py b/dashboard/src/booking/urls.py
index ed3b1d4..310aaa7 100644
--- a/dashboard/src/booking/urls.py
+++ b/dashboard/src/booking/urls.py
@@ -1,5 +1,6 @@
##############################################################################
# Copyright (c) 2016 Max Breitenfeldt and others.
+# Copyright (c) 2018 Parker Berberian, Sawyer Bergeron, and others.
#
# All rights reserved. This program and the accompanying materials
# are made available under the terms of the Apache License, Version 2.0
@@ -25,22 +26,29 @@ Including another URLconf
"""
from django.conf.urls import url
-from booking.views import *
-
+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'^(?P<resource_id>[0-9]+)/$', BookingFormView.as_view(), name='create'),
- url(r'^(?P<resource_id>[0-9]+)/edit/(?P<booking_id>[0-9]+)/$', BookingEditFormView.as_view(), name='edit'),
-
- url(r'^(?P<resource_id>[0-9]+)/bookings_json/$', ResourceBookingsJSON.as_view(),
- name='bookings_json'),
- url(r'^detail/$', BookingView.as_view(), name='detail_prefix'),
- url(r'^detail/(?P<booking_id>[0-9]+)/$', BookingView.as_view(), name='detail'),
+ url(r'^detail/(?P<booking_id>[0-9]+)/$', booking_detail_view, name='detail'),
+ url(r'^(?P<booking_id>[0-9]+)/$', booking_detail_view, name='booking_detail'),
url(r'^delete/$', BookingDeleteView.as_view(), name='delete_prefix'),
url(r'^delete/(?P<booking_id>[0-9]+)/$', BookingDeleteView.as_view(), name='delete'),
-
url(r'^delete/(?P<booking_id>[0-9]+)/confirm/$', bookingDelete, name='delete_booking'),
-
- url(r'^list/$', BookingListView.as_view(), name='list')
-] \ No newline at end of file
+ url(r'^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/dashboard/src/booking/views.py b/dashboard/src/booking/views.py
index 7e35af2..bad7dc9 100644
--- a/dashboard/src/booking/views.py
+++ b/dashboard/src/booking/views.py
@@ -1,5 +1,6 @@
##############################################################################
# Copyright (c) 2016 Max Breitenfeldt and others.
+# Copyright (c) 2018 Parker Berberian, Sawyer Bergeron, and others.
#
# All rights reserved. This program and the accompanying materials
# are made available under the terms of the Apache License, Version 2.0
@@ -7,164 +8,74 @@
# http://www.apache.org/licenses/LICENSE-2.0
##############################################################################
-from django.conf import settings
from django.contrib import messages
-from django.contrib.auth.mixins import LoginRequiredMixin
-from django.http import JsonResponse
from django.shortcuts import get_object_or_404
-from django.urls import reverse
+from django.http import JsonResponse, HttpResponse
from django.utils import timezone
from django.views import View
-from django.views.generic import FormView
from django.views.generic import TemplateView
-from jira import JIRAError
-from django.shortcuts import redirect
+from django.shortcuts import redirect, render
+from django.db.models import Q
+from django.urls import reverse
-from account.jira_util import get_jira
-from booking.forms import BookingForm, BookingEditForm
+from resource_inventory.models import ResourceBundle, HostProfile, Image, Host
+from resource_inventory.resource_manager import ResourceManager
+from account.models import Lab
from booking.models import Booking
-from dashboard.models import Resource
-
-def create_jira_ticket(user, booking):
- jira = get_jira(user)
- issue_dict = {
- 'project': 'PHAROS',
- 'summary': str(booking.resource) + ': Access Request',
- 'description': booking.purpose,
- 'issuetype': {'name': 'Task'},
- 'components': [{'name': 'POD Access Request'}],
- 'assignee': {'name': booking.resource.owner.username}
- }
- issue = jira.create_issue(fields=issue_dict)
- jira.add_attachment(issue, user.userprofile.pgp_public_key)
- jira.add_attachment(issue, user.userprofile.ssh_public_key)
- booking.jira_issue_id = issue.id
- booking.save()
+from booking.stats import StatisticsManager
+from booking.forms import HostReImageForm
+from api.models import JobFactory
+from workflow.views import login
+from booking.forms import QuickBookingForm
+from booking.quick_deployer import create_from_form, drop_filter
-class BookingFormView(FormView):
- template_name = "booking/booking_calendar.html"
- form_class = BookingForm
+def quick_create_clear_fields(request):
+ request.session['quick_create_forminfo'] = None
- def dispatch(self, request, *args, **kwargs):
- self.resource = get_object_or_404(Resource, id=self.kwargs['resource_id'])
- return super(BookingFormView, self).dispatch(request, *args, **kwargs)
- def get_context_data(self, **kwargs):
- title = 'Booking: ' + self.resource.name
- context = super(BookingFormView, self).get_context_data(**kwargs)
- context.update({'title': title, 'resource': self.resource})
- #raise PermissionDenied('check')
- return context
+def quick_create(request):
+ if not request.user.is_authenticated:
+ return login(request)
- def get_success_url(self):
- return reverse('booking:create', kwargs=self.kwargs)
-
- def form_valid(self, form):
- if not self.request.user.is_authenticated:
- messages.add_message(self.request, messages.ERROR,
- 'You need to be logged in to book a Pod.')
- return super(BookingFormView, self).form_invalid(form)
-
- user = self.request.user
- booking = Booking(start=form.cleaned_data['start'],
- end=form.cleaned_data['end'],
- purpose=form.cleaned_data['purpose'],
- opsys=form.cleaned_data['opsys'],
- installer=form.cleaned_data['installer'],
- scenario=form.cleaned_data['scenario'],
- resource=self.resource, user=user)
- try:
- booking.save()
- except ValueError as err:
- messages.add_message(self.request, messages.ERROR, err)
- return super(BookingFormView, self).form_invalid(form)
- try:
- if settings.CREATE_JIRA_TICKET:
- create_jira_ticket(user, booking)
- except JIRAError:
- messages.add_message(self.request, messages.ERROR, 'Failed to create Jira Ticket. '
- 'Please check your Jira '
- 'permissions.')
- booking.delete()
- return super(BookingFormView, self).form_invalid(form)
- messages.add_message(self.request, messages.SUCCESS, 'Booking saved')
- return super(BookingFormView, self).form_valid(form)
-
-
-class BookingEditFormView(FormView):
- template_name = "booking/booking_calendar.html"
- form_class = BookingEditForm
-
- def is_valid(self):
- return True
-
- def dispatch(self, request, *args, **kwargs):
- self.resource = get_object_or_404(Resource, id=self.kwargs['resource_id'])
- self.original_booking = get_object_or_404(Booking, id=self.kwargs['booking_id'])
- return super(BookingEditFormView, self).dispatch(request, *args, **kwargs)
+ if request.method == 'GET':
+ context = {}
- def get_context_data(self, **kwargs):
- title = 'Editing Booking on: ' + self.resource.name
- context = super(BookingEditFormView, self).get_context_data(**kwargs)
- context.update({'title': title, 'resource': self.resource})
- return context
+ r_manager = ResourceManager.getInstance()
+ profiles = {}
+ for lab in Lab.objects.all():
+ profiles[str(lab)] = r_manager.getAvailableHostTypes(lab)
+
+ context['lab_profile_map'] = profiles
+
+ context['form'] = QuickBookingForm(default_user=request.user.username, user=request.user)
+
+ context.update(drop_filter(request.user))
+
+ return render(request, 'booking/quick_deploy.html', context)
+ if request.method == 'POST':
+ form = QuickBookingForm(request.POST, 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:
+ messages.error(request, "Whoops, an error occurred: " + str(e))
+ 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")
+ return render(request, 'booking/quick_deploy.html', context)
- def get_form_kwargs(self):
- kwargs = super(BookingEditFormView, self).get_form_kwargs()
- kwargs['purpose'] = self.original_booking.purpose
- kwargs['start'] = self.original_booking.start
- kwargs['end'] = self.original_booking.end
- try:
- kwargs['installer'] = self.original_booking.installer
- except AttributeError:
- pass
- try:
- kwargs['scenario'] = self.original_booking.scenario
- except AttributeError:
- pass
- return kwargs
-
- def get_success_url(self):
- return reverse('booking:create', args=(self.resource.id,))
-
- def form_valid(self, form):
-
- if not self.request.user.is_authenticated:
- messages.add_message(self.request, messages.ERROR,
- 'You need to be logged in to book a Pod.')
- return super(BookingEditFormView, self).form_invalid(form)
-
- if not self.request.user == self.original_booking.user:
- messages.add_message(self.request, messages.ERROR,
- 'You are not the owner of this booking.')
- return super(BookingEditFormView, self).form_invalid(form)
-
- #Do Conflict Checks
- if self.original_booking.start != form.cleaned_data['start']:
- if timezone.now() > form.cleaned_data['start']:
- messages.add_message(self.request, messages.ERROR,
- 'Cannot change start date after it has occurred.')
- return super(BookingEditFormView, self).form_invalid(form)
- self.original_booking.start = form.cleaned_data['start']
- self.original_booking.end = form.cleaned_data['end']
- self.original_booking.purpose = form.cleaned_data['purpose']
- self.original_booking.installer = form.cleaned_data['installer']
- self.original_booking.scenario = form.cleaned_data['scenario']
- self.original_booking.reset = form.cleaned_data['reset']
- try:
- self.original_booking.save()
- except ValueError as err:
- messages.add_message(self.request, messages.ERROR, err)
- return super(BookingEditFormView, self).form_invalid(form)
-
- user = self.request.user
- return super(BookingEditFormView, self).form_valid(form)
class BookingView(TemplateView):
template_name = "booking/booking_detail.html"
-
def get_context_data(self, **kwargs):
booking = get_object_or_404(Booking, id=self.kwargs['booking_id'])
title = 'Booking Details'
@@ -172,6 +83,7 @@ class BookingView(TemplateView):
context.update({'title': title, 'booking': booking})
return context
+
class BookingDeleteView(TemplateView):
template_name = "booking/booking_delete.html"
@@ -182,12 +94,14 @@ class BookingDeleteView(TemplateView):
context.update({'title': title, 'booking': booking})
return context
+
def bookingDelete(request, booking_id):
booking = get_object_or_404(Booking, id=booking_id)
booking.delete()
messages.add_message(request, messages.SUCCESS, 'Booking deleted')
return redirect('../../../../')
+
class BookingListView(TemplateView):
template_name = "booking/booking_list.html"
@@ -201,8 +115,87 @@ class BookingListView(TemplateView):
class ResourceBookingsJSON(View):
def get(self, request, *args, **kwargs):
- resource = get_object_or_404(Resource, id=self.kwargs['resource_id'])
- bookings = resource.booking_set.get_queryset().values('id', 'start', 'end', 'purpose',
- 'jira_issue_status', 'opsys__name',
- 'installer__name', 'scenario__name')
- return JsonResponse({'bookings': list(bookings)}) \ No newline at end of file
+ resource = get_object_or_404(ResourceBundle, id=self.kwargs['resource_id'])
+ bookings = resource.booking_set.get_queryset().values(
+ 'id',
+ 'start',
+ 'end',
+ 'purpose',
+ 'jira_issue_status',
+ 'config_bundle__name'
+ )
+ return JsonResponse({'bookings': list(bookings)})
+
+
+def build_image_mapping(lab, user):
+ mapping = {}
+ for profile in HostProfile.objects.filter(labs=lab):
+ images = Image.objects.filter(
+ from_lab=lab,
+ host_type=profile
+ ).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)
+ }
+
+ 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 = Host.objects.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": "Booking Statistics"}
+ )
+
+
+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/dashboard/src/dashboard/__init__.py b/dashboard/src/dashboard/__init__.py
index b5914ce..b6fef6c 100644
--- a/dashboard/src/dashboard/__init__.py
+++ b/dashboard/src/dashboard/__init__.py
@@ -6,5 +6,3 @@
# which accompanies this distribution, and is available at
# http://www.apache.org/licenses/LICENSE-2.0
##############################################################################
-
-
diff --git a/dashboard/src/dashboard/actions.py b/dashboard/src/dashboard/actions.py
new file mode 100644
index 0000000..44b1fdd
--- /dev/null
+++ b/dashboard/src/dashboard/actions.py
@@ -0,0 +1,47 @@
+##############################################################################
+# 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 resource_inventory.models import Host, Vlan
+from account.models import Lab
+from booking.models import Booking
+from datetime import timedelta
+from django.utils import timezone
+
+
+def free_leaked_hosts(free_old_bookings=False, old_booking_age=timedelta(days=1)):
+ bundles = [booking.resource for booking in Booking.objects.filter(end__gt=timezone.now())]
+ active_hosts = set()
+ for bundle in bundles:
+ active_hosts.update([host for host in bundle.hosts.all()])
+
+ marked_hosts = set(Host.objects.filter(booked=True))
+
+ for host in (marked_hosts - active_hosts):
+ host.booked = False
+ host.save()
+
+
+def free_leaked_public_vlans():
+ booked_host_interfaces = []
+
+ for lab in Lab.objects.all():
+
+ for host in Host.objects.filter(booked=True).filter(lab=lab):
+ for interface in host.interfaces.all():
+ booked_host_interfaces.append(interface)
+
+ in_use_vlans = Vlan.objects.filter(public=True).distinct('vlan_id').filter(interface__in=booked_host_interfaces)
+
+ manager = lab.vlan_manager
+
+ for vlan in Vlan.objects.all():
+ if vlan not in in_use_vlans:
+ if vlan.public:
+ manager.release_public_vlan(vlan.vlan_id)
+ manager.release_vlans(vlan)
diff --git a/dashboard/src/dashboard/admin.py b/dashboard/src/dashboard/admin.py
index 0bfdef8..43b5386 100644
--- a/dashboard/src/dashboard/admin.py
+++ b/dashboard/src/dashboard/admin.py
@@ -1,5 +1,6 @@
##############################################################################
# Copyright (c) 2016 Max Breitenfeldt and others.
+# Copyright (c) 2018 Sawyer Bergeron, Parker Berberian, and others.
#
# All rights reserved. This program and the accompanying materials
# are made available under the terms of the Apache License, Version 2.0
@@ -10,11 +11,6 @@
from django.contrib import admin
-from dashboard.models import *
admin.site.site_header = "Pharos Dashboard Administration"
admin.site.site_title = "Pharos Dashboard"
-
-admin.site.register(Resource)
-admin.site.register(Server)
-admin.site.register(ResourceStatus)
diff --git a/dashboard/src/dashboard/context_processors.py b/dashboard/src/dashboard/context_processors.py
new file mode 100644
index 0000000..338f609
--- /dev/null
+++ b/dashboard/src/dashboard/context_processors.py
@@ -0,0 +1,13 @@
+##############################################################################
+# Copyright (c) 2018 Sawyer Bergeron, Parker Berberian, and others.
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+##############################################################################
+from django.conf import settings
+
+
+def debug(context):
+ return {'DEBUG': settings.DEBUG}
diff --git a/dashboard/src/dashboard/exceptions.py b/dashboard/src/dashboard/exceptions.py
new file mode 100644
index 0000000..7111bf8
--- /dev/null
+++ b/dashboard/src/dashboard/exceptions.py
@@ -0,0 +1,56 @@
+##############################################################################
+# Copyright (c) 2018 Parker Berberian, Sawyer Bergeron, and others.
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+##############################################################################
+
+
+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/dashboard/src/dashboard/migrations/0001_initial.py b/dashboard/src/dashboard/migrations/0001_initial.py
deleted file mode 100644
index aaf3945..0000000
--- a/dashboard/src/dashboard/migrations/0001_initial.py
+++ /dev/null
@@ -1,64 +0,0 @@
-# -*- coding: utf-8 -*-
-# Generated by Django 1.10 on 2016-11-03 13:33
-from __future__ import unicode_literals
-
-from django.conf import settings
-from django.db import migrations, models
-import django.db.models.deletion
-
-
-class Migration(migrations.Migration):
-
- initial = True
-
- dependencies = [
- migrations.swappable_dependency(settings.AUTH_USER_MODEL),
- ('jenkins', '0001_initial'),
- ]
-
- operations = [
- migrations.CreateModel(
- name='Resource',
- fields=[
- ('id', models.AutoField(primary_key=True, serialize=False)),
- ('name', models.CharField(max_length=100, unique=True)),
- ('description', models.CharField(blank=True, max_length=300, null=True)),
- ('url', models.CharField(blank=True, max_length=100, null=True)),
- ('owner', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='user_lab_owner', to=settings.AUTH_USER_MODEL)),
- ('slave', models.ForeignKey(null=True, on_delete=django.db.models.deletion.DO_NOTHING, to='jenkins.JenkinsSlave')),
- ('vpn_users', models.ManyToManyField(blank=True, related_name='user_vpn_users', to=settings.AUTH_USER_MODEL)),
- ],
- options={
- 'db_table': 'resource',
- },
- ),
- migrations.CreateModel(
- name='ResourceStatus',
- fields=[
- ('id', models.AutoField(primary_key=True, serialize=False)),
- ('timestamp', models.DateTimeField(auto_now_add=True)),
- ('type', models.CharField(max_length=20)),
- ('title', models.CharField(max_length=50)),
- ('content', models.CharField(max_length=5000)),
- ('resource', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='dashboard.Resource')),
- ],
- options={
- 'db_table': 'resource_status',
- },
- ),
- migrations.CreateModel(
- name='Server',
- fields=[
- ('id', models.AutoField(primary_key=True, serialize=False)),
- ('name', models.CharField(blank=True, max_length=100)),
- ('model', models.CharField(blank=True, max_length=100)),
- ('cpu', models.CharField(blank=True, max_length=100)),
- ('ram', models.CharField(blank=True, max_length=100)),
- ('storage', models.CharField(blank=True, max_length=100)),
- ('resource', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='dashboard.Resource')),
- ],
- options={
- 'db_table': 'server',
- },
- ),
- ]
diff --git a/dashboard/src/dashboard/migrations/0002_auto_20170505_0815.py b/dashboard/src/dashboard/migrations/0002_auto_20170505_0815.py
deleted file mode 100644
index 4285b88..0000000
--- a/dashboard/src/dashboard/migrations/0002_auto_20170505_0815.py
+++ /dev/null
@@ -1,42 +0,0 @@
-##############################################################################
-# Copyright (c) 2016 Max Breitenfeldt and others.
-#
-# All rights reserved. This program and the accompanying materials
-# are made available under the terms of the Apache License, Version 2.0
-# which accompanies this distribution, and is available at
-# http://www.apache.org/licenses/LICENSE-2.0
-##############################################################################
-
-
-# -*- coding: utf-8 -*-
-# Generated by Django 1.10 on 2017-05-05 08:15
-from __future__ import unicode_literals
-
-from django.conf import settings
-from django.db import migrations, models
-import django.db.models.deletion
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('dashboard', '0001_initial'),
- ]
-
- operations = [
- migrations.AddField(
- model_name='resource',
- name='dev_pod',
- field=models.BooleanField(default=False),
- ),
- migrations.AlterField(
- model_name='resource',
- name='owner',
- field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='user_lab_owner', to=settings.AUTH_USER_MODEL),
- ),
- migrations.AlterField(
- model_name='resource',
- name='slave',
- field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.DO_NOTHING, to='jenkins.JenkinsSlave'),
- ),
- ]
diff --git a/dashboard/src/dashboard/migrations/0003_resource_resource_lab.py b/dashboard/src/dashboard/migrations/0003_resource_resource_lab.py
deleted file mode 100644
index fff93fd..0000000
--- a/dashboard/src/dashboard/migrations/0003_resource_resource_lab.py
+++ /dev/null
@@ -1,22 +0,0 @@
-# -*- coding: utf-8 -*-
-# Generated by Django 1.10 on 2018-01-10 16:36
-from __future__ import unicode_literals
-
-from django.db import migrations, models
-import django.db.models.deletion
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('account', '0002_auto_20180110_1636'),
- ('dashboard', '0002_auto_20170505_0815'),
- ]
-
- operations = [
- migrations.AddField(
- model_name='resource',
- name='resource_lab',
- field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='lab_resource_set', to='account.Lab'),
- ),
- ]
diff --git a/dashboard/src/dashboard/models.py b/dashboard/src/dashboard/models.py
index ff55232..f9bd07e 100644
--- a/dashboard/src/dashboard/models.py
+++ b/dashboard/src/dashboard/models.py
@@ -1,97 +1,9 @@
##############################################################################
# Copyright (c) 2016 Max Breitenfeldt and others.
+# Copyright (c) 2018 Parker Berberian, Sawyer Bergeron, and others.
#
# All rights reserved. This program and the accompanying materials
# are made available under the terms of the Apache License, Version 2.0
# which accompanies this distribution, and is available at
# http://www.apache.org/licenses/LICENSE-2.0
##############################################################################
-
-
-from datetime import timedelta
-
-from django.contrib.auth.models import User
-from django.db import models
-from django.utils import timezone
-
-from jenkins.models import JenkinsSlave
-from account.models import Lab
-
-
-class Resource(models.Model):
- id = models.AutoField(primary_key=True)
- name = models.CharField(max_length=100, unique=True)
- description = models.CharField(max_length=300, blank=True, null=True)
- url = models.CharField(max_length=100, blank=True, null=True)
- resource_lab = models.ForeignKey(Lab, related_name='lab_resource_set', null=True, blank=True)
- owner = models.ForeignKey(User, related_name='user_lab_owner', null=True, blank=True)
- vpn_users = models.ManyToManyField(User, related_name='user_vpn_users', blank=True)
- slave = models.ForeignKey(JenkinsSlave, on_delete=models.DO_NOTHING, null=True, blank=True)
- dev_pod = models.BooleanField(default=False)
-
- def get_booking_utilization(self, weeks):
- """
- Return a dictionary containing the count of booked and free seconds for a resource in the
- range [now,now + weeks] if weeks is positive,
- or [now-weeks, now] if weeks is negative
- """
-
- length = timedelta(weeks=abs(weeks))
- now = timezone.now()
-
- start = now
- end = now + length
- if weeks < 0:
- start = now - length
- end = now
-
- bookings = self.booking_set.filter(start__lt=start + length, end__gt=start)
-
- booked_seconds = 0
- for booking in bookings:
- booking_start = booking.start
- booking_end = booking.end
- if booking_start < start:
- booking_start = start
- if booking_end > end:
- booking_end = start + length
- total = booking_end - booking_start
- booked_seconds += total.total_seconds()
-
- return {'booked_seconds': booked_seconds,
- 'available_seconds': length.total_seconds() - booked_seconds}
-
- class Meta:
- db_table = 'resource'
-
- def __str__(self):
- return self.name
-
-class Server(models.Model):
- id = models.AutoField(primary_key=True)
- resource = models.ForeignKey(Resource, on_delete=models.CASCADE)
- name = models.CharField(max_length=100, blank=True)
- model = models.CharField(max_length=100, blank=True)
- cpu = models.CharField(max_length=100, blank=True)
- ram = models.CharField(max_length=100, blank=True)
- storage = models.CharField(max_length=100, blank=True)
-
- class Meta:
- db_table = 'server'
-
- def __str__(self):
- return self.name
-
-class ResourceStatus(models.Model):
- id = models.AutoField(primary_key=True)
- resource = models.ForeignKey(Resource, on_delete=models.CASCADE)
- timestamp = models.DateTimeField(auto_now_add=True)
- type = models.CharField(max_length=20)
- title = models.CharField(max_length=50)
- content = models.CharField(max_length=5000)
-
- class Meta:
- db_table = 'resource_status'
-
- def __str__(self):
- return self.resource.name + ': ' + self.title + ' ' + str(self.timestamp)
diff --git a/dashboard/src/dashboard/populate_db_iol.py b/dashboard/src/dashboard/populate_db_iol.py
new file mode 100644
index 0000000..916dd97
--- /dev/null
+++ b/dashboard/src/dashboard/populate_db_iol.py
@@ -0,0 +1,350 @@
+##############################################################################
+# 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):
+ """
+ returns a dictionary of data from the yaml files
+ created by inspection scripts
+ """
+ data = []
+ for prof in ["hpe", "arm"]: # TODO
+ profile_dict = {}
+ host = {
+ "name": prof,
+ "type": 0,
+ "description": "some LaaS servers"
+ }
+ profile_dict['host'] = host
+ profile_dict['interfaces'] = []
+ for interface in [{"name": "eno1", "speed": 1000}, {"name": "eno2", "speed": 10000}]: # TODO
+ iface_dict = {}
+ iface_dict["name"] = interface['name']
+ iface_dict['speed'] = interface['speed']
+ profile_dict['interfaces'].append(iface_dict)
+
+ profile_dict['disks'] = []
+ for disk in [{"size": 1000, "type": "ssd", "name": "sda"}]: # TODO
+ disk_dict = {}
+ disk_dict['size'] = disk['size']
+ disk_dict['type'] = disk['type']
+ disk_dict['name'] = disk['name']
+ profile_dict['disks'].append(disk_dict)
+
+ # cpu
+ cpu = {}
+ cpu['cores'] = 4
+ cpu['arch'] = "x86"
+ cpu['cpus'] = 2
+ profile_dict['cpu'] = cpu
+
+ # ram
+ ram = {}
+ ram['amount'] = 256
+ ram['channels'] = 4
+ profile_dict['ram'] = ram
+
+ data.append(profile_dict)
+
+ return data
+
+ def get_lab_data(self, lab):
+ data = {}
+ path = "/pharos_dashboard/data/" + lab.name + "/"
+ host_file = open(path + "hostlist.json")
+ host_structure = json.loads(host_file.read())
+ host_file.close()
+ for profile in host_structure['profiles'].keys():
+ data[profile] = {}
+ prof_path = path + profile
+ for host in host_structure['profiles'][profile]:
+ host_file = open(prof_path + "/" + host + ".yaml")
+ host_data = yaml.load(host_file.read())
+ host_file.close()
+ data[profile][host] = host_data
+ return data
+
+ def make_profiles_and_hosts(self, lab, lab_data):
+ for host_profile_name, host_data_dict in lab_data.items():
+ if len(host_data_dict) < 1:
+ continue
+ host_profile = HostProfile.objects.create(
+ name=host_profile_name,
+ description=""
+ )
+ host_profile.labs.add(lab)
+ example_host_data = list(host_data_dict.values())[0]
+
+ cpu_data = example_host_data['cpu']
+ CpuProfile.objects.create(
+ cores=cpu_data['cores'],
+ architecture=cpu_data['arch'],
+ cpus=cpu_data['cpus'],
+ host=host_profile
+ )
+
+ ram_data = example_host_data['memory']
+ RamProfile.objects.create(
+ amount=int(ram_data[:-1]),
+ channels=1,
+ host=host_profile
+ )
+
+ disks_data = example_host_data['disk']
+ for disk_data in disks_data:
+ size = 0
+ try:
+ size = int(disk_data['size'].split('.')[0])
+ except 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/dashboard/src/dashboard/tasks.py b/dashboard/src/dashboard/tasks.py
index fa2ee9d..597629f 100644
--- a/dashboard/src/dashboard/tasks.py
+++ b/dashboard/src/dashboard/tasks.py
@@ -1,5 +1,6 @@
##############################################################################
# Copyright (c) 2016 Max Breitenfeldt and others.
+# Copyright (c) 2018 Parker Berberian, Sawyer Bergeron, and others.
#
# All rights reserved. This program and the accompanying materials
# are made available under the terms of the Apache License, Version 2.0
@@ -7,32 +8,101 @@
# http://www.apache.org/licenses/LICENSE-2.0
##############################################################################
-from datetime import timedelta
from celery import shared_task
from django.utils import timezone
-from django.conf import settings
from booking.models import Booking
+from notifier.manager import NotificationHandler
+from api.models import Job, JobStatus, SoftwareRelation, HostHardwareRelation, HostNetworkRelation, AccessRelation
+from resource_inventory.resource_manager import ResourceManager
-from jenkins.models import JenkinsStatistic
@shared_task
-def database_cleanup():
- now = timezone.now()
- JenkinsStatistic.objects.filter(timestamp__lt=now - timedelta(weeks=4)).delete()
-
-def booking_cleanup():
- expire_time = timedelta(days=int(settings.BOOKING_EXP_TIME))
- expire_number = int(settings.BOOKING_MAX_NUM)
- expired_set = Booking.objects.filter(end__lte=timezone.now())
- expired_count = len(expired_set)
-
- for booking in expired_set:
- if timezone.now() - booking.end > expire_time:
- booking.delete()
- expired_count = expired_count - 1
-
- if expired_count > expire_number:
- oldest = expired_set.order_by("end")[:expired_count-expire_number]
- for booking in oldest:
- booking.delete()
+def booking_poll():
+ def cleanup_hardware(qs):
+ for hostrelation in qs:
+ config = hostrelation.config
+ config.clear_delta()
+ config.set_power("off")
+ config.save()
+ hostrelation.status = JobStatus.NEW
+ hostrelation.save()
+
+ def cleanup_network(qs):
+ for hostrelation in qs:
+ network = hostrelation.config
+ network.interfaces.clear()
+ host = hostrelation.host
+ network.clear_delta()
+ vlans = []
+ for interface in host.interfaces.all():
+ for vlan in interface.config.all():
+ if vlan.public:
+ try:
+ host.lab.vlan_manager.release_public_vlan(vlan.vlan_id)
+ except Exception: # will fail if we already released in this loop
+ pass
+ else:
+ vlans.append(vlan.vlan_id)
+
+ # release all vlans
+ if len(vlans) > 0:
+ host.lab.vlan_manager.release_vlans(vlans)
+
+ interface.config.clear()
+ network.add_interface(interface)
+ network.save()
+ hostrelation.status = JobStatus.NEW
+ hostrelation.save()
+
+ def cleanup_software(qs):
+ if qs.exists():
+ relation = qs.first()
+ software = relation.config.opnfv
+ software.clear_delta()
+ software.save()
+ relation.status = JobStatus.NEW
+ relation.save()
+
+ def cleanup_access(qs):
+ for relation in qs:
+ 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_hardware(HostHardwareRelation.objects.filter(job=job))
+ cleanup_network(HostNetworkRelation.objects.filter(job=job))
+ cleanup_access(AccessRelation.objects.filter(job=job))
+ job.complete = True
+ job.save()
+ NotificationHandler.notify_booking_end(booking)
+
+
+@shared_task
+def free_hosts():
+ """
+ gets all hosts from the database that need to be freed and frees them
+ """
+ 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,
+ resource__isnull=False
+ )
+ for booking in bookings:
+ ResourceManager.getInstance().deleteResourceBundle(booking.resource)
diff --git a/dashboard/src/dashboard/templatetags/__init__.py b/dashboard/src/dashboard/templatetags/__init__.py
index b5914ce..b6fef6c 100644
--- a/dashboard/src/dashboard/templatetags/__init__.py
+++ b/dashboard/src/dashboard/templatetags/__init__.py
@@ -6,5 +6,3 @@
# which accompanies this distribution, and is available at
# http://www.apache.org/licenses/LICENSE-2.0
##############################################################################
-
-
diff --git a/dashboard/src/dashboard/templatetags/jenkins_filters.py b/dashboard/src/dashboard/templatetags/jenkins_filters.py
deleted file mode 100644
index e7e1425..0000000
--- a/dashboard/src/dashboard/templatetags/jenkins_filters.py
+++ /dev/null
@@ -1,38 +0,0 @@
-##############################################################################
-# Copyright (c) 2016 Max Breitenfeldt and others.
-#
-# All rights reserved. This program and the accompanying materials
-# are made available under the terms of the Apache License, Version 2.0
-# which accompanies this distribution, and is available at
-# http://www.apache.org/licenses/LICENSE-2.0
-##############################################################################
-
-
-from django.template.defaultfilters import register
-
-
-@register.filter
-def jenkins_job_color(job_result):
- if job_result == 'SUCCESS':
- return '#5cb85c'
- if job_result == 'FAILURE':
- return '#d9534f'
- if job_result == 'UNSTABLE':
- return '#EDD62B'
- return '#646F73' # job is still building
-
-
-@register.filter
-def jenkins_status_color(slave_status):
- if slave_status == 'offline':
- return '#d9534f'
- if slave_status == 'online':
- return '#5cb85c'
- if slave_status == 'online / idle':
- return '#5bc0de'
-
-
-@register.filter
-def jenkins_job_blink(job_result):
- if job_result == '': # job is still building
- return 'class=blink_me'
diff --git a/dashboard/src/dashboard/testing_utils.py b/dashboard/src/dashboard/testing_utils.py
new file mode 100644
index 0000000..a96b6d0
--- /dev/null
+++ b/dashboard/src/dashboard/testing_utils.py
@@ -0,0 +1,396 @@
+##############################################################################
+# 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
+import re
+from datetime import timedelta
+
+from dashboard.exceptions import InvalidHostnameException
+from booking.models import Booking
+from account.models import UserProfile, Lab, LabStatus, VlanManager, PublicNetwork
+from resource_inventory.models import (
+ Host,
+ HostProfile,
+ InterfaceProfile,
+ DiskProfile,
+ CpuProfile,
+ Opsys,
+ Image,
+ Scenario,
+ Installer,
+ OPNFVRole,
+ RamProfile,
+ Network,
+ GenericResourceBundle,
+ GenericResource,
+ GenericHost,
+ ConfigBundle,
+ GenericInterface,
+ HostConfiguration,
+ OPNFVConfig,
+ NetworkConnection,
+ HostOPNFVConfig
+)
+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):
+
+ grb, host_set = make_grb(topology, owner, lab)
+ config_bundle, opnfv_bundle = make_config_bundle(grb, owner, topology, host_set, installer, scenario)
+ resource = ResourceManager.getInstance().convertResourceBundle(grb, config=config_bundle)
+ if not resource:
+ raise Exception("Resource not created")
+
+ return Booking.objects.create(
+ resource=resource,
+ config_bundle=config_bundle,
+ start=start,
+ end=end,
+ owner=owner,
+ purpose=purpose,
+ project=project,
+ lab=lab,
+ opnfv_config=opnfv_bundle
+ )
+
+
+def make_config_bundle(grb, owner, topology={}, host_set={},
+ installer=None, scenario=None):
+ cb = ConfigBundle.objects.create(
+ owner=owner,
+ name="config bundle " + str(ConfigBundle.objects.count()),
+ description="cb generated by make_config_bundle() method"
+ )
+
+ opnfv_config = OPNFVConfig.objects.create(
+ installer=installer,
+ scenario=scenario,
+ bundle=cb
+ )
+
+ # generate host configurations based on topology and host set
+ for hostname, host_info in topology.items():
+ host_config = HostConfiguration.objects.create(
+ host=host_set[hostname],
+ image=host_info["image"],
+ bundle=cb,
+ is_head_node=host_info['role'].name.lower() == "jumphost"
+ )
+ HostOPNFVConfig.objects.create(
+ role=host_info["role"],
+ host_config=host_config,
+ opnfv_config=opnfv_config
+ )
+ return cb, opnfv_config
+
+
+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_net = lab.vlan_manager.get_vlan()
+ if not private_net:
+ raise Exception("No more generic vlans are available")
+ lab.vlan_manager.reserve_vlans([private_net])
+ network.vlan_id = private_net
+
+ network.save()
+ return network
+
+
+def make_grb(topology, owner, lab):
+
+ grb = GenericResourceBundle.objects.create(
+ owner=owner,
+ lab=lab,
+ name="Generic ResourceBundle " + str(GenericResourceBundle.objects.count()),
+ description="grb generated by make_grb() method"
+ )
+
+ networks = {}
+ host_set = {}
+
+ for hostname, info in topology.items():
+ host_profile = info["type"]
+
+ # need to construct host from hostname and type
+ generic_host = make_generic_host(grb, host_profile, hostname)
+ host_set[hostname] = generic_host
+
+ # set up networks
+ nets = info["nets"]
+ for interface_index, interface_profile in enumerate(host_profile.interfaceprofile.all()):
+ generic_interface = GenericInterface.objects.create(host=generic_host, profile=interface_profile)
+ netconfig = nets[interface_index]
+ for network_info in netconfig:
+ network_name = network_info["name"]
+ if network_name not in networks:
+ networks[network_name] = make_network(network_name, lab, grb, network_info['public'])
+
+ generic_interface.connections.add(NetworkConnection.objects.create(
+ network=networks[network_name],
+ vlan_is_tagged=network_info["tagged"]
+ ))
+
+ return grb, host_set
+
+
+def make_generic_host(grb, host_profile, hostname):
+ if not re.match(r"(?=^.{1,253}$)(^([A-Za-z0-9-_]{1,62}\.)*[A-Za-z0-9-_]{1,63})$", hostname):
+ raise InvalidHostnameException("Hostname must comply to RFC 952 and all extensions")
+ gresource = GenericResource.objects.create(bundle=grb, name=hostname)
+
+ return GenericHost.objects.create(resource=gresource, profile=host_profile)
+
+
+def make_user(is_superuser=False, username="testuser",
+ password="testpassword", email="default_email@user.com"):
+ user = User.objects.create_user(username=username, email=email, password=password)
+ 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 User.objects.first() or make_user()
+ profile = UserProfile.objects.create(
+ email_addr=email_addr,
+ company=company,
+ full_name=full_name,
+ booking_privledge=booking_privledge,
+ user=user
+ )
+ 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 not vlan_manager:
+ vlan_manager = make_vlan_manager()
+
+ if not user:
+ user = make_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_complete_host_profile(lab, name="test_hostprofile"):
+ host_profile = make_host_profile(lab, name=name)
+ make_disk_profile(host_profile, 500, name=name)
+ make_cpu_profile(host_profile)
+ make_interface_profile(host_profile, name=name)
+ make_ram_profile(host_profile)
+
+ return host_profile
+
+
+def make_host_profile(lab, host_type=0, name="test hostprofile"):
+ host_profile = HostProfile.objects.create(
+ host_type=host_type,
+ name=name,
+ description='test hostprofile instance'
+ )
+ host_profile.labs.add(lab)
+
+ return host_profile
+
+
+def make_ram_profile(host, channels=4, amount=256):
+ return RamProfile.objects.create(
+ host=host,
+ amount=amount,
+ channels=channels
+ )
+
+
+def make_disk_profile(hostprofile, size=0, media_type="SSD",
+ name="test diskprofile", rotation=0,
+ interface="sata"):
+ return DiskProfile.objects.create(
+ name=name,
+ size=size,
+ media_type=media_type,
+ host=hostprofile,
+ rotation=rotation,
+ interface=interface
+ )
+
+
+def make_cpu_profile(hostprofile,
+ cores=4,
+ architecture="x86_64",
+ cpus=4,):
+ return CpuProfile.objects.create(
+ cores=cores,
+ architecture=architecture,
+ cpus=cpus,
+ host=hostprofile,
+ cflags=''
+ )
+
+
+def make_interface_profile(hostprofile,
+ speed=1000,
+ name="test interface profile",
+ nic_type="pcie"):
+ return InterfaceProfile.objects.create(
+ host=hostprofile,
+ name=name,
+ speed=speed,
+ nic_type=nic_type
+ )
+
+
+def make_image(lab, lab_id, owner, os, host_profile,
+ public=True, name="default image", description="default image"):
+ return Image.objects.create(
+ from_lab=lab,
+ lab_id=lab_id,
+ os=os,
+ host_type=host_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, name="test OS"):
+ os = Opsys.objects.create(name=name)
+ for installer in installers:
+ os.sup_installers.add(installer)
+
+ return os
+
+
+def make_host(host_profile, lab, labid="test_host", name="test_host",
+ booked=False, working=True, config=None, template=None,
+ bundle=None, model="Model 1", vendor="ACME"):
+ return Host.objects.create(
+ lab=lab,
+ profile=host_profile,
+ name=name,
+ booked=booked,
+ working=working,
+ config=config,
+ template=template,
+ 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/dashboard/src/dashboard/tests/__init__.py b/dashboard/src/dashboard/tests/__init__.py
index b5914ce..b6fef6c 100644
--- a/dashboard/src/dashboard/tests/__init__.py
+++ b/dashboard/src/dashboard/tests/__init__.py
@@ -6,5 +6,3 @@
# which accompanies this distribution, and is available at
# http://www.apache.org/licenses/LICENSE-2.0
##############################################################################
-
-
diff --git a/dashboard/src/dashboard/tests/test_models.py b/dashboard/src/dashboard/tests/test_models.py
deleted file mode 100644
index 3a3aeab..0000000
--- a/dashboard/src/dashboard/tests/test_models.py
+++ /dev/null
@@ -1,69 +0,0 @@
-##############################################################################
-# Copyright (c) 2016 Max Breitenfeldt and others.
-#
-# All rights reserved. This program and the accompanying materials
-# are made available under the terms of the Apache License, Version 2.0
-# which accompanies this distribution, and is available at
-# http://www.apache.org/licenses/LICENSE-2.0
-##############################################################################
-
-
-from datetime import timedelta
-from math import ceil, floor
-
-from django.test import TestCase
-from django.utils import timezone
-
-from booking.models import *
-from dashboard.models import Resource
-from jenkins.models import JenkinsSlave
-
-
-class ResourceModelTestCase(TestCase):
- def setUp(self):
- self.slave = JenkinsSlave.objects.create(name='test', url='test')
- self.owner = User.objects.create(username='owner')
-
- self.res1 = Resource.objects.create(name='res1', slave=self.slave, description='x',
- url='x', owner=self.owner)
-
- def test_booking_utilization(self):
- utilization = self.res1.get_booking_utilization(1)
- self.assertTrue(utilization['booked_seconds'] == 0)
- self.assertTrue(utilization['available_seconds'] == timedelta(weeks=1).total_seconds())
-
- start = timezone.now() + timedelta(days=1)
- end = start + timedelta(days=1)
- booking = Booking.objects.create(start=start, end=end, purpose='test', resource=self.res1,
- user=self.owner)
-
- utilization = self.res1.get_booking_utilization(1)
- booked_seconds = timedelta(days=1).total_seconds()
- self.assertEqual(utilization['booked_seconds'], booked_seconds)
-
- utilization = self.res1.get_booking_utilization(-1)
- self.assertEqual(utilization['booked_seconds'], 0)
-
- booking.delete()
- start = timezone.now() - timedelta(days=1)
- end = start + timedelta(days=2)
- booking = Booking.objects.create(start=start, end=end, purpose='test', resource=self.res1,
- user=self.owner)
- booked_seconds = self.res1.get_booking_utilization(1)['booked_seconds']
- # use ceil because a fraction of the booked time has already passed now
- booked_seconds = ceil(booked_seconds)
- self.assertEqual(booked_seconds, timedelta(days=1).total_seconds())
-
- booking.delete()
- start = timezone.now() + timedelta(days=6)
- end = start + timedelta(days=2)
- booking = Booking.objects.create(start=start, end=end, purpose='test', resource=self.res1,
- user=self.owner)
- booked_seconds = self.res1.get_booking_utilization(1)['booked_seconds']
- booked_seconds = floor(booked_seconds)
- self.assertEqual(booked_seconds, timedelta(days=1).total_seconds())
-
-
-
-
-
diff --git a/dashboard/src/dashboard/tests/test_views.py b/dashboard/src/dashboard/tests/test_views.py
deleted file mode 100644
index f5e17c2..0000000
--- a/dashboard/src/dashboard/tests/test_views.py
+++ /dev/null
@@ -1,75 +0,0 @@
-##############################################################################
-# Copyright (c) 2016 Max Breitenfeldt and others.
-#
-# All rights reserved. This program and the accompanying materials
-# are made available under the terms of the Apache License, Version 2.0
-# which accompanies this distribution, and is available at
-# http://www.apache.org/licenses/LICENSE-2.0
-##############################################################################
-
-
-from django.test import TestCase
-from django.urls import reverse
-
-from dashboard.models import Resource
-from jenkins.models import JenkinsSlave
-
-
-class DashboardViewTestCase(TestCase):
- def setUp(self):
- self.slave_active = JenkinsSlave.objects.create(name='slave_active', url='x', active=True)
- self.slave_inactive = JenkinsSlave.objects.create(name='slave_inactive', url='x',
- active=False)
- self.res_active = Resource.objects.create(name='res_active', slave=self.slave_active,
- description='x', url='x')
- self.res_inactive = Resource.objects.create(name='res_inactive', slave=self.slave_inactive,
- description='x', url='x')
-
- def test_booking_utilization_json(self):
- url = reverse('dashboard:booking_utilization', kwargs={'resource_id': 0, 'weeks': 0})
- self.assertEqual(self.client.get(url).status_code, 404)
-
- url = reverse('dashboard:booking_utilization', kwargs={'resource_id': self.res_active.id,
- 'weeks': 0})
- response = self.client.get(url)
- self.assertEqual(response.status_code, 200)
- self.assertContains(response, 'data')
-
- def test_jenkins_utilization_json(self):
- url = reverse('dashboard:jenkins_utilization', kwargs={'resource_id': 0, 'weeks': 0})
- self.assertEqual(self.client.get(url).status_code, 404)
-
- url = reverse('dashboard:jenkins_utilization', kwargs={'resource_id': self.res_active.id,
- 'weeks': 0})
- response = self.client.get(url)
- self.assertEqual(response.status_code, 200)
- self.assertContains(response, 'data')
-
- def test_jenkins_slaves_view(self):
- url = reverse('dashboard:jenkins_slaves')
- response = self.client.get(url)
- self.assertEqual(response.status_code, 200)
- self.assertIn(self.slave_active, response.context['slaves'])
- self.assertNotIn(self.slave_inactive, response.context['slaves'])
-
- def test_ci_pods_view(self):
- url = reverse('dashboard:ci_pods')
- response = self.client.get(url)
- self.assertEqual(response.status_code, 200)
- self.assertEqual(len(response.context['ci_pods']), 0)
-
- self.slave_active.ci_slave = True
- self.slave_inactive.ci_slave = True
- self.slave_active.save()
- self.slave_inactive.save()
-
- response = self.client.get(url)
- self.assertIn(self.res_active, response.context['ci_pods'])
- self.assertNotIn(self.res_inactive, response.context['ci_pods'])
-
- def test_dev_pods_view(self):
- url = reverse('dashboard:dev_pods')
- response = self.client.get(url)
- self.assertEqual(response.status_code, 200)
- self.assertEqual(len(response.context['dev_pods']), 0)
-
diff --git a/dashboard/src/dashboard/urls.py b/dashboard/src/dashboard/urls.py
index 609e5d6..571a987 100644
--- a/dashboard/src/dashboard/urls.py
+++ b/dashboard/src/dashboard/urls.py
@@ -1,5 +1,6 @@
##############################################################################
# Copyright (c) 2016 Max Breitenfeldt and others.
+# Copyright (c) 2018 Sawyer Bergeron, Parker Berberian, and others.
#
# All rights reserved. This program and the accompanying materials
# are made available under the terms of the Apache License, Version 2.0
@@ -24,18 +25,17 @@ Including another URLconf
2. Add a URL to urlpatterns: url(r'^blog/', include('blog.urls'))
"""
from django.conf.urls import url
+from dashboard.views import (
+ landing_view,
+ lab_list_view,
+ lab_detail_view,
+ host_profile_detail_view
+)
-from dashboard.views import *
-
+app_name = "dashboard"
urlpatterns = [
- url(r'^ci_pods/$', CIPodsView.as_view(), name='ci_pods'),
- url(r'^dev_pods/$', DevelopmentPodsView.as_view(), name='dev_pods'),
- url(r'^jenkins_slaves/$', JenkinsSlavesView.as_view(), name='jenkins_slaves'),
- url(r'^resource/all/$', LabOwnerView.as_view(), name='resources'),
- url(r'^resource/(?P<resource_id>[0-9]+)/$', ResourceView.as_view(), name='resource'),
- url(r'^resource/(?P<resource_id>[0-9]+)/booking_utilization/(?P<weeks>-?\d+)/$',
- BookingUtilizationJSON.as_view(), name='booking_utilization'),
- url(r'^resource/(?P<resource_id>[0-9]+)/jenkins_utilization/(?P<weeks>-?\d+)/$',
- JenkinsUtilizationJSON.as_view(), name='jenkins_utilization'),
- url(r'^$', DevelopmentPodsView.as_view(), name="index"),
+ url(r'^$', landing_view, name='index'),
+ url(r'^lab/$', lab_list_view, name='all_labs'),
+ url(r'^lab/(?P<lab_name>.+)/$', lab_detail_view, name='lab_detail'),
+ url(r'^hosts/$', host_profile_detail_view, name="hostprofile_detail")
]
diff --git a/dashboard/src/dashboard/views.py b/dashboard/src/dashboard/views.py
index 4bab036..aaad7ab 100644
--- a/dashboard/src/dashboard/views.py
+++ b/dashboard/src/dashboard/views.py
@@ -1,5 +1,6 @@
##############################################################################
# Copyright (c) 2016 Max Breitenfeldt and others.
+# Copyright (c) 2018 Parker Berberian, Sawyer Bergeron, and others.
#
# All rights reserved. This program and the accompanying materials
# are made available under the terms of the Apache License, Version 2.0
@@ -8,139 +9,113 @@
##############################################################################
-from datetime import timedelta
-
-from django.http import JsonResponse
from django.shortcuts import get_object_or_404
-from django.utils import timezone
-from django.views import View
from django.views.generic import TemplateView
+from django.shortcuts import render
+from django.http import HttpResponseRedirect
-from booking.models import Booking
-from dashboard.models import Resource
-from jenkins.models import JenkinsSlave
+from account.models import Lab
+from resource_inventory.models import Image, HostProfile
+from workflow.views import create_session
+from workflow.workflow_manager import ManagerTracker
-class JenkinsSlavesView(TemplateView):
- template_name = "dashboard/jenkins_slaves.html"
- def get_context_data(self, **kwargs):
- slaves = JenkinsSlave.objects.filter(active=True)
- context = super(JenkinsSlavesView, self).get_context_data(**kwargs)
- context.update({'title': "Jenkins Slaves", 'slaves': slaves})
- return context
+def lab_list_view(request):
+ labs = Lab.objects.all()
+ context = {"labs": labs}
+ return render(request, "dashboard/lab_list.html", context)
-class CIPodsView(TemplateView):
- template_name = "dashboard/ci_pods.html"
- def get_context_data(self, **kwargs):
- ci_pods = Resource.objects.filter(slave__ci_slave=True, slave__active=True)
- context = super(CIPodsView, self).get_context_data(**kwargs)
- context.update({'title': "CI Pods", 'ci_pods': ci_pods})
- return context
+def lab_detail_view(request, lab_name):
+ user = None
+ if request.user.is_authenticated:
+ user = request.user
+ lab = get_object_or_404(Lab, name=lab_name)
-class DevelopmentPodsView(TemplateView):
- template_name = "dashboard/dev_pods.html"
+ images = Image.objects.filter(from_lab=lab).filter(public=True)
+ if user:
+ images = images | Image.objects.filter(from_lab=lab).filter(owner=user)
- def get_context_data(self, **kwargs):
- resources = Resource.objects.filter(dev_pod=True)
-
- bookings = Booking.objects.filter(start__lte=timezone.now())
- bookings = bookings.filter(end__gt=timezone.now())
-
- dev_pods = []
- for resource in resources:
- booking_utilization = resource.get_booking_utilization(weeks=4)
- total = booking_utilization['booked_seconds'] + booking_utilization['available_seconds']
- try:
- utilization_percentage = "%d%%" % (float(booking_utilization['booked_seconds']) /
- total * 100)
- except (ValueError, ZeroDivisionError):
- return ""
-
- dev_pod = (resource, None, utilization_percentage)
- for booking in bookings:
- if booking.resource == resource:
- dev_pod = (resource, booking, utilization_percentage)
- dev_pods.append(dev_pod)
-
- context = super(DevelopmentPodsView, self).get_context_data(**kwargs)
- context.update({'title': "Development Pods", 'dev_pods': dev_pods})
- return context
+ return render(
+ request,
+ "dashboard/lab_detail.html",
+ {
+ 'title': "Lab Overview",
+ 'lab': lab,
+ 'hostprofiles': lab.hostprofiles.all(),
+ 'images': images,
+ }
+ )
-class ResourceView(TemplateView):
- template_name = "dashboard/resource.html"
+def host_profile_detail_view(request):
- def get_context_data(self, **kwargs):
- resource = get_object_or_404(Resource, id=self.kwargs['resource_id'])
- bookings = Booking.objects.filter(resource=resource, end__gt=timezone.now())
- context = super(ResourceView, self).get_context_data(**kwargs)
- context.update({'title': str(resource), 'resource': resource, 'bookings': bookings})
- return context
+ return render(
+ request,
+ "dashboard/host_profile_detail.html",
+ {
+ 'title': "Host Types",
+ }
+ )
-class LabOwnerView(TemplateView):
- template_name = "dashboard/resource_all.html"
+def landing_view(request):
+ manager = None
+ manager_detected = False
+ if 'manager_session' in request.session:
- def get_context_data(self, **kwargs):
- resources = Resource.objects.filter(slave__dev_pod=True, slave__active=True)
- pods = []
- for resource in resources:
- utilization = resource.slave.get_utilization(timedelta(days=7))
- bookings = Booking.objects.filter(resource=resource, end__gt=timezone.now())
- pods.append((resource, utilization, bookings))
- context = super(LabOwnerView, self).get_context_data(**kwargs)
- context.update({'title': "Overview", 'pods': pods})
- return context
+ try:
+ manager = ManagerTracker.managers[request.session['manager_session']]
+
+ except KeyError:
+ pass
+
+ if manager is not None:
+ # no manager detected, don't display continue button
+ manager_detected = True
+ if request.method == 'GET':
+ return render(request, 'dashboard/landing.html', {'manager': manager_detected, 'title': "Welcome to the Lab as a Service Dashboard"})
-class BookingUtilizationJSON(View):
- def get(self, request, *args, **kwargs):
- resource = get_object_or_404(Resource, id=kwargs['resource_id'])
- utilization = resource.get_booking_utilization(int(kwargs['weeks']))
- utilization = [
- {
- 'label': 'Booked',
- 'data': utilization['booked_seconds'],
- 'color': '#d9534f'
- },
- {
- 'label': 'Available',
- 'data': utilization['available_seconds'],
- 'color': '#5cb85c'
- },
- ]
- return JsonResponse({'data': utilization})
-
-
-class JenkinsUtilizationJSON(View):
- def get(self, request, *args, **kwargs):
- resource = get_object_or_404(Resource, id=kwargs['resource_id'])
- weeks = int(kwargs['weeks'])
+ if request.method == 'POST':
try:
- utilization = resource.slave.get_utilization(timedelta(weeks=weeks))
- utilization = [
- {
- 'label': 'Offline',
- 'data': utilization['offline'],
- 'color': '#d9534f'
- },
- {
- 'label': 'Online',
- 'data': utilization['online'],
- 'color': '#5cb85c'
- },
- {
- 'label': 'Idle',
- 'data': utilization['idle'],
- 'color': '#5bc0de'
- },
- ]
- jutilization = JsonResponse({'data': utilization})
- except AttributeError:
- return JsonResponse({'data': ''})
- if jutilization:
- return jutilization
+ create = request.POST['create']
+
+ if manager is not None:
+ del manager
+
+ mgr_uuid = create_session(create, request=request,)
+ request.session['manager_session'] = mgr_uuid
+ return HttpResponseRedirect('/wf/')
+
+ except KeyError:
+ pass
+
+
+class LandingView(TemplateView):
+ template_name = "dashboard/landing.html"
+
+ def get_context_data(self, **kwargs):
+ context = super(LandingView, self).get_context_data(**kwargs)
+
+ hosts = []
+
+ for host_profile in HostProfile.objects.all():
+ name = host_profile.name
+ description = host_profile.description
+ in_labs = host_profile.labs
+
+ interfaces = host_profile.interfaceprofile
+ storage = host_profile.storageprofile
+ cpu = host_profile.cpuprofile
+ ram = host_profile.ramprofile
+
+ host = (name, description, in_labs, interfaces, storage, cpu, ram)
+ hosts.append(host)
+
+ context.update({'hosts': hosts})
+
+ return context
diff --git a/dashboard/src/jenkins/adapter.py b/dashboard/src/jenkins/adapter.py
deleted file mode 100644
index b48b868..0000000
--- a/dashboard/src/jenkins/adapter.py
+++ /dev/null
@@ -1,137 +0,0 @@
-##############################################################################
-# Copyright (c) 2016 Max Breitenfeldt and others.
-#
-# All rights reserved. This program and the accompanying materials
-# are made available under the terms of the Apache License, Version 2.0
-# which accompanies this distribution, and is available at
-# http://www.apache.org/licenses/LICENSE-2.0
-##############################################################################
-
-
-import logging
-import re
-from django.conf import settings
-
-import requests
-from django.core.cache import cache
-
-logger = logging.getLogger(__name__)
-
-# TODO: implement caching decorator, cache get_* functions
-def get_json(url):
- if cache.get(url) is None:
- try:
- response = requests.get(url)
- json = response.json()
- cache.set(url, json, 180) # cache result for 180 seconds
- return json
- except requests.exceptions.RequestException as e:
- logger.exception(e)
- except ValueError as e:
- logger.exception(e)
- else:
- return cache.get(url)
-
-
-def get_all_slaves():
- url = settings.ALL_SLAVES_URL
- json = get_json(url)
- if json is not None:
- return json['computer'] # return list of dictionaries
- return []
-
-
-
-
-def get_slave(slavename):
- slaves = get_all_slaves()
- for slave in slaves:
- if slave['displayName'] == slavename:
- return slave
- return {}
-
-
-def get_ci_slaves():
- url = settings.CI_SLAVES_URL
- json = get_json(url)
- if json is not None:
- return json['nodes']
- return []
-
-
-def get_all_jobs():
- url = settings.ALL_JOBS_URL
- json = get_json(url)
- if json is not None:
- return json['jobs'] # return list of dictionaries
- return []
-
-
-def get_jenkins_job(slavename):
- jobs = get_all_jobs()
- max_time = 0
- last_job = None
- for job in jobs:
- if job['lastBuild'] is not None:
- if job['lastBuild']['builtOn'] == slavename:
- if job['lastBuild']['building'] is True:
- return job # return active build
- if job['lastBuild']['timestamp'] > max_time:
- last_job = job
- max_time = job['lastBuild']['timestamp']
- return last_job
-
-
-def is_ci_slave(slavename):
- ci_slaves = get_ci_slaves()
- for ci_slave in ci_slaves:
- if ci_slave['nodeName'] == slavename:
- return True
- return False
-
-
-def is_dev_pod(slavename):
- if is_ci_slave(slavename):
- return False
- if slavename.find('pod') != -1:
- return True
- return False
-
-
-def parse_job(job):
- result = parse_job_string(job['lastBuild']['fullDisplayName'])
- result['building'] = job['lastBuild']['building']
- result['result'] = ''
- if not job['lastBuild']['building']:
- result['result'] = job['lastBuild']['result']
- result['url'] = job['url']
- return result
-
-
-def parse_job_string(full_displayname):
- job = {}
- job['scenario'] = ''
- job['installer'] = ''
- job['branch'] = ''
- tokens = re.split(r'[ -]', full_displayname)
- for i in range(len(tokens)):
- if tokens[i] == 'os':
- job['scenario'] = '-'.join(tokens[i: i + 4])
- elif tokens[i] in ['fuel', 'joid', 'apex', 'compass']:
- job['installer'] = tokens[i]
- elif tokens[i] in ['master', 'arno', 'brahmaputra', 'colorado']:
- job['branch'] = tokens[i]
- tokens = full_displayname.split(' ')
- job['name'] = tokens[0]
- return job
-
-def get_slave_url(slave):
- return settings.GET_SLAVE_URL + slave['displayName']
-
-
-def get_slave_status(slave):
- if not slave['offline'] and slave['idle']:
- return 'online / idle'
- if not slave['offline']:
- return 'online'
- return 'offline'
diff --git a/dashboard/src/jenkins/migrations/0001_initial.py b/dashboard/src/jenkins/migrations/0001_initial.py
deleted file mode 100644
index b1c7889..0000000
--- a/dashboard/src/jenkins/migrations/0001_initial.py
+++ /dev/null
@@ -1,53 +0,0 @@
-# -*- coding: utf-8 -*-
-# Generated by Django 1.10 on 2016-11-03 13:33
-from __future__ import unicode_literals
-
-from django.db import migrations, models
-import django.db.models.deletion
-
-
-class Migration(migrations.Migration):
-
- initial = True
-
- dependencies = [
- ]
-
- operations = [
- migrations.CreateModel(
- name='JenkinsSlave',
- fields=[
- ('id', models.AutoField(primary_key=True, serialize=False)),
- ('name', models.CharField(max_length=100, unique=True)),
- ('status', models.CharField(default='offline', max_length=30)),
- ('url', models.CharField(max_length=1024)),
- ('ci_slave', models.BooleanField(default=False)),
- ('dev_pod', models.BooleanField(default=False)),
- ('building', models.BooleanField(default=False)),
- ('last_job_name', models.CharField(default='', max_length=1024)),
- ('last_job_url', models.CharField(default='', max_length=1024)),
- ('last_job_scenario', models.CharField(default='', max_length=50)),
- ('last_job_branch', models.CharField(default='', max_length=50)),
- ('last_job_installer', models.CharField(default='', max_length=50)),
- ('last_job_result', models.CharField(default='', max_length=30)),
- ('active', models.BooleanField(default=False)),
- ],
- options={
- 'db_table': 'jenkins_slave',
- },
- ),
- migrations.CreateModel(
- name='JenkinsStatistic',
- fields=[
- ('id', models.AutoField(primary_key=True, serialize=False)),
- ('offline', models.BooleanField(default=False)),
- ('idle', models.BooleanField(default=False)),
- ('online', models.BooleanField(default=False)),
- ('timestamp', models.DateTimeField(auto_now_add=True)),
- ('slave', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='jenkins.JenkinsSlave')),
- ],
- options={
- 'db_table': 'jenkins_statistic',
- },
- ),
- ]
diff --git a/dashboard/src/jenkins/models.py b/dashboard/src/jenkins/models.py
deleted file mode 100644
index 8254ff3..0000000
--- a/dashboard/src/jenkins/models.py
+++ /dev/null
@@ -1,62 +0,0 @@
-##############################################################################
-# Copyright (c) 2016 Max Breitenfeldt and others.
-#
-# All rights reserved. This program and the accompanying materials
-# are made available under the terms of the Apache License, Version 2.0
-# which accompanies this distribution, and is available at
-# http://www.apache.org/licenses/LICENSE-2.0
-##############################################################################
-
-
-from django.db import models
-from django.utils import timezone
-
-
-class JenkinsSlave(models.Model):
- id = models.AutoField(primary_key=True)
- name = models.CharField(max_length=100, unique=True)
- status = models.CharField(max_length=30, default='offline')
- url = models.CharField(max_length=1024)
- ci_slave = models.BooleanField(default=False)
- dev_pod = models.BooleanField(default=False)
-
- building = models.BooleanField(default=False)
-
- last_job_name = models.CharField(max_length=1024, default='')
- last_job_url = models.CharField(max_length=1024, default='')
- last_job_scenario = models.CharField(max_length=50, default='')
- last_job_branch = models.CharField(max_length=50, default='')
- last_job_installer = models.CharField(max_length=50, default='')
- last_job_result = models.CharField(max_length=30, default='')
-
- active = models.BooleanField(default=False)
-
- def get_utilization(self, timedelta):
- """
- Return a dictionary containing the count of idle, online and offline measurements in the time from
- now-timedelta to now
- """
- utilization = {'idle': 0, 'online': 0, 'offline': 0}
- statistics = self.jenkinsstatistic_set.filter(timestamp__gte=timezone.now() - timedelta)
- utilization['idle'] = statistics.filter(idle=True).count()
- utilization['online'] = statistics.filter(online=True).count()
- utilization['offline'] = statistics.filter(offline=True).count()
- return utilization
-
- class Meta:
- db_table = 'jenkins_slave'
-
- def __str__(self):
- return self.name
-
-
-class JenkinsStatistic(models.Model):
- id = models.AutoField(primary_key=True)
- slave = models.ForeignKey(JenkinsSlave, on_delete=models.CASCADE)
- offline = models.BooleanField(default=False)
- idle = models.BooleanField(default=False)
- online = models.BooleanField(default=False)
- timestamp = models.DateTimeField(auto_now_add=True)
-
- class Meta:
- db_table = 'jenkins_statistic'
diff --git a/dashboard/src/jenkins/tasks.py b/dashboard/src/jenkins/tasks.py
deleted file mode 100644
index ea986c1..0000000
--- a/dashboard/src/jenkins/tasks.py
+++ /dev/null
@@ -1,64 +0,0 @@
-##############################################################################
-# Copyright (c) 2016 Max Breitenfeldt and others.
-#
-# All rights reserved. This program and the accompanying materials
-# are made available under the terms of the Apache License, Version 2.0
-# which accompanies this distribution, and is available at
-# http://www.apache.org/licenses/LICENSE-2.0
-##############################################################################
-
-
-from celery import shared_task
-
-from dashboard.models import Resource
-from jenkins.models import JenkinsSlave, JenkinsStatistic
-from .adapter import *
-
-
-@shared_task
-def sync_jenkins():
- update_jenkins_slaves()
-
-
-def update_jenkins_slaves():
- JenkinsSlave.objects.all().update(active=False)
-
- jenkins_slaves = get_all_slaves()
- for slave in jenkins_slaves:
- jenkins_slave, created = JenkinsSlave.objects.get_or_create(name=slave['displayName'],
- url=get_slave_url(slave))
- jenkins_slave.active = True
- jenkins_slave.ci_slave = is_ci_slave(slave['displayName'])
- jenkins_slave.dev_pod = is_dev_pod(slave['displayName'])
- jenkins_slave.status = get_slave_status(slave)
-
- # if this is a new slave and a pod, check if there is a resource for it, create one if not
- if created and 'pod' in slave['displayName']:
- # parse resource name from slave name
- # naming example: orange-pod1, resource name: Orange POD 1
- tokens = slave['displayName'].split('-')
- name = tokens[0].capitalize() + ' POD '# company name
- name += tokens[1][3:] # remove 'pod'
- resource, created = Resource.objects.get_or_create(name=name)
- resource.slave = jenkins_slave
- resource.save()
-
- last_job = get_jenkins_job(jenkins_slave.name)
- if last_job is not None:
- last_job = parse_job(last_job)
- jenkins_slave.last_job_name = last_job['name']
- jenkins_slave.last_job_url = last_job['url']
- jenkins_slave.last_job_scenario = last_job['scenario']
- jenkins_slave.last_job_branch = last_job['branch']
- jenkins_slave.last_job_installer = last_job['installer']
- jenkins_slave.last_job_result = last_job['result']
- jenkins_slave.save()
-
- jenkins_statistic = JenkinsStatistic(slave=jenkins_slave)
- if jenkins_slave.status == 'online' or jenkins_slave.status == 'building':
- jenkins_statistic.online = True
- if jenkins_slave.status == 'offline':
- jenkins_statistic.offline = True
- if jenkins_slave.status == 'online / idle':
- jenkins_statistic.idle = True
- jenkins_statistic.save()
diff --git a/dashboard/src/jenkins/tests.py b/dashboard/src/jenkins/tests.py
deleted file mode 100644
index 3723cd3..0000000
--- a/dashboard/src/jenkins/tests.py
+++ /dev/null
@@ -1,129 +0,0 @@
-##############################################################################
-# Copyright (c) 2016 Max Breitenfeldt and others.
-#
-# All rights reserved. This program and the accompanying materials
-# are made available under the terms of the Apache License, Version 2.0
-# which accompanies this distribution, and is available at
-# http://www.apache.org/licenses/LICENSE-2.0
-##############################################################################
-
-
-from datetime import timedelta
-from unittest import TestCase
-
-import jenkins.adapter as jenkins
-from jenkins.models import *
-
-
-# Tests that the data we get with the jenkinsadapter contains all the
-# data we need. These test will fail if;
-# - there is no internet connection
-# - the opnfv jenkins url has changed
-# - the jenkins api has changed
-# - jenkins is not set up / there is no data
-class JenkinsAdapterTestCase(TestCase):
- def test_get_all_slaves(self):
- slaves = jenkins.get_all_slaves()
- self.assertTrue(len(slaves) > 0)
- for slave in slaves:
- self.assertTrue('displayName' in slave)
- self.assertTrue('idle' in slave)
- self.assertTrue('offline' in slave)
-
- def test_get_slave(self):
- slaves = jenkins.get_all_slaves()
- self.assertEqual(slaves[0], jenkins.get_slave(slaves[0]['displayName']))
- self.assertEqual({}, jenkins.get_slave('098f6bcd4621d373cade4e832627b4f6'))
-
- def test_get_ci_slaves(self):
- slaves = jenkins.get_ci_slaves()
- self.assertTrue(len(slaves) > 0)
- for slave in slaves:
- self.assertTrue('nodeName' in slave)
-
- def test_get_jenkins_job(self):
- slaves = jenkins.get_ci_slaves()
- job = None
- for slave in slaves:
- job = jenkins.get_jenkins_job(slave['nodeName'])
- if job is not None:
- break
- # We need to test at least one job
- self.assertNotEqual(job, None)
-
- def test_get_all_jobs(self):
- jobs = jenkins.get_all_jobs()
- lastBuild = False
- self.assertTrue(len(jobs) > 0)
- for job in jobs:
- self.assertTrue('displayName' in job)
- self.assertTrue('url' in job)
- self.assertTrue('lastBuild' in job)
- if job['lastBuild'] is not None:
- lastBuild = True
- self.assertTrue('building' in job['lastBuild'])
- self.assertTrue('fullDisplayName' in job['lastBuild'])
- self.assertTrue('result' in job['lastBuild'])
- self.assertTrue('timestamp' in job['lastBuild'])
- self.assertTrue('builtOn' in job['lastBuild'])
- self.assertTrue(lastBuild)
-
- def test_parse_job(self):
- job = {
- "displayName": "apex-deploy-baremetal-os-nosdn-fdio-noha-colorado",
- "url": "https://build.opnfv.org/ci/job/apex-deploy-baremetal-os-nosdn-fdio-noha-colorado/",
- "lastBuild": {
- "building": False,
- "fullDisplayName": "apex-deploy-baremetal-os-nosdn-fdio-noha-colorado #37",
- "result": "SUCCESS",
- "timestamp": 1476283629917,
- "builtOn": "lf-pod1"
- }
- }
-
- job = jenkins.parse_job(job)
- self.assertEqual(job['scenario'], 'os-nosdn-fdio-noha')
- self.assertEqual(job['installer'], 'apex')
- self.assertEqual(job['branch'], 'colorado')
- self.assertEqual(job['result'], 'SUCCESS')
- self.assertEqual(job['building'], False)
- self.assertEqual(job['url'],
- "https://build.opnfv.org/ci/job/apex-deploy-baremetal-os-nosdn-fdio-noha-colorado/")
- self.assertEqual(job['name'],
- 'apex-deploy-baremetal-os-nosdn-fdio-noha-colorado')
-
- def test_get_slave_status(self):
- slave = {
- 'offline': True,
- 'idle': False
- }
- self.assertEqual(jenkins.get_slave_status(slave), 'offline')
- slave = {
- 'offline': False,
- 'idle': False
- }
- self.assertEqual(jenkins.get_slave_status(slave), 'online')
- slave = {
- 'offline': False,
- 'idle': True
- }
- self.assertEqual(jenkins.get_slave_status(slave), 'online / idle')
-
-
-class JenkinsModelTestCase(TestCase):
- def test_get_utilization(self):
- jenkins_slave = JenkinsSlave.objects.create(name='test', status='offline', url='')
- utilization = jenkins_slave.get_utilization(timedelta(weeks=1))
- self.assertEqual(utilization['idle'], 0)
- self.assertEqual(utilization['offline'], 0)
- self.assertEqual(utilization['online'], 0)
-
- for i in range(10):
- JenkinsStatistic.objects.create(slave=jenkins_slave,
- offline=True, idle=True,
- online=True)
-
- utilization = jenkins_slave.get_utilization(timedelta(weeks=1))
- self.assertEqual(utilization['idle'], 10)
- self.assertEqual(utilization['offline'], 10)
- self.assertEqual(utilization['online'], 10)
diff --git a/dashboard/src/manage.py b/dashboard/src/manage.py
index 80c496f..3c7aba5 100644..100755
--- a/dashboard/src/manage.py
+++ b/dashboard/src/manage.py
@@ -1,3 +1,5 @@
+#!/usr/bin/env python
+
##############################################################################
# Copyright (c) 2016 Max Breitenfeldt and others.
#
@@ -8,7 +10,6 @@
##############################################################################
-#!/usr/bin/env python
import os
import sys
diff --git a/dashboard/src/notifier/__init__.py b/dashboard/src/notifier/__init__.py
index e69de29..d65b13a 100644
--- a/dashboard/src/notifier/__init__.py
+++ b/dashboard/src/notifier/__init__.py
@@ -0,0 +1,8 @@
+##############################################################################
+# Copyright (c) 2018 Sawyer Bergeron and others.
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+##############################################################################
diff --git a/dashboard/src/notifier/admin.py b/dashboard/src/notifier/admin.py
index cfbe778..4a2984c 100644
--- a/dashboard/src/notifier/admin.py
+++ b/dashboard/src/notifier/admin.py
@@ -1,5 +1,5 @@
##############################################################################
-# Copyright (c) 2016 Max Breitenfeldt and others.
+# Copyright (c) 2018 Sawyer Bergeron, Parker Berberian, and others.
#
# All rights reserved. This program and the accompanying materials
# are made available under the terms of the Apache License, Version 2.0
@@ -9,6 +9,6 @@
from django.contrib import admin
-from notifier.models import *
+from notifier.models import Notification
-admin.site.register(Notifier)
+admin.site.register(Notification)
diff --git a/dashboard/src/notifier/apps.py b/dashboard/src/notifier/apps.py
index da5d3b0..52902da 100644
--- a/dashboard/src/notifier/apps.py
+++ b/dashboard/src/notifier/apps.py
@@ -1,5 +1,5 @@
##############################################################################
-# Copyright (c) 2016 Max Breitenfeldt and others.
+# Copyright (c) 2018 Sawyer Bergeron, Parker Berberian, and others.
#
# All rights reserved. This program and the accompanying materials
# are made available under the terms of the Apache License, Version 2.0
@@ -7,6 +7,7 @@
# http://www.apache.org/licenses/LICENSE-2.0
##############################################################################
+
from django.apps import AppConfig
diff --git a/dashboard/src/notifier/dispatchers.py b/dashboard/src/notifier/dispatchers.py
deleted file mode 100644
index c35fe2b..0000000
--- a/dashboard/src/notifier/dispatchers.py
+++ /dev/null
@@ -1,33 +0,0 @@
-##############################################################################
-# Copyright (c) 2016 Max Breitenfeldt and others.
-#
-# All rights reserved. This program and the accompanying materials
-# are made available under the terms of the Apache License, Version 2.0
-# which accompanies this distribution, and is available at
-# http://www.apache.org/licenses/LICENSE-2.0
-##############################################################################
-
-from django.db.models.signals import pre_save
-from django.dispatch import receiver
-from django.contrib import messages
-from django.core.mail import send_mail
-
-class DispatchHandler():
-
- @receiver(pre_save, sender='notifier.Notifier')
- def dispatch(sender, instance, *args, **kwargs):
- try:
- msg_type = getattr(DispatchHandler, instance.message_type)
- msg_type(instance)
- except AttributeError:
- instance.msg_sent = 'no dispatcher by given name exists: sending by email'
- email(instance)
-
- def email(instance):
- if instance.msg_sent != 'no dispatcher by given name exists: sending by email':
- instance.msg_sent = 'by email'
- send_mail(instance.title,instance.content,
- instance.sender,[instance.user.email_addr], fail_silently=False)
-
- def webnotification(instance):
- instance.msg_sent='by web notification' \ No newline at end of file
diff --git a/dashboard/src/notifier/manager.py b/dashboard/src/notifier/manager.py
new file mode 100644
index 0000000..240cf85
--- /dev/null
+++ b/dashboard/src/notifier/manager.py
@@ -0,0 +1,133 @@
+##############################################################################
+# 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 notifier.models import Notification
+
+from django.core.mail import send_mail
+from django.template.loader import render_to_string
+
+
+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 booking_notify(cls, booking, template, titles):
+ """
+ Creates 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, "user")) or task.user == user:
+ user_tasklist.append(
+ {
+ "title": task.type_str() + " Message: ",
+ "content": task.message
+ }
+ )
+ # gather up all the other needed info
+ context = {
+ "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, send the email
+ send_mail(
+ "Your Booking is Ready",
+ message,
+ os.environ.get("DEFAULT_FROM_EMAIL", "opnfv@pharos-dashboard"),
+ [user.userprofile.email_addr],
+ fail_silently=False
+ )
+
+ @classmethod
+ def email_booking_over(cls, booking):
+ template_name = "notifier/email_ended.txt"
+ hostnames = [host.template.resource.name for host in booking.resource.hosts.all()]
+ 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)
+
+ send_mail(
+ "Your Booking has Expired",
+ message,
+ os.environ.get("DEFAULT_FROM_EMAIL", "opnfv@pharos-dashboard"),
+ user.userprofile.email_addr,
+ fail_silently=False
+ )
+
+ @classmethod
+ def task_updated(cls, task):
+ """
+ called every time a lab updated info about a task.
+ currently only checks if the job is now done so I can send an email,
+ may add more functionality later
+ """
+ if task.job.is_fulfilled():
+ cls.email_job_fulfilled(task.job)
diff --git a/dashboard/src/notifier/migrations/0001_initial.py b/dashboard/src/notifier/migrations/0001_initial.py
index cac4d04..e5d0009 100644
--- a/dashboard/src/notifier/migrations/0001_initial.py
+++ b/dashboard/src/notifier/migrations/0001_initial.py
@@ -1,6 +1,4 @@
-# -*- coding: utf-8 -*-
-# Generated by Django 1.10 on 2017-12-14 21:41
-from __future__ import unicode_literals
+# Generated by Django 2.1 on 2018-09-14 14:48
from django.db import migrations, models
import django.db.models.deletion
@@ -12,11 +10,30 @@ class Migration(migrations.Migration):
initial = True
dependencies = [
- ('dashboard', '0002_auto_20170505_0815'),
+ ('account', '0001_initial'),
+ ('booking', '0001_initial'),
]
operations = [
migrations.CreateModel(
+ name='LabMessage',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('msg', models.TextField()),
+ ('lab', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='account.Lab')),
+ ],
+ ),
+ migrations.CreateModel(
+ name='MetaBooking',
+ fields=[
+ ('id', models.AutoField(primary_key=True, serialize=False)),
+ ('ending_notified', models.BooleanField(default=False)),
+ ('ended_notified', models.BooleanField(default=False)),
+ ('created_notified', models.BooleanField(default=False)),
+ ('booking', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='metabooking', to='booking.Booking')),
+ ],
+ ),
+ migrations.CreateModel(
name='Notifier',
fields=[
('id', models.AutoField(primary_key=True, serialize=False)),
diff --git a/dashboard/src/notifier/migrations/0002_auto_20181102_1631.py b/dashboard/src/notifier/migrations/0002_auto_20181102_1631.py
new file mode 100644
index 0000000..e5fef89
--- /dev/null
+++ b/dashboard/src/notifier/migrations/0002_auto_20181102_1631.py
@@ -0,0 +1,44 @@
+# 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/dashboard/src/notifier/migrations/0003_auto_20190123_1741.py b/dashboard/src/notifier/migrations/0003_auto_20190123_1741.py
new file mode 100644
index 0000000..f491993
--- /dev/null
+++ b/dashboard/src/notifier/migrations/0003_auto_20190123_1741.py
@@ -0,0 +1,23 @@
+# 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/dashboard/src/notifier/migrations/0004_auto_20190124_2115.py b/dashboard/src/notifier/migrations/0004_auto_20190124_2115.py
new file mode 100644
index 0000000..306ec7b
--- /dev/null
+++ b/dashboard/src/notifier/migrations/0004_auto_20190124_2115.py
@@ -0,0 +1,23 @@
+# 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/dashboard/src/notifier/migrations/0005_auto_20190306_1616.py b/dashboard/src/notifier/migrations/0005_auto_20190306_1616.py
new file mode 100644
index 0000000..d92c988
--- /dev/null
+++ b/dashboard/src/notifier/migrations/0005_auto_20190306_1616.py
@@ -0,0 +1,18 @@
+# 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/dashboard/src/notifier/models.py b/dashboard/src/notifier/models.py
index 9ebc6fc..49189e8 100644
--- a/dashboard/src/notifier/models.py
+++ b/dashboard/src/notifier/models.py
@@ -1,5 +1,5 @@
##############################################################################
-# Copyright (c) 2016 Max Breitenfeldt and others.
+# Copyright (c) 2018 Sawyer Bergeron, Parker Berberian, and others.
#
# All rights reserved. This program and the accompanying materials
# are made available under the terms of the Apache License, Version 2.0
@@ -8,31 +8,18 @@
##############################################################################
from django.db import models
-from jira import JIRA, JIRAError
-from dashboard.models import Resource
-from booking.models import Booking
-from django.contrib.auth.models import User
from account.models import UserProfile
-from django.contrib import messages
-from django.db.models.signals import pre_save
-from fernet_fields import EncryptedTextField
-class Notifier(models.Model):
- id = models.AutoField(primary_key=True)
- title = models.CharField(max_length=240)
- content = EncryptedTextField()
- user = models.ForeignKey(UserProfile, on_delete=models.CASCADE, null=True, blank=True)
- sender = models.CharField(max_length=240, default='unknown')
- message_type = models.CharField(max_length=240, default='email', choices=(
- ('email','Email'),
- ('webnotification', 'Web Notification')))
- msg_sent = ''
- import notifier.dispatchers
+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 getEmail(self):
- return self.user.email_addr
-
+ def to_preview_html(self):
+ return "<h3>" + self.title + "</h3>" # TODO - template?
diff --git a/dashboard/src/jenkins/admin.py b/dashboard/src/notifier/tests/test_dispatcher.py
index c499670..086f621 100644
--- a/dashboard/src/jenkins/admin.py
+++ b/dashboard/src/notifier/tests/test_dispatcher.py
@@ -1,5 +1,5 @@
##############################################################################
-# Copyright (c) 2016 Max Breitenfeldt and others.
+# Copyright (c) 2018 Sawyer Bergeron and others.
#
# All rights reserved. This program and the accompanying materials
# are made available under the terms of the Apache License, Version 2.0
@@ -7,11 +7,9 @@
# http://www.apache.org/licenses/LICENSE-2.0
##############################################################################
+from django.test import TestCase
-from django.conf import settings
-from django.contrib import admin
-from jenkins.models import JenkinsSlave
-
-if settings.DEBUG:
- admin.site.register(JenkinsSlave) \ No newline at end of file
+class DispatchTestCase(TestCase):
+ # This is a stub, it will be filled out as this feature is remade with saner practices.
+ pass
diff --git a/dashboard/src/notifier/tests/test_models.py b/dashboard/src/notifier/tests/test_models.py
new file mode 100644
index 0000000..d332254
--- /dev/null
+++ b/dashboard/src/notifier/tests/test_models.py
@@ -0,0 +1,30 @@
+##############################################################################
+# 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/dashboard/src/notifier/urls.py b/dashboard/src/notifier/urls.py
new file mode 100644
index 0000000..fedb9e8
--- /dev/null
+++ b/dashboard/src/notifier/urls.py
@@ -0,0 +1,19 @@
+##############################################################################
+# 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/dashboard/src/notifier/views.py b/dashboard/src/notifier/views.py
new file mode 100644
index 0000000..3a85eda
--- /dev/null
+++ b/dashboard/src/notifier/views.py
@@ -0,0 +1,58 @@
+##############################################################################
+# 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/dashboard/src/pharos_dashboard/celery.py b/dashboard/src/pharos_dashboard/celery.py
index f60f243..2a44a3d 100644
--- a/dashboard/src/pharos_dashboard/celery.py
+++ b/dashboard/src/pharos_dashboard/celery.py
@@ -27,4 +27,4 @@ app.autodiscover_tasks(lambda: settings.INSTALLED_APPS)
@app.task(bind=True)
def debug_task(self):
- print('Request: {0!r}'.format(self.request)) \ No newline at end of file
+ print('Request: {0!r}'.format(self.request))
diff --git a/dashboard/src/pharos_dashboard/settings.py b/dashboard/src/pharos_dashboard/settings.py
index 07e3920..86de78c 100644
--- a/dashboard/src/pharos_dashboard/settings.py
+++ b/dashboard/src/pharos_dashboard/settings.py
@@ -1,3 +1,11 @@
+##############################################################################
+# Copyright (c) 2018 Sawyer Bergeron and others.
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+##############################################################################
import os
from datetime import timedelta
@@ -8,15 +16,18 @@ BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# NOTE: os.environ only returns strings, so making a comparison to
# 'True' here will convert it to the correct Boolean value.
DEBUG = os.environ['DEBUG'] == 'True'
+TESTING = os.environ['TEST'] == 'True'
# Application definition
INSTALLED_APPS = [
'dashboard',
+ 'resource_inventory',
'booking',
'account',
- 'jenkins',
'notifier',
+ 'workflow',
+ 'api',
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
@@ -24,7 +35,7 @@ INSTALLED_APPS = [
'django.contrib.messages',
'django.contrib.staticfiles',
'django.contrib.humanize',
- 'bootstrap3',
+ 'bootstrap4',
'crispy_forms',
'rest_framework',
'rest_framework.authtoken',
@@ -46,11 +57,11 @@ ROOT_URLCONF = 'pharos_dashboard.urls'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
- 'DIRS': [os.path.join(BASE_DIR, 'templates')]
- ,
+ 'DIRS': [os.path.join(BASE_DIR, 'templates')],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
+ 'dashboard.context_processors.debug',
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
@@ -60,6 +71,10 @@ TEMPLATES = [
},
]
+TEMPLATE_CONTEXT_PROCESSORS = [
+ 'dashboard.context_processors.debug',
+]
+
WSGI_APPLICATION = 'pharos_dashboard.wsgi.application'
# Password validation
@@ -128,13 +143,12 @@ DATABASES = {
}
}
-
# Rest API Settings
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.DjangoModelPermissionsOrAnonReadOnly'
],
- 'DEFAULT_FILTER_BACKENDS': ('rest_framework.filters.DjangoFilterBackend',),
+ 'DEFAULT_FILTER_BACKENDS': ('django_filters.rest_framework.FilterSet',),
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.SessionAuthentication',
'rest_framework.authentication.TokenAuthentication',
@@ -165,42 +179,27 @@ OAUTH_CALLBACK_URL = os.environ['DASHBOARD_URL'] + '/accounts/authenticated'
CELERY_TIMEZONE = 'UTC'
RABBITMQ_URL = 'rabbitmq'
-RABBITMQ_USER = os.environ['RABBITMQ_USER']
-RABBITMQ_PASSWORD = os.environ['RABBITMQ_PASSWORD']
+RABBITMQ_DEFAULT_USER = os.environ['RABBITMQ_DEFAULT_USER']
+RABBITMQ_DEFAULT_PASS = os.environ['RABBITMQ_DEFAULT_PASS']
-BROKER_URL = 'amqp://' + RABBITMQ_USER + ':' + RABBITMQ_PASSWORD + '@rabbitmq:5672//'
-
-BOOKING_EXP_TIME = os,environ['BOOKING_EXPIRE_TIME']
-BOOKING_MAX_NUM = os.environ['BOOKING_MAXIMUM_NUMBER']
+BROKER_URL = 'amqp://' + RABBITMQ_DEFAULT_USER + ':' + RABBITMQ_DEFAULT_PASS + '@rabbitmq:5672//'
CELERYBEAT_SCHEDULE = {
- 'sync-jenkins': {
- 'task': 'jenkins.tasks.sync_jenkins',
- 'schedule': timedelta(minutes=5)
- },
- 'send-booking-notifications': {
- 'task': 'notification.tasks.send_booking_notifications',
- 'schedule': timedelta(minutes=5)
- },
- 'clean-database': {
- 'task': 'dashboard.tasks.database_cleanup',
- 'schedule': timedelta(hours=24)
+ 'booking_poll': {
+ 'task': 'dashboard.tasks.booking_poll',
+ 'schedule': timedelta(minutes=1)
},
- 'booking_cleanup': {
- 'task': 'dashboard.tasks.booking_cleanup',
- 'schedule': timedelta(hours=24)
+ 'free_hosts': {
+ 'task': 'dashboard.tasks.free_hosts',
+ 'schedule': timedelta(minutes=1)
},
}
-# Jenkins Settings
-ALL_SLAVES_URL = os.environ['JENKINS_URL'] + '/computer/api/json?tree=computer[displayName,offline,idle]'
-CI_SLAVES_URL = os.environ['JENKINS_URL'] + '/label/ci-pod/api/json?tree=nodes[nodeName,offline,idle]'
-ALL_JOBS_URL = os.environ['JENKINS_URL'] + '/api/json?tree=jobs[displayName,url,lastBuild[fullDisplayName,building,builtOn,timestamp,result]'
-GET_SLAVE_URL = os.environ['JENKINS_URL'] + '/computer/'
# Notifier Settings
EMAIL_HOST = os.environ['EMAIL_HOST']
EMAIL_PORT = os.environ['EMAIL_PORT']
EMAIL_HOST_USER = os.environ['EMAIL_HOST_USER']
EMAIL_HOST_PASSWORD = os.environ['EMAIL_HOST_PASSWORD']
-EMAIL_USE_TLS=True
+EMAIL_USE_TLS = True
DEFAULT_EMAIL_FROM = os.environ.get('DEFAULT_EMAIL_FROM', 'webmaster@localhost')
+SESSION_ENGINE = "django.contrib.sessions.backends.signed_cookies"
diff --git a/dashboard/src/pharos_dashboard/urls.py b/dashboard/src/pharos_dashboard/urls.py
index adcb5b8..8535bed 100644
--- a/dashboard/src/pharos_dashboard/urls.py
+++ b/dashboard/src/pharos_dashboard/urls.py
@@ -1,5 +1,6 @@
##############################################################################
# Copyright (c) 2016 Max Breitenfeldt and others.
+# Copyright (c) 2018 Sawyer Bergeron, Parker Berberian, and others.
#
# All rights reserved. This program and the accompanying materials
# are made available under the terms of the Apache License, Version 2.0
@@ -30,15 +31,17 @@ from django.contrib import admin
urlpatterns = [
+
+ url(r'^wf/', include('workflow.urls', namespace='workflow')),
url(r'^', include('dashboard.urls', namespace='dashboard')),
url(r'^booking/', include('booking.urls', namespace='booking')),
url(r'^accounts/', include('account.urls', namespace='account')),
-
+ url(r'^resource/', include('resource_inventory.urls', namespace='resource')),
url(r'^admin/', admin.site.urls),
url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework')),
-
- url(r'^api/', include('api.urls'))
+ url(r'^api/', include('api.urls')),
+ url(r'^messages/', include('notifier.urls', namespace='notifier'))
]
if settings.DEBUG is True:
- urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) \ No newline at end of file
+ urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
diff --git a/dashboard/src/jenkins/migrations/__init__.py b/dashboard/src/resource_inventory/__init__.py
index b5914ce..f903394 100644
--- a/dashboard/src/jenkins/migrations/__init__.py
+++ b/dashboard/src/resource_inventory/__init__.py
@@ -1,10 +1,8 @@
##############################################################################
-# Copyright (c) 2016 Max Breitenfeldt and others.
+# Copyright (c) 2018 Sawyer Bergeron, Parker Berberian, and others.
#
# All rights reserved. This program and the accompanying materials
# are made available under the terms of the Apache License, Version 2.0
# which accompanies this distribution, and is available at
# http://www.apache.org/licenses/LICENSE-2.0
##############################################################################
-
-
diff --git a/dashboard/src/resource_inventory/admin.py b/dashboard/src/resource_inventory/admin.py
new file mode 100644
index 0000000..7ff510b
--- /dev/null
+++ b/dashboard/src/resource_inventory/admin.py
@@ -0,0 +1,53 @@
+##############################################################################
+# Copyright (c) 2016 Max Breitenfeldt and others.
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+##############################################################################
+
+
+from django.contrib import admin
+
+from resource_inventory.models import (
+ HostProfile,
+ InterfaceProfile,
+ DiskProfile,
+ CpuProfile,
+ RamProfile,
+ GenericResourceBundle,
+ GenericResource,
+ GenericHost,
+ GenericInterface,
+ Host,
+ Interface,
+ Network,
+ Vlan,
+ ResourceBundle,
+ Scenario,
+ Installer,
+ Opsys,
+ ConfigBundle,
+ OPNFVConfig,
+ OPNFVRole,
+ Image,
+ HostConfiguration,
+ RemoteInfo
+)
+
+profiles = [HostProfile, InterfaceProfile, DiskProfile, CpuProfile, RamProfile]
+
+admin.site.register(profiles)
+
+generics = [GenericResourceBundle, GenericResource, GenericHost, GenericInterface]
+
+admin.site.register(generics)
+
+physical = [Host, Interface, Network, Vlan, ResourceBundle]
+
+admin.site.register(physical)
+
+config = [Scenario, Installer, Opsys, ConfigBundle, OPNFVConfig, OPNFVRole, Image, HostConfiguration, RemoteInfo]
+
+admin.site.register(config)
diff --git a/dashboard/src/jenkins/apps.py b/dashboard/src/resource_inventory/apps.py
index 41faf60..79768a7 100644
--- a/dashboard/src/jenkins/apps.py
+++ b/dashboard/src/resource_inventory/apps.py
@@ -7,9 +7,8 @@
# http://www.apache.org/licenses/LICENSE-2.0
##############################################################################
-
from django.apps import AppConfig
-class JenkinsConfig(AppConfig):
- name = 'jenkins'
+class ResourcesConfig(AppConfig):
+ name = 'hwresource'
diff --git a/dashboard/src/resource_inventory/idf_templater.py b/dashboard/src/resource_inventory/idf_templater.py
new file mode 100644
index 0000000..bf6eda0
--- /dev/null
+++ b/dashboard/src/resource_inventory/idf_templater.py
@@ -0,0 +1,151 @@
+##############################################################################
+# 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 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):
+ """
+ fills the installer descriptor file 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/dashboard/src/resource_inventory/migrations/0001_initial.py b/dashboard/src/resource_inventory/migrations/0001_initial.py
new file mode 100644
index 0000000..d01e8e7
--- /dev/null
+++ b/dashboard/src/resource_inventory/migrations/0001_initial.py
@@ -0,0 +1,328 @@
+# Generated by Django 2.1 on 2018-09-14 14:48
+
+from django.conf import settings
+import django.core.validators
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+ initial = True
+
+ dependencies = [
+ migrations.swappable_dependency(settings.AUTH_USER_MODEL),
+ ('account', '0001_initial'),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='ConfigBundle',
+ fields=[
+ ('id', models.AutoField(primary_key=True, serialize=False)),
+ ('name', models.CharField(max_length=200, unique=True)),
+ ('description', models.CharField(default='', max_length=1000)),
+ ],
+ ),
+ migrations.CreateModel(
+ name='CpuProfile',
+ fields=[
+ ('id', models.AutoField(primary_key=True, serialize=False)),
+ ('cores', models.IntegerField()),
+ ('architecture', models.CharField(choices=[('x86_64', 'x86_64'), ('aarch64', 'aarch64')], max_length=50)),
+ ('cpus', models.IntegerField()),
+ ('cflags', models.TextField(null=True)),
+ ],
+ ),
+ migrations.CreateModel(
+ name='DiskProfile',
+ fields=[
+ ('id', models.AutoField(primary_key=True, serialize=False)),
+ ('size', models.IntegerField()),
+ ('media_type', models.CharField(choices=[('SSD', 'SSD'), ('HDD', 'HDD')], max_length=50)),
+ ('name', models.CharField(max_length=50)),
+ ('rotation', models.IntegerField(default=0)),
+ ('interface', models.CharField(choices=[('sata', 'sata'), ('sas', 'sas'), ('ssd', 'ssd'), ('nvme', 'nvme'), ('scsi', 'scsi'), ('iscsi', 'iscsi')], default='sata', max_length=50)),
+ ],
+ ),
+ migrations.CreateModel(
+ name='GenericHost',
+ fields=[
+ ('id', models.AutoField(primary_key=True, serialize=False)),
+ ],
+ ),
+ migrations.CreateModel(
+ name='GenericInterface',
+ fields=[
+ ('id', models.AutoField(primary_key=True, serialize=False)),
+ ('host', models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, related_name='generic_interfaces', to='resource_inventory.GenericHost')),
+ ],
+ ),
+ migrations.CreateModel(
+ name='GenericResource',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('name', models.CharField(max_length=200, validators=[django.core.validators.RegexValidator(message='Enter a valid hostname. Full domain name may be 1-253 characters, each hostname 1-63 characters (including suffixed dot), and valid characters for hostnames are A-Z, a-z, 0-9, hyphen (-), and underscore (_)', regex='(?=^.{1,253}$)(?=(^([A-Za-z0-9\\-\\_]{1,62}\\.)*[A-Za-z0-9\\-\\_]{1,63}$))')])),
+ ],
+ ),
+ migrations.CreateModel(
+ name='GenericResourceBundle',
+ fields=[
+ ('id', models.AutoField(primary_key=True, serialize=False)),
+ ('name', models.CharField(max_length=300, unique=True)),
+ ('xml', models.TextField()),
+ ('description', models.CharField(default='', max_length=1000)),
+ ('lab', models.ForeignKey(null=True, on_delete=django.db.models.deletion.DO_NOTHING, to='account.Lab')),
+ ('owner', models.ForeignKey(null=True, on_delete=django.db.models.deletion.DO_NOTHING, to=settings.AUTH_USER_MODEL)),
+ ],
+ ),
+ migrations.CreateModel(
+ name='Host',
+ fields=[
+ ('id', models.AutoField(primary_key=True, serialize=False)),
+ ('booked', models.BooleanField(default=False)),
+ ('name', models.CharField(max_length=200, unique=True)),
+ ('labid', models.CharField(default='default_id', max_length=200)),
+ ('working', models.BooleanField(default=True)),
+ ('vendor', models.CharField(default='unknown', max_length=100)),
+ ('model', models.CharField(default='unknown', max_length=150)),
+ ],
+ ),
+ migrations.CreateModel(
+ name='HostConfiguration',
+ fields=[
+ ('id', models.AutoField(primary_key=True, serialize=False)),
+ ('bundle', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='hostConfigurations', to='resource_inventory.ConfigBundle')),
+ ('host', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='configuration', to='resource_inventory.GenericHost')),
+ ],
+ ),
+ migrations.CreateModel(
+ name='HostProfile',
+ fields=[
+ ('id', models.AutoField(primary_key=True, serialize=False)),
+ ('host_type', models.PositiveSmallIntegerField()),
+ ('name', models.CharField(max_length=200, unique=True)),
+ ('description', models.TextField()),
+ ('labs', models.ManyToManyField(related_name='hostprofiles', to='account.Lab')),
+ ],
+ ),
+ migrations.CreateModel(
+ name='Image',
+ fields=[
+ ('id', models.AutoField(primary_key=True, serialize=False)),
+ ('lab_id', models.IntegerField()),
+ ('name', models.CharField(max_length=200)),
+ ('public', models.BooleanField(default=True)),
+ ('description', models.TextField()),
+ ('from_lab', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='account.Lab')),
+ ('host_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='resource_inventory.HostProfile')),
+ ('owner', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL)),
+ ],
+ ),
+ migrations.CreateModel(
+ name='Installer',
+ fields=[
+ ('id', models.AutoField(primary_key=True, serialize=False)),
+ ('name', models.CharField(max_length=200)),
+ ],
+ ),
+ migrations.CreateModel(
+ name='Interface',
+ fields=[
+ ('id', models.AutoField(primary_key=True, serialize=False)),
+ ('mac_address', models.CharField(max_length=17)),
+ ('bus_address', models.CharField(max_length=50)),
+ ('name', models.CharField(default='eth0', max_length=100)),
+ ],
+ ),
+ migrations.CreateModel(
+ name='InterfaceProfile',
+ fields=[
+ ('id', models.AutoField(primary_key=True, serialize=False)),
+ ('speed', models.IntegerField()),
+ ('name', models.CharField(max_length=100)),
+ ('nic_type', models.CharField(choices=[('onboard', 'onboard'), ('pcie', 'pcie')], default='onboard', max_length=50)),
+ ('host', models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, related_name='interfaceprofile', to='resource_inventory.HostProfile')),
+ ],
+ ),
+ migrations.CreateModel(
+ name='Network',
+ fields=[
+ ('id', models.AutoField(primary_key=True, serialize=False)),
+ ('vlan_id', models.IntegerField()),
+ ('name', models.CharField(max_length=100)),
+ ],
+ ),
+ migrations.CreateModel(
+ name='OPNFVConfig',
+ fields=[
+ ('id', models.AutoField(primary_key=True, serialize=False)),
+ ('bundle', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='opnfv_config', to='resource_inventory.ConfigBundle')),
+ ('installer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='resource_inventory.Installer')),
+ ],
+ ),
+ migrations.CreateModel(
+ name='OPNFVRole',
+ fields=[
+ ('id', models.AutoField(primary_key=True, serialize=False)),
+ ('name', models.CharField(max_length=200)),
+ ('description', models.TextField()),
+ ],
+ ),
+ migrations.CreateModel(
+ name='Opsys',
+ fields=[
+ ('id', models.AutoField(primary_key=True, serialize=False)),
+ ('name', models.CharField(max_length=100)),
+ ('sup_installers', models.ManyToManyField(blank=True, to='resource_inventory.Installer')),
+ ],
+ ),
+ migrations.CreateModel(
+ name='RamProfile',
+ fields=[
+ ('id', models.AutoField(primary_key=True, serialize=False)),
+ ('amount', models.IntegerField()),
+ ('channels', models.IntegerField()),
+ ('host', models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, related_name='ramprofile', to='resource_inventory.HostProfile')),
+ ],
+ ),
+ migrations.CreateModel(
+ name='ResourceBundle',
+ fields=[
+ ('id', models.AutoField(primary_key=True, serialize=False)),
+ ('template', models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, to='resource_inventory.GenericResourceBundle')),
+ ],
+ ),
+ migrations.CreateModel(
+ name='Scenario',
+ fields=[
+ ('id', models.AutoField(primary_key=True, serialize=False)),
+ ('name', models.CharField(max_length=300)),
+ ],
+ ),
+ migrations.CreateModel(
+ name='Vlan',
+ fields=[
+ ('id', models.AutoField(primary_key=True, serialize=False)),
+ ('vlan_id', models.IntegerField()),
+ ('tagged', models.BooleanField()),
+ ],
+ ),
+ migrations.CreateModel(
+ name='GenericPod',
+ fields=[
+ ('genericresource_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='resource_inventory.GenericResource')),
+ ],
+ bases=('resource_inventory.genericresource',),
+ ),
+ migrations.AddField(
+ model_name='opnfvconfig',
+ name='scenario',
+ field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='resource_inventory.Scenario'),
+ ),
+ migrations.AddField(
+ model_name='interface',
+ name='config',
+ field=models.ManyToManyField(to='resource_inventory.Vlan'),
+ ),
+ migrations.AddField(
+ model_name='interface',
+ name='host',
+ field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='interfaces', to='resource_inventory.Host'),
+ ),
+ migrations.AddField(
+ model_name='installer',
+ name='sup_scenarios',
+ field=models.ManyToManyField(blank=True, to='resource_inventory.Scenario'),
+ ),
+ migrations.AddField(
+ model_name='hostconfiguration',
+ name='image',
+ field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='resource_inventory.Image'),
+ ),
+ migrations.AddField(
+ model_name='hostconfiguration',
+ name='opnfvRole',
+ field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='resource_inventory.OPNFVRole'),
+ ),
+ migrations.AddField(
+ model_name='host',
+ name='bundle',
+ field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='hosts', to='resource_inventory.ResourceBundle'),
+ ),
+ migrations.AddField(
+ model_name='host',
+ name='config',
+ field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='configuration', to='resource_inventory.HostConfiguration'),
+ ),
+ migrations.AddField(
+ model_name='host',
+ name='lab',
+ field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='account.Lab'),
+ ),
+ migrations.AddField(
+ model_name='host',
+ name='profile',
+ field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='resource_inventory.HostProfile'),
+ ),
+ migrations.AddField(
+ model_name='host',
+ name='template',
+ field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='resource_inventory.GenericHost'),
+ ),
+ migrations.AddField(
+ model_name='genericresource',
+ name='bundle',
+ field=models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, related_name='generic_resources', to='resource_inventory.GenericResourceBundle'),
+ ),
+ migrations.AddField(
+ model_name='genericinterface',
+ name='profile',
+ field=models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, to='resource_inventory.InterfaceProfile'),
+ ),
+ migrations.AddField(
+ model_name='genericinterface',
+ name='vlans',
+ field=models.ManyToManyField(to='resource_inventory.Vlan'),
+ ),
+ migrations.AddField(
+ model_name='generichost',
+ name='profile',
+ field=models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, to='resource_inventory.HostProfile'),
+ ),
+ migrations.AddField(
+ model_name='generichost',
+ name='resource',
+ field=models.OneToOneField(on_delete=django.db.models.deletion.DO_NOTHING, related_name='generic_host', to='resource_inventory.GenericResource'),
+ ),
+ migrations.AddField(
+ model_name='diskprofile',
+ name='host',
+ field=models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, related_name='storageprofile', to='resource_inventory.HostProfile'),
+ ),
+ migrations.AddField(
+ model_name='cpuprofile',
+ name='host',
+ field=models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, related_name='cpuprofile', to='resource_inventory.HostProfile'),
+ ),
+ migrations.AddField(
+ model_name='configbundle',
+ name='bundle',
+ field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='resource_inventory.GenericResourceBundle'),
+ ),
+ migrations.AddField(
+ model_name='configbundle',
+ name='owner',
+ field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL),
+ ),
+ migrations.AddField(
+ model_name='genericpod',
+ name='hosts',
+ field=models.ManyToManyField(to='resource_inventory.GenericHost'),
+ ),
+ migrations.AddField(
+ model_name='genericpod',
+ name='networks',
+ field=models.ManyToManyField(to='resource_inventory.Network'),
+ ),
+ ]
diff --git a/dashboard/src/resource_inventory/migrations/0002_auto_20180919_1459.py b/dashboard/src/resource_inventory/migrations/0002_auto_20180919_1459.py
new file mode 100644
index 0000000..80c9e6f
--- /dev/null
+++ b/dashboard/src/resource_inventory/migrations/0002_auto_20180919_1459.py
@@ -0,0 +1,18 @@
+# Generated by Django 2.1 on 2018-09-19 14:59
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('resource_inventory', '0001_initial'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='hostprofile',
+ name='host_type',
+ field=models.PositiveSmallIntegerField(default=0),
+ ),
+ ]
diff --git a/dashboard/src/resource_inventory/migrations/0003_vlan_public.py b/dashboard/src/resource_inventory/migrations/0003_vlan_public.py
new file mode 100644
index 0000000..07dc647
--- /dev/null
+++ b/dashboard/src/resource_inventory/migrations/0003_vlan_public.py
@@ -0,0 +1,18 @@
+# Generated by Django 2.1 on 2018-09-26 14:41
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('resource_inventory', '0002_auto_20180919_1459'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='vlan',
+ name='public',
+ field=models.BooleanField(default=False),
+ ),
+ ]
diff --git a/dashboard/src/resource_inventory/migrations/0004_auto_20181017_1532.py b/dashboard/src/resource_inventory/migrations/0004_auto_20181017_1532.py
new file mode 100644
index 0000000..3a7475c
--- /dev/null
+++ b/dashboard/src/resource_inventory/migrations/0004_auto_20181017_1532.py
@@ -0,0 +1,28 @@
+# 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/dashboard/src/resource_inventory/migrations/0005_image_os.py b/dashboard/src/resource_inventory/migrations/0005_image_os.py
new file mode 100644
index 0000000..ede008e
--- /dev/null
+++ b/dashboard/src/resource_inventory/migrations/0005_image_os.py
@@ -0,0 +1,19 @@
+# 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/dashboard/src/resource_inventory/migrations/0006_auto_20190124_1700.py b/dashboard/src/resource_inventory/migrations/0006_auto_20190124_1700.py
new file mode 100644
index 0000000..a5a972f
--- /dev/null
+++ b/dashboard/src/resource_inventory/migrations/0006_auto_20190124_1700.py
@@ -0,0 +1,76 @@
+# 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/dashboard/src/resource_inventory/migrations/0007_auto_20190306_1616.py b/dashboard/src/resource_inventory/migrations/0007_auto_20190306_1616.py
new file mode 100644
index 0000000..19a49c5
--- /dev/null
+++ b/dashboard/src/resource_inventory/migrations/0007_auto_20190306_1616.py
@@ -0,0 +1,31 @@
+# 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/dashboard/src/resource_inventory/migrations/0008_host_remote_management.py b/dashboard/src/resource_inventory/migrations/0008_host_remote_management.py
new file mode 100644
index 0000000..f74a535
--- /dev/null
+++ b/dashboard/src/resource_inventory/migrations/0008_host_remote_management.py
@@ -0,0 +1,19 @@
+# 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/dashboard/src/resource_inventory/migrations/0009_auto_20190315_1757.py b/dashboard/src/resource_inventory/migrations/0009_auto_20190315_1757.py
new file mode 100644
index 0000000..92ed0e9
--- /dev/null
+++ b/dashboard/src/resource_inventory/migrations/0009_auto_20190315_1757.py
@@ -0,0 +1,73 @@
+# 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/dashboard/src/resource_inventory/migrations/0010_auto_20190430_1405.py b/dashboard/src/resource_inventory/migrations/0010_auto_20190430_1405.py
new file mode 100644
index 0000000..3823eaf
--- /dev/null
+++ b/dashboard/src/resource_inventory/migrations/0010_auto_20190430_1405.py
@@ -0,0 +1,54 @@
+# 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/dashboard/src/resource_inventory/migrations/__init__.py b/dashboard/src/resource_inventory/migrations/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/dashboard/src/resource_inventory/migrations/__init__.py
diff --git a/dashboard/src/resource_inventory/models.py b/dashboard/src/resource_inventory/models.py
new file mode 100644
index 0000000..b9f2c44
--- /dev/null
+++ b/dashboard/src/resource_inventory/models.py
@@ -0,0 +1,371 @@
+##############################################################################
+# Copyright (c) 2018 Sawyer Bergeron, Parker Berberian, and others.
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+##############################################################################
+
+from django.contrib.auth.models import User
+from django.db import models
+from django.core.validators import RegexValidator
+
+import re
+
+from account.models import Lab
+
+
+# profile of resources hosted by labs
+class HostProfile(models.Model):
+ id = models.AutoField(primary_key=True)
+ host_type = models.PositiveSmallIntegerField(default=0)
+ name = models.CharField(max_length=200, unique=True)
+ description = models.TextField()
+ labs = models.ManyToManyField(Lab, related_name="hostprofiles")
+
+ def validate(self):
+ validname = re.compile(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
+
+
+class InterfaceProfile(models.Model):
+ id = models.AutoField(primary_key=True)
+ speed = models.IntegerField()
+ name = models.CharField(max_length=100)
+ host = models.ForeignKey(HostProfile, on_delete=models.CASCADE, related_name='interfaceprofile')
+ nic_type = models.CharField(
+ max_length=50,
+ choices=[
+ ("onboard", "onboard"),
+ ("pcie", "pcie")
+ ],
+ default="onboard"
+ )
+
+ def __str__(self):
+ return self.name + " for " + str(self.host)
+
+
+class DiskProfile(models.Model):
+ id = models.AutoField(primary_key=True)
+ size = models.IntegerField()
+ media_type = models.CharField(max_length=50, choices=[
+ ("SSD", "SSD"),
+ ("HDD", "HDD")
+ ])
+ name = models.CharField(max_length=50)
+ host = models.ForeignKey(HostProfile, on_delete=models.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(HostProfile, on_delete=models.CASCADE, related_name='cpuprofile')
+ cflags = models.TextField(null=True)
+
+ def __str__(self):
+ return str(self.architecture) + " " + str(self.cpus) + "S" + str(self.cores) + " C for " + str(self.host)
+
+
+class RamProfile(models.Model):
+ id = models.AutoField(primary_key=True)
+ amount = models.IntegerField()
+ channels = models.IntegerField()
+ host = models.ForeignKey(HostProfile, on_delete=models.CASCADE, related_name='ramprofile')
+
+ def __str__(self):
+ return str(self.amount) + "G for " + str(self.host)
+
+
+# Generic resource templates
+class GenericResourceBundle(models.Model):
+ id = models.AutoField(primary_key=True)
+ name = models.CharField(max_length=300, unique=True)
+ xml = models.TextField()
+ owner = models.ForeignKey(User, null=True, on_delete=models.SET_NULL)
+ lab = models.ForeignKey(Lab, null=True, on_delete=models.SET_NULL)
+ description = models.CharField(max_length=1000, default="")
+
+ def getHosts(self):
+ return_hosts = []
+ for genericResource in self.generic_resources.all():
+ return_hosts.append(genericResource.getHost())
+
+ return return_hosts
+
+ def __str__(self):
+ return self.name
+
+
+class Network(models.Model):
+ id = models.AutoField(primary_key=True)
+ name = models.CharField(max_length=100)
+ bundle = models.ForeignKey(GenericResourceBundle, on_delete=models.CASCADE, related_name="networks")
+ is_public = models.BooleanField()
+
+ def __str__(self):
+ return self.name
+
+
+class NetworkConnection(models.Model):
+ network = models.ForeignKey(Network, on_delete=models.CASCADE)
+ vlan_is_tagged = models.BooleanField()
+
+
+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(Network, on_delete=models.DO_NOTHING, null=True)
+
+ def __str__(self):
+ return str(self.vlan_id) + ("_T" if self.tagged else "")
+
+
+class GenericResource(models.Model):
+ bundle = models.ForeignKey(GenericResourceBundle, related_name='generic_resources', on_delete=models.CASCADE)
+ hostname_validchars = RegexValidator(regex=r'(?=^.{1,253}$)(?=(^([A-Za-z0-9\-\_]{1,62}\.)*[A-Za-z0-9\-\_]{1,63}$))', message="Enter a valid hostname. Full domain name may be 1-253 characters, each hostname 1-63 characters (including suffixed dot), and valid characters for hostnames are A-Z, a-z, 0-9, hyphen (-), and underscore (_)")
+ name = models.CharField(max_length=200, validators=[hostname_validchars])
+
+ def getHost(self):
+ return self.generic_host
+
+ def __str__(self):
+ return self.name
+
+ def validate(self):
+ validname = re.compile(r'(?=^.{1,253}$)(?=(^([A-Za-z0-9\-\_]{1,62}\.)*[A-Za-z0-9\-\_]{1,63}$))')
+ if not validname.match(self.name):
+ return "Enter a valid hostname. Full domain name may be 1-253 characters, each hostname 1-63 characters (including suffixed dot), and valid characters for hostnames are A-Z, a-z, 0-9, hyphen (-), and underscore (_)"
+ else:
+ return None
+
+
+# Host template
+class GenericHost(models.Model):
+ id = models.AutoField(primary_key=True)
+ profile = models.ForeignKey(HostProfile, on_delete=models.CASCADE)
+ resource = models.OneToOneField(GenericResource, related_name='generic_host', on_delete=models.CASCADE)
+
+ def __str__(self):
+ return self.resource.name
+
+
+# Physical, actual resources
+class ResourceBundle(models.Model):
+ id = models.AutoField(primary_key=True)
+ template = models.ForeignKey(GenericResourceBundle, on_delete=models.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_host(self, role="Jumphost"):
+ return Host.objects.filter(bundle=self, config__is_head_node=True).first() # should only ever be one, but it is not an invariant in the models
+
+
+class GenericInterface(models.Model):
+ id = models.AutoField(primary_key=True)
+ profile = models.ForeignKey(InterfaceProfile, on_delete=models.CASCADE)
+ host = models.ForeignKey(GenericHost, on_delete=models.CASCADE, related_name='generic_interfaces')
+ connections = models.ManyToManyField(NetworkConnection)
+
+ def __str__(self):
+ return "type " + str(self.profile) + " on host " + str(self.host)
+
+
+class Scenario(models.Model):
+ id = models.AutoField(primary_key=True)
+ name = models.CharField(max_length=300)
+
+ def __str__(self):
+ return self.name
+
+
+class Installer(models.Model):
+ id = models.AutoField(primary_key=True)
+ name = models.CharField(max_length=200)
+ sup_scenarios = models.ManyToManyField(Scenario, blank=True)
+
+ def __str__(self):
+ return self.name
+
+
+class Opsys(models.Model):
+ id = models.AutoField(primary_key=True)
+ name = models.CharField(max_length=100)
+ sup_installers = models.ManyToManyField(Installer, blank=True)
+
+ def __str__(self):
+ return self.name
+
+
+class NetworkRole(models.Model):
+ name = models.CharField(max_length=100)
+ network = models.ForeignKey(Network, on_delete=models.CASCADE)
+
+
+class ConfigBundle(models.Model):
+ id = models.AutoField(primary_key=True)
+ owner = models.ForeignKey(User, on_delete=models.CASCADE)
+ name = models.CharField(max_length=200, unique=True)
+ description = models.CharField(max_length=1000, default="")
+ bundle = models.ForeignKey(GenericResourceBundle, null=True, on_delete=models.CASCADE)
+
+ def __str__(self):
+ return self.name
+
+
+class OPNFVConfig(models.Model):
+ id = models.AutoField(primary_key=True)
+ installer = models.ForeignKey(Installer, on_delete=models.CASCADE)
+ scenario = models.ForeignKey(Scenario, on_delete=models.CASCADE)
+ bundle = models.ForeignKey(ConfigBundle, related_name="opnfv_config", on_delete=models.CASCADE)
+ 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
+
+
+class Image(models.Model):
+ """
+ model for representing OS images / snapshots of hosts
+ """
+ id = models.AutoField(primary_key=True)
+ lab_id = models.IntegerField() # ID the lab who holds this image knows
+ from_lab = models.ForeignKey(Lab, on_delete=models.CASCADE)
+ name = models.CharField(max_length=200)
+ owner = models.ForeignKey(User, null=True, on_delete=models.SET_NULL)
+ public = models.BooleanField(default=True)
+ host_type = models.ForeignKey(HostProfile, on_delete=models.CASCADE)
+ description = models.TextField()
+ os = models.ForeignKey(Opsys, null=True, on_delete=models.CASCADE)
+
+ def __str__(self):
+ return self.name
+
+
+def get_sentinal_opnfv_role():
+ return OPNFVRole.objects.get_or_create(name="deleted", description="Role was deleted.")
+
+
+class HostConfiguration(models.Model):
+ """
+ model to represent a complete configuration for a single
+ physical host
+ """
+ id = models.AutoField(primary_key=True)
+ host = models.ForeignKey(GenericHost, related_name="configuration", on_delete=models.CASCADE)
+ image = models.ForeignKey(Image, on_delete=models.PROTECT)
+ bundle = models.ForeignKey(ConfigBundle, related_name="hostConfigurations", null=True, on_delete=models.CASCADE)
+ is_head_node = models.BooleanField(default=False)
+
+ def __str__(self):
+ return "config with " + str(self.host) + " and image " + str(self.image)
+
+
+class HostOPNFVConfig(models.Model):
+ role = models.ForeignKey(OPNFVRole, related_name="host_opnfv_configs", on_delete=models.CASCADE)
+ host_config = models.ForeignKey(HostConfiguration, related_name="host_opnfv_config", on_delete=models.CASCADE)
+ opnfv_config = models.ForeignKey(OPNFVConfig, related_name="host_opnfv_config", on_delete=models.CASCADE)
+
+
+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
+
+
+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
+
+
+# Concrete host, actual machine in a lab
+class Host(models.Model):
+ id = models.AutoField(primary_key=True)
+ template = models.ForeignKey(GenericHost, on_delete=models.SET_NULL, null=True)
+ booked = models.BooleanField(default=False)
+ name = models.CharField(max_length=200, unique=True)
+ bundle = models.ForeignKey(ResourceBundle, related_name='hosts', on_delete=models.SET_NULL, null=True)
+ config = models.ForeignKey(HostConfiguration, null=True, related_name="configuration", on_delete=models.SET_NULL)
+ labid = models.CharField(max_length=200, default="default_id")
+ profile = models.ForeignKey(HostProfile, on_delete=models.CASCADE)
+ lab = models.ForeignKey(Lab, on_delete=models.CASCADE)
+ working = models.BooleanField(default=True)
+ vendor = models.CharField(max_length=100, default="unknown")
+ model = models.CharField(max_length=150, default="unknown")
+ remote_management = models.ForeignKey(RemoteInfo, default=get_default_remote_info, on_delete=models.SET(get_default_remote_info))
+
+ def __str__(self):
+ return self.name
+
+
+class Interface(models.Model):
+ id = models.AutoField(primary_key=True)
+ mac_address = models.CharField(max_length=17)
+ bus_address = models.CharField(max_length=50)
+ name = models.CharField(max_length=100, default="eth0")
+ config = models.ManyToManyField(Vlan)
+ host = models.ForeignKey(Host, on_delete=models.CASCADE, related_name='interfaces')
+
+ def __str__(self):
+ return self.mac_address + " on host " + str(self.host)
+
+
+class OPNFV_SETTINGS():
+ """
+ This is a static configuration class
+ """
+ # all the required network types in PDF/IDF spec
+ NETWORK_ROLES = ["public", "private", "admin", "mgmt"]
diff --git a/dashboard/src/resource_inventory/pdf_templater.py b/dashboard/src/resource_inventory/pdf_templater.py
new file mode 100644
index 0000000..2302530
--- /dev/null
+++ b/dashboard/src/resource_inventory/pdf_templater.py
@@ -0,0 +1,193 @@
+##############################################################################
+# 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 Host, InterfaceProfile
+
+
+class PDFTemplater:
+ """
+ Utility class to create a full PDF yaml file
+ """
+
+ @classmethod
+ def makePDF(cls, booking):
+ """
+ fills the pod descriptor file template with info about the resource
+ """
+ template = "dashboard/pdf.yaml"
+ info = {}
+ info['details'] = cls.get_pdf_details(booking.resource)
+ info['jumphost'] = cls.get_pdf_jumphost(booking)
+ 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 = "https://wiki.opnfv.org/display/INF/Pharos+Laas"
+
+ try:
+ # try to get more specific info that may fail, we dont care if it does
+ booking_owner = booking.models.Booking.objects.get(resource=resource).owner
+ owner = booking_owner.username
+ email = booking_owner.userprofile.email_addr
+ except Exception:
+ 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):
+ 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 = Host.objects.filter(
+ bundle=booking.resource,
+ config__is_head_node=True
+ ).first()
+
+ return jumphost
+
+ @classmethod
+ def get_pdf_jumphost(cls, booking):
+ """
+ returns 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):
+ """
+ returns a list of all the "nodes" (every host except jumphost)
+ """
+ pdf_nodes = []
+ nodes = set(Host.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):
+ """
+ method to gather all needed info about a host
+ returns a dict
+ """
+ host_info = {}
+ host_info['name'] = host.template.resource.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):
+ """
+ returns "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):
+ """
+ returns 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):
+ """
+ returns a dict describing given interface
+ """
+ iface_info = {}
+ iface_info['features'] = "none"
+ iface_info['mac_address'] = interface.mac_address
+ iface_info['name'] = interface.name
+ speed = "unknown"
+ try:
+ profile = InterfaceProfile.objects.get(host=interface.host.profile, name=interface.name)
+ speed = str(int(profile.speed / 1000)) + "gb"
+ except Exception:
+ pass
+ iface_info['speed'] = speed
+ return iface_info
+
+ @classmethod
+ def get_pdf_host_remote_management(cls, host):
+ """
+ gives 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/dashboard/src/resource_inventory/resource_manager.py b/dashboard/src/resource_inventory/resource_manager.py
new file mode 100644
index 0000000..652e4e3
--- /dev/null
+++ b/dashboard/src/resource_inventory/resource_manager.py
@@ -0,0 +1,174 @@
+##############################################################################
+# 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 dashboard.exceptions import (
+ ResourceExistenceException,
+ ResourceAvailabilityException,
+ ResourceProvisioningException,
+ ModelValidationException,
+)
+from resource_inventory.models import (
+ Host,
+ HostConfiguration,
+ ResourceBundle,
+ HostProfile,
+ Network,
+ Vlan
+)
+
+
+class ResourceManager:
+
+ instance = None
+
+ def __init__(self):
+ pass
+
+ @staticmethod
+ def getInstance():
+ if ResourceManager.instance is None:
+ ResourceManager.instance = ResourceManager()
+ return ResourceManager.instance
+
+ def getAvailableHostTypes(self, lab):
+ hostset = Host.objects.filter(lab=lab).filter(booked=False).filter(working=True)
+ hostprofileset = HostProfile.objects.filter(host__in=hostset, labs=lab)
+ return set(hostprofileset)
+
+ def hostsAvailable(self, grb):
+ """
+ This method will check if the given GenericResourceBundle
+ is available. No changes to the database
+ """
+
+ # count up hosts
+ profile_count = {}
+ for host in grb.getHosts():
+ if host.profile not in profile_count:
+ profile_count[host.profile] = 0
+ profile_count[host.profile] += 1
+
+ # check that all required hosts are available
+ for profile in profile_count.keys():
+ available = Host.objects.filter(
+ booked=False,
+ lab=grb.lab,
+ profile=profile
+ ).count()
+ needed = profile_count[profile]
+ if available < needed:
+ return False
+ return True
+
+ # public interface
+ def deleteResourceBundle(self, resourceBundle):
+ for host in Host.objects.filter(bundle=resourceBundle):
+ self.releaseHost(host)
+ resourceBundle.delete()
+
+ def get_vlans(self, genericResourceBundle):
+ networks = {}
+ vlan_manager = genericResourceBundle.lab.vlan_manager
+ for network in genericResourceBundle.networks.all():
+ if network.is_public:
+ public_net = vlan_manager.get_public_vlan()
+ vlan_manager.reserve_public_vlan(public_net.vlan)
+ networks[network.name] = public_net.vlan
+ else:
+ vlan = vlan_manager.get_vlan()
+ vlan_manager.reserve_vlans(vlan)
+ networks[network.name] = vlan
+ return networks
+
+ def convertResourceBundle(self, genericResourceBundle, config=None):
+ """
+ Takes in a GenericResourceBundle and 'converts' it into a ResourceBundle
+ """
+ resource_bundle = ResourceBundle.objects.create(template=genericResourceBundle)
+ generic_hosts = genericResourceBundle.getHosts()
+ physical_hosts = []
+
+ vlan_map = self.get_vlans(genericResourceBundle)
+
+ for generic_host in generic_hosts:
+ host_config = None
+ if config:
+ host_config = HostConfiguration.objects.get(bundle=config, host=generic_host)
+ try:
+ physical_host = self.acquireHost(generic_host, genericResourceBundle.lab.name)
+ except ResourceAvailabilityException:
+ self.fail_acquire(physical_hosts, vlan_map, genericResourceBundle)
+ raise ResourceAvailabilityException("Could not provision hosts, not enough available")
+ try:
+ physical_host.bundle = resource_bundle
+ physical_host.template = generic_host
+ physical_host.config = host_config
+ physical_hosts.append(physical_host)
+
+ self.configureNetworking(physical_host, vlan_map)
+ except Exception:
+ self.fail_acquire(physical_hosts, vlan_map, genericResourceBundle)
+ raise ResourceProvisioningException("Network configuration failed.")
+ try:
+ physical_host.save()
+ except Exception:
+ self.fail_acquire(physical_hosts, vlan_map, genericResourceBundle)
+ raise ModelValidationException("Saving hosts failed")
+
+ return resource_bundle
+
+ def configureNetworking(self, host, vlan_map):
+ generic_interfaces = list(host.template.generic_interfaces.all())
+ for int_num, physical_interface in enumerate(host.interfaces.all()):
+ generic_interface = generic_interfaces[int_num]
+ physical_interface.config.clear()
+ for connection in generic_interface.connections.all():
+ 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=connection.network
+ )
+ )
+
+ # private interface
+ def acquireHost(self, genericHost, labName):
+ host_full_set = Host.objects.filter(lab__name__exact=labName, profile=genericHost.profile)
+ if not host_full_set.first():
+ raise ResourceExistenceException("No matching servers found")
+ host_set = host_full_set.filter(booked=False)
+ if not host_set.first():
+ raise ResourceAvailabilityException("No unbooked hosts match requested hosts")
+ host = host_set.first()
+ host.booked = True
+ host.template = genericHost
+ host.save()
+ return host
+
+ def releaseHost(self, host):
+ host.template = None
+ host.bundle = None
+ host.booked = False
+ host.save()
+
+ def releaseNetworks(self, grb, vlan_manager, vlans):
+ for net_name, vlan_id in vlans.items():
+ net = Network.objects.get(name=net_name, bundle=grb)
+ if(net.is_public):
+ vlan_manager.release_public_vlan(vlan_id)
+ else:
+ vlan_manager.release_vlans(vlan_id)
+
+ def fail_acquire(self, hosts, vlans, grb):
+ vlan_manager = grb.lab.vlan_manager
+ self.releaseNetworks(grb, vlan_manager, vlans)
+ for host in hosts:
+ self.releaseHost(host)
diff --git a/dashboard/src/resource_inventory/tests/test_managers.py b/dashboard/src/resource_inventory/tests/test_managers.py
new file mode 100644
index 0000000..0e7c673
--- /dev/null
+++ b/dashboard/src/resource_inventory/tests/test_managers.py
@@ -0,0 +1,249 @@
+##############################################################################
+# Copyright (c) 2018 Parker Berberian, Sawyer Bergeron, and others.
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+##############################################################################
+
+from django.test import TestCase
+from django.contrib.auth.models import User
+
+from resource.inventory_manager import InventoryManager
+from resource.resource_manager import ResourceManager
+from 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
diff --git a/dashboard/src/resource_inventory/tests/test_models.py b/dashboard/src/resource_inventory/tests/test_models.py
new file mode 100644
index 0000000..e1b2106
--- /dev/null
+++ b/dashboard/src/resource_inventory/tests/test_models.py
@@ -0,0 +1,173 @@
+##############################################################################
+# 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(
+ lab_id=0,
+ from_lab=lab,
+ name="an image for testing",
+ owner=owner
+ )
+
+ @staticmethod
+ def makeGenericHost():
+ profile = HostProfile.objects.create(
+ host_type=0,
+ name="test lab for config bundle",
+ description="this is a test profile"
+ )
+ user = User.objects.create(username="test sample user 12")
+ bundle = GenericResourceBundle.objects.create(
+ name="Generic bundle for config tests",
+ xml="",
+ owner=user,
+ description=""
+ )
+
+ resource = GenericResource.objects.create(
+ bundle=bundle,
+ name="a test generic resource"
+ )
+
+ return GenericHost.objects.create(
+ profile=profile,
+ resource=resource
+ )
+
+ @staticmethod
+ def makeHostConfiguration():
+ host = ConfigUtil.makeGenericHost()
+ image = ConfigUtil.makeImage()
+ bundle = ConfigUtil.makeConfigBundle()
+ opnfvRole = ConfigUtil.makeOPNFVRole()
+ return HostConfiguration.objects.create(
+ host=host,
+ image=image,
+ bundle=bundle,
+ opnfvRole=opnfvRole
+ )
+
+
+class ScenarioTestCase(TestCase):
+
+ def test_save(self):
+ self.assertTrue(ConfigUtil.makeScenario())
+
+
+class InstallerTestCase(TestCase):
+
+ def test_save(self):
+ self.assertTrue(ConfigUtil.makeInstaller())
+
+
+class OperatingSystemTestCase(TestCase):
+
+ def test_save(self):
+ self.assertTrue(ConfigUtil.makeOpsys())
+
+
+class ConfigBundleTestCase(TestCase):
+
+ def test_save(self):
+ self.assertTrue(ConfigUtil.makeConfigBundle())
+
+
+class OPNFVConfigTestCase(TestCase):
+
+ def test_save(self):
+ self.assertTrue(ConfigUtil.makeOPNFVConfig())
+
+
+class OPNFVRoleTestCase(TestCase):
+
+ def test_save(self):
+ self.assertTrue(ConfigUtil.makeOPNFVRole())
+
+
+class HostConfigurationTestCase(TestCase):
+
+ def test_save(self):
+ self.assertTrue(ConfigUtil.makeHostConfiguration())
+
+
+class ImageTestCase(TestCase):
+
+ def test_save(self):
+ self.assertTrue(ConfigUtil.makeImage())
diff --git a/dashboard/src/resource_inventory/urls.py b/dashboard/src/resource_inventory/urls.py
new file mode 100644
index 0000000..a72871b
--- /dev/null
+++ b/dashboard/src/resource_inventory/urls.py
@@ -0,0 +1,35 @@
+##############################################################################
+# Copyright (c) 2016 Max Breitenfeldt and others.
+# Copyright (c) 2018 Parker Berberian, Sawyer Bergeron, and others.
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+##############################################################################
+
+
+"""pharos_dashboard URL Configuration
+
+The `urlpatterns` list routes URLs to views. For more information please see:
+ https://docs.djangoproject.com/en/1.10/topics/http/urls/
+Examples:
+Function views
+ 1. Add an import: from my_app import views
+ 2. Add a URL to urlpatterns: url(r'^$', views.home, name='home')
+Class-based views
+ 1. Add an import: from other_app.views import Home
+ 2. Add a URL to urlpatterns: url(r'^$', Home.as_view(), name='home')
+Including another URLconf
+ 1. Import the include() function: from django.conf.urls import url, include
+ 2. Add a URL to urlpatterns: url(r'^blog/', include('blog.urls'))
+"""
+from django.conf.urls import url
+from resource_inventory.views import HostView, 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/dashboard/src/resource_inventory/views.py b/dashboard/src/resource_inventory/views.py
new file mode 100644
index 0000000..8c3d899
--- /dev/null
+++ b/dashboard/src/resource_inventory/views.py
@@ -0,0 +1,38 @@
+##############################################################################
+# 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 HostProfile, Host
+
+
+class HostView(TemplateView):
+ template_name = "resource/hosts.html"
+
+ def get_context_data(self, **kwargs):
+ context = super(HostView, self).get_context_data(**kwargs)
+ hosts = Host.objects.filter(working=True)
+ context.update({'hosts': hosts, 'title': "Hardware Resources"})
+ return context
+
+
+def hostprofile_detail_view(request, hostprofile_id):
+ hostprofile = get_object_or_404(HostProfile, id=hostprofile_id)
+
+ return render(
+ request,
+ "resource/hostprofile_detail.html",
+ {
+ 'title': "Host Type: " + str(hostprofile.name),
+ 'hostprofile': hostprofile
+ }
+ )
diff --git a/dashboard/src/static/bower.json b/dashboard/src/static/bower.json
index f473747..dda786d 100644
--- a/dashboard/src/static/bower.json
+++ b/dashboard/src/static/bower.json
@@ -16,9 +16,14 @@
"tests"
],
"dependencies": {
- "eonasdan-bootstrap-datetimepicker": "^4.17.37",
"fullcalendar": "^2.9.0",
"jquery-migrate": "^3.0.0",
- "startbootstrap-sb-admin-2-blackrockdigital": "^3.3.7"
+ "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/dashboard/src/static/css/base.css b/dashboard/src/static/css/base.css
new file mode 100644
index 0000000..c51728c
--- /dev/null
+++ b/dashboard/src/static/css/base.css
@@ -0,0 +1,8 @@
+/* Rotating arrows when dropdown happens */
+i.fas.rotate {
+ transition: transform 0.3s ease-in-out;
+}
+
+a[aria-expanded="true"] > i.rotate {
+ transform: rotate(180deg);
+}
diff --git a/dashboard/src/static/css/detail_view.css b/dashboard/src/static/css/detail_view.css
new file mode 100644
index 0000000..c3d0a4d
--- /dev/null
+++ b/dashboard/src/static/css/detail_view.css
@@ -0,0 +1,39 @@
+.card_container {
+ display: grid;
+ grid-template-columns: 1fr 1fr 1fr 1fr;
+ grid-gap: 25px 25px;
+ justify-items: stretch;
+}
+
+.card_container ul > li {
+ padding: 7px !important;
+ font-size: 16px;
+}
+
+.detail_button_container .btn {
+ width: 49%;
+}
+
+.detail_button_container .btn-danger {
+ float: right;
+}
+
+#modal_warning {
+ transition: max-height 0.5s ease-out;
+ overflow: hidden;
+}
+
+.detail_card {
+ border-radius: 5px;
+ border-top: 1px solid #ccc;
+ border-left: 1px solid #ccc;
+ border-right: 1px solid #ccc;
+ border-bottom: 1px solid #ccc;
+ margin: 5px;
+ padding-left: 25px;
+ padding-right: 25px;
+ padding-bottom: 15px;
+ display: flex;
+ flex-direction: column;
+ justify-content: space-between;
+}
diff --git a/dashboard/src/static/css/graph_common.css b/dashboard/src/static/css/graph_common.css
new file mode 100644
index 0000000..cff1516
--- /dev/null
+++ b/dashboard/src/static/css/graph_common.css
@@ -0,0 +1,172 @@
+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 {
+ background: url('../img/mxgraph/window.gif');
+ border:1px solid #c3c3c3;
+ position: absolute;
+ overflow: hidden;
+ z-index: 3;
+}
+table.mxWindow {
+ border-collapse: collapse;
+ table-layout: fixed;
+ font-family: Arial;
+ font-size: 8pt;
+}
+td.mxWindowTitle {
+ background: url('/static/img/mxgraph/window-title.gif') repeat-x;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ text-align: center;
+ font-weight: bold;
+ overflow: hidden;
+ height: 13px;
+ padding: 2px;
+ padding-top: 4px;
+ padding-bottom: 6px;
+ color: black;
+}
+td.mxWindowPane {
+ vertical-align: top;
+ padding: 0px;
+}
+div.mxWindowPane {
+ overflow: hidden;
+ position: relative;
+}
+td.mxWindowPane td {
+ font-family: Arial;
+ font-size: 8pt;
+}
+td.mxWindowPane input, td.mxWindowPane select, td.mxWindowPane textarea, td.mxWindowPane radio {
+ font-family: Arial;
+ font-size: 8pt;
+ padding: 1px;
+}
+td.mxWindowPane button {
+ color: #fff;
+ background-color: #337ab7;
+ border-color: #2e6da4;
+ display: inline-block;
+ margin: 2%;
+ font-size: 14px;
+ font-weight: 400;
+ line-height: 1.42857143;
+ text-align: center;
+ white-space: nowrap;
+ vertical-align: middle;
+ -ms-touch-action: manipulation;
+ touch-action: manipulation;
+ cursor: pointer;
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+ background-image: none;
+ border: 1px solid transparent;
+ border-radius: 4px;
+}
+img.mxToolbarItem {
+ margin-right: 6px;
+ margin-bottom: 6px;
+ border-width: 1px;
+}
+select.mxToolbarCombo {
+ vertical-align: top;
+ border-style: inset;
+ border-width: 2px;
+}
+div.mxToolbarComboContainer {
+ padding: 2px;
+}
+img.mxToolbarMode {
+ margin: 2px;
+ margin-right: 4px;
+ margin-bottom: 4px;
+ border-width: 0px;
+}
+img.mxToolbarModeSelected {
+ margin: 0px;
+ margin-right: 2px;
+ margin-bottom: 2px;
+ border-width: 2px;
+ border-style: inset;
+}
+div.mxTooltip {
+ -webkit-box-shadow: 3px 3px 12px #C0C0C0;
+ -moz-box-shadow: 3px 3px 12px #C0C0C0;
+ box-shadow: 3px 3px 12px #C0C0C0;
+ background: #FFFFCC;
+ border-style: solid;
+ border-width: 1px;
+ border-color: black;
+ font-family: Arial;
+ font-size: 8pt;
+ position: absolute;
+ cursor: default;
+ padding: 4px;
+ color: black;
+}
+div.mxPopupMenu {
+ -webkit-box-shadow: 3px 3px 12px #C0C0C0;
+ -moz-box-shadow: 3px 3px 12px #C0C0C0;
+ box-shadow: 3px 3px 12px #C0C0C0;
+ background: url('/static/img/mxgraph/window.gif');
+ position: absolute;
+ border-style: solid;
+ border-width: 1px;
+ border-color: black;
+}
+table.mxPopupMenu {
+ border-collapse: collapse;
+ margin-top: 1px;
+ margin-bottom: 1px;
+}
+tr.mxPopupMenuItem {
+ color: black;
+ cursor: pointer;
+}
+tr.mxPopupMenuItemHover {
+ background-color: #000066;
+ color: #FFFFFF;
+ cursor: pointer;
+}
+td.mxPopupMenuItem {
+ padding: 2px 30px 2px 10px;
+ white-space: nowrap;
+ font-family: Arial;
+ font-size: 8pt;
+}
+td.mxPopupMenuIcon {
+ background-color: #D0D0D0;
+ padding: 2px 4px 2px 4px;
+}
+.mxDisabled {
+ opacity: 0.2 !important;
+ cursor:default !important;
+}
diff --git a/dashboard/src/static/css/theme.css b/dashboard/src/static/css/theme.css
index bd15637..fb350e2 100644
--- a/dashboard/src/static/css/theme.css
+++ b/dashboard/src/static/css/theme.css
@@ -10,4 +10,10 @@
.modal p {
word-wrap: break-word;
+}
+
+.create_drop {
+ display: none;
+ width: 100%;
+
} \ No newline at end of file
diff --git a/dashboard/src/static/img/mxgraph/add.png b/dashboard/src/static/img/mxgraph/add.png
new file mode 100644
index 0000000..bf5f8ed
--- /dev/null
+++ b/dashboard/src/static/img/mxgraph/add.png
Binary files differ
diff --git a/dashboard/src/static/img/mxgraph/button.gif b/dashboard/src/static/img/mxgraph/button.gif
new file mode 100644
index 0000000..ad55cab
--- /dev/null
+++ b/dashboard/src/static/img/mxgraph/button.gif
Binary files differ
diff --git a/dashboard/src/static/img/mxgraph/camera.png b/dashboard/src/static/img/mxgraph/camera.png
new file mode 100644
index 0000000..aecc94d
--- /dev/null
+++ b/dashboard/src/static/img/mxgraph/camera.png
Binary files differ
diff --git a/dashboard/src/static/img/mxgraph/check.png b/dashboard/src/static/img/mxgraph/check.png
new file mode 100644
index 0000000..ce81bce
--- /dev/null
+++ b/dashboard/src/static/img/mxgraph/check.png
Binary files differ
diff --git a/dashboard/src/static/img/mxgraph/close.gif b/dashboard/src/static/img/mxgraph/close.gif
new file mode 100644
index 0000000..1069e94
--- /dev/null
+++ b/dashboard/src/static/img/mxgraph/close.gif
Binary files differ
diff --git a/dashboard/src/static/img/mxgraph/close.png b/dashboard/src/static/img/mxgraph/close.png
new file mode 100644
index 0000000..4de4396
--- /dev/null
+++ b/dashboard/src/static/img/mxgraph/close.png
Binary files differ
diff --git a/dashboard/src/static/img/mxgraph/collapsed.gif b/dashboard/src/static/img/mxgraph/collapsed.gif
new file mode 100644
index 0000000..0276444
--- /dev/null
+++ b/dashboard/src/static/img/mxgraph/collapsed.gif
Binary files differ
diff --git a/dashboard/src/static/img/mxgraph/connector.gif b/dashboard/src/static/img/mxgraph/connector.gif
new file mode 100644
index 0000000..326e061
--- /dev/null
+++ b/dashboard/src/static/img/mxgraph/connector.gif
Binary files differ
diff --git a/dashboard/src/static/img/mxgraph/copy.png b/dashboard/src/static/img/mxgraph/copy.png
new file mode 100644
index 0000000..a987d43
--- /dev/null
+++ b/dashboard/src/static/img/mxgraph/copy.png
Binary files differ
diff --git a/dashboard/src/static/img/mxgraph/cut.png b/dashboard/src/static/img/mxgraph/cut.png
new file mode 100644
index 0000000..52bf944
--- /dev/null
+++ b/dashboard/src/static/img/mxgraph/cut.png
Binary files differ
diff --git a/dashboard/src/static/img/mxgraph/delete2.png b/dashboard/src/static/img/mxgraph/delete2.png
new file mode 100644
index 0000000..be78c61
--- /dev/null
+++ b/dashboard/src/static/img/mxgraph/delete2.png
Binary files differ
diff --git a/dashboard/src/static/img/mxgraph/dot.gif b/dashboard/src/static/img/mxgraph/dot.gif
new file mode 100644
index 0000000..08b9947
--- /dev/null
+++ b/dashboard/src/static/img/mxgraph/dot.gif
Binary files differ
diff --git a/dashboard/src/static/img/mxgraph/error.gif b/dashboard/src/static/img/mxgraph/error.gif
new file mode 100644
index 0000000..14e1aee
--- /dev/null
+++ b/dashboard/src/static/img/mxgraph/error.gif
Binary files differ
diff --git a/dashboard/src/static/img/mxgraph/expanded.gif b/dashboard/src/static/img/mxgraph/expanded.gif
new file mode 100644
index 0000000..3767b0b
--- /dev/null
+++ b/dashboard/src/static/img/mxgraph/expanded.gif
Binary files differ
diff --git a/dashboard/src/static/img/mxgraph/export1.png b/dashboard/src/static/img/mxgraph/export1.png
new file mode 100644
index 0000000..b8a01b8
--- /dev/null
+++ b/dashboard/src/static/img/mxgraph/export1.png
Binary files differ
diff --git a/dashboard/src/static/img/mxgraph/fit_to_size.png b/dashboard/src/static/img/mxgraph/fit_to_size.png
new file mode 100644
index 0000000..4de46b0
--- /dev/null
+++ b/dashboard/src/static/img/mxgraph/fit_to_size.png
Binary files differ
diff --git a/dashboard/src/static/img/mxgraph/gradient_background.jpg b/dashboard/src/static/img/mxgraph/gradient_background.jpg
new file mode 100644
index 0000000..7dbf35b
--- /dev/null
+++ b/dashboard/src/static/img/mxgraph/gradient_background.jpg
Binary files differ
diff --git a/dashboard/src/static/img/mxgraph/green-dot.gif b/dashboard/src/static/img/mxgraph/green-dot.gif
new file mode 100644
index 0000000..acaf7b2
--- /dev/null
+++ b/dashboard/src/static/img/mxgraph/green-dot.gif
Binary files differ
diff --git a/dashboard/src/static/img/mxgraph/group.png b/dashboard/src/static/img/mxgraph/group.png
new file mode 100644
index 0000000..585ad79
--- /dev/null
+++ b/dashboard/src/static/img/mxgraph/group.png
Binary files differ
diff --git a/dashboard/src/static/img/mxgraph/handle-connect.png b/dashboard/src/static/img/mxgraph/handle-connect.png
new file mode 100644
index 0000000..513ab2b
--- /dev/null
+++ b/dashboard/src/static/img/mxgraph/handle-connect.png
Binary files differ
diff --git a/dashboard/src/static/img/mxgraph/handle-main.png b/dashboard/src/static/img/mxgraph/handle-main.png
new file mode 100644
index 0000000..ee067ff
--- /dev/null
+++ b/dashboard/src/static/img/mxgraph/handle-main.png
Binary files differ
diff --git a/dashboard/src/static/img/mxgraph/icons48/column.png b/dashboard/src/static/img/mxgraph/icons48/column.png
new file mode 100644
index 0000000..5ae2c24
--- /dev/null
+++ b/dashboard/src/static/img/mxgraph/icons48/column.png
Binary files differ
diff --git a/dashboard/src/static/img/mxgraph/icons48/earth.png b/dashboard/src/static/img/mxgraph/icons48/earth.png
new file mode 100644
index 0000000..4493880
--- /dev/null
+++ b/dashboard/src/static/img/mxgraph/icons48/earth.png
Binary files differ
diff --git a/dashboard/src/static/img/mxgraph/icons48/gear.png b/dashboard/src/static/img/mxgraph/icons48/gear.png
new file mode 100644
index 0000000..647d897
--- /dev/null
+++ b/dashboard/src/static/img/mxgraph/icons48/gear.png
Binary files differ
diff --git a/dashboard/src/static/img/mxgraph/icons48/keys.png b/dashboard/src/static/img/mxgraph/icons48/keys.png
new file mode 100644
index 0000000..41828e4
--- /dev/null
+++ b/dashboard/src/static/img/mxgraph/icons48/keys.png
Binary files differ
diff --git a/dashboard/src/static/img/mxgraph/icons48/mail_new.png b/dashboard/src/static/img/mxgraph/icons48/mail_new.png
new file mode 100644
index 0000000..16c6662
--- /dev/null
+++ b/dashboard/src/static/img/mxgraph/icons48/mail_new.png
Binary files differ
diff --git a/dashboard/src/static/img/mxgraph/icons48/server.png b/dashboard/src/static/img/mxgraph/icons48/server.png
new file mode 100644
index 0000000..9621c6e
--- /dev/null
+++ b/dashboard/src/static/img/mxgraph/icons48/server.png
Binary files differ
diff --git a/dashboard/src/static/img/mxgraph/icons48/table.png b/dashboard/src/static/img/mxgraph/icons48/table.png
new file mode 100644
index 0000000..d4df646
--- /dev/null
+++ b/dashboard/src/static/img/mxgraph/icons48/table.png
Binary files differ
diff --git a/dashboard/src/static/img/mxgraph/key.png b/dashboard/src/static/img/mxgraph/key.png
new file mode 100644
index 0000000..e66758a
--- /dev/null
+++ b/dashboard/src/static/img/mxgraph/key.png
Binary files differ
diff --git a/dashboard/src/static/img/mxgraph/loading.gif b/dashboard/src/static/img/mxgraph/loading.gif
new file mode 100644
index 0000000..118f4b0
--- /dev/null
+++ b/dashboard/src/static/img/mxgraph/loading.gif
Binary files differ
diff --git a/dashboard/src/static/img/mxgraph/maximize.gif b/dashboard/src/static/img/mxgraph/maximize.gif
new file mode 100644
index 0000000..e27cf3e
--- /dev/null
+++ b/dashboard/src/static/img/mxgraph/maximize.gif
Binary files differ
diff --git a/dashboard/src/static/img/mxgraph/minimize.gif b/dashboard/src/static/img/mxgraph/minimize.gif
new file mode 100644
index 0000000..1e95e7c
--- /dev/null
+++ b/dashboard/src/static/img/mxgraph/minimize.gif
Binary files differ
diff --git a/dashboard/src/static/img/mxgraph/navigate_minus.png b/dashboard/src/static/img/mxgraph/navigate_minus.png
new file mode 100644
index 0000000..71edaf9
--- /dev/null
+++ b/dashboard/src/static/img/mxgraph/navigate_minus.png
Binary files differ
diff --git a/dashboard/src/static/img/mxgraph/navigate_plus.png b/dashboard/src/static/img/mxgraph/navigate_plus.png
new file mode 100644
index 0000000..b5b7e87
--- /dev/null
+++ b/dashboard/src/static/img/mxgraph/navigate_plus.png
Binary files differ
diff --git a/dashboard/src/static/img/mxgraph/normalize.gif b/dashboard/src/static/img/mxgraph/normalize.gif
new file mode 100644
index 0000000..34a8d30
--- /dev/null
+++ b/dashboard/src/static/img/mxgraph/normalize.gif
Binary files differ
diff --git a/dashboard/src/static/img/mxgraph/paste.png b/dashboard/src/static/img/mxgraph/paste.png
new file mode 100644
index 0000000..fd628d9
--- /dev/null
+++ b/dashboard/src/static/img/mxgraph/paste.png
Binary files differ
diff --git a/dashboard/src/static/img/mxgraph/plus.png b/dashboard/src/static/img/mxgraph/plus.png
new file mode 100644
index 0000000..24a84bb
--- /dev/null
+++ b/dashboard/src/static/img/mxgraph/plus.png
Binary files differ
diff --git a/dashboard/src/static/img/mxgraph/point.gif b/dashboard/src/static/img/mxgraph/point.gif
new file mode 100644
index 0000000..9074c39
--- /dev/null
+++ b/dashboard/src/static/img/mxgraph/point.gif
Binary files differ
diff --git a/dashboard/src/static/img/mxgraph/press32.png b/dashboard/src/static/img/mxgraph/press32.png
new file mode 100644
index 0000000..f00e3f7
--- /dev/null
+++ b/dashboard/src/static/img/mxgraph/press32.png
Binary files differ
diff --git a/dashboard/src/static/img/mxgraph/print32.png b/dashboard/src/static/img/mxgraph/print32.png
new file mode 100644
index 0000000..0cca86c
--- /dev/null
+++ b/dashboard/src/static/img/mxgraph/print32.png
Binary files differ
diff --git a/dashboard/src/static/img/mxgraph/printer.png b/dashboard/src/static/img/mxgraph/printer.png
new file mode 100644
index 0000000..6004816
--- /dev/null
+++ b/dashboard/src/static/img/mxgraph/printer.png
Binary files differ
diff --git a/dashboard/src/static/img/mxgraph/redo.png b/dashboard/src/static/img/mxgraph/redo.png
new file mode 100644
index 0000000..3eae59c
--- /dev/null
+++ b/dashboard/src/static/img/mxgraph/redo.png
Binary files differ
diff --git a/dashboard/src/static/img/mxgraph/resize.gif b/dashboard/src/static/img/mxgraph/resize.gif
new file mode 100644
index 0000000..ff558db
--- /dev/null
+++ b/dashboard/src/static/img/mxgraph/resize.gif
Binary files differ
diff --git a/dashboard/src/static/img/mxgraph/separator.gif b/dashboard/src/static/img/mxgraph/separator.gif
new file mode 100644
index 0000000..5c1b895
--- /dev/null
+++ b/dashboard/src/static/img/mxgraph/separator.gif
Binary files differ
diff --git a/dashboard/src/static/img/mxgraph/sidebar_bg.gif b/dashboard/src/static/img/mxgraph/sidebar_bg.gif
new file mode 100644
index 0000000..67e8244
--- /dev/null
+++ b/dashboard/src/static/img/mxgraph/sidebar_bg.gif
Binary files differ
diff --git a/dashboard/src/static/img/mxgraph/spacer.gif b/dashboard/src/static/img/mxgraph/spacer.gif
new file mode 100644
index 0000000..35d42e8
--- /dev/null
+++ b/dashboard/src/static/img/mxgraph/spacer.gif
Binary files differ
diff --git a/dashboard/src/static/img/mxgraph/submenu.gif b/dashboard/src/static/img/mxgraph/submenu.gif
new file mode 100644
index 0000000..ffe7617
--- /dev/null
+++ b/dashboard/src/static/img/mxgraph/submenu.gif
Binary files differ
diff --git a/dashboard/src/static/img/mxgraph/toolbar_bg.gif b/dashboard/src/static/img/mxgraph/toolbar_bg.gif
new file mode 100644
index 0000000..87b9374
--- /dev/null
+++ b/dashboard/src/static/img/mxgraph/toolbar_bg.gif
Binary files differ
diff --git a/dashboard/src/static/img/mxgraph/transparent.gif b/dashboard/src/static/img/mxgraph/transparent.gif
new file mode 100644
index 0000000..76040f2
--- /dev/null
+++ b/dashboard/src/static/img/mxgraph/transparent.gif
Binary files differ
diff --git a/dashboard/src/static/img/mxgraph/undo.png b/dashboard/src/static/img/mxgraph/undo.png
new file mode 100644
index 0000000..4ba0ffb
--- /dev/null
+++ b/dashboard/src/static/img/mxgraph/undo.png
Binary files differ
diff --git a/dashboard/src/static/img/mxgraph/view_1_1.png b/dashboard/src/static/img/mxgraph/view_1_1.png
new file mode 100644
index 0000000..88657a1
--- /dev/null
+++ b/dashboard/src/static/img/mxgraph/view_1_1.png
Binary files differ
diff --git a/dashboard/src/static/img/mxgraph/view_1_132.png b/dashboard/src/static/img/mxgraph/view_1_132.png
new file mode 100644
index 0000000..e9a1b72
--- /dev/null
+++ b/dashboard/src/static/img/mxgraph/view_1_132.png
Binary files differ
diff --git a/dashboard/src/static/img/mxgraph/view_next.png b/dashboard/src/static/img/mxgraph/view_next.png
new file mode 100644
index 0000000..b4094f0
--- /dev/null
+++ b/dashboard/src/static/img/mxgraph/view_next.png
Binary files differ
diff --git a/dashboard/src/static/img/mxgraph/view_previous.png b/dashboard/src/static/img/mxgraph/view_previous.png
new file mode 100644
index 0000000..b385b44
--- /dev/null
+++ b/dashboard/src/static/img/mxgraph/view_previous.png
Binary files differ
diff --git a/dashboard/src/static/img/mxgraph/warning.gif b/dashboard/src/static/img/mxgraph/warning.gif
new file mode 100644
index 0000000..705235f
--- /dev/null
+++ b/dashboard/src/static/img/mxgraph/warning.gif
Binary files differ
diff --git a/dashboard/src/static/img/mxgraph/warning.png b/dashboard/src/static/img/mxgraph/warning.png
new file mode 100644
index 0000000..2f78789
--- /dev/null
+++ b/dashboard/src/static/img/mxgraph/warning.png
Binary files differ
diff --git a/dashboard/src/static/img/mxgraph/window-title.gif b/dashboard/src/static/img/mxgraph/window-title.gif
new file mode 100644
index 0000000..231def8
--- /dev/null
+++ b/dashboard/src/static/img/mxgraph/window-title.gif
Binary files differ
diff --git a/dashboard/src/static/img/mxgraph/window.gif b/dashboard/src/static/img/mxgraph/window.gif
new file mode 100644
index 0000000..6631c4f
--- /dev/null
+++ b/dashboard/src/static/img/mxgraph/window.gif
Binary files differ
diff --git a/dashboard/src/static/img/mxgraph/wires-grid.gif b/dashboard/src/static/img/mxgraph/wires-grid.gif
new file mode 100644
index 0000000..ad888a2
--- /dev/null
+++ b/dashboard/src/static/img/mxgraph/wires-grid.gif
Binary files differ
diff --git a/dashboard/src/static/img/mxgraph/zoom_in.png b/dashboard/src/static/img/mxgraph/zoom_in.png
new file mode 100644
index 0000000..ad6abb9
--- /dev/null
+++ b/dashboard/src/static/img/mxgraph/zoom_in.png
Binary files differ
diff --git a/dashboard/src/static/img/mxgraph/zoom_in32.png b/dashboard/src/static/img/mxgraph/zoom_in32.png
new file mode 100644
index 0000000..438ff0f
--- /dev/null
+++ b/dashboard/src/static/img/mxgraph/zoom_in32.png
Binary files differ
diff --git a/dashboard/src/static/img/mxgraph/zoom_out.png b/dashboard/src/static/img/mxgraph/zoom_out.png
new file mode 100644
index 0000000..0566f26
--- /dev/null
+++ b/dashboard/src/static/img/mxgraph/zoom_out.png
Binary files differ
diff --git a/dashboard/src/static/img/mxgraph/zoom_out32.png b/dashboard/src/static/img/mxgraph/zoom_out32.png
new file mode 100644
index 0000000..8edb765
--- /dev/null
+++ b/dashboard/src/static/img/mxgraph/zoom_out32.png
Binary files differ
diff --git a/dashboard/src/static/js/dashboard.js b/dashboard/src/static/js/dashboard.js
new file mode 100644
index 0000000..84c3703
--- /dev/null
+++ b/dashboard/src/static/js/dashboard.js
@@ -0,0 +1,1134 @@
+class MultipleSelectFilterWidget {
+
+ constructor(neighbors, items, initial) {
+ this.inputs = [];
+ this.graph_neighbors = neighbors;
+ this.filter_items = items;
+ 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;
+ for(let item_class in initial_data) {
+ const selected_items = initial_data[item_class];
+ for( let node_id in selected_items ){
+ const node = this.filter_items[node_id];
+ const selection_data = selected_items[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);
+ }
+ }
+ }
+ }
+
+ 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('disabled_node', 'cleared_node');
+ elem.classList.add('selected_node');
+ }
+
+ clear(node) {
+ const elem = document.getElementById(node['id']);
+ node['selected'] = false;
+ node['selectable'] = true;
+ elem.classList.add('cleared_node')
+ elem.classList.remove('disabled_node', 'selected_node');
+ }
+
+ disable_node(node) {
+ const elem = document.getElementById(node['id']);
+ node['selected'] = false;
+ node['selectable'] = false;
+ elem.classList.remove('cleared_node', 'selected_node');
+ elem.classList.add('disabled_node');
+ }
+
+ processClick(id){
+ const node = this.filter_items[id];
+ if(!node['selectable'])
+ 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.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");
+ 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.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("dropdown_item");
+ this.dropdown_count++;
+ const label = document.createElement("H5")
+ label.appendChild(document.createTextNode(node['name']))
+ div.appendChild(label);
+ div.appendChild(this.make_input(div, node, prepopulate));
+ div.appendChild(this.make_remove_button(div, node));
+ 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);
+ delete this.result[node.class][node.id]['values'][div.id];
+
+ //checks if we have removed last item in class
+ if(jQuery.isEmptyObject(this.result[node.class][node.id]['values'])){
+ 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, values: {}}
+
+ this.result[node.class][node.id]['values'][childKey] = childValue;
+ }
+
+ finish(){
+ document.getElementById("filter_field").value = JSON.stringify(this.result);
+ }
+}
+
+class NetworkStep {
+ constructor(debug, xml, hosts, added_hosts, removed_host_ids, graphContainer, overviewContainer, toolbarContainer){
+ if(!this.check_support())
+ 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;
+
+ this.editor.setGraphContainer(graphContainer);
+ this.doGlobalConfig();
+ this.prefill(xml, hosts, added_hosts, removed_host_ids);
+ 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);
+
+ if(this.debug){
+ this.editor.addAction('printXML', function(editor, cell) {
+ mxLog.write(this.encodeGraph());
+ mxLog.show();
+ }.bind(this));
+ this.addToolbarButton(this.editor, toolbarContainer, 'printXML', '', '/static/img/mxgraph/fit_to_size.png', true);
+ }
+
+ 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);
+
+ if(!this.has_public_net){
+ this.addPublicNetwork();
+ }
+ }
+
+ check_support(){
+ if (!mxClient.isBrowserSupported()) {
+ mxUtils.error('Browser is not supported', 200, false);
+ return false;
+ }
+ return true;
+ }
+
+ prefill(xml, hosts, added_hosts, removed_host_ids){
+ //populate existing data
+ if(xml){
+ this.restoreFromXml(xml, this.editor);
+ } else if(hosts){
+ for(const host of hosts)
+ this.makeHost(host);
+ }
+
+ //apply any changes
+ if(added_hosts){
+ for(const host of added_hosts)
+ this.makeHost(host);
+ this.updateHosts([]); //TODO: why?
+ }
+ this.updateHosts(removed_host_ids);
+ }
+
+ 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";
+ 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);
+ }
+ }
+
+ 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, label, image, isTransparent) {
+ const button = document.createElement('button');
+ button.style.fontSize = '10';
+ if (image != null) {
+ const img = document.createElement('img');
+ img.setAttribute('src', image);
+ img.style.width = '16px';
+ img.style.height = '16px';
+ img.style.verticalAlign = 'middle';
+ img.style.marginRight = '2px';
+ button.appendChild(img);
+ }
+ if (isTransparent) {
+ button.style.background = 'transparent';
+ button.style.color = '#FFFFFF';
+ button.style.border = 'none';
+ }
+ mxEvent.addListener(button, 'click', function(evt) {
+ editor.execute(action);
+ });
+ mxUtils.write(button, label);
+ 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;
+ }
+
+ 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 newNet = document.createElement("li");
+ const colorBlob = document.createElement("div");
+ colorBlob.className = "colorblob";
+ const textContainer = document.createElement("p");
+ textContainer.className = "network_innertext";
+ newNet.id = net_id;
+ const deletebutton = document.createElement("button");
+ deletebutton.className = "btn btn-danger";
+ deletebutton.style = "float: right; height: 20px; line-height: 8px; vertical-align: middle; width: 20px; padding-left: 5px;";
+ deletebutton.appendChild(document.createTextNode("X"));
+ deletebutton.addEventListener("click", function() { this.createDeleteDialog(net_id); }.bind(this), false);
+ textContainer.appendChild(document.createTextNode(net_name));
+ colorBlob.style['background'] = color;
+ newNet.appendChild(colorBlob);
+ newNet.appendChild(textContainer);
+ if( net_name != "public" ) {
+ newNet.appendChild(deletebutton);
+ }
+ document.getElementById("network_list").appendChild(newNet);
+ }
+
+ makeHost(hostInfo) {
+ 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);
+ this.graph.refresh(port);
+ }
+ this.graph.refresh(host);
+ }
+
+ submitForm() {
+ const form = document.getElementById("xml_form");
+ const input_elem = document.getElementById("hidden_xml_input");
+ input_elem.value = this.encodeGraph(this.graph);
+ const req = new XMLHttpRequest();
+ req.open("POST", "/wf/workflow/", false);
+ req.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
+ req.onerror = function() { alert("problem with form submission"); }
+ const formData = $("#xml_form").serialize();
+ req.send(formData);
+ }
+}
+
+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 result_entry = document.createElement("li");
+ const result_button = document.createElement("a");
+ const obj = this.items[id];
+ const result_text = this.generate_element_text(obj);
+ result_button.appendChild(document.createTextNode(result_text));
+ result_button.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.setAttribute('class', 'entry_tooltip');
+ result_button.appendChild(tooltip);
+ result_entry.appendChild(result_button);
+ 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);
+ }
+
+ let list_html = "";
+
+ for( const item_id of this.added_items )
+ {
+ const item = this.items[item_id];
+
+ const element_entry_text = this.generate_element_text(item);
+
+ list_html += '<div class="list_entry">'
+ + '<p class="added_entry_text">'
+ + element_entry_text
+ + '</p>'
+ + '<button onclick="searchable_select_multiple_widget.remove_item('
+ + item_id
+ + ')" class="btn-remove btn">remove</button>';
+ list_html += '</div>';
+ }
+ added_list.innerHTML = list_html;
+ }
+}
diff --git a/dashboard/src/static/js/mxClient.min.js b/dashboard/src/static/js/mxClient.min.js
new file mode 100644
index 0000000..150a0ac
--- /dev/null
+++ b/dashboard/src/static/js/mxClient.min.js
@@ -0,0 +1,1808 @@
+var mxClient={VERSION:"3.9.6",IS_IE:0<=navigator.userAgent.indexOf("MSIE"),IS_IE6:0<=navigator.userAgent.indexOf("MSIE 6"),IS_IE11:!!navigator.userAgent.match(/Trident\/7\./),IS_EDGE:!!navigator.userAgent.match(/Edge\//),IS_QUIRKS:0<=navigator.userAgent.indexOf("MSIE")&&(null==document.documentMode||5==document.documentMode),IS_EM:"spellcheck"in document.createElement("textarea")&&8==document.documentMode,VML_PREFIX:"v",OFFICE_PREFIX:"o",IS_NS:0<=navigator.userAgent.indexOf("Mozilla/")&&0>navigator.userAgent.indexOf("MSIE")&&
+0>navigator.userAgent.indexOf("Edge/"),IS_OP:0<=navigator.userAgent.indexOf("Opera/")||0<=navigator.userAgent.indexOf("OPR/"),IS_OT:0<=navigator.userAgent.indexOf("Presto/")&&0>navigator.userAgent.indexOf("Presto/2.4.")&&0>navigator.userAgent.indexOf("Presto/2.3.")&&0>navigator.userAgent.indexOf("Presto/2.2.")&&0>navigator.userAgent.indexOf("Presto/2.1.")&&0>navigator.userAgent.indexOf("Presto/2.0.")&&0>navigator.userAgent.indexOf("Presto/1."),IS_SF:0<=navigator.userAgent.indexOf("AppleWebKit/")&&
+0>navigator.userAgent.indexOf("Chrome/")&&0>navigator.userAgent.indexOf("Edge/"),IS_IOS:navigator.userAgent.match(/(iPad|iPhone|iPod)/g)?!0:!1,IS_GC:0<=navigator.userAgent.indexOf("Chrome/")&&0>navigator.userAgent.indexOf("Edge/"),IS_CHROMEAPP:null!=window.chrome&&null!=chrome.app&&null!=chrome.app.runtime,IS_FF:0<=navigator.userAgent.indexOf("Firefox/"),IS_MT:0<=navigator.userAgent.indexOf("Firefox/")&&0>navigator.userAgent.indexOf("Firefox/1.")&&0>navigator.userAgent.indexOf("Firefox/2.")||0<=navigator.userAgent.indexOf("Iceweasel/")&&
+0>navigator.userAgent.indexOf("Iceweasel/1.")&&0>navigator.userAgent.indexOf("Iceweasel/2.")||0<=navigator.userAgent.indexOf("SeaMonkey/")&&0>navigator.userAgent.indexOf("SeaMonkey/1.")||0<=navigator.userAgent.indexOf("Iceape/")&&0>navigator.userAgent.indexOf("Iceape/1."),IS_SVG:0<=navigator.userAgent.indexOf("Firefox/")||0<=navigator.userAgent.indexOf("Iceweasel/")||0<=navigator.userAgent.indexOf("Seamonkey/")||0<=navigator.userAgent.indexOf("Iceape/")||0<=navigator.userAgent.indexOf("Galeon/")||
+0<=navigator.userAgent.indexOf("Epiphany/")||0<=navigator.userAgent.indexOf("AppleWebKit/")||0<=navigator.userAgent.indexOf("Gecko/")||0<=navigator.userAgent.indexOf("Opera/")||null!=document.documentMode&&9<=document.documentMode,NO_FO:!document.createElementNS||"[object SVGForeignObjectElement]"!=document.createElementNS("http://www.w3.org/2000/svg","foreignObject")||0<=navigator.userAgent.indexOf("Opera/"),IS_VML:"MICROSOFT INTERNET EXPLORER"==navigator.appName.toUpperCase(),IS_WIN:0<navigator.appVersion.indexOf("Win"),
+IS_MAC:0<navigator.appVersion.indexOf("Mac"),IS_TOUCH:"ontouchstart"in document.documentElement,IS_POINTER:null!=window.PointerEvent&&!(0<navigator.appVersion.indexOf("Mac")),IS_LOCAL:0>document.location.href.indexOf("http://")&&0>document.location.href.indexOf("https://"),defaultBundles:[],isBrowserSupported:function(){return mxClient.IS_VML||mxClient.IS_SVG},link:function(a,b,c){c=c||document;if(mxClient.IS_IE6)c.write('<link rel="'+a+'" href="'+b+'" charset="UTF-8" type="text/css"/>');else{var d=
+c.createElement("link");d.setAttribute("rel",a);d.setAttribute("href",b);d.setAttribute("charset","UTF-8");d.setAttribute("type","text/css");c.getElementsByTagName("head")[0].appendChild(d)}},loadResources:function(a,b){function c(){0==--d&&a()}for(var d=mxClient.defaultBundles.length,e=0;e<mxClient.defaultBundles.length;e++)mxResources.add(mxClient.defaultBundles[e],b,c)},include:function(a){document.write('<script src="'+a+'">\x3c/script>')},dispose:function(){for(var a=0;a<mxEvent.objects.length;a++)null!=
+mxEvent.objects[a].mxListenerList&&mxEvent.removeAllListeners(mxEvent.objects[a])}};"undefined"==typeof mxLoadResources&&(mxLoadResources=!0);"undefined"==typeof mxForceIncludes&&(mxForceIncludes=!1);"undefined"==typeof mxResourceExtension&&(mxResourceExtension=".txt");"undefined"==typeof mxLoadStylesheets&&(mxLoadStylesheets=!0);
+"undefined"!=typeof mxBasePath&&0<mxBasePath.length?("/"==mxBasePath.substring(mxBasePath.length-1)&&(mxBasePath=mxBasePath.substring(0,mxBasePath.length-1)),mxClient.basePath=mxBasePath):mxClient.basePath=".";"undefined"!=typeof mxImageBasePath&&0<mxImageBasePath.length?("/"==mxImageBasePath.substring(mxImageBasePath.length-1)&&(mxImageBasePath=mxImageBasePath.substring(0,mxImageBasePath.length-1)),mxClient.imageBasePath=mxImageBasePath):mxClient.imageBasePath=mxClient.basePath+"/images";
+mxClient.language="undefined"!=typeof mxLanguage&&null!=mxLanguage?mxLanguage:mxClient.IS_IE?navigator.userLanguage:navigator.language;mxClient.defaultLanguage="undefined"!=typeof mxDefaultLanguage&&null!=mxDefaultLanguage?mxDefaultLanguage:"en";mxLoadStylesheets&&mxClient.link("stylesheet",mxClient.basePath+"/css/common.css");"undefined"!=typeof mxLanguages&&null!=mxLanguages&&(mxClient.languages=mxLanguages);
+mxClient.IS_VML&&(mxClient.IS_SVG?mxClient.IS_VML=!1:(8==document.documentMode?(document.namespaces.add(mxClient.VML_PREFIX,"urn:schemas-microsoft-com:vml","#default#VML"),document.namespaces.add(mxClient.OFFICE_PREFIX,"urn:schemas-microsoft-com:office:office","#default#VML")):(document.namespaces.add(mxClient.VML_PREFIX,"urn:schemas-microsoft-com:vml"),document.namespaces.add(mxClient.OFFICE_PREFIX,"urn:schemas-microsoft-com:office:office")),mxClient.IS_QUIRKS&&30<=document.styleSheets.length?function(){var a=
+document.createElement("style");a.type="text/css";a.styleSheet.cssText=mxClient.VML_PREFIX+"\\:*{behavior:url(#default#VML)}"+mxClient.OFFICE_PREFIX+"\\:*{behavior:url(#default#VML)}";document.getElementsByTagName("head")[0].appendChild(a)}():document.createStyleSheet().cssText=mxClient.VML_PREFIX+"\\:*{behavior:url(#default#VML)}"+mxClient.OFFICE_PREFIX+"\\:*{behavior:url(#default#VML)}",mxLoadStylesheets&&mxClient.link("stylesheet",mxClient.basePath+"/css/explorer.css"),window.attachEvent("onunload",
+mxClient.dispose)));
+var mxLog={consoleName:"Console",TRACE:!1,DEBUG:!0,WARN:!0,buffer:"",init:function(){if(null==mxLog.window&&null!=document.body){var a=mxLog.consoleName+" - mxGraph "+mxClient.VERSION,b=document.createElement("table");b.setAttribute("width","100%");b.setAttribute("height","100%");var c=document.createElement("tbody"),d=document.createElement("tr"),e=document.createElement("td");e.style.verticalAlign="top";mxLog.textarea=document.createElement("textarea");mxLog.textarea.setAttribute("wrap","off");
+mxLog.textarea.setAttribute("readOnly","true");mxLog.textarea.style.height="100%";mxLog.textarea.style.resize="none";mxLog.textarea.value=mxLog.buffer;mxLog.textarea.style.width=mxClient.IS_NS&&"BackCompat"!=document.compatMode?"99%":"100%";e.appendChild(mxLog.textarea);d.appendChild(e);c.appendChild(d);d=document.createElement("tr");mxLog.td=document.createElement("td");mxLog.td.style.verticalAlign="top";mxLog.td.setAttribute("height","30px");d.appendChild(mxLog.td);c.appendChild(d);b.appendChild(c);
+mxLog.addButton("Info",function(a){mxLog.info()});mxLog.addButton("DOM",function(a){a=mxUtils.getInnerHtml(document.body);mxLog.debug(a)});mxLog.addButton("Trace",function(a){mxLog.TRACE=!mxLog.TRACE;mxLog.TRACE?mxLog.debug("Tracing enabled"):mxLog.debug("Tracing disabled")});mxLog.addButton("Copy",function(a){try{mxUtils.copy(mxLog.textarea.value)}catch(k){mxUtils.alert(k)}});mxLog.addButton("Show",function(a){try{mxUtils.popup(mxLog.textarea.value)}catch(k){mxUtils.alert(k)}});mxLog.addButton("Clear",
+function(a){mxLog.textarea.value=""});d=c=0;"number"===typeof window.innerWidth?(c=window.innerHeight,d=window.innerWidth):(c=document.documentElement.clientHeight||document.body.clientHeight,d=document.body.clientWidth);mxLog.window=new mxWindow(a,b,Math.max(0,d-320),Math.max(0,c-210),300,160);mxLog.window.setMaximizable(!0);mxLog.window.setScrollable(!1);mxLog.window.setResizable(!0);mxLog.window.setClosable(!0);mxLog.window.destroyOnClose=!1;if((mxClient.IS_NS||mxClient.IS_IE)&&!mxClient.IS_GC&&
+!mxClient.IS_SF&&"BackCompat"!=document.compatMode||11==document.documentMode){var f=mxLog.window.getElement(),a=function(a,b){mxLog.textarea.style.height=Math.max(0,f.offsetHeight-70)+"px"};mxLog.window.addListener(mxEvent.RESIZE_END,a);mxLog.window.addListener(mxEvent.MAXIMIZE,a);mxLog.window.addListener(mxEvent.NORMALIZE,a);mxLog.textarea.style.height="92px"}}},info:function(){mxLog.writeln(mxUtils.toString(navigator))},addButton:function(a,b){var c=document.createElement("button");mxUtils.write(c,
+a);mxEvent.addListener(c,"click",b);mxLog.td.appendChild(c)},isVisible:function(){return null!=mxLog.window?mxLog.window.isVisible():!1},show:function(){mxLog.setVisible(!0)},setVisible:function(a){null==mxLog.window&&mxLog.init();null!=mxLog.window&&mxLog.window.setVisible(a)},enter:function(a){if(mxLog.TRACE)return mxLog.writeln("Entering "+a),(new Date).getTime()},leave:function(a,b){if(mxLog.TRACE){var c=0!=b?" ("+((new Date).getTime()-b)+" ms)":"";mxLog.writeln("Leaving "+a+c)}},debug:function(){mxLog.DEBUG&&
+mxLog.writeln.apply(this,arguments)},warn:function(){mxLog.WARN&&mxLog.writeln.apply(this,arguments)},write:function(){for(var a="",b=0;b<arguments.length;b++)a+=arguments[b],b<arguments.length-1&&(a+=" ");null!=mxLog.textarea?(mxLog.textarea.value+=a,0<=navigator.userAgent.indexOf("Presto/2.5")&&(mxLog.textarea.style.visibility="hidden",mxLog.textarea.style.visibility="visible"),mxLog.textarea.scrollTop=mxLog.textarea.scrollHeight):mxLog.buffer+=a},writeln:function(){for(var a="",b=0;b<arguments.length;b++)a+=
+arguments[b],b<arguments.length-1&&(a+=" ");mxLog.write(a+"\n")}},mxObjectIdentity={FIELD_NAME:"mxObjectId",counter:0,get:function(a){if(null!=a){if(null==a[mxObjectIdentity.FIELD_NAME])if("object"===typeof a){var b=mxUtils.getFunctionName(a.constructor);a[mxObjectIdentity.FIELD_NAME]=b+"#"+mxObjectIdentity.counter++}else"function"===typeof a&&(a[mxObjectIdentity.FIELD_NAME]="Function#"+mxObjectIdentity.counter++);return a[mxObjectIdentity.FIELD_NAME]}return null},clear:function(a){"object"!==typeof a&&
+"function"!==typeof a||delete a[mxObjectIdentity.FIELD_NAME]}};function mxDictionary(){this.clear()}mxDictionary.prototype.map=null;mxDictionary.prototype.clear=function(){this.map={}};mxDictionary.prototype.get=function(a){a=mxObjectIdentity.get(a);return this.map[a]};mxDictionary.prototype.put=function(a,b){var c=mxObjectIdentity.get(a),d=this.map[c];this.map[c]=b;return d};mxDictionary.prototype.remove=function(a){a=mxObjectIdentity.get(a);var b=this.map[a];delete this.map[a];return b};
+mxDictionary.prototype.getKeys=function(){var a=[],b;for(b in this.map)a.push(b);return a};mxDictionary.prototype.getValues=function(){var a=[],b;for(b in this.map)a.push(this.map[b]);return a};mxDictionary.prototype.visit=function(a){for(var b in this.map)a(b,this.map[b])};
+var mxResources={resources:{},extension:mxResourceExtension,resourcesEncoded:!1,loadDefaultBundle:!0,loadSpecialBundle:!0,isLanguageSupported:function(a){return null!=mxClient.languages?0<=mxUtils.indexOf(mxClient.languages,a):!0},getDefaultBundle:function(a,b){return mxResources.loadDefaultBundle||!mxResources.isLanguageSupported(b)?a+mxResources.extension:null},getSpecialBundle:function(a,b){if(null==mxClient.languages||!this.isLanguageSupported(b)){var c=b.indexOf("-");0<c&&(b=b.substring(0,c))}return mxResources.loadSpecialBundle&&
+mxResources.isLanguageSupported(b)&&b!=mxClient.defaultLanguage?a+"_"+b+mxResources.extension:null},add:function(a,b,c){b=null!=b?b:null!=mxClient.language?mxClient.language.toLowerCase():mxConstants.NONE;if(b!=mxConstants.NONE){var d=mxResources.getDefaultBundle(a,b),e=mxResources.getSpecialBundle(a,b),f=function(){if(null!=e)if(c)mxUtils.get(e,function(a){mxResources.parse(a.getText());c()},function(){c()});else try{var a=mxUtils.load(e);a.isReady()&&mxResources.parse(a.getText())}catch(l){}else null!=
+c&&c()};if(null!=d)if(c)mxUtils.get(d,function(a){mxResources.parse(a.getText());f()},function(){f()});else try{var g=mxUtils.load(d);g.isReady()&&mxResources.parse(g.getText());f()}catch(k){}else f()}},parse:function(a){if(null!=a){a=a.split("\n");for(var b=0;b<a.length;b++)if("#"!=a[b].charAt(0)){var c=a[b].indexOf("=");if(0<c){var d=a[b].substring(0,c),e=a[b].length;13==a[b].charCodeAt(e-1)&&e--;c=a[b].substring(c+1,e);this.resourcesEncoded?(c=c.replace(/\\(?=u[a-fA-F\d]{4})/g,"%"),mxResources.resources[d]=
+unescape(c)):mxResources.resources[d]=c}}}},get:function(a,b,c){a=mxResources.resources[a];null==a&&(a=c);null!=a&&null!=b&&(a=mxResources.replacePlaceholders(a,b));return a},replacePlaceholders:function(a,b){for(var c=[],d=null,e=0;e<a.length;e++){var f=a.charAt(e);"{"==f?d="":null!=d&&"}"==f?(d=parseInt(d)-1,0<=d&&d<b.length&&c.push(b[d]),d=null):null!=d?d+=f:c.push(f)}return c.join("")},loadResources:function(a){mxResources.add(mxClient.basePath+"/resources/editor",null,function(){mxResources.add(mxClient.basePath+
+"/resources/graph",null,a)})}};function mxPoint(a,b){this.x=null!=a?a:0;this.y=null!=b?b:0}mxPoint.prototype.x=null;mxPoint.prototype.y=null;mxPoint.prototype.equals=function(a){return null!=a&&a.x==this.x&&a.y==this.y};mxPoint.prototype.clone=function(){return mxUtils.clone(this)};function mxRectangle(a,b,c,d){mxPoint.call(this,a,b);this.width=null!=c?c:0;this.height=null!=d?d:0}mxRectangle.prototype=new mxPoint;mxRectangle.prototype.constructor=mxRectangle;mxRectangle.prototype.width=null;
+mxRectangle.prototype.height=null;mxRectangle.prototype.setRect=function(a,b,c,d){this.x=a;this.y=b;this.width=c;this.height=d};mxRectangle.prototype.getCenterX=function(){return this.x+this.width/2};mxRectangle.prototype.getCenterY=function(){return this.y+this.height/2};
+mxRectangle.prototype.add=function(a){if(null!=a){var b=Math.min(this.x,a.x),c=Math.min(this.y,a.y),d=Math.max(this.x+this.width,a.x+a.width);a=Math.max(this.y+this.height,a.y+a.height);this.x=b;this.y=c;this.width=d-b;this.height=a-c}};mxRectangle.prototype.intersect=function(a){if(null!=a){var b=this.x+this.width,c=a.x+a.width,d=this.y+this.height,e=a.y+a.height;this.x=Math.max(this.x,a.x);this.y=Math.max(this.y,a.y);this.width=Math.min(b,c)-this.x;this.height=Math.min(d,e)-this.y}};
+mxRectangle.prototype.grow=function(a){this.x-=a;this.y-=a;this.width+=2*a;this.height+=2*a};mxRectangle.prototype.getPoint=function(){return new mxPoint(this.x,this.y)};mxRectangle.prototype.rotate90=function(){var a=(this.width-this.height)/2;this.x+=a;this.y-=a;a=this.width;this.width=this.height;this.height=a};mxRectangle.prototype.equals=function(a){return null!=a&&a.x==this.x&&a.y==this.y&&a.width==this.width&&a.height==this.height};
+mxRectangle.fromRectangle=function(a){return new mxRectangle(a.x,a.y,a.width,a.height)};
+var mxEffects={animateChanges:function(a,b,c){var d=0,e=function(){for(var g=!1,k=0;k<b.length;k++){var l=b[k];if(l instanceof mxGeometryChange||l instanceof mxTerminalChange||l instanceof mxValueChange||l instanceof mxChildChange||l instanceof mxStyleChange){var m=a.getView().getState(l.cell||l.child,!1);if(null!=m)if(g=!0,l.constructor!=mxGeometryChange||a.model.isEdge(l.cell))mxUtils.setOpacity(m.shape.node,100*d/10);else{var n=a.getView().scale,p=(l.geometry.x-l.previous.x)*n,q=(l.geometry.y-
+l.previous.y)*n,r=(l.geometry.width-l.previous.width)*n,n=(l.geometry.height-l.previous.height)*n;0==d?(m.x-=p,m.y-=q,m.width-=r,m.height-=n):(m.x+=p/10,m.y+=q/10,m.width+=r/10,m.height+=n/10);a.cellRenderer.redraw(m);mxEffects.cascadeOpacity(a,l.cell,100*d/10)}}}10>d&&g?(d++,window.setTimeout(e,f)):null!=c&&c()},f=30;e()},cascadeOpacity:function(a,b,c){for(var d=a.model.getChildCount(b),e=0;e<d;e++){var f=a.model.getChildAt(b,e),g=a.getView().getState(f);null!=g&&(mxUtils.setOpacity(g.shape.node,
+c),mxEffects.cascadeOpacity(a,f,c))}b=a.model.getEdges(b);if(null!=b)for(e=0;e<b.length;e++)d=a.getView().getState(b[e]),null!=d&&mxUtils.setOpacity(d.shape.node,c)},fadeOut:function(a,b,c,d,e,f){d=d||40;e=e||30;var g=b||100;mxUtils.setOpacity(a,g);if(f||null==f){var k=function(){g=Math.max(g-d,0);mxUtils.setOpacity(a,g);0<g?window.setTimeout(k,e):(a.style.visibility="hidden",c&&a.parentNode&&a.parentNode.removeChild(a))};window.setTimeout(k,e)}else a.style.visibility="hidden",c&&a.parentNode&&a.parentNode.removeChild(a)}},
+mxUtils={errorResource:"none"!=mxClient.language?"error":"",closeResource:"none"!=mxClient.language?"close":"",errorImage:mxClient.imageBasePath+"/error.gif",removeCursors:function(a){null!=a.style&&(a.style.cursor="");a=a.childNodes;if(null!=a)for(var b=a.length,c=0;c<b;c+=1)mxUtils.removeCursors(a[c])},getCurrentStyle:function(){return mxClient.IS_IE&&(null==document.documentMode||9>document.documentMode)?function(a){return null!=a?a.currentStyle:null}:function(a){return null!=a?window.getComputedStyle(a,
+""):null}}(),parseCssNumber:function(a){"thin"==a?a="2":"medium"==a?a="4":"thick"==a&&(a="6");a=parseFloat(a);isNaN(a)&&(a=0);return a},setPrefixedStyle:function(){var a=null;mxClient.IS_OT?a="O":mxClient.IS_SF||mxClient.IS_GC?a="Webkit":mxClient.IS_MT?a="Moz":mxClient.IS_IE&&9<=document.documentMode&&10>document.documentMode&&(a="ms");return function(b,c,d){b[c]=d;null!=a&&0<c.length&&(c=a+c.substring(0,1).toUpperCase()+c.substring(1),b[c]=d)}}(),hasScrollbars:function(a){a=mxUtils.getCurrentStyle(a);
+return null!=a&&("scroll"==a.overflow||"auto"==a.overflow)},bind:function(a,b){return function(){return b.apply(a,arguments)}},eval:function(a){var b=null;if(0<=a.indexOf("function"))try{eval("var _mxJavaScriptExpression="+a),b=_mxJavaScriptExpression,_mxJavaScriptExpression=null}catch(c){mxLog.warn(c.message+" while evaluating "+a)}else try{b=eval(a)}catch(c){mxLog.warn(c.message+" while evaluating "+a)}return b},findNode:function(a,b,c){if(a.nodeType==mxConstants.NODETYPE_ELEMENT){var d=a.getAttribute(b);
+if(null!=d&&d==c)return a}for(a=a.firstChild;null!=a;){d=mxUtils.findNode(a,b,c);if(null!=d)return d;a=a.nextSibling}return null},getFunctionName:function(a){var b=null;null!=a&&(null!=a.name?b=a.name:(b=mxUtils.trim(a.toString()),/^function\s/.test(b)&&(b=mxUtils.ltrim(b.substring(9)),a=b.indexOf("("),0<a&&(b=b.substring(0,a)))));return b},indexOf:function(a,b){if(null!=a&&null!=b)for(var c=0;c<a.length;c++)if(a[c]==b)return c;return-1},forEach:function(a,b){if(null!=a&&null!=b)for(var c=0;c<a.length;c++)b(a[c]);
+return a},remove:function(a,b){var c=null;if("object"==typeof b)for(var d=mxUtils.indexOf(b,a);0<=d;)b.splice(d,1),c=a,d=mxUtils.indexOf(b,a);for(var e in b)b[e]==a&&(delete b[e],c=a);return c},isNode:function(a,b,c,d){return null==a||isNaN(a.nodeType)||null!=b&&a.nodeName.toLowerCase()!=b.toLowerCase()?!1:null==c||a.getAttribute(c)==d},isAncestorNode:function(a,b){for(var c=b;null!=c;){if(c==a)return!0;c=c.parentNode}return!1},getChildNodes:function(a,b){b=b||mxConstants.NODETYPE_ELEMENT;for(var c=
+[],d=a.firstChild;null!=d;)d.nodeType==b&&c.push(d),d=d.nextSibling;return c},importNode:function(a,b,c){if(mxClient.IS_IE&&(null==document.documentMode||10>document.documentMode))switch(b.nodeType){case 1:var d=a.createElement(b.nodeName);if(b.attributes&&0<b.attributes.length){for(var e=0;e<b.attributes.length;e++)d.setAttribute(b.attributes[e].nodeName,b.getAttribute(b.attributes[e].nodeName));if(c&&b.childNodes&&0<b.childNodes.length)for(e=0;e<b.childNodes.length;e++)d.appendChild(mxUtils.importNode(a,
+b.childNodes[e],c))}return d;case 3:case 4:case 8:return a.createTextNode(b.value)}else return a.importNode(b,c)},createXmlDocument:function(){var a=null;document.implementation&&document.implementation.createDocument?a=document.implementation.createDocument("","",null):window.ActiveXObject&&(a=new ActiveXObject("Microsoft.XMLDOM"));return a},parseXml:function(){return window.DOMParser?function(a){return(new DOMParser).parseFromString(a,"text/xml")}:function(a){var b=mxUtils.createXmlDocument();b.async=
+!1;b.validateOnParse=!1;b.resolveExternals=!1;b.loadXML(a);return b}}(),clearSelection:function(){return document.selection?function(){document.selection.empty()}:window.getSelection?function(){window.getSelection().empty?window.getSelection().empty():window.getSelection().removeAllRanges&&window.getSelection().removeAllRanges()}:function(){}}(),getPrettyXml:function(a,b,c){var d=[];if(null!=a)if(b=b||" ",c=c||"",a.nodeType==mxConstants.NODETYPE_TEXT)a=mxUtils.trim(mxUtils.getTextContent(a)),0<a.length&&
+d.push(c+mxUtils.htmlEntities(a)+"\n");else{d.push(c+"<"+a.nodeName);var e=a.attributes;if(null!=e)for(var f=0;f<e.length;f++){var g=mxUtils.htmlEntities(e[f].value);d.push(" "+e[f].nodeName+'="'+g+'"')}e=a.firstChild;if(null!=e){for(d.push(">\n");null!=e;)d.push(mxUtils.getPrettyXml(e,b,c+b)),e=e.nextSibling;d.push(c+"</"+a.nodeName+">\n")}else d.push("/>\n")}return d.join("")},removeWhitespace:function(a,b){for(var c=b?a.previousSibling:a.nextSibling;null!=c&&c.nodeType==mxConstants.NODETYPE_TEXT;){var d=
+b?c.previousSibling:c.nextSibling,e=mxUtils.getTextContent(c);0==mxUtils.trim(e).length&&c.parentNode.removeChild(c);c=d}},htmlEntities:function(a,b){a=String(a||"");a=a.replace(/&/g,"&amp;");a=a.replace(/"/g,"&quot;");a=a.replace(/\'/g,"&#39;");a=a.replace(/</g,"&lt;");a=a.replace(/>/g,"&gt;");if(null==b||b)a=a.replace(/\n/g,"&#xa;");return a},isVml:function(a){return null!=a&&"urn:schemas-microsoft-com:vml"==a.tagUrn},getXml:function(a,b){var c="";null!=window.XMLSerializer?c=(new XMLSerializer).serializeToString(a):
+null!=a.xml&&(c=a.xml.replace(/\r\n\t[\t]*/g,"").replace(/>\r\n/g,">").replace(/\r\n/g,"\n"));return c=c.replace(/\n/g,b||"&#xa;")},extractTextWithWhitespace:function(a){function b(a){if(1!=a.length||"BR"!=a[0].nodeName&&"\n"!=a[0].innerHTML)for(var e=0;e<a.length;e++){var g=a[e];"BR"==g.nodeName||"\n"==g.innerHTML||(1==a.length||0==e)&&"DIV"==g.nodeName&&"<br>"==g.innerHTML.toLowerCase()?d.push("\n"):(3===g.nodeType||4===g.nodeType?0<g.nodeValue.length&&d.push(g.nodeValue):8!==g.nodeType&&0<g.childNodes.length&&
+b(g.childNodes),e<a.length-1&&0<=mxUtils.indexOf(c,a[e+1].nodeName)&&d.push("\n"))}}var c="BLOCKQUOTE DIV H1 H2 H3 H4 H5 H6 OL P PRE TABLE UL".split(" "),d=[];b(a);return d.join("")},replaceTrailingNewlines:function(a,b){for(var c="";0<a.length&&"\n"==a.charAt(a.length-1);)a=a.substring(0,a.length-1),c+=b;return a+c},getTextContent:function(a){return mxClient.IS_IE&&void 0!==a.innerText?a.innerText:null!=a?a[void 0===a.textContent?"text":"textContent"]:""},setTextContent:function(a,b){void 0!==a.innerText?
+a.innerText=b:a[void 0===a.textContent?"text":"textContent"]=b},getInnerHtml:function(){return mxClient.IS_IE?function(a){return null!=a?a.innerHTML:""}:function(a){return null!=a?(new XMLSerializer).serializeToString(a):""}}(),getOuterHtml:function(){return mxClient.IS_IE?function(a){if(null!=a){if(null!=a.outerHTML)return a.outerHTML;var b=[];b.push("<"+a.nodeName);var c=a.attributes;if(null!=c)for(var d=0;d<c.length;d++){var e=c[d].value;null!=e&&0<e.length&&(b.push(" "),b.push(c[d].nodeName),
+b.push('="'),b.push(e),b.push('"'))}0==a.innerHTML.length?b.push("/>"):(b.push(">"),b.push(a.innerHTML),b.push("</"+a.nodeName+">"));return b.join("")}return""}:function(a){return null!=a?(new XMLSerializer).serializeToString(a):""}}(),write:function(a,b){var c=a.ownerDocument.createTextNode(b);null!=a&&a.appendChild(c);return c},writeln:function(a,b){var c=a.ownerDocument.createTextNode(b);null!=a&&(a.appendChild(c),a.appendChild(document.createElement("br")));return c},br:function(a,b){b=b||1;for(var c=
+null,d=0;d<b;d++)null!=a&&(c=a.ownerDocument.createElement("br"),a.appendChild(c));return c},button:function(a,b,c){c=null!=c?c:document;c=c.createElement("button");mxUtils.write(c,a);mxEvent.addListener(c,"click",function(a){b(a)});return c},para:function(a,b){var c=document.createElement("p");mxUtils.write(c,b);null!=a&&a.appendChild(c);return c},addTransparentBackgroundFilter:function(a){a.style.filter+="progid:DXImageTransform.Microsoft.AlphaImageLoader(src='"+mxClient.imageBasePath+"/transparent.gif', sizingMethod='scale')"},
+linkAction:function(a,b,c,d,e){return mxUtils.link(a,b,function(){c.execute(d)},e)},linkInvoke:function(a,b,c,d,e,f){return mxUtils.link(a,b,function(){c[d](e)},f)},link:function(a,b,c,d){var e=document.createElement("span");e.style.color="blue";e.style.textDecoration="underline";e.style.cursor="pointer";null!=d&&(e.style.paddingLeft=d+"px");mxEvent.addListener(e,"click",c);mxUtils.write(e,b);null!=a&&a.appendChild(e);return e},fit:function(a){var b=parseInt(a.offsetLeft),c=parseInt(a.offsetWidth),
+d=mxUtils.getDocumentScrollOrigin(a.ownerDocument),e=d.x,d=d.y,f=document.body,g=document.documentElement,k=e+(f.clientWidth||g.clientWidth);b+c>k&&(a.style.left=Math.max(e,k-c)+"px");b=parseInt(a.offsetTop);c=parseInt(a.offsetHeight);f=d+Math.max(f.clientHeight||0,g.clientHeight);b+c>f&&(a.style.top=Math.max(d,f-c)+"px")},load:function(a){a=new mxXmlRequest(a,null,"GET",!1);a.send();return a},get:function(a,b,c,d,e,f){a=new mxXmlRequest(a,null,"GET");null!=d&&a.setBinary(d);a.send(b,c,e,f);return a},
+getAll:function(a,b,c){for(var d=a.length,e=[],f=0,g=function(){0==f&&null!=c&&c();f++},k=0;k<a.length;k++)(function(a,c){mxUtils.get(a,function(a){var f=a.getStatus();200>f||299<f?g():(e[c]=a,d--,0==d&&b(e))},g)})(a[k],k);0==d&&b(e)},post:function(a,b,c,d){return(new mxXmlRequest(a,b)).send(c,d)},submit:function(a,b,c,d){return(new mxXmlRequest(a,b)).simulate(c,d)},loadInto:function(a,b,c){mxClient.IS_IE?b.onreadystatechange=function(){4==b.readyState&&c()}:b.addEventListener("load",c,!1);b.load(a)},
+getValue:function(a,b,c){a=null!=a?a[b]:null;null==a&&(a=c);return a},getNumber:function(a,b,c){a=null!=a?a[b]:null;null==a&&(a=c||0);return Number(a)},getColor:function(a,b,c){a=null!=a?a[b]:null;null==a?a=c:a==mxConstants.NONE&&(a=null);return a},clone:function(a,b,c){c=null!=c?c:!1;var d=null;if(null!=a&&"function"==typeof a.constructor){var d=new a.constructor,e;for(e in a)e!=mxObjectIdentity.FIELD_NAME&&(null==b||0>mxUtils.indexOf(b,e))&&(d[e]=c||"object"!=typeof a[e]?a[e]:mxUtils.clone(a[e]))}return d},
+equalPoints:function(a,b){if(null==a&&null!=b||null!=a&&null==b||null!=a&&null!=b&&a.length!=b.length)return!1;if(null!=a&&null!=b)for(var c=0;c<a.length;c++)if(a[c]==b[c]||null!=a[c]&&!a[c].equals(b[c]))return!1;return!0},equalEntries:function(a,b){if(null==a&&null!=b||null!=a&&null==b||null!=a&&null!=b&&a.length!=b.length)return!1;if(null!=a&&null!=b){var c=0,d;for(d in b)c++;for(d in a)if(c--,!(mxUtils.isNaN(a[d])&&mxUtils.isNaN(b[d])||a[d]==b[d]))return!1}return 0==c},removeDuplicates:function(a){for(var b=
+new mxDictionary,c=[],d=0;d<a.length;d++)b.get(a[d])||(c.push(a[d]),b.put(a[d],!0));return c},isNaN:function(a){return"number"==typeof a&&isNaN(a)},extend:function(a,b){var c=function(){};c.prototype=b.prototype;a.prototype=new c;a.prototype.constructor=a},toString:function(a){var b="",c;for(c in a)try{if(null==a[c])b+=c+" = [null]\n";else if("function"==typeof a[c])b+=c+" => [Function]\n";else if("object"==typeof a[c])var d=mxUtils.getFunctionName(a[c].constructor),b=b+(c+" => ["+d+"]\n");else b+=
+c+" = "+a[c]+"\n"}catch(e){b+=c+"="+e.message}return b},toRadians:function(a){return Math.PI*a/180},toDegree:function(a){return 180*a/Math.PI},arcToCurves:function(a,b,c,d,e,f,g,k,l){k-=a;l-=b;if(0===c||0===d)return q;c=Math.abs(c);d=Math.abs(d);var m=-k/2,n=-l/2,p=Math.cos(e*Math.PI/180),q=Math.sin(e*Math.PI/180);e=p*m+q*n;var m=-1*q*m+p*n,n=e*e,r=m*m,t=c*c,u=d*d,x=n/t+r/u;1<x?(c*=Math.sqrt(x),d*=Math.sqrt(x),f=0):(x=1,f===g&&(x=-1),f=x*Math.sqrt((t*u-t*r-u*n)/(t*r+u*n)));n=f*c*m/d;r=-1*f*d*e/c;
+k=p*n-q*r+k/2;l=q*n+p*r+l/2;t=Math.atan2((m-r)/d,(e-n)/c)-Math.atan2(0,1);f=0<=t?t:2*Math.PI+t;t=Math.atan2((-m-r)/d,(-e-n)/c)-Math.atan2((m-r)/d,(e-n)/c);e=0<=t?t:2*Math.PI+t;0==g&&0<e?e-=2*Math.PI:0!=g&&0>e&&(e+=2*Math.PI);g=2*e/Math.PI;g=Math.ceil(0>g?-1*g:g);e/=g;m=8/3*Math.sin(e/4)*Math.sin(e/4)/Math.sin(e/2);n=p*c;p*=d;c*=q;d*=q;for(var y=Math.cos(f),A=Math.sin(f),r=-m*(n*A+d*y),t=-m*(c*A-p*y),q=[],z=0;z<g;++z){f+=e;var y=Math.cos(f),A=Math.sin(f),u=n*y-d*A+k,x=c*y+p*A+l,v=-m*(n*A+d*y),y=-m*
+(c*A-p*y),A=6*z;q[A]=Number(r+a);q[A+1]=Number(t+b);q[A+2]=Number(u-v+a);q[A+3]=Number(x-y+b);q[A+4]=Number(u+a);q[A+5]=Number(x+b);r=u+v;t=x+y}return q},getBoundingBox:function(a,b,c){var d=null;if(null!=a&&null!=b&&0!=b){b=mxUtils.toRadians(b);var d=Math.cos(b),e=Math.sin(b);c=null!=c?c:new mxPoint(a.x+a.width/2,a.y+a.height/2);var f=new mxPoint(a.x,a.y);b=new mxPoint(a.x+a.width,a.y);var g=new mxPoint(b.x,a.y+a.height);a=new mxPoint(a.x,g.y);f=mxUtils.getRotatedPoint(f,d,e,c);b=mxUtils.getRotatedPoint(b,
+d,e,c);g=mxUtils.getRotatedPoint(g,d,e,c);a=mxUtils.getRotatedPoint(a,d,e,c);d=new mxRectangle(f.x,f.y,0,0);d.add(new mxRectangle(b.x,b.y,0,0));d.add(new mxRectangle(g.x,g.y,0,0));d.add(new mxRectangle(a.x,a.y,0,0))}return d},getRotatedPoint:function(a,b,c,d){d=null!=d?d:new mxPoint;var e=a.x-d.x;a=a.y-d.y;return new mxPoint(e*b-a*c+d.x,a*b+e*c+d.y)},getPortConstraints:function(a,b,c,d){b=mxUtils.getValue(a.style,mxConstants.STYLE_PORT_CONSTRAINT,mxUtils.getValue(b.style,c?mxConstants.STYLE_SOURCE_PORT_CONSTRAINT:
+mxConstants.STYLE_TARGET_PORT_CONSTRAINT,null));if(null==b)return d;d=b.toString();b=mxConstants.DIRECTION_MASK_NONE;c=0;1==mxUtils.getValue(a.style,mxConstants.STYLE_PORT_CONSTRAINT_ROTATION,0)&&(c=mxUtils.getValue(a.style,mxConstants.STYLE_ROTATION,0));a=0;45<c?(a=1,135<=c&&(a=2)):-45>c&&(a=3,-135>=c&&(a=2));if(0<=d.indexOf(mxConstants.DIRECTION_NORTH))switch(a){case 0:b|=mxConstants.DIRECTION_MASK_NORTH;break;case 1:b|=mxConstants.DIRECTION_MASK_EAST;break;case 2:b|=mxConstants.DIRECTION_MASK_SOUTH;
+break;case 3:b|=mxConstants.DIRECTION_MASK_WEST}if(0<=d.indexOf(mxConstants.DIRECTION_WEST))switch(a){case 0:b|=mxConstants.DIRECTION_MASK_WEST;break;case 1:b|=mxConstants.DIRECTION_MASK_NORTH;break;case 2:b|=mxConstants.DIRECTION_MASK_EAST;break;case 3:b|=mxConstants.DIRECTION_MASK_SOUTH}if(0<=d.indexOf(mxConstants.DIRECTION_SOUTH))switch(a){case 0:b|=mxConstants.DIRECTION_MASK_SOUTH;break;case 1:b|=mxConstants.DIRECTION_MASK_WEST;break;case 2:b|=mxConstants.DIRECTION_MASK_NORTH;break;case 3:b|=
+mxConstants.DIRECTION_MASK_EAST}if(0<=d.indexOf(mxConstants.DIRECTION_EAST))switch(a){case 0:b|=mxConstants.DIRECTION_MASK_EAST;break;case 1:b|=mxConstants.DIRECTION_MASK_SOUTH;break;case 2:b|=mxConstants.DIRECTION_MASK_WEST;break;case 3:b|=mxConstants.DIRECTION_MASK_NORTH}return b},reversePortConstraints:function(a){var b;b=(a&mxConstants.DIRECTION_MASK_WEST)<<3;b|=(a&mxConstants.DIRECTION_MASK_NORTH)<<1;b|=(a&mxConstants.DIRECTION_MASK_SOUTH)>>1;return b|=(a&mxConstants.DIRECTION_MASK_EAST)>>3},
+findNearestSegment:function(a,b,c){var d=-1;if(0<a.absolutePoints.length)for(var e=a.absolutePoints[0],f=null,g=1;g<a.absolutePoints.length;g++){var k=a.absolutePoints[g],e=mxUtils.ptSegDistSq(e.x,e.y,k.x,k.y,b,c);if(null==f||e<f)f=e,d=g-1;e=k}return d},getDirectedBounds:function(a,b,c,d,e){var f=mxUtils.getValue(c,mxConstants.STYLE_DIRECTION,mxConstants.DIRECTION_EAST);d=null!=d?d:mxUtils.getValue(c,mxConstants.STYLE_FLIPH,!1);e=null!=e?e:mxUtils.getValue(c,mxConstants.STYLE_FLIPV,!1);b.x=Math.round(Math.max(0,
+Math.min(a.width,b.x)));b.y=Math.round(Math.max(0,Math.min(a.height,b.y)));b.width=Math.round(Math.max(0,Math.min(a.width,b.width)));b.height=Math.round(Math.max(0,Math.min(a.height,b.height)));if(e&&(f==mxConstants.DIRECTION_SOUTH||f==mxConstants.DIRECTION_NORTH)||d&&(f==mxConstants.DIRECTION_EAST||f==mxConstants.DIRECTION_WEST))c=b.x,b.x=b.width,b.width=c;if(d&&(f==mxConstants.DIRECTION_SOUTH||f==mxConstants.DIRECTION_NORTH)||e&&(f==mxConstants.DIRECTION_EAST||f==mxConstants.DIRECTION_WEST))c=b.y,
+b.y=b.height,b.height=c;d=mxRectangle.fromRectangle(b);f==mxConstants.DIRECTION_SOUTH?(d.y=b.x,d.x=b.height,d.width=b.y,d.height=b.width):f==mxConstants.DIRECTION_WEST?(d.y=b.height,d.x=b.width,d.width=b.x,d.height=b.y):f==mxConstants.DIRECTION_NORTH&&(d.y=b.width,d.x=b.y,d.width=b.height,d.height=b.x);return new mxRectangle(a.x+d.x,a.y+d.y,a.width-d.width-d.x,a.height-d.height-d.y)},getPerimeterPoint:function(a,b,c){for(var d=null,e=0;e<a.length-1;e++){var f=mxUtils.intersection(a[e].x,a[e].y,a[e+
+1].x,a[e+1].y,b.x,b.y,c.x,c.y);if(null!=f){var g=c.x-f.x,k=c.y-f.y,f={p:f,distSq:k*k+g*g};null!=f&&(null==d||d.distSq>f.distSq)&&(d=f)}}return null!=d?d.p:null},rectangleIntersectsSegment:function(a,b,c){var d=a.y,e=a.x,f=d+a.height,g=e+a.width;a=b.x;var k=c.x;b.x>c.x&&(a=c.x,k=b.x);k>g&&(k=g);a<e&&(a=e);if(a>k)return!1;var e=b.y,g=c.y,l=c.x-b.x;1E-7<Math.abs(l)&&(c=(c.y-b.y)/l,b=b.y-c*b.x,e=c*a+b,g=c*k+b);e>g&&(b=g,g=e,e=b);g>f&&(g=f);e<d&&(e=d);return e>g?!1:!0},contains:function(a,b,c){return a.x<=
+b&&a.x+a.width>=b&&a.y<=c&&a.y+a.height>=c},intersects:function(a,b){var c=a.width,d=a.height,e=b.width,f=b.height;if(0>=e||0>=f||0>=c||0>=d)return!1;var g=a.x,k=a.y,l=b.x,m=b.y,e=e+l,f=f+m,c=c+g,d=d+k;return(e<l||e>g)&&(f<m||f>k)&&(c<g||c>l)&&(d<k||d>m)},intersectsHotspot:function(a,b,c,d,e,f){d=null!=d?d:1;e=null!=e?e:0;f=null!=f?f:0;if(0<d){var g=a.getCenterX(),k=a.getCenterY(),l=a.width,m=a.height,n=mxUtils.getValue(a.style,mxConstants.STYLE_STARTSIZE)*a.view.scale;0<n&&(mxUtils.getValue(a.style,
+mxConstants.STYLE_HORIZONTAL,!0)?(k=a.y+n/2,m=n):(g=a.x+n/2,l=n));l=Math.max(e,l*d);m=Math.max(e,m*d);0<f&&(l=Math.min(l,f),m=Math.min(m,f));d=new mxRectangle(g-l/2,k-m/2,l,m);g=mxUtils.toRadians(mxUtils.getValue(a.style,mxConstants.STYLE_ROTATION)||0);0!=g&&(e=Math.cos(-g),f=Math.sin(-g),g=new mxPoint(a.getCenterX(),a.getCenterY()),a=mxUtils.getRotatedPoint(new mxPoint(b,c),e,f,g),b=a.x,c=a.y);return mxUtils.contains(d,b,c)}return!0},getOffset:function(a,b){for(var c=0,d=0,e=!1,f=a,g=document.body,
+k=document.documentElement;null!=f&&f!=g&&f!=k&&!e;){var l=mxUtils.getCurrentStyle(f);null!=l&&(e=e||"fixed"==l.position);f=f.parentNode}b||e||(e=mxUtils.getDocumentScrollOrigin(a.ownerDocument),c+=e.x,d+=e.y);e=a.getBoundingClientRect();null!=e&&(c+=e.left,d+=e.top);return new mxPoint(c,d)},getDocumentScrollOrigin:function(a){if(mxClient.IS_QUIRKS)return new mxPoint(a.body.scrollLeft,a.body.scrollTop);a=a.defaultView||a.parentWindow;return new mxPoint(null!=a&&void 0!==window.pageXOffset?window.pageXOffset:
+(document.documentElement||document.body.parentNode||document.body).scrollLeft,null!=a&&void 0!==window.pageYOffset?window.pageYOffset:(document.documentElement||document.body.parentNode||document.body).scrollTop)},getScrollOrigin:function(a,b,c){b=null!=b?b:!1;c=null!=c?c:!0;for(var d=null!=a?a.ownerDocument:document,e=d.body,f=d.documentElement,g=new mxPoint,k=!1;null!=a&&a!=e&&a!=f;){isNaN(a.scrollLeft)||isNaN(a.scrollTop)||(g.x+=a.scrollLeft,g.y+=a.scrollTop);var l=mxUtils.getCurrentStyle(a);
+null!=l&&(k=k||"fixed"==l.position);a=b?a.parentNode:null}!k&&c&&(a=mxUtils.getDocumentScrollOrigin(d),g.x+=a.x,g.y+=a.y);return g},convertPoint:function(a,b,c){var d=mxUtils.getScrollOrigin(a,!1);a=mxUtils.getOffset(a);a.x-=d.x;a.y-=d.y;return new mxPoint(b-a.x,c-a.y)},ltrim:function(a,b){return null!=a?a.replace(new RegExp("^["+(b||"\\s")+"]+","g"),""):null},rtrim:function(a,b){return null!=a?a.replace(new RegExp("["+(b||"\\s")+"]+$","g"),""):null},trim:function(a,b){return mxUtils.ltrim(mxUtils.rtrim(a,
+b),b)},isNumeric:function(a){return!isNaN(parseFloat(a))&&isFinite(a)&&("string"!=typeof a||0>a.toLowerCase().indexOf("0x"))},isInteger:function(a){return String(parseInt(a))===String(a)},mod:function(a,b){return(a%b+b)%b},intersection:function(a,b,c,d,e,f,g,k){var l=(k-f)*(c-a)-(g-e)*(d-b);g=((g-e)*(b-f)-(k-f)*(a-e))/l;e=((c-a)*(b-f)-(d-b)*(a-e))/l;return 0<=g&&1>=g&&0<=e&&1>=e?new mxPoint(a+g*(c-a),b+g*(d-b)):null},ptSegDistSq:function(a,b,c,d,e,f){c-=a;d-=b;e-=a;f-=b;0>=e*c+f*d?c=0:(e=c-e,f=d-
+f,a=e*c+f*d,c=0>=a?0:a*a/(c*c+d*d));e=e*e+f*f-c;0>e&&(e=0);return e},ptLineDist:function(a,b,c,d,e,f){return Math.abs((d-b)*e-(c-a)*f+c*b-d*a)/Math.sqrt((d-b)*(d-b)+(c-a)*(c-a))},relativeCcw:function(a,b,c,d,e,f){c-=a;d-=b;e-=a;f-=b;a=e*d-f*c;0==a&&(a=e*c+f*d,0<a&&(a=(e-c)*c+(f-d)*d,0>a&&(a=0)));return 0>a?-1:0<a?1:0},animateChanges:function(a,b){mxEffects.animateChanges.apply(this,arguments)},cascadeOpacity:function(a,b,c){mxEffects.cascadeOpacity.apply(this,arguments)},fadeOut:function(a,b,c,d,
+e,f){mxEffects.fadeOut.apply(this,arguments)},setOpacity:function(a,b){mxUtils.isVml(a)?a.style.filter=100<=b?"":"alpha(opacity="+b/5+")":mxClient.IS_IE&&("undefined"===typeof document.documentMode||9>document.documentMode)?a.style.filter=100<=b?"":"alpha(opacity="+b+")":a.style.opacity=b/100},createImage:function(a){var b;mxClient.IS_IE6&&"CSS1Compat"!=document.compatMode?(b=document.createElement(mxClient.VML_PREFIX+":image"),b.setAttribute("src",a),b.style.borderStyle="none"):(b=document.createElement("img"),
+b.setAttribute("src",a),b.setAttribute("border","0"));return b},sortCells:function(a,b){b=null!=b?b:!0;var c=new mxDictionary;a.sort(function(a,e){var d=c.get(a);null==d&&(d=mxCellPath.create(a).split(mxCellPath.PATH_SEPARATOR),c.put(a,d));var g=c.get(e);null==g&&(g=mxCellPath.create(e).split(mxCellPath.PATH_SEPARATOR),c.put(e,g));d=mxCellPath.compare(d,g);return 0==d?0:0<d==b?1:-1});return a},getStylename:function(a){return null!=a&&(a=a.split(";")[0],0>a.indexOf("="))?a:""},getStylenames:function(a){var b=
+[];if(null!=a){a=a.split(";");for(var c=0;c<a.length;c++)0>a[c].indexOf("=")&&b.push(a[c])}return b},indexOfStylename:function(a,b){if(null!=a&&null!=b)for(var c=a.split(";"),d=0,e=0;e<c.length;e++){if(c[e]==b)return d;d+=c[e].length+1}return-1},addStylename:function(a,b){0>mxUtils.indexOfStylename(a,b)&&(null==a?a="":0<a.length&&";"!=a.charAt(a.length-1)&&(a+=";"),a+=b);return a},removeStylename:function(a,b){var c=[];if(null!=a)for(var d=a.split(";"),e=0;e<d.length;e++)d[e]!=b&&c.push(d[e]);return c.join(";")},
+removeAllStylenames:function(a){var b=[];if(null!=a){a=a.split(";");for(var c=0;c<a.length;c++)0<=a[c].indexOf("=")&&b.push(a[c])}return b.join(";")},setCellStyles:function(a,b,c,d){if(null!=b&&0<b.length){a.beginUpdate();try{for(var e=0;e<b.length;e++)if(null!=b[e]){var f=mxUtils.setStyle(a.getStyle(b[e]),c,d);a.setStyle(b[e],f)}}finally{a.endUpdate()}}},setStyle:function(a,b,c){var d=null!=c&&("undefined"==typeof c.length||0<c.length);if(null==a||0==a.length)d&&(a=b+"="+c+";");else if(a.substring(0,
+b.length+1)==b+"="){var e=a.indexOf(";");a=d?b+"="+c+(0>e?";":a.substring(e)):0>e||e==a.length-1?"":a.substring(e+1)}else{var f=a.indexOf(";"+b+"=");0>f?d&&(d=";"==a.charAt(a.length-1)?"":";",a=a+d+b+"="+c+";"):(e=a.indexOf(";",f+1),a=d?a.substring(0,f+1)+b+"="+c+(0>e?";":a.substring(e)):a.substring(0,f)+(0>e?";":a.substring(e)))}return a},setCellStyleFlags:function(a,b,c,d,e){if(null!=b&&0<b.length){a.beginUpdate();try{for(var f=0;f<b.length;f++)if(null!=b[f]){var g=mxUtils.setStyleFlag(a.getStyle(b[f]),
+c,d,e);a.setStyle(b[f],g)}}finally{a.endUpdate()}}},setStyleFlag:function(a,b,c,d){if(null==a||0==a.length)a=d||null==d?b+"="+c:b+"=0";else{var e=a.indexOf(b+"=");if(0>e)e=";"==a.charAt(a.length-1)?"":";",a=d||null==d?a+e+b+"="+c:a+e+b+"=0";else{var f=a.indexOf(";",e),g;g=0>f?a.substring(e+b.length+1):a.substring(e+b.length+1,f);g=null==d?parseInt(g)^c:d?parseInt(g)|c:parseInt(g)&~c;a=a.substring(0,e)+b+"="+g+(0<=f?a.substring(f):"")}}return a},getAlignmentAsPoint:function(a,b){var c=0,d=0;a==mxConstants.ALIGN_CENTER?
+c=-.5:a==mxConstants.ALIGN_RIGHT&&(c=-1);b==mxConstants.ALIGN_MIDDLE?d=-.5:b==mxConstants.ALIGN_BOTTOM&&(d=-1);return new mxPoint(c,d)},getSizeForString:function(a,b,c,d){b=null!=b?b:mxConstants.DEFAULT_FONTSIZE;c=null!=c?c:mxConstants.DEFAULT_FONTFAMILY;var e=document.createElement("div");e.style.fontFamily=c;e.style.fontSize=Math.round(b)+"px";e.style.lineHeight=Math.round(b*mxConstants.LINE_HEIGHT)+"px";e.style.position="absolute";e.style.visibility="hidden";e.style.display=mxClient.IS_QUIRKS?
+"inline":"inline-block";e.style.zoom="1";null!=d?(e.style.width=d+"px",e.style.whiteSpace="normal"):e.style.whiteSpace="nowrap";e.innerHTML=a;document.body.appendChild(e);a=new mxRectangle(0,0,e.offsetWidth,e.offsetHeight);document.body.removeChild(e);return a},getViewXml:function(a,b,c,d,e){d=null!=d?d:0;e=null!=e?e:0;b=null!=b?b:1;null==c&&(c=[a.getModel().getRoot()]);var f=a.getView(),g=null,k=f.isEventsEnabled();f.setEventsEnabled(!1);var l=f.drawPane,m=f.overlayPane;a.dialect==mxConstants.DIALECT_SVG?
+(f.drawPane=document.createElementNS(mxConstants.NS_SVG,"g"),f.canvas.appendChild(f.drawPane),f.overlayPane=document.createElementNS(mxConstants.NS_SVG,"g")):(f.drawPane=f.drawPane.cloneNode(!1),f.canvas.appendChild(f.drawPane),f.overlayPane=f.overlayPane.cloneNode(!1));f.canvas.appendChild(f.overlayPane);var n=f.getTranslate();f.translate=new mxPoint(d,e);b=new mxTemporaryCellStates(a.getView(),b,c);try{g=(new mxCodec).encode(a.getView())}finally{b.destroy(),f.translate=n,f.canvas.removeChild(f.drawPane),
+f.canvas.removeChild(f.overlayPane),f.drawPane=l,f.overlayPane=m,f.setEventsEnabled(k)}return g},getScaleForPageCount:function(a,b,c,d){if(1>a)return 1;c=null!=c?c:mxConstants.PAGE_FORMAT_A4_PORTRAIT;d=null!=d?d:0;var e=c.width-2*d;c=c.height-2*d;d=b.getGraphBounds().clone();b=b.getView().getScale();d.width/=b;d.height/=b;b=d.width;var f=Math.sqrt(a);d=Math.sqrt(b/d.height/(e/c));c=f*d;d=f/d;if(1>c&&d>a){var g=d/a;d=a;c/=g}1>d&&c>a&&(g=c/a,c=a,d/=g);g=Math.ceil(c)*Math.ceil(d);for(f=0;g>a;){var g=
+Math.floor(c)/c,k=Math.floor(d)/d;1==g&&(g=Math.floor(c-1)/c);1==k&&(k=Math.floor(d-1)/d);g=g>k?g:k;c*=g;d*=g;g=Math.ceil(c)*Math.ceil(d);f++;if(10<f)break}return e*c/b*.99999},show:function(a,b,c,d,e,f){c=null!=c?c:0;d=null!=d?d:0;null==b?b=window.open().document:b.open();9==document.documentMode&&b.writeln('\x3c!--[if IE]><meta http-equiv="X-UA-Compatible" content="IE=9"><![endif]--\x3e');var g=a.getGraphBounds(),k=Math.ceil(c-g.x),l=Math.ceil(d-g.y);null==e&&(e=Math.ceil(g.width+c)+Math.ceil(Math.ceil(g.x)-
+g.x));null==f&&(f=Math.ceil(g.height+d)+Math.ceil(Math.ceil(g.y)-g.y));if(mxClient.IS_IE||11==document.documentMode){d="<html><head>";g=document.getElementsByTagName("base");for(c=0;c<g.length;c++)d+=g[c].outerHTML;d+="<style>";for(c=0;c<document.styleSheets.length;c++)try{d+=document.styleSheets[c].cssText}catch(m){}d=d+'</style></head><body style="margin:0px;">'+('<div style="position:absolute;overflow:hidden;width:'+e+"px;height:"+f+'px;"><div style="position:relative;left:'+k+"px;top:"+l+'px;">')+
+a.container.innerHTML;b.writeln(d+"</div></div></body><html>");b.close()}else{b.writeln("<html><head>");g=document.getElementsByTagName("base");for(c=0;c<g.length;c++)b.writeln(mxUtils.getOuterHtml(g[c]));d=document.getElementsByTagName("link");for(c=0;c<d.length;c++)b.writeln(mxUtils.getOuterHtml(d[c]));d=document.getElementsByTagName("style");for(c=0;c<d.length;c++)b.writeln(mxUtils.getOuterHtml(d[c]));b.writeln('</head><body style="margin:0px;"></body></html>');b.close();c=b.createElement("div");
+c.position="absolute";c.overflow="hidden";c.style.width=e+"px";c.style.height=f+"px";e=b.createElement("div");e.style.position="absolute";e.style.left=k+"px";e.style.top=l+"px";f=a.container.firstChild;for(d=null;null!=f;)g=f.cloneNode(!0),f==a.view.drawPane.ownerSVGElement?(c.appendChild(g),d=g):e.appendChild(g),f=f.nextSibling;b.body.appendChild(c);null!=e.firstChild&&b.body.appendChild(e);null!=d&&(d.style.minWidth="",d.style.minHeight="",d.firstChild.setAttribute("transform","translate("+k+","+
+l+")"))}mxUtils.removeCursors(b.body);return b},printScreen:function(a){var b=window.open();a.getGraphBounds();mxUtils.show(a,b.document);a=function(){b.focus();b.print();b.close()};mxClient.IS_GC?b.setTimeout(a,500):a()},popup:function(a,b){if(b){var c=document.createElement("div");c.style.overflow="scroll";c.style.width="636px";c.style.height="460px";var d=document.createElement("pre");d.innerHTML=mxUtils.htmlEntities(a,!1).replace(/\n/g,"<br>").replace(/ /g,"&nbsp;");c.appendChild(d);c=new mxWindow("Popup Window",
+c,document.body.clientWidth/2-320,Math.max(document.body.clientHeight||0,document.documentElement.clientHeight)/2-240,640,480,!1,!0);c.setClosable(!0);c.setVisible(!0)}else mxClient.IS_NS?(c=window.open(),c.document.writeln("<pre>"+mxUtils.htmlEntities(a)+"</pre"),c.document.close()):(c=window.open(),d=c.document.createElement("pre"),d.innerHTML=mxUtils.htmlEntities(a,!1).replace(/\n/g,"<br>").replace(/ /g,"&nbsp;"),c.document.body.appendChild(d))},alert:function(a){alert(a)},prompt:function(a,b){return prompt(a,
+null!=b?b:"")},confirm:function(a){return confirm(a)},error:function(a,b,c,d){var e=document.createElement("div");e.style.padding="20px";var f=document.createElement("img");f.setAttribute("src",d||mxUtils.errorImage);f.setAttribute("valign","bottom");f.style.verticalAlign="middle";e.appendChild(f);e.appendChild(document.createTextNode(" "));e.appendChild(document.createTextNode(" "));e.appendChild(document.createTextNode(" "));mxUtils.write(e,a);a=document.body.clientWidth;d=document.body.clientHeight||
+document.documentElement.clientHeight;var g=new mxWindow(mxResources.get(mxUtils.errorResource)||mxUtils.errorResource,e,(a-b)/2,d/4,b,null,!1,!0);c&&(mxUtils.br(e),b=document.createElement("p"),c=document.createElement("button"),mxClient.IS_IE?c.style.cssText="float:right":c.setAttribute("style","float:right"),mxEvent.addListener(c,"click",function(a){g.destroy()}),mxUtils.write(c,mxResources.get(mxUtils.closeResource)||mxUtils.closeResource),b.appendChild(c),e.appendChild(b),mxUtils.br(e),g.setClosable(!0));
+g.setVisible(!0);return g},makeDraggable:function(a,b,c,d,e,f,g,k,l,m){a=new mxDragSource(a,c);a.dragOffset=new mxPoint(null!=e?e:0,null!=f?f:mxConstants.TOOLTIP_VERTICAL_OFFSET);a.autoscroll=g;a.setGuidesEnabled(!1);null!=l&&(a.highlightDropTargets=l);null!=m&&(a.getDropTarget=m);a.getGraphForEvent=function(a){return"function"==typeof b?b(a):b};null!=d&&(a.createDragElement=function(){return d.cloneNode(!0)},k&&(a.createPreviewElement=function(a){var b=d.cloneNode(!0),c=parseInt(b.style.width),e=
+parseInt(b.style.height);b.style.width=Math.round(c*a.view.scale)+"px";b.style.height=Math.round(e*a.view.scale)+"px";return b}));return a}},mxConstants={DEFAULT_HOTSPOT:.3,MIN_HOTSPOT_SIZE:8,MAX_HOTSPOT_SIZE:0,RENDERING_HINT_EXACT:"exact",RENDERING_HINT_FASTER:"faster",RENDERING_HINT_FASTEST:"fastest",DIALECT_SVG:"svg",DIALECT_VML:"vml",DIALECT_MIXEDHTML:"mixedHtml",DIALECT_PREFERHTML:"preferHtml",DIALECT_STRICTHTML:"strictHtml",NS_SVG:"http://www.w3.org/2000/svg",NS_XHTML:"http://www.w3.org/1999/xhtml",
+NS_XLINK:"http://www.w3.org/1999/xlink",SHADOWCOLOR:"gray",VML_SHADOWCOLOR:"gray",SHADOW_OFFSET_X:2,SHADOW_OFFSET_Y:3,SHADOW_OPACITY:1,NODETYPE_ELEMENT:1,NODETYPE_ATTRIBUTE:2,NODETYPE_TEXT:3,NODETYPE_CDATA:4,NODETYPE_ENTITY_REFERENCE:5,NODETYPE_ENTITY:6,NODETYPE_PROCESSING_INSTRUCTION:7,NODETYPE_COMMENT:8,NODETYPE_DOCUMENT:9,NODETYPE_DOCUMENTTYPE:10,NODETYPE_DOCUMENT_FRAGMENT:11,NODETYPE_NOTATION:12,TOOLTIP_VERTICAL_OFFSET:16,DEFAULT_VALID_COLOR:"#00FF00",DEFAULT_INVALID_COLOR:"#FF0000",OUTLINE_HIGHLIGHT_COLOR:"#00FF00",
+OUTLINE_HIGHLIGHT_STROKEWIDTH:5,HIGHLIGHT_STROKEWIDTH:3,HIGHLIGHT_SIZE:2,HIGHLIGHT_OPACITY:100,CURSOR_MOVABLE_VERTEX:"move",CURSOR_MOVABLE_EDGE:"move",CURSOR_LABEL_HANDLE:"default",CURSOR_TERMINAL_HANDLE:"pointer",CURSOR_BEND_HANDLE:"crosshair",CURSOR_VIRTUAL_BEND_HANDLE:"crosshair",CURSOR_CONNECT:"pointer",HIGHLIGHT_COLOR:"#00FF00",CONNECT_TARGET_COLOR:"#0000FF",INVALID_CONNECT_TARGET_COLOR:"#FF0000",DROP_TARGET_COLOR:"#0000FF",VALID_COLOR:"#00FF00",INVALID_COLOR:"#FF0000",EDGE_SELECTION_COLOR:"#00FF00",
+VERTEX_SELECTION_COLOR:"#00FF00",VERTEX_SELECTION_STROKEWIDTH:1,EDGE_SELECTION_STROKEWIDTH:1,VERTEX_SELECTION_DASHED:!0,EDGE_SELECTION_DASHED:!0,GUIDE_COLOR:"#FF0000",GUIDE_STROKEWIDTH:1,OUTLINE_COLOR:"#0099FF",OUTLINE_STROKEWIDTH:mxClient.IS_IE?2:3,HANDLE_SIZE:6,LABEL_HANDLE_SIZE:4,HANDLE_FILLCOLOR:"#00FF00",HANDLE_STROKECOLOR:"black",LABEL_HANDLE_FILLCOLOR:"yellow",CONNECT_HANDLE_FILLCOLOR:"#0000FF",LOCKED_HANDLE_FILLCOLOR:"#FF0000",OUTLINE_HANDLE_FILLCOLOR:"#00FFFF",OUTLINE_HANDLE_STROKECOLOR:"#0033FF",
+DEFAULT_FONTFAMILY:"Arial,Helvetica",DEFAULT_FONTSIZE:11,DEFAULT_TEXT_DIRECTION:"",LINE_HEIGHT:1.2,WORD_WRAP:"normal",ABSOLUTE_LINE_HEIGHT:!1,DEFAULT_FONTSTYLE:0,DEFAULT_STARTSIZE:40,DEFAULT_MARKERSIZE:6,DEFAULT_IMAGESIZE:24,ENTITY_SEGMENT:30,RECTANGLE_ROUNDING_FACTOR:.15,LINE_ARCSIZE:20,ARROW_SPACING:0,ARROW_WIDTH:30,ARROW_SIZE:30,PAGE_FORMAT_A4_PORTRAIT:new mxRectangle(0,0,827,1169),PAGE_FORMAT_A4_LANDSCAPE:new mxRectangle(0,0,1169,827),PAGE_FORMAT_LETTER_PORTRAIT:new mxRectangle(0,0,850,1100),
+PAGE_FORMAT_LETTER_LANDSCAPE:new mxRectangle(0,0,1100,850),NONE:"none",STYLE_PERIMETER:"perimeter",STYLE_SOURCE_PORT:"sourcePort",STYLE_TARGET_PORT:"targetPort",STYLE_PORT_CONSTRAINT:"portConstraint",STYLE_PORT_CONSTRAINT_ROTATION:"portConstraintRotation",STYLE_SOURCE_PORT_CONSTRAINT:"sourcePortConstraint",STYLE_TARGET_PORT_CONSTRAINT:"targetPortConstraint",STYLE_OPACITY:"opacity",STYLE_FILL_OPACITY:"fillOpacity",STYLE_STROKE_OPACITY:"strokeOpacity",STYLE_TEXT_OPACITY:"textOpacity",STYLE_TEXT_DIRECTION:"textDirection",
+STYLE_OVERFLOW:"overflow",STYLE_ORTHOGONAL:"orthogonal",STYLE_EXIT_X:"exitX",STYLE_EXIT_Y:"exitY",STYLE_EXIT_PERIMETER:"exitPerimeter",STYLE_ENTRY_X:"entryX",STYLE_ENTRY_Y:"entryY",STYLE_ENTRY_PERIMETER:"entryPerimeter",STYLE_WHITE_SPACE:"whiteSpace",STYLE_ROTATION:"rotation",STYLE_FILLCOLOR:"fillColor",STYLE_POINTER_EVENTS:"pointerEvents",STYLE_SWIMLANE_FILLCOLOR:"swimlaneFillColor",STYLE_MARGIN:"margin",STYLE_GRADIENTCOLOR:"gradientColor",STYLE_GRADIENT_DIRECTION:"gradientDirection",STYLE_STROKECOLOR:"strokeColor",
+STYLE_SEPARATORCOLOR:"separatorColor",STYLE_STROKEWIDTH:"strokeWidth",STYLE_ALIGN:"align",STYLE_VERTICAL_ALIGN:"verticalAlign",STYLE_LABEL_WIDTH:"labelWidth",STYLE_LABEL_POSITION:"labelPosition",STYLE_VERTICAL_LABEL_POSITION:"verticalLabelPosition",STYLE_IMAGE_ASPECT:"imageAspect",STYLE_IMAGE_ALIGN:"imageAlign",STYLE_IMAGE_VERTICAL_ALIGN:"imageVerticalAlign",STYLE_GLASS:"glass",STYLE_IMAGE:"image",STYLE_IMAGE_WIDTH:"imageWidth",STYLE_IMAGE_HEIGHT:"imageHeight",STYLE_IMAGE_BACKGROUND:"imageBackground",
+STYLE_IMAGE_BORDER:"imageBorder",STYLE_FLIPH:"flipH",STYLE_FLIPV:"flipV",STYLE_NOLABEL:"noLabel",STYLE_NOEDGESTYLE:"noEdgeStyle",STYLE_LABEL_BACKGROUNDCOLOR:"labelBackgroundColor",STYLE_LABEL_BORDERCOLOR:"labelBorderColor",STYLE_LABEL_PADDING:"labelPadding",STYLE_INDICATOR_SHAPE:"indicatorShape",STYLE_INDICATOR_IMAGE:"indicatorImage",STYLE_INDICATOR_COLOR:"indicatorColor",STYLE_INDICATOR_STROKECOLOR:"indicatorStrokeColor",STYLE_INDICATOR_GRADIENTCOLOR:"indicatorGradientColor",STYLE_INDICATOR_SPACING:"indicatorSpacing",
+STYLE_INDICATOR_WIDTH:"indicatorWidth",STYLE_INDICATOR_HEIGHT:"indicatorHeight",STYLE_INDICATOR_DIRECTION:"indicatorDirection",STYLE_SHADOW:"shadow",STYLE_SEGMENT:"segment",STYLE_ENDARROW:"endArrow",STYLE_STARTARROW:"startArrow",STYLE_ENDSIZE:"endSize",STYLE_STARTSIZE:"startSize",STYLE_SWIMLANE_LINE:"swimlaneLine",STYLE_ENDFILL:"endFill",STYLE_STARTFILL:"startFill",STYLE_DASHED:"dashed",STYLE_DASH_PATTERN:"dashPattern",STYLE_FIX_DASH:"fixDash",STYLE_ROUNDED:"rounded",STYLE_CURVED:"curved",STYLE_ARCSIZE:"arcSize",
+STYLE_ABSOLUTE_ARCSIZE:"absoluteArcSize",STYLE_SOURCE_PERIMETER_SPACING:"sourcePerimeterSpacing",STYLE_TARGET_PERIMETER_SPACING:"targetPerimeterSpacing",STYLE_PERIMETER_SPACING:"perimeterSpacing",STYLE_SPACING:"spacing",STYLE_SPACING_TOP:"spacingTop",STYLE_SPACING_LEFT:"spacingLeft",STYLE_SPACING_BOTTOM:"spacingBottom",STYLE_SPACING_RIGHT:"spacingRight",STYLE_HORIZONTAL:"horizontal",STYLE_DIRECTION:"direction",STYLE_ELBOW:"elbow",STYLE_FONTCOLOR:"fontColor",STYLE_FONTFAMILY:"fontFamily",STYLE_FONTSIZE:"fontSize",
+STYLE_FONTSTYLE:"fontStyle",STYLE_ASPECT:"aspect",STYLE_AUTOSIZE:"autosize",STYLE_FOLDABLE:"foldable",STYLE_EDITABLE:"editable",STYLE_BENDABLE:"bendable",STYLE_MOVABLE:"movable",STYLE_RESIZABLE:"resizable",STYLE_RESIZE_WIDTH:"resizeWidth",STYLE_RESIZE_HEIGHT:"resizeHeight",STYLE_ROTATABLE:"rotatable",STYLE_CLONEABLE:"cloneable",STYLE_DELETABLE:"deletable",STYLE_SHAPE:"shape",STYLE_EDGE:"edgeStyle",STYLE_JETTY_SIZE:"jettySize",STYLE_SOURCE_JETTY_SIZE:"sourceJettySize",STYLE_TARGET_JETTY_SIZE:"targetJettySize",
+STYLE_LOOP:"loopStyle",STYLE_ORTHOGONAL_LOOP:"orthogonalLoop",STYLE_ROUTING_CENTER_X:"routingCenterX",STYLE_ROUTING_CENTER_Y:"routingCenterY",FONT_BOLD:1,FONT_ITALIC:2,FONT_UNDERLINE:4,SHAPE_RECTANGLE:"rectangle",SHAPE_ELLIPSE:"ellipse",SHAPE_DOUBLE_ELLIPSE:"doubleEllipse",SHAPE_RHOMBUS:"rhombus",SHAPE_LINE:"line",SHAPE_IMAGE:"image",SHAPE_ARROW:"arrow",SHAPE_ARROW_CONNECTOR:"arrowConnector",SHAPE_LABEL:"label",SHAPE_CYLINDER:"cylinder",SHAPE_SWIMLANE:"swimlane",SHAPE_CONNECTOR:"connector",SHAPE_ACTOR:"actor",
+SHAPE_CLOUD:"cloud",SHAPE_TRIANGLE:"triangle",SHAPE_HEXAGON:"hexagon",ARROW_CLASSIC:"classic",ARROW_CLASSIC_THIN:"classicThin",ARROW_BLOCK:"block",ARROW_BLOCK_THIN:"blockThin",ARROW_OPEN:"open",ARROW_OPEN_THIN:"openThin",ARROW_OVAL:"oval",ARROW_DIAMOND:"diamond",ARROW_DIAMOND_THIN:"diamondThin",ALIGN_LEFT:"left",ALIGN_CENTER:"center",ALIGN_RIGHT:"right",ALIGN_TOP:"top",ALIGN_MIDDLE:"middle",ALIGN_BOTTOM:"bottom",DIRECTION_NORTH:"north",DIRECTION_SOUTH:"south",DIRECTION_EAST:"east",DIRECTION_WEST:"west",
+TEXT_DIRECTION_DEFAULT:"",TEXT_DIRECTION_AUTO:"auto",TEXT_DIRECTION_LTR:"ltr",TEXT_DIRECTION_RTL:"rtl",DIRECTION_MASK_NONE:0,DIRECTION_MASK_WEST:1,DIRECTION_MASK_NORTH:2,DIRECTION_MASK_SOUTH:4,DIRECTION_MASK_EAST:8,DIRECTION_MASK_ALL:15,ELBOW_VERTICAL:"vertical",ELBOW_HORIZONTAL:"horizontal",EDGESTYLE_ELBOW:"elbowEdgeStyle",EDGESTYLE_ENTITY_RELATION:"entityRelationEdgeStyle",EDGESTYLE_LOOP:"loopEdgeStyle",EDGESTYLE_SIDETOSIDE:"sideToSideEdgeStyle",EDGESTYLE_TOPTOBOTTOM:"topToBottomEdgeStyle",EDGESTYLE_ORTHOGONAL:"orthogonalEdgeStyle",
+EDGESTYLE_SEGMENT:"segmentEdgeStyle",PERIMETER_ELLIPSE:"ellipsePerimeter",PERIMETER_RECTANGLE:"rectanglePerimeter",PERIMETER_RHOMBUS:"rhombusPerimeter",PERIMETER_HEXAGON:"hexagonPerimeter",PERIMETER_TRIANGLE:"trianglePerimeter"};function mxEventObject(a){this.name=a;this.properties=[];for(var b=1;b<arguments.length;b+=2)null!=arguments[b+1]&&(this.properties[arguments[b]]=arguments[b+1])}mxEventObject.prototype.name=null;mxEventObject.prototype.properties=null;mxEventObject.prototype.consumed=!1;
+mxEventObject.prototype.getName=function(){return this.name};mxEventObject.prototype.getProperties=function(){return this.properties};mxEventObject.prototype.getProperty=function(a){return this.properties[a]};mxEventObject.prototype.isConsumed=function(){return this.consumed};mxEventObject.prototype.consume=function(){this.consumed=!0};function mxMouseEvent(a,b){this.evt=a;this.sourceState=this.state=b}mxMouseEvent.prototype.consumed=!1;mxMouseEvent.prototype.evt=null;
+mxMouseEvent.prototype.graphX=null;mxMouseEvent.prototype.graphY=null;mxMouseEvent.prototype.state=null;mxMouseEvent.prototype.sourceState=null;mxMouseEvent.prototype.getEvent=function(){return this.evt};mxMouseEvent.prototype.getSource=function(){return mxEvent.getSource(this.evt)};mxMouseEvent.prototype.isSource=function(a){return null!=a?mxUtils.isAncestorNode(a.node,this.getSource()):!1};mxMouseEvent.prototype.getX=function(){return mxEvent.getClientX(this.getEvent())};
+mxMouseEvent.prototype.getY=function(){return mxEvent.getClientY(this.getEvent())};mxMouseEvent.prototype.getGraphX=function(){return this.graphX};mxMouseEvent.prototype.getGraphY=function(){return this.graphY};mxMouseEvent.prototype.getState=function(){return this.state};mxMouseEvent.prototype.getCell=function(){var a=this.getState();return null!=a?a.cell:null};mxMouseEvent.prototype.isPopupTrigger=function(){return mxEvent.isPopupTrigger(this.getEvent())};mxMouseEvent.prototype.isConsumed=function(){return this.consumed};
+mxMouseEvent.prototype.consume=function(a){(null!=a?a:1)&&this.evt.preventDefault&&this.evt.preventDefault();mxClient.IS_IE&&(this.evt.returnValue=!0);this.consumed=!0};function mxEventSource(a){this.setEventSource(a)}mxEventSource.prototype.eventListeners=null;mxEventSource.prototype.eventsEnabled=!0;mxEventSource.prototype.eventSource=null;mxEventSource.prototype.isEventsEnabled=function(){return this.eventsEnabled};mxEventSource.prototype.setEventsEnabled=function(a){this.eventsEnabled=a};
+mxEventSource.prototype.getEventSource=function(){return this.eventSource};mxEventSource.prototype.setEventSource=function(a){this.eventSource=a};mxEventSource.prototype.addListener=function(a,b){null==this.eventListeners&&(this.eventListeners=[]);this.eventListeners.push(a);this.eventListeners.push(b)};mxEventSource.prototype.removeListener=function(a){if(null!=this.eventListeners)for(var b=0;b<this.eventListeners.length;)this.eventListeners[b+1]==a?this.eventListeners.splice(b,2):b+=2};
+mxEventSource.prototype.fireEvent=function(a,b){if(null!=this.eventListeners&&this.isEventsEnabled()){null==a&&(a=new mxEventObject);null==b&&(b=this.getEventSource());null==b&&(b=this);for(var c=[b,a],d=0;d<this.eventListeners.length;d+=2){var e=this.eventListeners[d];null!=e&&e!=a.getName()||this.eventListeners[d+1].apply(this,c)}}};
+var mxEvent={objects:[],addListener:function(){var a=function(a,c,d){null==a.mxListenerList&&(a.mxListenerList=[],mxEvent.objects.push(a));a.mxListenerList.push({name:c,f:d})};return window.addEventListener?function(b,c,d){b.addEventListener(c,d,!1);a(b,c,d)}:function(b,c,d){b.attachEvent("on"+c,d);a(b,c,d)}}(),removeListener:function(){var a=function(a,c,d){if(null!=a.mxListenerList){c=a.mxListenerList.length;for(var b=0;b<c;b++)if(a.mxListenerList[b].f==d){a.mxListenerList.splice(b,1);break}0==
+a.mxListenerList.length&&(a.mxListenerList=null,a=mxUtils.indexOf(mxEvent.objects,a),0<=a&&mxEvent.objects.splice(a,1))}};return window.removeEventListener?function(b,c,d){b.removeEventListener(c,d,!1);a(b,c,d)}:function(b,c,d){b.detachEvent("on"+c,d);a(b,c,d)}}(),removeAllListeners:function(a){var b=a.mxListenerList;if(null!=b)for(;0<b.length;){var c=b[0];mxEvent.removeListener(a,c.name,c.f)}},addGestureListeners:function(a,b,c,d){null!=b&&mxEvent.addListener(a,mxClient.IS_POINTER?"pointerdown":
+"mousedown",b);null!=c&&mxEvent.addListener(a,mxClient.IS_POINTER?"pointermove":"mousemove",c);null!=d&&mxEvent.addListener(a,mxClient.IS_POINTER?"pointerup":"mouseup",d);!mxClient.IS_POINTER&&mxClient.IS_TOUCH&&(null!=b&&mxEvent.addListener(a,"touchstart",b),null!=c&&mxEvent.addListener(a,"touchmove",c),null!=d&&mxEvent.addListener(a,"touchend",d))},removeGestureListeners:function(a,b,c,d){null!=b&&mxEvent.removeListener(a,mxClient.IS_POINTER?"pointerdown":"mousedown",b);null!=c&&mxEvent.removeListener(a,
+mxClient.IS_POINTER?"pointermove":"mousemove",c);null!=d&&mxEvent.removeListener(a,mxClient.IS_POINTER?"pointerup":"mouseup",d);!mxClient.IS_POINTER&&mxClient.IS_TOUCH&&(null!=b&&mxEvent.removeListener(a,"touchstart",b),null!=c&&mxEvent.removeListener(a,"touchmove",c),null!=d&&mxEvent.removeListener(a,"touchend",d))},redirectMouseEvents:function(a,b,c,d,e,f,g){var k=function(a){return"function"==typeof c?c(a):c};mxEvent.addGestureListeners(a,function(a){null!=d?d(a):mxEvent.isConsumed(a)||b.fireMouseEvent(mxEvent.MOUSE_DOWN,
+new mxMouseEvent(a,k(a)))},function(a){null!=e?e(a):mxEvent.isConsumed(a)||b.fireMouseEvent(mxEvent.MOUSE_MOVE,new mxMouseEvent(a,k(a)))},function(a){null!=f?f(a):mxEvent.isConsumed(a)||b.fireMouseEvent(mxEvent.MOUSE_UP,new mxMouseEvent(a,k(a)))});mxEvent.addListener(a,"dblclick",function(a){if(null!=g)g(a);else if(!mxEvent.isConsumed(a)){var c=k(a);b.dblClick(a,null!=c?c.cell:null)}})},release:function(a){if(null!=a&&(mxEvent.removeAllListeners(a),a=a.childNodes,null!=a))for(var b=a.length,c=0;c<
+b;c+=1)mxEvent.release(a[c])},addMouseWheelListener:function(a){if(null!=a){var b=function(b){null==b&&(b=window.event);var c;c=mxClient.IS_FF?-b.detail/2:b.wheelDelta/120;0!=c&&a(b,0<c)};mxClient.IS_NS&&null==document.documentMode?mxEvent.addListener(window,mxClient.IS_SF||mxClient.IS_GC?"mousewheel":"DOMMouseScroll",b):mxEvent.addListener(document,"mousewheel",b)}},disableContextMenu:function(a){mxEvent.addListener(a,"contextmenu",function(a){a.preventDefault&&a.preventDefault();return!1})},getSource:function(a){return null!=
+a.srcElement?a.srcElement:a.target},isConsumed:function(a){return null!=a.isConsumed&&a.isConsumed},isTouchEvent:function(a){return null!=a.pointerType?"touch"==a.pointerType||a.pointerType===a.MSPOINTER_TYPE_TOUCH:null!=a.mozInputSource?5==a.mozInputSource:0==a.type.indexOf("touch")},isPenEvent:function(a){return null!=a.pointerType?"pen"==a.pointerType||a.pointerType===a.MSPOINTER_TYPE_PEN:null!=a.mozInputSource?2==a.mozInputSource:0==a.type.indexOf("pen")},isMultiTouchEvent:function(a){return null!=
+a.type&&0==a.type.indexOf("touch")&&null!=a.touches&&1<a.touches.length},isMouseEvent:function(a){return null!=a.pointerType?"mouse"==a.pointerType||a.pointerType===a.MSPOINTER_TYPE_MOUSE:null!=a.mozInputSource?1==a.mozInputSource:0==a.type.indexOf("mouse")},isLeftMouseButton:function(a){return"buttons"in a&&("mousedown"==a.type||"mousemove"==a.type)?1==a.buttons:"which"in a?1===a.which:1===a.button},isMiddleMouseButton:function(a){return"which"in a?2===a.which:4===a.button},isRightMouseButton:function(a){return"which"in
+a?3===a.which:2===a.button},isPopupTrigger:function(a){return mxEvent.isRightMouseButton(a)||mxClient.IS_MAC&&mxEvent.isControlDown(a)&&!mxEvent.isShiftDown(a)&&!mxEvent.isMetaDown(a)&&!mxEvent.isAltDown(a)},isShiftDown:function(a){return null!=a?a.shiftKey:!1},isAltDown:function(a){return null!=a?a.altKey:!1},isControlDown:function(a){return null!=a?a.ctrlKey:!1},isMetaDown:function(a){return null!=a?a.metaKey:!1},getMainEvent:function(a){"touchstart"!=a.type&&"touchmove"!=a.type||null==a.touches||
+null==a.touches[0]?"touchend"==a.type&&null!=a.changedTouches&&null!=a.changedTouches[0]&&(a=a.changedTouches[0]):a=a.touches[0];return a},getClientX:function(a){return mxEvent.getMainEvent(a).clientX},getClientY:function(a){return mxEvent.getMainEvent(a).clientY},consume:function(a,b,c){c=null!=c?c:!0;if(null!=b?b:1)a.preventDefault?(c&&a.stopPropagation(),a.preventDefault()):c&&(a.cancelBubble=!0);a.isConsumed=!0;a.preventDefault||(a.returnValue=!1)},LABEL_HANDLE:-1,ROTATION_HANDLE:-2,CUSTOM_HANDLE:-100,
+VIRTUAL_HANDLE:-1E5,MOUSE_DOWN:"mouseDown",MOUSE_MOVE:"mouseMove",MOUSE_UP:"mouseUp",ACTIVATE:"activate",RESIZE_START:"resizeStart",RESIZE:"resize",RESIZE_END:"resizeEnd",MOVE_START:"moveStart",MOVE:"move",MOVE_END:"moveEnd",PAN_START:"panStart",PAN:"pan",PAN_END:"panEnd",MINIMIZE:"minimize",NORMALIZE:"normalize",MAXIMIZE:"maximize",HIDE:"hide",SHOW:"show",CLOSE:"close",DESTROY:"destroy",REFRESH:"refresh",SIZE:"size",SELECT:"select",FIRED:"fired",FIRE_MOUSE_EVENT:"fireMouseEvent",GESTURE:"gesture",
+TAP_AND_HOLD:"tapAndHold",GET:"get",RECEIVE:"receive",CONNECT:"connect",DISCONNECT:"disconnect",SUSPEND:"suspend",RESUME:"resume",MARK:"mark",ROOT:"root",POST:"post",OPEN:"open",SAVE:"save",BEFORE_ADD_VERTEX:"beforeAddVertex",ADD_VERTEX:"addVertex",AFTER_ADD_VERTEX:"afterAddVertex",DONE:"done",EXECUTE:"execute",EXECUTED:"executed",BEGIN_UPDATE:"beginUpdate",START_EDIT:"startEdit",END_UPDATE:"endUpdate",END_EDIT:"endEdit",BEFORE_UNDO:"beforeUndo",UNDO:"undo",REDO:"redo",CHANGE:"change",NOTIFY:"notify",
+LAYOUT_CELLS:"layoutCells",CLICK:"click",SCALE:"scale",TRANSLATE:"translate",SCALE_AND_TRANSLATE:"scaleAndTranslate",UP:"up",DOWN:"down",ADD:"add",REMOVE:"remove",CLEAR:"clear",ADD_CELLS:"addCells",CELLS_ADDED:"cellsAdded",MOVE_CELLS:"moveCells",CELLS_MOVED:"cellsMoved",RESIZE_CELLS:"resizeCells",CELLS_RESIZED:"cellsResized",TOGGLE_CELLS:"toggleCells",CELLS_TOGGLED:"cellsToggled",ORDER_CELLS:"orderCells",CELLS_ORDERED:"cellsOrdered",REMOVE_CELLS:"removeCells",CELLS_REMOVED:"cellsRemoved",GROUP_CELLS:"groupCells",
+UNGROUP_CELLS:"ungroupCells",REMOVE_CELLS_FROM_PARENT:"removeCellsFromParent",FOLD_CELLS:"foldCells",CELLS_FOLDED:"cellsFolded",ALIGN_CELLS:"alignCells",LABEL_CHANGED:"labelChanged",CONNECT_CELL:"connectCell",CELL_CONNECTED:"cellConnected",SPLIT_EDGE:"splitEdge",FLIP_EDGE:"flipEdge",START_EDITING:"startEditing",EDITING_STARTED:"editingStarted",EDITING_STOPPED:"editingStopped",ADD_OVERLAY:"addOverlay",REMOVE_OVERLAY:"removeOverlay",UPDATE_CELL_SIZE:"updateCellSize",ESCAPE:"escape",DOUBLE_CLICK:"doubleClick",
+START:"start",RESET:"reset"};function mxXmlRequest(a,b,c,d,e,f){this.url=a;this.params=b;this.method=c||"POST";this.async=null!=d?d:!0;this.username=e;this.password=f}mxXmlRequest.prototype.url=null;mxXmlRequest.prototype.params=null;mxXmlRequest.prototype.method=null;mxXmlRequest.prototype.async=null;mxXmlRequest.prototype.binary=!1;mxXmlRequest.prototype.withCredentials=!1;mxXmlRequest.prototype.username=null;mxXmlRequest.prototype.password=null;mxXmlRequest.prototype.request=null;
+mxXmlRequest.prototype.decodeSimulateValues=!1;mxXmlRequest.prototype.isBinary=function(){return this.binary};mxXmlRequest.prototype.setBinary=function(a){this.binary=a};mxXmlRequest.prototype.getText=function(){return this.request.responseText};mxXmlRequest.prototype.isReady=function(){return 4==this.request.readyState};mxXmlRequest.prototype.getDocumentElement=function(){var a=this.getXml();return null!=a?a.documentElement:null};
+mxXmlRequest.prototype.getXml=function(){var a=this.request.responseXML;if(9<=document.documentMode||null==a||null==a.documentElement)a=mxUtils.parseXml(this.request.responseText);return a};mxXmlRequest.prototype.getText=function(){return this.request.responseText};mxXmlRequest.prototype.getStatus=function(){return this.request.status};
+mxXmlRequest.prototype.create=function(){if(window.XMLHttpRequest)return function(){var a=new XMLHttpRequest;this.isBinary()&&a.overrideMimeType&&a.overrideMimeType("text/plain; charset=x-user-defined");return a};if("undefined"!=typeof ActiveXObject)return function(){return new ActiveXObject("Microsoft.XMLHTTP")}}();
+mxXmlRequest.prototype.send=function(a,b,c,d){this.request=this.create();null!=this.request&&(null!=a&&(this.request.onreadystatechange=mxUtils.bind(this,function(){this.isReady()&&(a(this),this.onreadystatechaange=null)})),this.request.open(this.method,this.url,this.async,this.username,this.password),this.setRequestHeaders(this.request,this.params),window.XMLHttpRequest&&this.withCredentials&&(this.request.withCredentials="true"),!mxClient.IS_QUIRKS&&(null==document.documentMode||9<document.documentMode)&&
+window.XMLHttpRequest&&null!=c&&null!=d&&(this.request.timeout=c,this.request.ontimeout=d),this.request.send(this.params))};mxXmlRequest.prototype.setRequestHeaders=function(a,b){null!=b&&a.setRequestHeader("Content-Type","application/x-www-form-urlencoded")};
+mxXmlRequest.prototype.simulate=function(a,b){a=a||document;var c=null;a==document&&(c=window.onbeforeunload,window.onbeforeunload=null);var d=a.createElement("form");d.setAttribute("method",this.method);d.setAttribute("action",this.url);null!=b&&d.setAttribute("target",b);d.style.display="none";d.style.visibility="hidden";for(var e=0<this.params.indexOf("&")?this.params.split("&"):this.params.split(),f=0;f<e.length;f++){var g=e[f].indexOf("=");if(0<g){var k=e[f].substring(0,g),g=e[f].substring(g+
+1);this.decodeSimulateValues&&(g=decodeURIComponent(g));var l=a.createElement("textarea");l.setAttribute("wrap","off");l.setAttribute("name",k);mxUtils.write(l,g);d.appendChild(l)}}a.body.appendChild(d);d.submit();null!=d.parentNode&&d.parentNode.removeChild(d);null!=c&&(window.onbeforeunload=c)};
+var mxClipboard={STEPSIZE:10,insertCount:1,cells:null,setCells:function(a){mxClipboard.cells=a},getCells:function(){return mxClipboard.cells},isEmpty:function(){return null==mxClipboard.getCells()},cut:function(a,b){b=mxClipboard.copy(a,b);mxClipboard.insertCount=0;mxClipboard.removeCells(a,b);return b},removeCells:function(a,b){a.removeCells(b)},copy:function(a,b){b=b||a.getSelectionCells();var c=a.getExportableCells(a.model.getTopmostCells(b));mxClipboard.insertCount=1;mxClipboard.setCells(a.cloneCells(c));
+return c},paste:function(a){var b=null;if(!mxClipboard.isEmpty()){var b=a.getImportableCells(mxClipboard.getCells()),c=mxClipboard.insertCount*mxClipboard.STEPSIZE,d=a.getDefaultParent(),b=a.importCells(b,c,c,d);mxClipboard.insertCount++;a.setSelectionCells(b)}return b}};
+function mxWindow(a,b,c,d,e,f,g,k,l,m){null!=b&&(g=null!=g?g:!0,this.content=b,this.init(c,d,e,f,m),this.installMaximizeHandler(),this.installMinimizeHandler(),this.installCloseHandler(),this.setMinimizable(g),this.setTitle(a),(null==k||k)&&this.installMoveHandler(),null!=l&&null!=l.parentNode?l.parentNode.replaceChild(this.div,l):document.body.appendChild(this.div))}mxWindow.prototype=new mxEventSource;mxWindow.prototype.constructor=mxWindow;mxWindow.prototype.closeImage=mxClient.imageBasePath+"/close.gif";
+mxWindow.prototype.minimizeImage=mxClient.imageBasePath+"/minimize.gif";mxWindow.prototype.normalizeImage=mxClient.imageBasePath+"/normalize.gif";mxWindow.prototype.maximizeImage=mxClient.imageBasePath+"/maximize.gif";mxWindow.prototype.resizeImage=mxClient.imageBasePath+"/resize.gif";mxWindow.prototype.visible=!1;mxWindow.prototype.minimumSize=new mxRectangle(0,0,50,40);mxWindow.prototype.destroyOnClose=!0;
+mxWindow.prototype.contentHeightCorrection=8==document.documentMode||7==document.documentMode?6:2;mxWindow.prototype.title=null;mxWindow.prototype.content=null;
+mxWindow.prototype.init=function(a,b,c,d,e){e=null!=e?e:"mxWindow";this.div=document.createElement("div");this.div.className=e;this.div.style.left=a+"px";this.div.style.top=b+"px";this.table=document.createElement("table");this.table.className=e;mxClient.IS_POINTER&&(this.div.style.touchAction="none");null!=c&&(mxClient.IS_QUIRKS||(this.div.style.width=c+"px"),this.table.style.width=c+"px");null!=d&&(mxClient.IS_QUIRKS||(this.div.style.height=d+"px"),this.table.style.height=d+"px");a=document.createElement("tbody");
+b=document.createElement("tr");this.title=document.createElement("td");this.title.className=e+"Title";this.buttons=document.createElement("div");this.buttons.style.position="absolute";this.buttons.style.display="inline-block";this.buttons.style.right="4px";this.buttons.style.top="5px";this.title.appendChild(this.buttons);b.appendChild(this.title);a.appendChild(b);b=document.createElement("tr");this.td=document.createElement("td");this.td.className=e+"Pane";7==document.documentMode&&(this.td.style.height=
+"100%");this.contentWrapper=document.createElement("div");this.contentWrapper.className=e+"Pane";this.contentWrapper.style.width="100%";this.contentWrapper.appendChild(this.content);if(mxClient.IS_QUIRKS||"DIV"!=this.content.nodeName.toUpperCase())this.contentWrapper.style.height="100%";this.td.appendChild(this.contentWrapper);b.appendChild(this.td);a.appendChild(b);this.table.appendChild(a);this.div.appendChild(this.table);e=mxUtils.bind(this,function(a){this.activate()});mxEvent.addGestureListeners(this.title,
+e);mxEvent.addGestureListeners(this.table,e);this.hide()};mxWindow.prototype.setTitle=function(a){for(var b=this.title.firstChild;null!=b;){var c=b.nextSibling;b.nodeType==mxConstants.NODETYPE_TEXT&&b.parentNode.removeChild(b);b=c}mxUtils.write(this.title,a||"");this.title.appendChild(this.buttons)};mxWindow.prototype.setScrollable=function(a){0>navigator.userAgent.indexOf("Presto/2.5")&&(this.contentWrapper.style.overflow=a?"auto":"hidden")};
+mxWindow.prototype.activate=function(){if(mxWindow.activeWindow!=this){var a=mxUtils.getCurrentStyle(this.getElement()),a=null!=a?a.zIndex:3;if(mxWindow.activeWindow){var b=mxWindow.activeWindow.getElement();null!=b&&null!=b.style&&(b.style.zIndex=a)}b=mxWindow.activeWindow;this.getElement().style.zIndex=parseInt(a)+1;mxWindow.activeWindow=this;this.fireEvent(new mxEventObject(mxEvent.ACTIVATE,"previousWindow",b))}};mxWindow.prototype.getElement=function(){return this.div};
+mxWindow.prototype.fit=function(){mxUtils.fit(this.div)};mxWindow.prototype.isResizable=function(){return null!=this.resize?"none"!=this.resize.style.display:!1};
+mxWindow.prototype.setResizable=function(a){if(a)if(null==this.resize){this.resize=document.createElement("img");this.resize.style.position="absolute";this.resize.style.bottom="2px";this.resize.style.right="2px";this.resize.setAttribute("src",this.resizeImage);this.resize.style.cursor="nw-resize";var b=null,c=null,d=null,e=null;a=mxUtils.bind(this,function(a){this.activate();b=mxEvent.getClientX(a);c=mxEvent.getClientY(a);d=this.div.offsetWidth;e=this.div.offsetHeight;mxEvent.addGestureListeners(document,
+null,f,g);this.fireEvent(new mxEventObject(mxEvent.RESIZE_START,"event",a));mxEvent.consume(a)});var f=mxUtils.bind(this,function(a){if(null!=b&&null!=c){var f=mxEvent.getClientX(a)-b,g=mxEvent.getClientY(a)-c;this.setSize(d+f,e+g);this.fireEvent(new mxEventObject(mxEvent.RESIZE,"event",a));mxEvent.consume(a)}}),g=mxUtils.bind(this,function(a){null!=b&&null!=c&&(c=b=null,mxEvent.removeGestureListeners(document,null,f,g),this.fireEvent(new mxEventObject(mxEvent.RESIZE_END,"event",a)),mxEvent.consume(a))});
+mxEvent.addGestureListeners(this.resize,a,f,g);this.div.appendChild(this.resize)}else this.resize.style.display="inline";else null!=this.resize&&(this.resize.style.display="none")};
+mxWindow.prototype.setSize=function(a,b){a=Math.max(this.minimumSize.width,a);b=Math.max(this.minimumSize.height,b);mxClient.IS_QUIRKS||(this.div.style.width=a+"px",this.div.style.height=b+"px");this.table.style.width=a+"px";this.table.style.height=b+"px";mxClient.IS_QUIRKS||(this.contentWrapper.style.height=this.div.offsetHeight-this.title.offsetHeight-this.contentHeightCorrection+"px")};mxWindow.prototype.setMinimizable=function(a){this.minimize.style.display=a?"":"none"};
+mxWindow.prototype.getMinimumSize=function(){return new mxRectangle(0,0,0,this.title.offsetHeight)};
+mxWindow.prototype.installMinimizeHandler=function(){this.minimize=document.createElement("img");this.minimize.setAttribute("src",this.minimizeImage);this.minimize.setAttribute("title","Minimize");this.minimize.style.cursor="pointer";this.minimize.style.marginLeft="2px";this.minimize.style.display="none";this.buttons.appendChild(this.minimize);var a=!1,b=null,c=null,d=mxUtils.bind(this,function(d){this.activate();if(a)a=!1,this.minimize.setAttribute("src",this.minimizeImage),this.minimize.setAttribute("title",
+"Minimize"),this.contentWrapper.style.display="",this.maximize.style.display=b,mxClient.IS_QUIRKS||(this.div.style.height=c),this.table.style.height=c,null!=this.resize&&(this.resize.style.visibility=""),this.fireEvent(new mxEventObject(mxEvent.NORMALIZE,"event",d));else{a=!0;this.minimize.setAttribute("src",this.normalizeImage);this.minimize.setAttribute("title","Normalize");this.contentWrapper.style.display="none";b=this.maximize.style.display;this.maximize.style.display="none";c=this.table.style.height;
+var e=this.getMinimumSize();0<e.height&&(mxClient.IS_QUIRKS||(this.div.style.height=e.height+"px"),this.table.style.height=e.height+"px");0<e.width&&(mxClient.IS_QUIRKS||(this.div.style.width=e.width+"px"),this.table.style.width=e.width+"px");null!=this.resize&&(this.resize.style.visibility="hidden");this.fireEvent(new mxEventObject(mxEvent.MINIMIZE,"event",d))}mxEvent.consume(d)});mxEvent.addGestureListeners(this.minimize,d)};
+mxWindow.prototype.setMaximizable=function(a){this.maximize.style.display=a?"":"none"};
+mxWindow.prototype.installMaximizeHandler=function(){this.maximize=document.createElement("img");this.maximize.setAttribute("src",this.maximizeImage);this.maximize.setAttribute("title","Maximize");this.maximize.style.cursor="default";this.maximize.style.marginLeft="2px";this.maximize.style.cursor="pointer";this.maximize.style.display="none";this.buttons.appendChild(this.maximize);var a=!1,b=null,c=null,d=null,e=null,f=null,g=mxUtils.bind(this,function(g){this.activate();if("none"!=this.maximize.style.display){if(a)a=
+!1,this.maximize.setAttribute("src",this.maximizeImage),this.maximize.setAttribute("title","Maximize"),this.contentWrapper.style.display="",this.minimize.style.display=f,this.div.style.left=b+"px",this.div.style.top=c+"px",mxClient.IS_QUIRKS||(this.div.style.height=d,this.div.style.width=e,k=mxUtils.getCurrentStyle(this.contentWrapper),"auto"!=k.overflow&&null==this.resize)||(this.contentWrapper.style.height=this.div.offsetHeight-this.title.offsetHeight-this.contentHeightCorrection+"px"),this.table.style.height=
+d,this.table.style.width=e,null!=this.resize&&(this.resize.style.visibility=""),this.fireEvent(new mxEventObject(mxEvent.NORMALIZE,"event",g));else{a=!0;this.maximize.setAttribute("src",this.normalizeImage);this.maximize.setAttribute("title","Normalize");this.contentWrapper.style.display="";f=this.minimize.style.display;this.minimize.style.display="none";b=parseInt(this.div.style.left);c=parseInt(this.div.style.top);d=this.table.style.height;e=this.table.style.width;this.div.style.left="0px";this.div.style.top=
+"0px";k=Math.max(document.body.clientHeight||0,document.documentElement.clientHeight||0);mxClient.IS_QUIRKS||(this.div.style.width=document.body.clientWidth-2+"px",this.div.style.height=k-2+"px");this.table.style.width=document.body.clientWidth-2+"px";this.table.style.height=k-2+"px";null!=this.resize&&(this.resize.style.visibility="hidden");if(!mxClient.IS_QUIRKS){var k=mxUtils.getCurrentStyle(this.contentWrapper);if("auto"==k.overflow||null!=this.resize)this.contentWrapper.style.height=this.div.offsetHeight-
+this.title.offsetHeight-this.contentHeightCorrection+"px"}this.fireEvent(new mxEventObject(mxEvent.MAXIMIZE,"event",g))}mxEvent.consume(g)}});mxEvent.addGestureListeners(this.maximize,g);mxEvent.addListener(this.title,"dblclick",g)};
+mxWindow.prototype.installMoveHandler=function(){this.title.style.cursor="move";mxEvent.addGestureListeners(this.title,mxUtils.bind(this,function(a){var b=mxEvent.getClientX(a),c=mxEvent.getClientY(a),d=this.getX(),e=this.getY(),f=mxUtils.bind(this,function(a){var f=mxEvent.getClientX(a)-b,g=mxEvent.getClientY(a)-c;this.setLocation(d+f,e+g);this.fireEvent(new mxEventObject(mxEvent.MOVE,"event",a));mxEvent.consume(a)}),g=mxUtils.bind(this,function(a){mxEvent.removeGestureListeners(document,null,f,
+g);this.fireEvent(new mxEventObject(mxEvent.MOVE_END,"event",a));mxEvent.consume(a)});mxEvent.addGestureListeners(document,null,f,g);this.fireEvent(new mxEventObject(mxEvent.MOVE_START,"event",a));mxEvent.consume(a)}));mxClient.IS_POINTER&&(this.title.style.touchAction="none")};mxWindow.prototype.setLocation=function(a,b){this.div.style.left=a+"px";this.div.style.top=b+"px"};mxWindow.prototype.getX=function(){return parseInt(this.div.style.left)};mxWindow.prototype.getY=function(){return parseInt(this.div.style.top)};
+mxWindow.prototype.installCloseHandler=function(){this.closeImg=document.createElement("img");this.closeImg.setAttribute("src",this.closeImage);this.closeImg.setAttribute("title","Close");this.closeImg.style.marginLeft="2px";this.closeImg.style.cursor="pointer";this.closeImg.style.display="none";this.buttons.appendChild(this.closeImg);mxEvent.addGestureListeners(this.closeImg,mxUtils.bind(this,function(a){this.fireEvent(new mxEventObject(mxEvent.CLOSE,"event",a));this.destroyOnClose?this.destroy():
+this.setVisible(!1);mxEvent.consume(a)}))};mxWindow.prototype.setImage=function(a){this.image=document.createElement("img");this.image.setAttribute("src",a);this.image.setAttribute("align","left");this.image.style.marginRight="4px";this.image.style.marginLeft="0px";this.image.style.marginTop="-2px";this.title.insertBefore(this.image,this.title.firstChild)};mxWindow.prototype.setClosable=function(a){this.closeImg.style.display=a?"":"none"};
+mxWindow.prototype.isVisible=function(){return null!=this.div?"none"!=this.div.style.display:!1};mxWindow.prototype.setVisible=function(a){null!=this.div&&this.isVisible()!=a&&(a?this.show():this.hide())};
+mxWindow.prototype.show=function(){this.div.style.display="";this.activate();var a=mxUtils.getCurrentStyle(this.contentWrapper);mxClient.IS_QUIRKS||"auto"!=a.overflow&&null==this.resize||"none"==this.contentWrapper.style.display||(this.contentWrapper.style.height=this.div.offsetHeight-this.title.offsetHeight-this.contentHeightCorrection+"px");this.fireEvent(new mxEventObject(mxEvent.SHOW))};mxWindow.prototype.hide=function(){this.div.style.display="none";this.fireEvent(new mxEventObject(mxEvent.HIDE))};
+mxWindow.prototype.destroy=function(){this.fireEvent(new mxEventObject(mxEvent.DESTROY));null!=this.div&&(mxEvent.release(this.div),this.div.parentNode.removeChild(this.div),this.div=null);this.contentWrapper=this.content=this.title=null};function mxForm(a){this.table=document.createElement("table");this.table.className=a;this.body=document.createElement("tbody");this.table.appendChild(this.body)}mxForm.prototype.table=null;mxForm.prototype.body=!1;mxForm.prototype.getTable=function(){return this.table};
+mxForm.prototype.addButtons=function(a,b){var c=document.createElement("tr"),d=document.createElement("td");c.appendChild(d);var d=document.createElement("td"),e=document.createElement("button");mxUtils.write(e,mxResources.get("ok")||"OK");d.appendChild(e);mxEvent.addListener(e,"click",function(){a()});e=document.createElement("button");mxUtils.write(e,mxResources.get("cancel")||"Cancel");d.appendChild(e);mxEvent.addListener(e,"click",function(){b()});c.appendChild(d);this.body.appendChild(c)};
+mxForm.prototype.addText=function(a,b,c){var d=document.createElement("input");d.setAttribute("type",c||"text");d.value=b;return this.addField(a,d)};mxForm.prototype.addCheckbox=function(a,b){var c=document.createElement("input");c.setAttribute("type","checkbox");this.addField(a,c);b&&(c.checked=!0);return c};mxForm.prototype.addTextarea=function(a,b,c){var d=document.createElement("textarea");mxClient.IS_NS&&c--;d.setAttribute("rows",c||2);d.value=b;return this.addField(a,d)};
+mxForm.prototype.addCombo=function(a,b,c){var d=document.createElement("select");null!=c&&d.setAttribute("size",c);b&&d.setAttribute("multiple","true");return this.addField(a,d)};mxForm.prototype.addOption=function(a,b,c,d){var e=document.createElement("option");mxUtils.writeln(e,b);e.setAttribute("value",c);d&&e.setAttribute("selected",d);a.appendChild(e)};
+mxForm.prototype.addField=function(a,b){var c=document.createElement("tr"),d=document.createElement("td");mxUtils.write(d,a);c.appendChild(d);d=document.createElement("td");d.appendChild(b);c.appendChild(d);this.body.appendChild(c);return b};function mxImage(a,b,c){this.src=a;this.width=b;this.height=c}mxImage.prototype.src=null;mxImage.prototype.width=null;mxImage.prototype.height=null;
+function mxDivResizer(a,b){if("div"==a.nodeName.toLowerCase()){null==b&&(b=window);this.div=a;var c=mxUtils.getCurrentStyle(a);null!=c&&(this.resizeWidth="auto"==c.width,this.resizeHeight="auto"==c.height);mxEvent.addListener(b,"resize",mxUtils.bind(this,function(a){this.handlingResize||(this.handlingResize=!0,this.resize(),this.handlingResize=!1)}));this.resize()}}mxDivResizer.prototype.resizeWidth=!0;mxDivResizer.prototype.resizeHeight=!0;mxDivResizer.prototype.handlingResize=!1;
+mxDivResizer.prototype.resize=function(){var a=this.getDocumentWidth(),b=this.getDocumentHeight(),c=parseInt(this.div.style.left),d=parseInt(this.div.style.right),e=parseInt(this.div.style.top),f=parseInt(this.div.style.bottom);this.resizeWidth&&!isNaN(c)&&!isNaN(d)&&0<=c&&0<=d&&0<a-d-c&&(this.div.style.width=a-d-c+"px");this.resizeHeight&&!isNaN(e)&&!isNaN(f)&&0<=e&&0<=f&&0<b-e-f&&(this.div.style.height=b-e-f+"px")};mxDivResizer.prototype.getDocumentWidth=function(){return document.body.clientWidth};
+mxDivResizer.prototype.getDocumentHeight=function(){return document.body.clientHeight};function mxDragSource(a,b){this.element=a;this.dropHandler=b;mxEvent.addGestureListeners(a,mxUtils.bind(this,function(a){this.mouseDown(a)}));mxEvent.addListener(a,"dragstart",function(a){mxEvent.consume(a)});this.eventConsumer=function(a,b){var c=b.getProperty("eventName"),d=b.getProperty("event");c!=mxEvent.MOUSE_DOWN&&d.consume()}}mxDragSource.prototype.element=null;mxDragSource.prototype.dropHandler=null;
+mxDragSource.prototype.dragOffset=null;mxDragSource.prototype.dragElement=null;mxDragSource.prototype.previewElement=null;mxDragSource.prototype.enabled=!0;mxDragSource.prototype.currentGraph=null;mxDragSource.prototype.currentDropTarget=null;mxDragSource.prototype.currentPoint=null;mxDragSource.prototype.currentGuide=null;mxDragSource.prototype.currentHighlight=null;mxDragSource.prototype.autoscroll=!0;mxDragSource.prototype.guidesEnabled=!0;mxDragSource.prototype.gridEnabled=!0;
+mxDragSource.prototype.highlightDropTargets=!0;mxDragSource.prototype.dragElementZIndex=100;mxDragSource.prototype.dragElementOpacity=70;mxDragSource.prototype.checkEventSource=!0;mxDragSource.prototype.isEnabled=function(){return this.enabled};mxDragSource.prototype.setEnabled=function(a){this.enabled=a};mxDragSource.prototype.isGuidesEnabled=function(){return this.guidesEnabled};mxDragSource.prototype.setGuidesEnabled=function(a){this.guidesEnabled=a};mxDragSource.prototype.isGridEnabled=function(){return this.gridEnabled};
+mxDragSource.prototype.setGridEnabled=function(a){this.gridEnabled=a};mxDragSource.prototype.getGraphForEvent=function(a){return null};mxDragSource.prototype.getDropTarget=function(a,b,c,d){return a.getCellAt(b,c)};mxDragSource.prototype.createDragElement=function(a){return this.element.cloneNode(!0)};mxDragSource.prototype.createPreviewElement=function(a){return null};mxDragSource.prototype.isActive=function(){return null!=this.mouseMoveHandler};
+mxDragSource.prototype.reset=function(){null!=this.currentGraph&&(this.dragExit(this.currentGraph),this.currentGraph=null);this.removeDragElement();this.removeListeners();this.stopDrag()};
+mxDragSource.prototype.mouseDown=function(a){this.enabled&&!mxEvent.isConsumed(a)&&null==this.mouseMoveHandler&&(this.startDrag(a),this.mouseMoveHandler=mxUtils.bind(this,this.mouseMove),this.mouseUpHandler=mxUtils.bind(this,this.mouseUp),mxEvent.addGestureListeners(document,null,this.mouseMoveHandler,this.mouseUpHandler),mxClient.IS_TOUCH&&!mxEvent.isMouseEvent(a)&&(this.eventSource=mxEvent.getSource(a),mxEvent.addGestureListeners(this.eventSource,null,this.mouseMoveHandler,this.mouseUpHandler)))};
+mxDragSource.prototype.startDrag=function(a){this.dragElement=this.createDragElement(a);this.dragElement.style.position="absolute";this.dragElement.style.zIndex=this.dragElementZIndex;mxUtils.setOpacity(this.dragElement,this.dragElementOpacity);this.checkEventSource&&mxClient.IS_SVG&&(this.dragElement.style.pointerEvents="none")};mxDragSource.prototype.stopDrag=function(){this.removeDragElement()};
+mxDragSource.prototype.removeDragElement=function(){null!=this.dragElement&&(null!=this.dragElement.parentNode&&this.dragElement.parentNode.removeChild(this.dragElement),this.dragElement=null)};mxDragSource.prototype.getElementForEvent=function(a){return mxEvent.isTouchEvent(a)||mxEvent.isPenEvent(a)?document.elementFromPoint(mxEvent.getClientX(a),mxEvent.getClientY(a)):mxEvent.getSource(a)};
+mxDragSource.prototype.graphContainsEvent=function(a,b){var c=mxEvent.getClientX(b),d=mxEvent.getClientY(b),e=mxUtils.getOffset(a.container),f=mxUtils.getScrollOrigin(),g=this.getElementForEvent(b);if(this.checkEventSource)for(;null!=g&&g!=a.container;)g=g.parentNode;return null!=g&&c>=e.x-f.x&&d>=e.y-f.y&&c<=e.x-f.x+a.container.offsetWidth&&d<=e.y-f.y+a.container.offsetHeight};
+mxDragSource.prototype.mouseMove=function(a){var b=this.getGraphForEvent(a);null==b||this.graphContainsEvent(b,a)||(b=null);b!=this.currentGraph&&(null!=this.currentGraph&&this.dragExit(this.currentGraph,a),this.currentGraph=b,null!=this.currentGraph&&this.dragEnter(this.currentGraph,a));null!=this.currentGraph&&this.dragOver(this.currentGraph,a);if(null==this.dragElement||null!=this.previewElement&&"visible"==this.previewElement.style.visibility)null!=this.dragElement&&(this.dragElement.style.visibility=
+"hidden");else{var b=mxEvent.getClientX(a),c=mxEvent.getClientY(a);null==this.dragElement.parentNode&&document.body.appendChild(this.dragElement);this.dragElement.style.visibility="visible";null!=this.dragOffset&&(b+=this.dragOffset.x,c+=this.dragOffset.y);var d=mxUtils.getDocumentScrollOrigin(document);this.dragElement.style.left=b+d.x+"px";this.dragElement.style.top=c+d.y+"px"}mxEvent.consume(a)};
+mxDragSource.prototype.mouseUp=function(a){if(null!=this.currentGraph){if(null!=this.currentPoint&&(null==this.previewElement||"hidden"!=this.previewElement.style.visibility)){var b=this.currentGraph.view.scale,c=this.currentGraph.view.translate;this.drop(this.currentGraph,a,this.currentDropTarget,this.currentPoint.x/b-c.x,this.currentPoint.y/b-c.y)}this.dragExit(this.currentGraph);this.currentGraph=null}this.stopDrag();this.removeListeners();mxEvent.consume(a)};
+mxDragSource.prototype.removeListeners=function(){null!=this.eventSource&&(mxEvent.removeGestureListeners(this.eventSource,null,this.mouseMoveHandler,this.mouseUpHandler),this.eventSource=null);mxEvent.removeGestureListeners(document,null,this.mouseMoveHandler,this.mouseUpHandler);this.mouseUpHandler=this.mouseMoveHandler=null};
+mxDragSource.prototype.dragEnter=function(a,b){a.isMouseDown=!0;a.isMouseTrigger=mxEvent.isMouseEvent(b);this.previewElement=this.createPreviewElement(a);null!=this.previewElement&&this.checkEventSource&&mxClient.IS_SVG&&(this.previewElement.style.pointerEvents="none");this.isGuidesEnabled()&&null!=this.previewElement&&(this.currentGuide=new mxGuide(a,a.graphHandler.getGuideStates()));this.highlightDropTargets&&(this.currentHighlight=new mxCellHighlight(a,mxConstants.DROP_TARGET_COLOR));a.addListener(mxEvent.FIRE_MOUSE_EVENT,
+this.eventConsumer)};mxDragSource.prototype.dragExit=function(a,b){this.currentPoint=this.currentDropTarget=null;a.isMouseDown=!1;a.removeListener(this.eventConsumer);null!=this.previewElement&&(null!=this.previewElement.parentNode&&this.previewElement.parentNode.removeChild(this.previewElement),this.previewElement=null);null!=this.currentGuide&&(this.currentGuide.destroy(),this.currentGuide=null);null!=this.currentHighlight&&(this.currentHighlight.destroy(),this.currentHighlight=null)};
+mxDragSource.prototype.dragOver=function(a,b){var c=mxUtils.getOffset(a.container),d=mxUtils.getScrollOrigin(a.container),e=mxEvent.getClientX(b)-c.x+d.x-a.panDx,c=mxEvent.getClientY(b)-c.y+d.y-a.panDy;a.autoScroll&&(null==this.autoscroll||this.autoscroll)&&a.scrollPointToVisible(e,c,a.autoExtend);null!=this.currentHighlight&&a.isDropEnabled()&&(this.currentDropTarget=this.getDropTarget(a,e,c,b),d=a.getView().getState(this.currentDropTarget),this.currentHighlight.highlight(d));if(null!=this.previewElement){null==
+this.previewElement.parentNode&&(a.container.appendChild(this.previewElement),this.previewElement.style.zIndex="3",this.previewElement.style.position="absolute");var d=this.isGridEnabled()&&a.isGridEnabledEvent(b),f=!0;if(null!=this.currentGuide&&this.currentGuide.isEnabledForEvent(b))var f=parseInt(this.previewElement.style.width),g=parseInt(this.previewElement.style.height),f=new mxRectangle(0,0,f,g),c=new mxPoint(e,c),c=this.currentGuide.move(f,c,d),f=!1,e=c.x,c=c.y;else if(d)var d=a.view.scale,
+g=a.view.translate,k=a.gridSize/2,e=(a.snap(e/d-g.x-k)+g.x)*d,c=(a.snap(c/d-g.y-k)+g.y)*d;null!=this.currentGuide&&f&&this.currentGuide.hide();null!=this.previewOffset&&(e+=this.previewOffset.x,c+=this.previewOffset.y);this.previewElement.style.left=Math.round(e)+"px";this.previewElement.style.top=Math.round(c)+"px";this.previewElement.style.visibility="visible"}this.currentPoint=new mxPoint(e,c)};
+mxDragSource.prototype.drop=function(a,b,c,d,e){this.dropHandler.apply(this,arguments);"hidden"!=a.container.style.visibility&&a.container.focus()};function mxToolbar(a){this.container=a}mxToolbar.prototype=new mxEventSource;mxToolbar.prototype.constructor=mxToolbar;mxToolbar.prototype.container=null;mxToolbar.prototype.enabled=!0;mxToolbar.prototype.noReset=!1;mxToolbar.prototype.updateDefaultMode=!0;
+mxToolbar.prototype.addItem=function(a,b,c,d,e,f){var g=document.createElement(null!=b?"img":"button"),k=e||(null!=f?"mxToolbarMode":"mxToolbarItem");g.className=k;g.setAttribute("src",b);null!=a&&(null!=b?g.setAttribute("title",a):mxUtils.write(g,a));this.container.appendChild(g);null!=c&&(mxEvent.addListener(g,"click",c),mxClient.IS_TOUCH&&mxEvent.addListener(g,"touchend",c));a=mxUtils.bind(this,function(a){null!=d?g.setAttribute("src",b):g.style.backgroundColor=""});mxEvent.addGestureListeners(g,
+mxUtils.bind(this,function(a){null!=d?g.setAttribute("src",d):g.style.backgroundColor="gray";if(null!=f){null==this.menu&&(this.menu=new mxPopupMenu,this.menu.init());var b=this.currentImg;this.menu.isMenuShowing()&&this.menu.hideMenu();b!=g&&(this.currentImg=g,this.menu.factoryMethod=f,b=new mxPoint(g.offsetLeft,g.offsetTop+g.offsetHeight),this.menu.popup(b.x,b.y,null,a),this.menu.isMenuShowing()&&(g.className=k+"Selected",this.menu.hideMenu=function(){mxPopupMenu.prototype.hideMenu.apply(this);
+g.className=k;this.currentImg=null}))}}),null,a);mxEvent.addListener(g,"mouseout",a);return g};mxToolbar.prototype.addCombo=function(a){var b=document.createElement("div");b.style.display="inline";b.className="mxToolbarComboContainer";var c=document.createElement("select");c.className=a||"mxToolbarCombo";b.appendChild(c);this.container.appendChild(b);return c};
+mxToolbar.prototype.addActionCombo=function(a,b){var c=document.createElement("select");c.className=b||"mxToolbarCombo";this.addOption(c,a,null);mxEvent.addListener(c,"change",function(a){var b=c.options[c.selectedIndex];c.selectedIndex=0;null!=b.funct&&b.funct(a)});this.container.appendChild(c);return c};mxToolbar.prototype.addOption=function(a,b,c){var d=document.createElement("option");mxUtils.writeln(d,b);"function"==typeof c?d.funct=c:d.setAttribute("value",c);a.appendChild(d);return d};
+mxToolbar.prototype.addSwitchMode=function(a,b,c,d,e){var f=document.createElement("img");f.initialClassName=e||"mxToolbarMode";f.className=f.initialClassName;f.setAttribute("src",b);f.altIcon=d;null!=a&&f.setAttribute("title",a);mxEvent.addListener(f,"click",mxUtils.bind(this,function(a){a=this.selectedMode.altIcon;null!=a?(this.selectedMode.altIcon=this.selectedMode.getAttribute("src"),this.selectedMode.setAttribute("src",a)):this.selectedMode.className=this.selectedMode.initialClassName;this.updateDefaultMode&&
+(this.defaultMode=f);this.selectedMode=f;a=f.altIcon;null!=a?(f.altIcon=f.getAttribute("src"),f.setAttribute("src",a)):f.className=f.initialClassName+"Selected";this.fireEvent(new mxEventObject(mxEvent.SELECT));c()}));this.container.appendChild(f);null==this.defaultMode&&(this.defaultMode=f,this.selectMode(f),c());return f};
+mxToolbar.prototype.addMode=function(a,b,c,d,e,f){f=null!=f?f:!0;var g=document.createElement(null!=b?"img":"button");g.initialClassName=e||"mxToolbarMode";g.className=g.initialClassName;g.setAttribute("src",b);g.altIcon=d;null!=a&&g.setAttribute("title",a);this.enabled&&f&&(mxEvent.addListener(g,"click",mxUtils.bind(this,function(a){this.selectMode(g,c);this.noReset=!1})),mxEvent.addListener(g,"dblclick",mxUtils.bind(this,function(a){this.selectMode(g,c);this.noReset=!0})),null==this.defaultMode&&
+(this.defaultMode=g,this.defaultFunction=c,this.selectMode(g,c)));this.container.appendChild(g);return g};
+mxToolbar.prototype.selectMode=function(a,b){if(this.selectedMode!=a){if(null!=this.selectedMode){var c=this.selectedMode.altIcon;null!=c?(this.selectedMode.altIcon=this.selectedMode.getAttribute("src"),this.selectedMode.setAttribute("src",c)):this.selectedMode.className=this.selectedMode.initialClassName}this.selectedMode=a;c=this.selectedMode.altIcon;null!=c?(this.selectedMode.altIcon=this.selectedMode.getAttribute("src"),this.selectedMode.setAttribute("src",c)):this.selectedMode.className=this.selectedMode.initialClassName+
+"Selected";this.fireEvent(new mxEventObject(mxEvent.SELECT,"function",b))}};mxToolbar.prototype.resetMode=function(a){!a&&this.noReset||this.selectedMode==this.defaultMode||this.selectMode(this.defaultMode,this.defaultFunction)};mxToolbar.prototype.addSeparator=function(a){return this.addItem(null,a,null)};mxToolbar.prototype.addBreak=function(){mxUtils.br(this.container)};
+mxToolbar.prototype.addLine=function(){var a=document.createElement("hr");a.style.marginRight="6px";a.setAttribute("size","1");this.container.appendChild(a)};mxToolbar.prototype.destroy=function(){mxEvent.release(this.container);this.selectedMode=this.defaultFunction=this.defaultMode=this.container=null;null!=this.menu&&this.menu.destroy()};function mxUndoableEdit(a,b){this.source=a;this.changes=[];this.significant=null!=b?b:!0}mxUndoableEdit.prototype.source=null;
+mxUndoableEdit.prototype.changes=null;mxUndoableEdit.prototype.significant=null;mxUndoableEdit.prototype.undone=!1;mxUndoableEdit.prototype.redone=!1;mxUndoableEdit.prototype.isEmpty=function(){return 0==this.changes.length};mxUndoableEdit.prototype.isSignificant=function(){return this.significant};mxUndoableEdit.prototype.add=function(a){this.changes.push(a)};mxUndoableEdit.prototype.notify=function(){};mxUndoableEdit.prototype.die=function(){};
+mxUndoableEdit.prototype.undo=function(){if(!this.undone){this.source.fireEvent(new mxEventObject(mxEvent.START_EDIT));for(var a=this.changes.length-1;0<=a;a--){var b=this.changes[a];null!=b.execute?b.execute():null!=b.undo&&b.undo();this.source.fireEvent(new mxEventObject(mxEvent.EXECUTED,"change",b))}this.undone=!0;this.redone=!1;this.source.fireEvent(new mxEventObject(mxEvent.END_EDIT))}this.notify()};
+mxUndoableEdit.prototype.redo=function(){if(!this.redone){this.source.fireEvent(new mxEventObject(mxEvent.START_EDIT));for(var a=this.changes.length,b=0;b<a;b++){var c=this.changes[b];null!=c.execute?c.execute():null!=c.redo&&c.redo();this.source.fireEvent(new mxEventObject(mxEvent.EXECUTED,"change",c))}this.undone=!1;this.redone=!0;this.source.fireEvent(new mxEventObject(mxEvent.END_EDIT))}this.notify()};function mxUndoManager(a){this.size=null!=a?a:100;this.clear()}mxUndoManager.prototype=new mxEventSource;
+mxUndoManager.prototype.constructor=mxUndoManager;mxUndoManager.prototype.size=null;mxUndoManager.prototype.history=null;mxUndoManager.prototype.indexOfNextAdd=0;mxUndoManager.prototype.isEmpty=function(){return 0==this.history.length};mxUndoManager.prototype.clear=function(){this.history=[];this.indexOfNextAdd=0;this.fireEvent(new mxEventObject(mxEvent.CLEAR))};mxUndoManager.prototype.canUndo=function(){return 0<this.indexOfNextAdd};
+mxUndoManager.prototype.undo=function(){for(;0<this.indexOfNextAdd;){var a=this.history[--this.indexOfNextAdd];a.undo();if(a.isSignificant()){this.fireEvent(new mxEventObject(mxEvent.UNDO,"edit",a));break}}};mxUndoManager.prototype.canRedo=function(){return this.indexOfNextAdd<this.history.length};
+mxUndoManager.prototype.redo=function(){for(var a=this.history.length;this.indexOfNextAdd<a;){var b=this.history[this.indexOfNextAdd++];b.redo();if(b.isSignificant()){this.fireEvent(new mxEventObject(mxEvent.REDO,"edit",b));break}}};mxUndoManager.prototype.undoableEditHappened=function(a){this.trim();0<this.size&&this.size==this.history.length&&this.history.shift();this.history.push(a);this.indexOfNextAdd=this.history.length;this.fireEvent(new mxEventObject(mxEvent.ADD,"edit",a))};
+mxUndoManager.prototype.trim=function(){if(this.history.length>this.indexOfNextAdd)for(var a=this.history.splice(this.indexOfNextAdd,this.history.length-this.indexOfNextAdd),b=0;b<a.length;b++)a[b].die()};var mxUrlConverter=function(){};mxUrlConverter.prototype.enabled=!0;mxUrlConverter.prototype.baseUrl=null;mxUrlConverter.prototype.baseDomain=null;
+mxUrlConverter.prototype.updateBaseUrl=function(){this.baseDomain=location.protocol+"//"+location.host;this.baseUrl=this.baseDomain+location.pathname;var a=this.baseUrl.lastIndexOf("/");0<a&&(this.baseUrl=this.baseUrl.substring(0,a+1))};mxUrlConverter.prototype.isEnabled=function(){return this.enabled};mxUrlConverter.prototype.setEnabled=function(a){this.enabled=a};mxUrlConverter.prototype.getBaseUrl=function(){return this.baseUrl};mxUrlConverter.prototype.setBaseUrl=function(a){this.baseUrl=a};
+mxUrlConverter.prototype.getBaseDomain=function(){return this.baseDomain};mxUrlConverter.prototype.setBaseDomain=function(a){this.baseDomain=a};mxUrlConverter.prototype.isRelativeUrl=function(a){return"//"!=a.substring(0,2)&&"http://"!=a.substring(0,7)&&"https://"!=a.substring(0,8)&&"data:image"!=a.substring(0,10)&&"file://"!=a.substring(0,7)};
+mxUrlConverter.prototype.convert=function(a){this.isEnabled()&&this.isRelativeUrl(a)&&(null==this.getBaseUrl()&&this.updateBaseUrl(),a="/"==a.charAt(0)?this.getBaseDomain()+a:this.getBaseUrl()+a);return a};
+function mxPanningManager(a){this.thread=null;this.active=!1;this.dy=this.dx=this.t0y=this.t0x=this.tdy=this.tdx=0;this.scrollbars=!1;this.scrollTop=this.scrollLeft=0;this.mouseListener={mouseDown:function(a,b){},mouseMove:function(a,b){},mouseUp:mxUtils.bind(this,function(a,b){this.active&&this.stop()})};a.addMouseListener(this.mouseListener);this.mouseUpListener=mxUtils.bind(this,function(){this.active&&this.stop()});mxEvent.addListener(document,"mouseup",this.mouseUpListener);var b=mxUtils.bind(this,
+function(){this.scrollbars=mxUtils.hasScrollbars(a.container);this.scrollLeft=a.container.scrollLeft;this.scrollTop=a.container.scrollTop;return window.setInterval(mxUtils.bind(this,function(){this.tdx-=this.dx;this.tdy-=this.dy;this.scrollbars?(a.panGraph(-a.container.scrollLeft-Math.ceil(this.dx),-a.container.scrollTop-Math.ceil(this.dy)),a.panDx=this.scrollLeft-a.container.scrollLeft,a.panDy=this.scrollTop-a.container.scrollTop,a.fireEvent(new mxEventObject(mxEvent.PAN))):a.panGraph(this.getDx(),
+this.getDy())}),this.delay)});this.isActive=function(){return active};this.getDx=function(){return Math.round(this.tdx)};this.getDy=function(){return Math.round(this.tdy)};this.start=function(){this.t0x=a.view.translate.x;this.t0y=a.view.translate.y;this.active=!0};this.panTo=function(c,d,e,f){this.active||this.start();this.scrollLeft=a.container.scrollLeft;this.scrollTop=a.container.scrollTop;var g=a.container;this.dx=c+(null!=e?e:0)-g.scrollLeft-g.clientWidth;this.dx=0>this.dx&&Math.abs(this.dx)<
+this.border?this.border+this.dx:this.handleMouseOut?Math.max(this.dx,0):0;0==this.dx&&(this.dx=c-g.scrollLeft,this.dx=0<this.dx&&this.dx<this.border?this.dx-this.border:this.handleMouseOut?Math.min(0,this.dx):0);this.dy=d+(null!=f?f:0)-g.scrollTop-g.clientHeight;this.dy=0>this.dy&&Math.abs(this.dy)<this.border?this.border+this.dy:this.handleMouseOut?Math.max(this.dy,0):0;0==this.dy&&(this.dy=d-g.scrollTop,this.dy=0<this.dy&&this.dy<this.border?this.dy-this.border:this.handleMouseOut?Math.min(0,this.dy):
+0);0!=this.dx||0!=this.dy?(this.dx*=this.damper,this.dy*=this.damper,null==this.thread&&(this.thread=b())):null!=this.thread&&(window.clearInterval(this.thread),this.thread=null)};this.stop=function(){if(this.active)if(this.active=!1,null!=this.thread&&(window.clearInterval(this.thread),this.thread=null),this.tdy=this.tdx=0,this.scrollbars)a.panDx=0,a.panDy=0,a.fireEvent(new mxEventObject(mxEvent.PAN));else{var b=a.panDx,d=a.panDy;if(0!=b||0!=d)a.panGraph(0,0),a.view.setTranslate(this.t0x+b/a.view.scale,
+this.t0y+d/a.view.scale)}};this.destroy=function(){a.removeMouseListener(this.mouseListener);mxEvent.removeListener(document,"mouseup",this.mouseUpListener)}}mxPanningManager.prototype.damper=1/6;mxPanningManager.prototype.delay=10;mxPanningManager.prototype.handleMouseOut=!0;mxPanningManager.prototype.border=0;function mxPopupMenu(a){this.factoryMethod=a;null!=a&&this.init()}mxPopupMenu.prototype=new mxEventSource;mxPopupMenu.prototype.constructor=mxPopupMenu;
+mxPopupMenu.prototype.submenuImage=mxClient.imageBasePath+"/submenu.gif";mxPopupMenu.prototype.zIndex=10006;mxPopupMenu.prototype.factoryMethod=null;mxPopupMenu.prototype.useLeftButtonForPopup=!1;mxPopupMenu.prototype.enabled=!0;mxPopupMenu.prototype.itemCount=0;mxPopupMenu.prototype.autoExpand=!1;mxPopupMenu.prototype.smartSeparators=!1;mxPopupMenu.prototype.labels=!0;
+mxPopupMenu.prototype.init=function(){this.table=document.createElement("table");this.table.className="mxPopupMenu";this.tbody=document.createElement("tbody");this.table.appendChild(this.tbody);this.div=document.createElement("div");this.div.className="mxPopupMenu";this.div.style.display="inline";this.div.style.zIndex=this.zIndex;this.div.appendChild(this.table);mxEvent.disableContextMenu(this.div)};mxPopupMenu.prototype.isEnabled=function(){return this.enabled};
+mxPopupMenu.prototype.setEnabled=function(a){this.enabled=a};mxPopupMenu.prototype.isPopupTrigger=function(a){return a.isPopupTrigger()||this.useLeftButtonForPopup&&mxEvent.isLeftMouseButton(a.getEvent())};
+mxPopupMenu.prototype.addItem=function(a,b,c,d,e,f,g){d=d||this;this.itemCount++;d.willAddSeparator&&(d.containsItems&&this.addSeparator(d,!0),d.willAddSeparator=!1);d.containsItems=!0;var k=document.createElement("tr");k.className="mxPopupMenuItem";var l=document.createElement("td");l.className="mxPopupMenuIcon";null!=b?(e=document.createElement("img"),e.src=b,l.appendChild(e)):null!=e&&(b=document.createElement("div"),b.className=e,l.appendChild(b));k.appendChild(l);this.labels&&(l=document.createElement("td"),
+l.className="mxPopupMenuItem"+(null==f||f?"":" mxDisabled"),mxUtils.write(l,a),l.align="left",k.appendChild(l),a=document.createElement("td"),a.className="mxPopupMenuItem"+(null==f||f?"":" mxDisabled"),a.style.paddingRight="6px",a.style.textAlign="right",k.appendChild(a),null==d.div&&this.createSubmenu(d));d.tbody.appendChild(k);if(0!=g&&0!=f){var m=null;mxEvent.addGestureListeners(k,mxUtils.bind(this,function(a){this.eventReceiver=k;d.activeRow!=k&&d.activeRow!=d&&(null!=d.activeRow&&null!=d.activeRow.div.parentNode&&
+this.hideSubmenu(d),null!=k.div&&(this.showSubmenu(d,k),d.activeRow=k));if(mxClient.IS_QUIRKS||8==document.documentMode)m=document.selection.createRange();mxEvent.consume(a)}),mxUtils.bind(this,function(a){d.activeRow!=k&&d.activeRow!=d&&(null!=d.activeRow&&null!=d.activeRow.div.parentNode&&this.hideSubmenu(d),this.autoExpand&&null!=k.div&&(this.showSubmenu(d,k),d.activeRow=k));k.className="mxPopupMenuItemHover"}),mxUtils.bind(this,function(a){if(this.eventReceiver==k){d.activeRow!=k&&this.hideMenu();
+if(null!=m){try{m.select()}catch(p){}m=null}null!=c&&c(a)}this.eventReceiver=null;mxEvent.consume(a)}));mxEvent.addListener(k,"mouseout",mxUtils.bind(this,function(a){k.className="mxPopupMenuItem"}))}return k};mxPopupMenu.prototype.addCheckmark=function(a,b){var c=a.firstChild.nextSibling;c.style.backgroundImage="url('"+b+"')";c.style.backgroundRepeat="no-repeat";c.style.backgroundPosition="2px 50%"};
+mxPopupMenu.prototype.createSubmenu=function(a){a.table=document.createElement("table");a.table.className="mxPopupMenu";a.tbody=document.createElement("tbody");a.table.appendChild(a.tbody);a.div=document.createElement("div");a.div.className="mxPopupMenu";a.div.style.position="absolute";a.div.style.display="inline";a.div.style.zIndex=this.zIndex;a.div.appendChild(a.table);var b=document.createElement("img");b.setAttribute("src",this.submenuImage);td=a.firstChild.nextSibling.nextSibling;td.appendChild(b)};
+mxPopupMenu.prototype.showSubmenu=function(a,b){if(null!=b.div){b.div.style.left=a.div.offsetLeft+b.offsetLeft+b.offsetWidth-1+"px";b.div.style.top=a.div.offsetTop+b.offsetTop+"px";document.body.appendChild(b.div);var c=parseInt(b.div.offsetLeft),d=parseInt(b.div.offsetWidth),e=mxUtils.getDocumentScrollOrigin(document),f=document.documentElement;c+d>e.x+(document.body.clientWidth||f.clientWidth)&&(b.div.style.left=Math.max(0,a.div.offsetLeft-d+(mxClient.IS_IE?6:-6))+"px");mxUtils.fit(b.div)}};
+mxPopupMenu.prototype.addSeparator=function(a,b){a=a||this;if(this.smartSeparators&&!b)a.willAddSeparator=!0;else if(null!=a.tbody){a.willAddSeparator=!1;var c=document.createElement("tr"),d=document.createElement("td");d.className="mxPopupMenuIcon";d.style.padding="0 0 0 0px";c.appendChild(d);d=document.createElement("td");d.style.padding="0 0 0 0px";d.setAttribute("colSpan","2");var e=document.createElement("hr");e.setAttribute("size","1");d.appendChild(e);c.appendChild(d);a.tbody.appendChild(c)}};
+mxPopupMenu.prototype.popup=function(a,b,c,d){if(null!=this.div&&null!=this.tbody&&null!=this.factoryMethod){this.div.style.left=a+"px";for(this.div.style.top=b+"px";null!=this.tbody.firstChild;)mxEvent.release(this.tbody.firstChild),this.tbody.removeChild(this.tbody.firstChild);this.itemCount=0;this.factoryMethod(this,c,d);0<this.itemCount&&(this.showMenu(),this.fireEvent(new mxEventObject(mxEvent.SHOW)))}};
+mxPopupMenu.prototype.isMenuShowing=function(){return null!=this.div&&this.div.parentNode==document.body};mxPopupMenu.prototype.showMenu=function(){9<=document.documentMode&&(this.div.style.filter="none");document.body.appendChild(this.div);mxUtils.fit(this.div)};mxPopupMenu.prototype.hideMenu=function(){null!=this.div&&(null!=this.div.parentNode&&this.div.parentNode.removeChild(this.div),this.hideSubmenu(this),this.containsItems=!1,this.fireEvent(new mxEventObject(mxEvent.HIDE)))};
+mxPopupMenu.prototype.hideSubmenu=function(a){null!=a.activeRow&&(this.hideSubmenu(a.activeRow),null!=a.activeRow.div.parentNode&&a.activeRow.div.parentNode.removeChild(a.activeRow.div),a.activeRow=null)};mxPopupMenu.prototype.destroy=function(){null!=this.div&&(mxEvent.release(this.div),null!=this.div.parentNode&&this.div.parentNode.removeChild(this.div),this.div=null)};
+function mxAutoSaveManager(a){this.changeHandler=mxUtils.bind(this,function(a,c){this.isEnabled()&&this.graphModelChanged(c.getProperty("edit").changes)});this.setGraph(a)}mxAutoSaveManager.prototype=new mxEventSource;mxAutoSaveManager.prototype.constructor=mxAutoSaveManager;mxAutoSaveManager.prototype.graph=null;mxAutoSaveManager.prototype.autoSaveDelay=10;mxAutoSaveManager.prototype.autoSaveThrottle=2;mxAutoSaveManager.prototype.autoSaveThreshold=5;mxAutoSaveManager.prototype.ignoredChanges=0;
+mxAutoSaveManager.prototype.lastSnapshot=0;mxAutoSaveManager.prototype.enabled=!0;mxAutoSaveManager.prototype.changeHandler=null;mxAutoSaveManager.prototype.isEnabled=function(){return this.enabled};mxAutoSaveManager.prototype.setEnabled=function(a){this.enabled=a};mxAutoSaveManager.prototype.setGraph=function(a){null!=this.graph&&this.graph.getModel().removeListener(this.changeHandler);this.graph=a;null!=this.graph&&this.graph.getModel().addListener(mxEvent.CHANGE,this.changeHandler)};
+mxAutoSaveManager.prototype.save=function(){};mxAutoSaveManager.prototype.graphModelChanged=function(a){a=((new Date).getTime()-this.lastSnapshot)/1E3;a>this.autoSaveDelay||this.ignoredChanges>=this.autoSaveThreshold&&a>this.autoSaveThrottle?(this.save(),this.reset()):this.ignoredChanges++};mxAutoSaveManager.prototype.reset=function(){this.lastSnapshot=(new Date).getTime();this.ignoredChanges=0};mxAutoSaveManager.prototype.destroy=function(){this.setGraph(null)};
+function mxAnimation(a){this.delay=null!=a?a:20}mxAnimation.prototype=new mxEventSource;mxAnimation.prototype.constructor=mxAnimation;mxAnimation.prototype.delay=null;mxAnimation.prototype.thread=null;mxAnimation.prototype.isRunning=function(){return null!=this.thread};mxAnimation.prototype.startAnimation=function(){null==this.thread&&(this.thread=window.setInterval(mxUtils.bind(this,this.updateAnimation),this.delay))};mxAnimation.prototype.updateAnimation=function(){this.fireEvent(new mxEventObject(mxEvent.EXECUTE))};
+mxAnimation.prototype.stopAnimation=function(){null!=this.thread&&(window.clearInterval(this.thread),this.thread=null,this.fireEvent(new mxEventObject(mxEvent.DONE)))};function mxMorphing(a,b,c,d){mxAnimation.call(this,d);this.graph=a;this.steps=null!=b?b:6;this.ease=null!=c?c:1.5}mxMorphing.prototype=new mxAnimation;mxMorphing.prototype.constructor=mxMorphing;mxMorphing.prototype.graph=null;mxMorphing.prototype.steps=null;mxMorphing.prototype.step=0;mxMorphing.prototype.ease=null;
+mxMorphing.prototype.cells=null;mxMorphing.prototype.updateAnimation=function(){mxAnimation.prototype.updateAnimation.apply(this,arguments);var a=new mxCellStatePreview(this.graph);if(null!=this.cells)for(var b=0;b<this.cells.length;b++)this.animateCell(this.cells[b],a,!1);else this.animateCell(this.graph.getModel().getRoot(),a,!0);this.show(a);(a.isEmpty()||this.step++>=this.steps)&&this.stopAnimation()};mxMorphing.prototype.show=function(a){a.show()};
+mxMorphing.prototype.animateCell=function(a,b,c){var d=this.graph.getView().getState(a),e=null;if(null!=d&&(e=this.getDelta(d),this.graph.getModel().isVertex(a)&&(0!=e.x||0!=e.y))){var f=this.graph.view.getTranslate(),g=this.graph.view.getScale();e.x+=f.x*g;e.y+=f.y*g;b.moveState(d,-e.x/this.ease,-e.y/this.ease)}if(c&&!this.stopRecursion(d,e))for(d=this.graph.getModel().getChildCount(a),e=0;e<d;e++)this.animateCell(this.graph.getModel().getChildAt(a,e),b,c)};
+mxMorphing.prototype.stopRecursion=function(a,b){return null!=b&&(0!=b.x||0!=b.y)};mxMorphing.prototype.getDelta=function(a){var b=this.getOriginForCell(a.cell),c=this.graph.getView().getTranslate(),d=this.graph.getView().getScale();return new mxPoint((b.x-(a.x/d-c.x))*d,(b.y-(a.y/d-c.y))*d)};
+mxMorphing.prototype.getOriginForCell=function(a){var b=null;if(null!=a){var c=this.graph.getModel().getParent(a);a=this.graph.getCellGeometry(a);b=this.getOriginForCell(c);null!=a&&(a.relative?(c=this.graph.getCellGeometry(c),null!=c&&(b.x+=a.x*c.width,b.y+=a.y*c.height)):(b.x+=a.x,b.y+=a.y))}null==b&&(b=this.graph.view.getTranslate(),b=new mxPoint(-b.x,-b.y));return b};function mxImageBundle(a){this.images=[];this.alt=null!=a?a:!1}mxImageBundle.prototype.images=null;
+mxImageBundle.prototype.images=null;mxImageBundle.prototype.putImage=function(a,b,c){this.images[a]={value:b,fallback:c}};mxImageBundle.prototype.getImage=function(a){var b=null;null!=a&&(a=this.images[a],null!=a&&(b=this.alt?a.fallback:a.value));return b};function mxImageExport(){}mxImageExport.prototype.includeOverlays=!1;
+mxImageExport.prototype.drawState=function(a,b){null!=a&&(this.visitStatesRecursive(a,b,mxUtils.bind(this,function(){this.drawCellState.apply(this,arguments)})),this.includeOverlays&&this.visitStatesRecursive(a,b,mxUtils.bind(this,function(){this.drawOverlays.apply(this,arguments)})))};
+mxImageExport.prototype.visitStatesRecursive=function(a,b,c){if(null!=a){c(a,b);for(var d=a.view.graph,e=d.model.getChildCount(a.cell),f=0;f<e;f++){var g=d.view.getState(d.model.getChildAt(a.cell,f));this.visitStatesRecursive(g,b,c)}}};mxImageExport.prototype.getLinkForCellState=function(a,b){return null};mxImageExport.prototype.drawCellState=function(a,b){var c=this.getLinkForCellState(a,b);null!=c&&b.setLink(c);this.drawShape(a,b);this.drawText(a,b);null!=c&&b.setLink(null)};
+mxImageExport.prototype.drawShape=function(a,b){a.shape instanceof mxShape&&a.shape.checkBounds()&&(b.save(),a.shape.paint(b),b.restore())};mxImageExport.prototype.drawText=function(a,b){null!=a.text&&a.text.checkBounds()&&(b.save(),a.text.paint(b),b.restore())};mxImageExport.prototype.drawOverlays=function(a,b){null!=a.overlays&&a.overlays.visit(function(a,d){d instanceof mxShape&&d.paint(b)})};function mxAbstractCanvas2D(){this.converter=this.createUrlConverter();this.reset()}
+mxAbstractCanvas2D.prototype.state=null;mxAbstractCanvas2D.prototype.states=null;mxAbstractCanvas2D.prototype.path=null;mxAbstractCanvas2D.prototype.rotateHtml=!0;mxAbstractCanvas2D.prototype.lastX=0;mxAbstractCanvas2D.prototype.lastY=0;mxAbstractCanvas2D.prototype.moveOp="M";mxAbstractCanvas2D.prototype.lineOp="L";mxAbstractCanvas2D.prototype.quadOp="Q";mxAbstractCanvas2D.prototype.curveOp="C";mxAbstractCanvas2D.prototype.closeOp="Z";mxAbstractCanvas2D.prototype.pointerEvents=!1;
+mxAbstractCanvas2D.prototype.createUrlConverter=function(){return new mxUrlConverter};mxAbstractCanvas2D.prototype.reset=function(){this.state=this.createState();this.states=[]};
+mxAbstractCanvas2D.prototype.createState=function(){return{dx:0,dy:0,scale:1,alpha:1,fillAlpha:1,strokeAlpha:1,fillColor:null,gradientFillAlpha:1,gradientColor:null,gradientAlpha:1,gradientDirection:null,strokeColor:null,strokeWidth:1,dashed:!1,dashPattern:"3 3",fixDash:!1,lineCap:"flat",lineJoin:"miter",miterLimit:10,fontColor:"#000000",fontBackgroundColor:null,fontBorderColor:null,fontSize:mxConstants.DEFAULT_FONTSIZE,fontFamily:mxConstants.DEFAULT_FONTFAMILY,fontStyle:0,shadow:!1,shadowColor:mxConstants.SHADOWCOLOR,
+shadowAlpha:mxConstants.SHADOW_OPACITY,shadowDx:mxConstants.SHADOW_OFFSET_X,shadowDy:mxConstants.SHADOW_OFFSET_Y,rotation:0,rotationCx:0,rotationCy:0}};mxAbstractCanvas2D.prototype.format=function(a){return Math.round(parseFloat(a))};
+mxAbstractCanvas2D.prototype.addOp=function(){if(null!=this.path&&(this.path.push(arguments[0]),2<arguments.length))for(var a=this.state,b=2;b<arguments.length;b+=2)this.lastX=arguments[b-1],this.lastY=arguments[b],this.path.push(this.format((this.lastX+a.dx)*a.scale)),this.path.push(this.format((this.lastY+a.dy)*a.scale))};mxAbstractCanvas2D.prototype.rotatePoint=function(a,b,c,d,e){c*=Math.PI/180;return mxUtils.getRotatedPoint(new mxPoint(a,b),Math.cos(c),Math.sin(c),new mxPoint(d,e))};
+mxAbstractCanvas2D.prototype.save=function(){this.states.push(this.state);this.state=mxUtils.clone(this.state)};mxAbstractCanvas2D.prototype.restore=function(){0<this.states.length&&(this.state=this.states.pop())};mxAbstractCanvas2D.prototype.setLink=function(a){};mxAbstractCanvas2D.prototype.scale=function(a){this.state.scale*=a;this.state.strokeWidth*=a};mxAbstractCanvas2D.prototype.translate=function(a,b){this.state.dx+=a;this.state.dy+=b};mxAbstractCanvas2D.prototype.rotate=function(a,b,c,d,e){};
+mxAbstractCanvas2D.prototype.setAlpha=function(a){this.state.alpha=a};mxAbstractCanvas2D.prototype.setFillAlpha=function(a){this.state.fillAlpha=a};mxAbstractCanvas2D.prototype.setStrokeAlpha=function(a){this.state.strokeAlpha=a};mxAbstractCanvas2D.prototype.setFillColor=function(a){a==mxConstants.NONE&&(a=null);this.state.fillColor=a;this.state.gradientColor=null};
+mxAbstractCanvas2D.prototype.setGradient=function(a,b,c,d,e,f,g,k,l){c=this.state;c.fillColor=a;c.gradientFillAlpha=null!=k?k:1;c.gradientColor=b;c.gradientAlpha=null!=l?l:1;c.gradientDirection=g};mxAbstractCanvas2D.prototype.setStrokeColor=function(a){a==mxConstants.NONE&&(a=null);this.state.strokeColor=a};mxAbstractCanvas2D.prototype.setStrokeWidth=function(a){this.state.strokeWidth=a};mxAbstractCanvas2D.prototype.setDashed=function(a,b){this.state.dashed=a;this.state.fixDash=b};
+mxAbstractCanvas2D.prototype.setDashPattern=function(a){this.state.dashPattern=a};mxAbstractCanvas2D.prototype.setLineCap=function(a){this.state.lineCap=a};mxAbstractCanvas2D.prototype.setLineJoin=function(a){this.state.lineJoin=a};mxAbstractCanvas2D.prototype.setMiterLimit=function(a){this.state.miterLimit=a};mxAbstractCanvas2D.prototype.setFontColor=function(a){a==mxConstants.NONE&&(a=null);this.state.fontColor=a};
+mxAbstractCanvas2D.prototype.setFontBackgroundColor=function(a){a==mxConstants.NONE&&(a=null);this.state.fontBackgroundColor=a};mxAbstractCanvas2D.prototype.setFontBorderColor=function(a){a==mxConstants.NONE&&(a=null);this.state.fontBorderColor=a};mxAbstractCanvas2D.prototype.setFontSize=function(a){this.state.fontSize=parseFloat(a)};mxAbstractCanvas2D.prototype.setFontFamily=function(a){this.state.fontFamily=a};
+mxAbstractCanvas2D.prototype.setFontStyle=function(a){null==a&&(a=0);this.state.fontStyle=a};mxAbstractCanvas2D.prototype.setShadow=function(a){this.state.shadow=a};mxAbstractCanvas2D.prototype.setShadowColor=function(a){a==mxConstants.NONE&&(a=null);this.state.shadowColor=a};mxAbstractCanvas2D.prototype.setShadowAlpha=function(a){this.state.shadowAlpha=a};mxAbstractCanvas2D.prototype.setShadowOffset=function(a,b){this.state.shadowDx=a;this.state.shadowDy=b};
+mxAbstractCanvas2D.prototype.begin=function(){this.lastY=this.lastX=0;this.path=[]};mxAbstractCanvas2D.prototype.moveTo=function(a,b){this.addOp(this.moveOp,a,b)};mxAbstractCanvas2D.prototype.lineTo=function(a,b){this.addOp(this.lineOp,a,b)};mxAbstractCanvas2D.prototype.quadTo=function(a,b,c,d){this.addOp(this.quadOp,a,b,c,d)};mxAbstractCanvas2D.prototype.curveTo=function(a,b,c,d,e,f){this.addOp(this.curveOp,a,b,c,d,e,f)};
+mxAbstractCanvas2D.prototype.arcTo=function(a,b,c,d,e,f,g){a=mxUtils.arcToCurves(this.lastX,this.lastY,a,b,c,d,e,f,g);if(null!=a)for(b=0;b<a.length;b+=6)this.curveTo(a[b],a[b+1],a[b+2],a[b+3],a[b+4],a[b+5])};mxAbstractCanvas2D.prototype.close=function(a,b,c,d,e,f){this.addOp(this.closeOp)};mxAbstractCanvas2D.prototype.end=function(){};function mxXmlCanvas2D(a){mxAbstractCanvas2D.call(this);this.root=a;this.writeDefaults()}mxUtils.extend(mxXmlCanvas2D,mxAbstractCanvas2D);
+mxXmlCanvas2D.prototype.textEnabled=!0;mxXmlCanvas2D.prototype.compressed=!0;
+mxXmlCanvas2D.prototype.writeDefaults=function(){var a;a=this.createElement("fontfamily");a.setAttribute("family",mxConstants.DEFAULT_FONTFAMILY);this.root.appendChild(a);a=this.createElement("fontsize");a.setAttribute("size",mxConstants.DEFAULT_FONTSIZE);this.root.appendChild(a);a=this.createElement("shadowcolor");a.setAttribute("color",mxConstants.SHADOWCOLOR);this.root.appendChild(a);a=this.createElement("shadowalpha");a.setAttribute("alpha",mxConstants.SHADOW_OPACITY);this.root.appendChild(a);
+a=this.createElement("shadowoffset");a.setAttribute("dx",mxConstants.SHADOW_OFFSET_X);a.setAttribute("dy",mxConstants.SHADOW_OFFSET_Y);this.root.appendChild(a)};mxXmlCanvas2D.prototype.format=function(a){return parseFloat(parseFloat(a).toFixed(2))};mxXmlCanvas2D.prototype.createElement=function(a){return this.root.ownerDocument.createElement(a)};mxXmlCanvas2D.prototype.save=function(){this.compressed&&mxAbstractCanvas2D.prototype.save.apply(this,arguments);this.root.appendChild(this.createElement("save"))};
+mxXmlCanvas2D.prototype.restore=function(){this.compressed&&mxAbstractCanvas2D.prototype.restore.apply(this,arguments);this.root.appendChild(this.createElement("restore"))};mxXmlCanvas2D.prototype.scale=function(a){var b=this.createElement("scale");b.setAttribute("scale",a);this.root.appendChild(b)};mxXmlCanvas2D.prototype.translate=function(a,b){var c=this.createElement("translate");c.setAttribute("dx",this.format(a));c.setAttribute("dy",this.format(b));this.root.appendChild(c)};
+mxXmlCanvas2D.prototype.rotate=function(a,b,c,d,e){var f=this.createElement("rotate");if(0!=a||b||c)f.setAttribute("theta",this.format(a)),f.setAttribute("flipH",b?"1":"0"),f.setAttribute("flipV",c?"1":"0"),f.setAttribute("cx",this.format(d)),f.setAttribute("cy",this.format(e)),this.root.appendChild(f)};
+mxXmlCanvas2D.prototype.setAlpha=function(a){if(this.compressed){if(this.state.alpha==a)return;mxAbstractCanvas2D.prototype.setAlpha.apply(this,arguments)}var b=this.createElement("alpha");b.setAttribute("alpha",this.format(a));this.root.appendChild(b)};mxXmlCanvas2D.prototype.setFillAlpha=function(a){if(this.compressed){if(this.state.fillAlpha==a)return;mxAbstractCanvas2D.prototype.setFillAlpha.apply(this,arguments)}var b=this.createElement("fillalpha");b.setAttribute("alpha",this.format(a));this.root.appendChild(b)};
+mxXmlCanvas2D.prototype.setStrokeAlpha=function(a){if(this.compressed){if(this.state.strokeAlpha==a)return;mxAbstractCanvas2D.prototype.setStrokeAlpha.apply(this,arguments)}var b=this.createElement("strokealpha");b.setAttribute("alpha",this.format(a));this.root.appendChild(b)};
+mxXmlCanvas2D.prototype.setFillColor=function(a){a==mxConstants.NONE&&(a=null);if(this.compressed){if(this.state.fillColor==a)return;mxAbstractCanvas2D.prototype.setFillColor.apply(this,arguments)}var b=this.createElement("fillcolor");b.setAttribute("color",null!=a?a:mxConstants.NONE);this.root.appendChild(b)};
+mxXmlCanvas2D.prototype.setGradient=function(a,b,c,d,e,f,g,k,l){if(null!=a&&null!=b){mxAbstractCanvas2D.prototype.setGradient.apply(this,arguments);var m=this.createElement("gradient");m.setAttribute("c1",a);m.setAttribute("c2",b);m.setAttribute("x",this.format(c));m.setAttribute("y",this.format(d));m.setAttribute("w",this.format(e));m.setAttribute("h",this.format(f));null!=g&&m.setAttribute("direction",g);null!=k&&m.setAttribute("alpha1",k);null!=l&&m.setAttribute("alpha2",l);this.root.appendChild(m)}};
+mxXmlCanvas2D.prototype.setStrokeColor=function(a){a==mxConstants.NONE&&(a=null);if(this.compressed){if(this.state.strokeColor==a)return;mxAbstractCanvas2D.prototype.setStrokeColor.apply(this,arguments)}var b=this.createElement("strokecolor");b.setAttribute("color",null!=a?a:mxConstants.NONE);this.root.appendChild(b)};
+mxXmlCanvas2D.prototype.setStrokeWidth=function(a){if(this.compressed){if(this.state.strokeWidth==a)return;mxAbstractCanvas2D.prototype.setStrokeWidth.apply(this,arguments)}var b=this.createElement("strokewidth");b.setAttribute("width",this.format(a));this.root.appendChild(b)};
+mxXmlCanvas2D.prototype.setDashed=function(a,b){if(this.compressed){if(this.state.dashed==a)return;mxAbstractCanvas2D.prototype.setDashed.apply(this,arguments)}var c=this.createElement("dashed");c.setAttribute("dashed",a?"1":"0");null!=b&&c.setAttribute("fixDash",b?"1":"0");this.root.appendChild(c)};
+mxXmlCanvas2D.prototype.setDashPattern=function(a){if(this.compressed){if(this.state.dashPattern==a)return;mxAbstractCanvas2D.prototype.setDashPattern.apply(this,arguments)}var b=this.createElement("dashpattern");b.setAttribute("pattern",a);this.root.appendChild(b)};mxXmlCanvas2D.prototype.setLineCap=function(a){if(this.compressed){if(this.state.lineCap==a)return;mxAbstractCanvas2D.prototype.setLineCap.apply(this,arguments)}var b=this.createElement("linecap");b.setAttribute("cap",a);this.root.appendChild(b)};
+mxXmlCanvas2D.prototype.setLineJoin=function(a){if(this.compressed){if(this.state.lineJoin==a)return;mxAbstractCanvas2D.prototype.setLineJoin.apply(this,arguments)}var b=this.createElement("linejoin");b.setAttribute("join",a);this.root.appendChild(b)};mxXmlCanvas2D.prototype.setMiterLimit=function(a){if(this.compressed){if(this.state.miterLimit==a)return;mxAbstractCanvas2D.prototype.setMiterLimit.apply(this,arguments)}var b=this.createElement("miterlimit");b.setAttribute("limit",a);this.root.appendChild(b)};
+mxXmlCanvas2D.prototype.setFontColor=function(a){if(this.textEnabled){a==mxConstants.NONE&&(a=null);if(this.compressed){if(this.state.fontColor==a)return;mxAbstractCanvas2D.prototype.setFontColor.apply(this,arguments)}var b=this.createElement("fontcolor");b.setAttribute("color",null!=a?a:mxConstants.NONE);this.root.appendChild(b)}};
+mxXmlCanvas2D.prototype.setFontBackgroundColor=function(a){if(this.textEnabled){a==mxConstants.NONE&&(a=null);if(this.compressed){if(this.state.fontBackgroundColor==a)return;mxAbstractCanvas2D.prototype.setFontBackgroundColor.apply(this,arguments)}var b=this.createElement("fontbackgroundcolor");b.setAttribute("color",null!=a?a:mxConstants.NONE);this.root.appendChild(b)}};
+mxXmlCanvas2D.prototype.setFontBorderColor=function(a){if(this.textEnabled){a==mxConstants.NONE&&(a=null);if(this.compressed){if(this.state.fontBorderColor==a)return;mxAbstractCanvas2D.prototype.setFontBorderColor.apply(this,arguments)}var b=this.createElement("fontbordercolor");b.setAttribute("color",null!=a?a:mxConstants.NONE);this.root.appendChild(b)}};
+mxXmlCanvas2D.prototype.setFontSize=function(a){if(this.textEnabled){if(this.compressed){if(this.state.fontSize==a)return;mxAbstractCanvas2D.prototype.setFontSize.apply(this,arguments)}var b=this.createElement("fontsize");b.setAttribute("size",a);this.root.appendChild(b)}};
+mxXmlCanvas2D.prototype.setFontFamily=function(a){if(this.textEnabled){if(this.compressed){if(this.state.fontFamily==a)return;mxAbstractCanvas2D.prototype.setFontFamily.apply(this,arguments)}var b=this.createElement("fontfamily");b.setAttribute("family",a);this.root.appendChild(b)}};
+mxXmlCanvas2D.prototype.setFontStyle=function(a){if(this.textEnabled){null==a&&(a=0);if(this.compressed){if(this.state.fontStyle==a)return;mxAbstractCanvas2D.prototype.setFontStyle.apply(this,arguments)}var b=this.createElement("fontstyle");b.setAttribute("style",a);this.root.appendChild(b)}};
+mxXmlCanvas2D.prototype.setShadow=function(a){if(this.compressed){if(this.state.shadow==a)return;mxAbstractCanvas2D.prototype.setShadow.apply(this,arguments)}var b=this.createElement("shadow");b.setAttribute("enabled",a?"1":"0");this.root.appendChild(b)};
+mxXmlCanvas2D.prototype.setShadowColor=function(a){if(this.compressed){a==mxConstants.NONE&&(a=null);if(this.state.shadowColor==a)return;mxAbstractCanvas2D.prototype.setShadowColor.apply(this,arguments)}var b=this.createElement("shadowcolor");b.setAttribute("color",null!=a?a:mxConstants.NONE);this.root.appendChild(b)};
+mxXmlCanvas2D.prototype.setShadowAlpha=function(a){if(this.compressed){if(this.state.shadowAlpha==a)return;mxAbstractCanvas2D.prototype.setShadowAlpha.apply(this,arguments)}var b=this.createElement("shadowalpha");b.setAttribute("alpha",a);this.root.appendChild(b)};
+mxXmlCanvas2D.prototype.setShadowOffset=function(a,b){if(this.compressed){if(this.state.shadowDx==a&&this.state.shadowDy==b)return;mxAbstractCanvas2D.prototype.setShadowOffset.apply(this,arguments)}var c=this.createElement("shadowoffset");c.setAttribute("dx",a);c.setAttribute("dy",b);this.root.appendChild(c)};
+mxXmlCanvas2D.prototype.rect=function(a,b,c,d){var e=this.createElement("rect");e.setAttribute("x",this.format(a));e.setAttribute("y",this.format(b));e.setAttribute("w",this.format(c));e.setAttribute("h",this.format(d));this.root.appendChild(e)};
+mxXmlCanvas2D.prototype.roundrect=function(a,b,c,d,e,f){var g=this.createElement("roundrect");g.setAttribute("x",this.format(a));g.setAttribute("y",this.format(b));g.setAttribute("w",this.format(c));g.setAttribute("h",this.format(d));g.setAttribute("dx",this.format(e));g.setAttribute("dy",this.format(f));this.root.appendChild(g)};
+mxXmlCanvas2D.prototype.ellipse=function(a,b,c,d){var e=this.createElement("ellipse");e.setAttribute("x",this.format(a));e.setAttribute("y",this.format(b));e.setAttribute("w",this.format(c));e.setAttribute("h",this.format(d));this.root.appendChild(e)};
+mxXmlCanvas2D.prototype.image=function(a,b,c,d,e,f,g,k){e=this.converter.convert(e);var l=this.createElement("image");l.setAttribute("x",this.format(a));l.setAttribute("y",this.format(b));l.setAttribute("w",this.format(c));l.setAttribute("h",this.format(d));l.setAttribute("src",e);l.setAttribute("aspect",f?"1":"0");l.setAttribute("flipH",g?"1":"0");l.setAttribute("flipV",k?"1":"0");this.root.appendChild(l)};
+mxXmlCanvas2D.prototype.begin=function(){this.root.appendChild(this.createElement("begin"));this.lastY=this.lastX=0};mxXmlCanvas2D.prototype.moveTo=function(a,b){var c=this.createElement("move");c.setAttribute("x",this.format(a));c.setAttribute("y",this.format(b));this.root.appendChild(c);this.lastX=a;this.lastY=b};
+mxXmlCanvas2D.prototype.lineTo=function(a,b){var c=this.createElement("line");c.setAttribute("x",this.format(a));c.setAttribute("y",this.format(b));this.root.appendChild(c);this.lastX=a;this.lastY=b};mxXmlCanvas2D.prototype.quadTo=function(a,b,c,d){var e=this.createElement("quad");e.setAttribute("x1",this.format(a));e.setAttribute("y1",this.format(b));e.setAttribute("x2",this.format(c));e.setAttribute("y2",this.format(d));this.root.appendChild(e);this.lastX=c;this.lastY=d};
+mxXmlCanvas2D.prototype.curveTo=function(a,b,c,d,e,f){var g=this.createElement("curve");g.setAttribute("x1",this.format(a));g.setAttribute("y1",this.format(b));g.setAttribute("x2",this.format(c));g.setAttribute("y2",this.format(d));g.setAttribute("x3",this.format(e));g.setAttribute("y3",this.format(f));this.root.appendChild(g);this.lastX=e;this.lastY=f};mxXmlCanvas2D.prototype.close=function(){this.root.appendChild(this.createElement("close"))};
+mxXmlCanvas2D.prototype.text=function(a,b,c,d,e,f,g,k,l,m,n,p,q){if(this.textEnabled&&null!=e){mxUtils.isNode(e)&&(e=mxUtils.getOuterHtml(e));var r=this.createElement("text");r.setAttribute("x",this.format(a));r.setAttribute("y",this.format(b));r.setAttribute("w",this.format(c));r.setAttribute("h",this.format(d));r.setAttribute("str",e);null!=f&&r.setAttribute("align",f);null!=g&&r.setAttribute("valign",g);r.setAttribute("wrap",k?"1":"0");null==l&&(l="");r.setAttribute("format",l);null!=m&&r.setAttribute("overflow",
+m);null!=n&&r.setAttribute("clip",n?"1":"0");null!=p&&r.setAttribute("rotation",p);null!=q&&r.setAttribute("dir",q);this.root.appendChild(r)}};mxXmlCanvas2D.prototype.stroke=function(){this.root.appendChild(this.createElement("stroke"))};mxXmlCanvas2D.prototype.fill=function(){this.root.appendChild(this.createElement("fill"))};mxXmlCanvas2D.prototype.fillAndStroke=function(){this.root.appendChild(this.createElement("fillstroke"))};
+function mxSvgCanvas2D(a,b){mxAbstractCanvas2D.call(this);this.root=a;this.gradients=[];this.defs=null;this.styleEnabled=null!=b?b:!1;var c=null;if(a.ownerDocument!=document)for(c=a;null!=c&&"svg"!=c.nodeName;)c=c.parentNode;null!=c&&(0<c.getElementsByTagName("defs").length&&(this.defs=c.getElementsByTagName("defs")[0]),null==this.defs&&(this.defs=this.createElement("defs"),null!=c.firstChild?c.insertBefore(this.defs,c.firstChild):c.appendChild(this.defs)),this.styleEnabled&&this.defs.appendChild(this.createStyle()))}
+mxUtils.extend(mxSvgCanvas2D,mxAbstractCanvas2D);(function(){mxSvgCanvas2D.prototype.useDomParser=!mxClient.IS_IE&&"function"===typeof DOMParser&&"function"===typeof XMLSerializer;if(mxSvgCanvas2D.prototype.useDomParser)try{var a=(new DOMParser).parseFromString("test text","text/html");mxSvgCanvas2D.prototype.useDomParser=null!=a}catch(b){mxSvgCanvas2D.prototype.useDomParser=!1}})();mxSvgCanvas2D.prototype.node=null;mxSvgCanvas2D.prototype.matchHtmlAlignment=!0;
+mxSvgCanvas2D.prototype.textEnabled=!0;mxSvgCanvas2D.prototype.foEnabled=!0;mxSvgCanvas2D.prototype.foAltText="[Object]";mxSvgCanvas2D.prototype.foOffset=0;mxSvgCanvas2D.prototype.textOffset=0;mxSvgCanvas2D.prototype.imageOffset=0;mxSvgCanvas2D.prototype.strokeTolerance=0;mxSvgCanvas2D.prototype.minStrokeWidth=1;mxSvgCanvas2D.prototype.refCount=0;mxSvgCanvas2D.prototype.blockImagePointerEvents=!1;mxSvgCanvas2D.prototype.lineHeightCorrection=1;mxSvgCanvas2D.prototype.pointerEventsValue="all";
+mxSvgCanvas2D.prototype.fontMetricsPadding=10;mxSvgCanvas2D.prototype.cacheOffsetSize=!0;mxSvgCanvas2D.prototype.format=function(a){return parseFloat(parseFloat(a).toFixed(2))};mxSvgCanvas2D.prototype.getBaseUrl=function(){var a=window.location.href,b=a.lastIndexOf("#");0<b&&(a=a.substring(0,b));return a};mxSvgCanvas2D.prototype.reset=function(){mxAbstractCanvas2D.prototype.reset.apply(this,arguments);this.gradients=[]};
+mxSvgCanvas2D.prototype.createStyle=function(a){a=this.createElement("style");a.setAttribute("type","text/css");mxUtils.write(a,"svg{font-family:"+mxConstants.DEFAULT_FONTFAMILY+";font-size:"+mxConstants.DEFAULT_FONTSIZE+";fill:none;stroke-miterlimit:10}");return a};
+mxSvgCanvas2D.prototype.createElement=function(a,b){if(null!=this.root.ownerDocument.createElementNS)return this.root.ownerDocument.createElementNS(b||mxConstants.NS_SVG,a);var c=this.root.ownerDocument.createElement(a);null!=b&&c.setAttribute("xmlns",b);return c};
+mxSvgCanvas2D.prototype.createAlternateContent=function(a,b,c,d,e,f,g,k,l,m,n,p,q){return null!=this.foAltText?(a=this.state,b=this.createElement("text"),b.setAttribute("x",Math.round(d/2)),b.setAttribute("y",Math.round((e+a.fontSize)/2)),b.setAttribute("fill",a.fontColor||"black"),b.setAttribute("text-anchor","middle"),b.setAttribute("font-size",a.fontSize+"px"),b.setAttribute("font-family",a.fontFamily),(a.fontStyle&mxConstants.FONT_BOLD)==mxConstants.FONT_BOLD&&b.setAttribute("font-weight","bold"),
+(a.fontStyle&mxConstants.FONT_ITALIC)==mxConstants.FONT_ITALIC&&b.setAttribute("font-style","italic"),(a.fontStyle&mxConstants.FONT_UNDERLINE)==mxConstants.FONT_UNDERLINE&&b.setAttribute("text-decoration","underline"),mxUtils.write(b,this.foAltText),b):null};
+mxSvgCanvas2D.prototype.createGradientId=function(a,b,c,d,e){"#"==a.charAt(0)&&(a=a.substring(1));"#"==b.charAt(0)&&(b=b.substring(1));a=a.toLowerCase()+"-"+c;b=b.toLowerCase()+"-"+d;c=null;null==e||e==mxConstants.DIRECTION_SOUTH?c="s":e==mxConstants.DIRECTION_EAST?c="e":(d=a,a=b,b=d,e==mxConstants.DIRECTION_NORTH?c="s":e==mxConstants.DIRECTION_WEST&&(c="e"));return"mx-gradient-"+a+"-"+b+"-"+c};
+mxSvgCanvas2D.prototype.getSvgGradient=function(a,b,c,d,e){var f=this.createGradientId(a,b,c,d,e),g=this.gradients[f];if(null==g){var k=this.root.ownerSVGElement,l=0,m=f+"-"+l;if(null!=k)for(g=k.ownerDocument.getElementById(m);null!=g&&g.ownerSVGElement!=k;)m=f+"-"+l++,g=k.ownerDocument.getElementById(m);else m="id"+ ++this.refCount;null==g&&(g=this.createSvgGradient(a,b,c,d,e),g.setAttribute("id",m),null!=this.defs?this.defs.appendChild(g):k.appendChild(g));this.gradients[f]=g}return g.getAttribute("id")};
+mxSvgCanvas2D.prototype.createSvgGradient=function(a,b,c,d,e){var f=this.createElement("linearGradient");f.setAttribute("x1","0%");f.setAttribute("y1","0%");f.setAttribute("x2","0%");f.setAttribute("y2","0%");null==e||e==mxConstants.DIRECTION_SOUTH?f.setAttribute("y2","100%"):e==mxConstants.DIRECTION_EAST?f.setAttribute("x2","100%"):e==mxConstants.DIRECTION_NORTH?f.setAttribute("y1","100%"):e==mxConstants.DIRECTION_WEST&&f.setAttribute("x1","100%");c=1>c?";stop-opacity:"+c:"";e=this.createElement("stop");
+e.setAttribute("offset","0%");e.setAttribute("style","stop-color:"+a+c);f.appendChild(e);c=1>d?";stop-opacity:"+d:"";e=this.createElement("stop");e.setAttribute("offset","100%");e.setAttribute("style","stop-color:"+b+c);f.appendChild(e);return f};
+mxSvgCanvas2D.prototype.addNode=function(a,b){var c=this.node,d=this.state;if(null!=c){if("path"==c.nodeName)if(null!=this.path&&0<this.path.length)c.setAttribute("d",this.path.join(" "));else return;a&&null!=d.fillColor?this.updateFill():this.styleEnabled||("ellipse"==c.nodeName&&mxClient.IS_FF?c.setAttribute("fill","transparent"):c.setAttribute("fill","none"),a=!1);b&&null!=d.strokeColor?this.updateStroke():this.styleEnabled||c.setAttribute("stroke","none");null!=d.transform&&0<d.transform.length&&
+c.setAttribute("transform",d.transform);d.shadow&&this.root.appendChild(this.createShadow(c));0<this.strokeTolerance&&!a&&this.root.appendChild(this.createTolerance(c));!this.pointerEvents||"path"==c.nodeName&&this.path[this.path.length-1]!=this.closeOp?this.pointerEvents||null!=this.originalRoot||c.setAttribute("pointer-events","none"):c.setAttribute("pointer-events",this.pointerEventsValue);("rect"!=c.nodeName&&"path"!=c.nodeName&&"ellipse"!=c.nodeName||"none"!=c.getAttribute("fill")&&"transparent"!=
+c.getAttribute("fill")||"none"!=c.getAttribute("stroke")||"none"!=c.getAttribute("pointer-events"))&&this.root.appendChild(c);this.node=null}};
+mxSvgCanvas2D.prototype.updateFill=function(){var a=this.state;(1>a.alpha||1>a.fillAlpha)&&this.node.setAttribute("fill-opacity",a.alpha*a.fillAlpha);if(null!=a.fillColor)if(null!=a.gradientColor)if(a=this.getSvgGradient(a.fillColor,a.gradientColor,a.gradientFillAlpha,a.gradientAlpha,a.gradientDirection),mxClient.IS_CHROME_APP||mxClient.IS_IE||mxClient.IS_IE11||mxClient.IS_EDGE||this.root.ownerDocument!=document)this.node.setAttribute("fill","url(#"+a+")");else{var b=this.getBaseUrl().replace(/([\(\)])/g,
+"\\$1");this.node.setAttribute("fill","url("+b+"#"+a+")")}else this.node.setAttribute("fill",a.fillColor.toLowerCase())};mxSvgCanvas2D.prototype.getCurrentStrokeWidth=function(){return Math.max(this.minStrokeWidth,Math.max(.01,this.format(this.state.strokeWidth*this.state.scale)))};
+mxSvgCanvas2D.prototype.updateStroke=function(){var a=this.state;this.node.setAttribute("stroke",a.strokeColor.toLowerCase());(1>a.alpha||1>a.strokeAlpha)&&this.node.setAttribute("stroke-opacity",a.alpha*a.strokeAlpha);var b=this.getCurrentStrokeWidth();1!=b&&this.node.setAttribute("stroke-width",b);"path"==this.node.nodeName&&this.updateStrokeAttributes();a.dashed&&this.node.setAttribute("stroke-dasharray",this.createDashPattern((a.fixDash?1:a.strokeWidth)*a.scale))};
+mxSvgCanvas2D.prototype.updateStrokeAttributes=function(){var a=this.state;null!=a.lineJoin&&"miter"!=a.lineJoin&&this.node.setAttribute("stroke-linejoin",a.lineJoin);if(null!=a.lineCap){var b=a.lineCap;"flat"==b&&(b="butt");"butt"!=b&&this.node.setAttribute("stroke-linecap",b)}null==a.miterLimit||this.styleEnabled&&10==a.miterLimit||this.node.setAttribute("stroke-miterlimit",a.miterLimit)};
+mxSvgCanvas2D.prototype.createDashPattern=function(a){var b=[];if("string"===typeof this.state.dashPattern){var c=this.state.dashPattern.split(" ");if(0<c.length)for(var d=0;d<c.length;d++)b[d]=Number(c[d])*a}return b.join(" ")};
+mxSvgCanvas2D.prototype.createTolerance=function(a){a=a.cloneNode(!0);var b=parseFloat(a.getAttribute("stroke-width")||1)+this.strokeTolerance;a.setAttribute("pointer-events","stroke");a.setAttribute("visibility","hidden");a.removeAttribute("stroke-dasharray");a.setAttribute("stroke-width",b);a.setAttribute("fill","none");a.setAttribute("stroke",mxClient.IS_OT?"none":"white");return a};
+mxSvgCanvas2D.prototype.createShadow=function(a){a=a.cloneNode(!0);var b=this.state;"none"==a.getAttribute("fill")||mxClient.IS_FF&&"transparent"==a.getAttribute("fill")||a.setAttribute("fill",b.shadowColor);"none"!=a.getAttribute("stroke")&&a.setAttribute("stroke",b.shadowColor);a.setAttribute("transform","translate("+this.format(b.shadowDx*b.scale)+","+this.format(b.shadowDy*b.scale)+")"+(b.transform||""));a.setAttribute("opacity",b.shadowAlpha);return a};
+mxSvgCanvas2D.prototype.setLink=function(a){if(null==a)this.root=this.originalRoot;else{this.originalRoot=this.root;var b=this.createElement("a");null==b.setAttributeNS||this.root.ownerDocument!=document&&null==document.documentMode?b.setAttribute("xlink:href",a):b.setAttributeNS(mxConstants.NS_XLINK,"xlink:href",a);this.root.appendChild(b);this.root=b}};
+mxSvgCanvas2D.prototype.rotate=function(a,b,c,d,e){if(0!=a||b||c){var f=this.state;d+=f.dx;e+=f.dy;d*=f.scale;e*=f.scale;f.transform=f.transform||"";if(b&&c)a+=180;else if(b!=c){var g=b?d:0,k=b?-1:1,l=c?e:0,m=c?-1:1;f.transform+="translate("+this.format(g)+","+this.format(l)+")scale("+this.format(k)+","+this.format(m)+")translate("+this.format(-g)+","+this.format(-l)+")"}if(b?!c:c)a*=-1;0!=a&&(f.transform+="rotate("+this.format(a)+","+this.format(d)+","+this.format(e)+")");f.rotation+=a;f.rotationCx=
+d;f.rotationCy=e}};mxSvgCanvas2D.prototype.begin=function(){mxAbstractCanvas2D.prototype.begin.apply(this,arguments);this.node=this.createElement("path")};mxSvgCanvas2D.prototype.rect=function(a,b,c,d){var e=this.state,f=this.createElement("rect");f.setAttribute("x",this.format((a+e.dx)*e.scale));f.setAttribute("y",this.format((b+e.dy)*e.scale));f.setAttribute("width",this.format(c*e.scale));f.setAttribute("height",this.format(d*e.scale));this.node=f};
+mxSvgCanvas2D.prototype.roundrect=function(a,b,c,d,e,f){this.rect(a,b,c,d);0<e&&this.node.setAttribute("rx",this.format(e*this.state.scale));0<f&&this.node.setAttribute("ry",this.format(f*this.state.scale))};mxSvgCanvas2D.prototype.ellipse=function(a,b,c,d){var e=this.state,f=this.createElement("ellipse");f.setAttribute("cx",Math.round((a+c/2+e.dx)*e.scale));f.setAttribute("cy",Math.round((b+d/2+e.dy)*e.scale));f.setAttribute("rx",c/2*e.scale);f.setAttribute("ry",d/2*e.scale);this.node=f};
+mxSvgCanvas2D.prototype.image=function(a,b,c,d,e,f,g,k){e=this.converter.convert(e);f=null!=f?f:!0;g=null!=g?g:!1;k=null!=k?k:!1;var l=this.state;a+=l.dx;b+=l.dy;var m=this.createElement("image");m.setAttribute("x",this.format(a*l.scale)+this.imageOffset);m.setAttribute("y",this.format(b*l.scale)+this.imageOffset);m.setAttribute("width",this.format(c*l.scale));m.setAttribute("height",this.format(d*l.scale));null==m.setAttributeNS?m.setAttribute("xlink:href",e):m.setAttributeNS(mxConstants.NS_XLINK,
+"xlink:href",e);f||m.setAttribute("preserveAspectRatio","none");(1>l.alpha||1>l.fillAlpha)&&m.setAttribute("opacity",l.alpha*l.fillAlpha);e=this.state.transform||"";if(g||k){var n=f=1,p=0,q=0;g&&(f=-1,p=-c-2*a);k&&(n=-1,q=-d-2*b);e+="scale("+f+","+n+")translate("+p*l.scale+","+q*l.scale+")"}0<e.length&&m.setAttribute("transform",e);this.pointerEvents||m.setAttribute("pointer-events","none");this.root.appendChild(m);this.blockImagePointerEvents&&(m.setAttribute("style","pointer-events:none"),m=this.createElement("rect"),
+m.setAttribute("visibility","hidden"),m.setAttribute("pointer-events","fill"),m.setAttribute("x",this.format(a*l.scale)),m.setAttribute("y",this.format(b*l.scale)),m.setAttribute("width",this.format(c*l.scale)),m.setAttribute("height",this.format(d*l.scale)),this.root.appendChild(m))};
+mxSvgCanvas2D.prototype.convertHtml=function(a){if(this.useDomParser){var b=(new DOMParser).parseFromString(a,"text/html");null!=b&&(a=(new XMLSerializer).serializeToString(b.body),"<body"==a.substring(0,5)&&(a=a.substring(a.indexOf(">",5)+1)),"</body>"==a.substring(a.length-7,a.length)&&(a=a.substring(0,a.length-7)))}else{if(null!=document.implementation&&null!=document.implementation.createDocument){var b=document.implementation.createDocument("http://www.w3.org/1999/xhtml","html",null),c=b.createElement("body");
+b.documentElement.appendChild(c);var d=document.createElement("div");d.innerHTML=a;for(a=d.firstChild;null!=a;)d=a.nextSibling,c.appendChild(b.adoptNode(a)),a=d;return c.innerHTML}b=document.createElement("textarea");b.innerHTML=a.replace(/&amp;/g,"&amp;amp;").replace(/&#60;/g,"&amp;lt;").replace(/&#62;/g,"&amp;gt;").replace(/&lt;/g,"&amp;lt;").replace(/&gt;/g,"&amp;gt;").replace(/</g,"&lt;").replace(/>/g,"&gt;");a=b.value.replace(/&/g,"&amp;").replace(/&amp;lt;/g,"&lt;").replace(/&amp;gt;/g,"&gt;").replace(/&amp;amp;/g,
+"&amp;").replace(/<br>/g,"<br />").replace(/<hr>/g,"<hr />").replace(/(<img[^>]+)>/gm,"$1 />")}return a};
+mxSvgCanvas2D.prototype.createDiv=function(a,b,c,d,e){c=this.state;d="display:inline-block;font-size:"+c.fontSize+"px;font-family:"+c.fontFamily+";color:"+c.fontColor+";line-height:"+(mxConstants.ABSOLUTE_LINE_HEIGHT?c.fontSize*mxConstants.LINE_HEIGHT+"px":mxConstants.LINE_HEIGHT*this.lineHeightCorrection)+";"+d;(c.fontStyle&mxConstants.FONT_BOLD)==mxConstants.FONT_BOLD&&(d+="font-weight:bold;");(c.fontStyle&mxConstants.FONT_ITALIC)==mxConstants.FONT_ITALIC&&(d+="font-style:italic;");(c.fontStyle&
+mxConstants.FONT_UNDERLINE)==mxConstants.FONT_UNDERLINE&&(d+="text-decoration:underline;");b==mxConstants.ALIGN_CENTER?d+="text-align:center;":b==mxConstants.ALIGN_RIGHT&&(d+="text-align:right;");b="";null!=c.fontBackgroundColor&&(b+="background-color:"+c.fontBackgroundColor+";");null!=c.fontBorderColor&&(b+="border:1px solid "+c.fontBorderColor+";");mxUtils.isNode(a)||(a=this.convertHtml(a),"fill"!=e&&"width"!=e?a='<div xmlns="http://www.w3.org/1999/xhtml" style="display:inline-block;text-align:inherit;text-decoration:inherit;'+
+b+'">'+a+"</div>":d+=b);if(!mxClient.IS_IE&&document.createElementNS)return e=document.createElementNS("http://www.w3.org/1999/xhtml","div"),e.setAttribute("style",d),mxUtils.isNode(a)?this.root.ownerDocument!=document?e.appendChild(a.cloneNode(!0)):e.appendChild(a):e.innerHTML=a,e;mxUtils.isNode(a)&&this.root.ownerDocument!=document&&(a=a.outerHTML);return mxUtils.parseXml('<div xmlns="http://www.w3.org/1999/xhtml" style="'+d+'">'+a+"</div>").documentElement};
+mxSvgCanvas2D.prototype.invalidateCachedOffsetSize=function(a){delete a.firstChild.mxCachedOffsetWidth;delete a.firstChild.mxCachedFinalOffsetWidth;delete a.firstChild.mxCachedFinalOffsetHeight};
+mxSvgCanvas2D.prototype.updateText=function(a,b,c,d,e,f,g,k,l,m,n){if(null!=n&&null!=n.firstChild&&null!=n.firstChild.firstChild&&null!=n.firstChild.firstChild.firstChild){n=n.firstChild;var p=n.firstChild,q=p.firstChild;m=null!=m?m:0;var r=this.state;a+=r.dx;b+=r.dy;l?(q.style.maxHeight=Math.round(d)+"px",q.style.maxWidth=Math.round(c)+"px"):"fill"==k?(q.style.width=Math.round(c+1)+"px",q.style.height=Math.round(d+1)+"px"):"width"==k&&(q.style.width=Math.round(c+1)+"px",0<d&&(q.style.maxHeight=Math.round(d)+
+"px"));g&&0<c&&(q.style.width=Math.round(c+1)+"px");var t,u=q;null!=u.firstChild&&"DIV"==u.firstChild.nodeName&&(u=u.firstChild);var x=null!=n.mxCachedOffsetWidth?n.mxCachedOffsetWidth:u.offsetWidth;t=x+0;g&&"fill"!=k&&(l&&(t=Math.min(t,c)),q.style.width=Math.round(t+1)+"px");t=null!=n.mxCachedFinalOffsetWidth?n.mxCachedFinalOffsetWidth:u.offsetWidth;g=null!=n.mxCachedFinalOffsetHeight?n.mxCachedFinalOffsetHeight:u.offsetHeight;this.cacheOffsetSize&&(n.mxCachedOffsetWidth=x,n.mxCachedFinalOffsetWidth=
+t,n.mxCachedFinalOffsetHeight=g);t+=0;g-=2;l&&(g=Math.min(g,d),t=Math.min(t,c));"width"==k?d=g:"fill"!=k&&(c=t,d=g);g=l=0;e==mxConstants.ALIGN_CENTER?l-=c/2:e==mxConstants.ALIGN_RIGHT&&(l-=c);a+=l;f==mxConstants.ALIGN_MIDDLE?g-=d/2:f==mxConstants.ALIGN_BOTTOM&&(g-=d);"fill"!=k&&mxClient.IS_FF&&mxClient.IS_WIN&&(g-=2);b+=g;e=1!=r.scale?"scale("+r.scale+")":"";0!=r.rotation&&this.rotateHtml?(e+="rotate("+r.rotation+","+c/2+","+d/2+")",b=this.rotatePoint((a+c/2)*r.scale,(b+d/2)*r.scale,r.rotation,r.rotationCx,
+r.rotationCy),a=b.x-c*r.scale/2,b=b.y-d*r.scale/2):(a*=r.scale,b*=r.scale);0!=m&&(e+="rotate("+m+","+-l+","+-g+")");n.setAttribute("transform","translate("+Math.round(a)+","+Math.round(b)+")"+e);p.setAttribute("width",Math.round(Math.max(1,c)));p.setAttribute("height",Math.round(Math.max(1,d)))}};
+mxSvgCanvas2D.prototype.text=function(a,b,c,d,e,f,g,k,l,m,n,p,q){if(this.textEnabled&&null!=e){p=null!=p?p:0;var r=this.state;a+=r.dx;b+=r.dy;if(this.foEnabled&&"html"==l){var t="vertical-align:top;";n?t+="overflow:hidden;max-height:"+Math.round(d)+"px;max-width:"+Math.round(c)+"px;":"fill"==m?t+="width:"+Math.round(c+1)+"px;height:"+Math.round(d+1)+"px;overflow:hidden;":"width"==m&&(t+="width:"+Math.round(c+1)+"px;",0<d&&(t+="max-height:"+Math.round(d)+"px;overflow:hidden;"));var t=k&&0<c?t+("width:"+
+Math.round(c+1)+"px;white-space:normal;word-wrap:"+mxConstants.WORD_WRAP+";"):t+"white-space:nowrap;",u=this.createElement("g");1>r.alpha&&u.setAttribute("opacity",r.alpha);var x=this.createElement("foreignObject");x.setAttribute("style","overflow:visible;");x.setAttribute("pointer-events","all");t=this.createDiv(e,f,g,t,m);if(null!=t){null!=q&&t.setAttribute("dir",q);u.appendChild(x);this.root.appendChild(u);var y,A;q=y=2;if(!mxClient.IS_IE||9!=document.documentMode&&mxClient.IS_SVG){this.root.ownerDocument!=
+document?(t.style.visibility="hidden",document.body.appendChild(t)):x.appendChild(t);var z=t;null!=z.firstChild&&"DIV"==z.firstChild.nodeName&&(z=z.firstChild,k&&"break-word"==t.style.wordWrap&&(z.style.width="100%"));v=z.offsetWidth;0==v&&t.parentNode==x&&(t.style.visibility="hidden",document.body.appendChild(t),v=z.offsetWidth);this.cacheOffsetSize&&(u.mxCachedOffsetWidth=v);!n&&k&&0<c&&this.root.ownerDocument!=document&&"fill"!=m&&"width"!=m&&(B=t.style.whiteSpace,t.style.whiteSpace="nowrap",v<
+z.offsetWidth&&(t.style.whiteSpace=B));y=v+y-1;k&&"fill"!=m&&"width"!=m&&(n&&(y=Math.min(y,c)),t.style.width=y+"px");y=z.offsetWidth;A=z.offsetHeight;this.cacheOffsetSize&&(u.mxCachedFinalOffsetWidth=y,u.mxCachedFinalOffsetHeight=A);A-=q;t.parentNode!=x&&(x.appendChild(t),t.style.visibility="")}else{z=document.createElement("div");z.style.cssText=t.getAttribute("style");z.style.display=mxClient.IS_QUIRKS?"inline":"inline-block";z.style.position="absolute";z.style.visibility="hidden";A=document.createElement("div");
+A.style.display=mxClient.IS_QUIRKS?"inline":"inline-block";A.style.wordWrap=mxConstants.WORD_WRAP;A.innerHTML=mxUtils.isNode(e)?e.outerHTML:e;z.appendChild(A);document.body.appendChild(z);8!=document.documentMode&&9!=document.documentMode&&null!=r.fontBorderColor&&(y+=2,q+=2);if(k&&0<c){var v=A.offsetWidth;padDx=0;if(!n&&k&&0<c&&this.root.ownerDocument!=document&&"fill"!=m){var B=z.style.whiteSpace;A.style.whiteSpace="nowrap";v<A.offsetWidth&&(z.style.whiteSpace=B)}n&&(v=Math.min(v,c));z.style.width=
+v+"px";y=A.offsetWidth+y+padDx;A=A.offsetHeight+q;z.style.display="inline-block";z.style.position="";z.style.visibility="";z.style.width=y+"px";t.setAttribute("style",z.style.cssText)}else y=A.offsetWidth+y,A=A.offsetHeight+q;z.parentNode.removeChild(z);x.appendChild(t)}n&&(A=Math.min(A,d),y=Math.min(y,c));"width"==m?d=A:"fill"!=m&&(c=y,d=A);1>r.alpha&&u.setAttribute("opacity",r.alpha);q=t=0;f==mxConstants.ALIGN_CENTER?t-=c/2:f==mxConstants.ALIGN_RIGHT&&(t-=c);a+=t;g==mxConstants.ALIGN_MIDDLE?q-=
+d/2:g==mxConstants.ALIGN_BOTTOM&&(q-=d);"fill"!=m&&mxClient.IS_FF&&mxClient.IS_WIN&&(q-=2);b+=q;z=1!=r.scale?"scale("+r.scale+")":"";0!=r.rotation&&this.rotateHtml?(z+="rotate("+r.rotation+","+c/2+","+d/2+")",b=this.rotatePoint((a+c/2)*r.scale,(b+d/2)*r.scale,r.rotation,r.rotationCx,r.rotationCy),a=b.x-c*r.scale/2,b=b.y-d*r.scale/2):(a*=r.scale,b*=r.scale);0!=p&&(z+="rotate("+p+","+-t+","+-q+")");u.setAttribute("transform","translate("+(Math.round(a)+this.foOffset)+","+(Math.round(b)+this.foOffset)+
+")"+z);x.setAttribute("width",Math.round(Math.max(1,c)));x.setAttribute("height",Math.round(Math.max(1,d)));this.root.ownerDocument!=document&&(a=this.createAlternateContent(x,a,b,c,d,e,f,g,k,l,m,n,p),null!=a&&(x.setAttribute("requiredFeatures","http://www.w3.org/TR/SVG11/feature#Extensibility"),c=this.createElement("switch"),c.appendChild(x),c.appendChild(a),u.appendChild(c)))}}else this.plainText(a,b,c,d,e,f,g,k,m,n,p,q)}};
+mxSvgCanvas2D.prototype.createClip=function(a,b,c,d){a=Math.round(a);b=Math.round(b);c=Math.round(c);d=Math.round(d);for(var e="mx-clip-"+a+"-"+b+"-"+c+"-"+d,f=0,g=e+"-"+f;null!=document.getElementById(g);)g=e+"-"+ ++f;clip=this.createElement("clipPath");clip.setAttribute("id",g);e=this.createElement("rect");e.setAttribute("x",a);e.setAttribute("y",b);e.setAttribute("width",c);e.setAttribute("height",d);clip.appendChild(e);return clip};
+mxSvgCanvas2D.prototype.plainText=function(a,b,c,d,e,f,g,k,l,m,n,p){n=null!=n?n:0;k=this.state;var q=k.fontSize,r=this.createElement("g"),t=k.transform||"";this.updateFont(r);0!=n&&(t+="rotate("+n+","+this.format(a*k.scale)+","+this.format(b*k.scale)+")");null!=p&&r.setAttribute("direction",p);m&&0<c&&0<d&&(p=a,n=b,f==mxConstants.ALIGN_CENTER?p-=c/2:f==mxConstants.ALIGN_RIGHT&&(p-=c),"fill"!=l&&(g==mxConstants.ALIGN_MIDDLE?n-=d/2:g==mxConstants.ALIGN_BOTTOM&&(n-=d)),n=this.createClip(p*k.scale-2,
+n*k.scale-2,c*k.scale+4,d*k.scale+4),null!=this.defs?this.defs.appendChild(n):this.root.appendChild(n),mxClient.IS_CHROME_APP||mxClient.IS_IE||mxClient.IS_IE11||mxClient.IS_EDGE||this.root.ownerDocument!=document?r.setAttribute("clip-path","url(#"+n.getAttribute("id")+")"):(p=this.getBaseUrl().replace(/([\(\)])/g,"\\$1"),r.setAttribute("clip-path","url("+p+"#"+n.getAttribute("id")+")")));n=f==mxConstants.ALIGN_RIGHT?"end":f==mxConstants.ALIGN_CENTER?"middle":"start";"start"!=n&&r.setAttribute("text-anchor",
+n);this.styleEnabled&&q==mxConstants.DEFAULT_FONTSIZE||r.setAttribute("font-size",q*k.scale+"px");0<t.length&&r.setAttribute("transform",t);1>k.alpha&&r.setAttribute("opacity",k.alpha);t=e.split("\n");p=Math.round(q*mxConstants.LINE_HEIGHT);var u=q+(t.length-1)*p;n=b+q-1;g==mxConstants.ALIGN_MIDDLE?"fill"==l?n-=d/2:(m=(this.matchHtmlAlignment&&m&&0<d?Math.min(u,d):u)/2,n-=m+1):g==mxConstants.ALIGN_BOTTOM&&("fill"==l?n-=d:(m=this.matchHtmlAlignment&&m&&0<d?Math.min(u,d):u,n-=m+2));for(m=0;m<t.length;m++)0<
+t[m].length&&0<mxUtils.trim(t[m]).length&&(q=this.createElement("text"),q.setAttribute("x",this.format(a*k.scale)+this.textOffset),q.setAttribute("y",this.format(n*k.scale)+this.textOffset),mxUtils.write(q,t[m]),r.appendChild(q)),n+=p;this.root.appendChild(r);this.addTextBackground(r,e,a,b,c,"fill"==l?d:u,f,g,l)};
+mxSvgCanvas2D.prototype.updateFont=function(a){var b=this.state;a.setAttribute("fill",b.fontColor);this.styleEnabled&&b.fontFamily==mxConstants.DEFAULT_FONTFAMILY||a.setAttribute("font-family",b.fontFamily);(b.fontStyle&mxConstants.FONT_BOLD)==mxConstants.FONT_BOLD&&a.setAttribute("font-weight","bold");(b.fontStyle&mxConstants.FONT_ITALIC)==mxConstants.FONT_ITALIC&&a.setAttribute("font-style","italic");(b.fontStyle&mxConstants.FONT_UNDERLINE)==mxConstants.FONT_UNDERLINE&&a.setAttribute("text-decoration",
+"underline")};
+mxSvgCanvas2D.prototype.addTextBackground=function(a,b,c,d,e,f,g,k,l){var m=this.state;if(null!=m.fontBackgroundColor||null!=m.fontBorderColor){var n=null;if("fill"==l||"width"==l)g==mxConstants.ALIGN_CENTER?c-=e/2:g==mxConstants.ALIGN_RIGHT&&(c-=e),k==mxConstants.ALIGN_MIDDLE?d-=f/2:k==mxConstants.ALIGN_BOTTOM&&(d-=f),n=new mxRectangle((c+1)*m.scale,d*m.scale,(e-2)*m.scale,(f+2)*m.scale);else if(null!=a.getBBox&&this.root.ownerDocument==document)try{var n=a.getBBox(),p=mxClient.IS_IE&&mxClient.IS_SVG,
+n=new mxRectangle(n.x,n.y+(p?0:1),n.width,n.height+(p?1:0))}catch(q){}else n=document.createElement("div"),n.style.lineHeight=mxConstants.ABSOLUTE_LINE_HEIGHT?m.fontSize*mxConstants.LINE_HEIGHT+"px":mxConstants.LINE_HEIGHT,n.style.fontSize=m.fontSize+"px",n.style.fontFamily=m.fontFamily,n.style.whiteSpace="nowrap",n.style.position="absolute",n.style.visibility="hidden",n.style.display=mxClient.IS_QUIRKS?"inline":"inline-block",n.style.zoom="1",(m.fontStyle&mxConstants.FONT_BOLD)==mxConstants.FONT_BOLD&&
+(n.style.fontWeight="bold"),(m.fontStyle&mxConstants.FONT_ITALIC)==mxConstants.FONT_ITALIC&&(n.style.fontStyle="italic"),b=mxUtils.htmlEntities(b,!1),n.innerHTML=b.replace(/\n/g,"<br/>"),document.body.appendChild(n),e=n.offsetWidth,f=n.offsetHeight,n.parentNode.removeChild(n),g==mxConstants.ALIGN_CENTER?c-=e/2:g==mxConstants.ALIGN_RIGHT&&(c-=e),k==mxConstants.ALIGN_MIDDLE?d-=f/2:k==mxConstants.ALIGN_BOTTOM&&(d-=f),n=new mxRectangle((c+1)*m.scale,(d+2)*m.scale,e*m.scale,(f+1)*m.scale);null!=n&&(b=
+this.createElement("rect"),b.setAttribute("fill",m.fontBackgroundColor||"none"),b.setAttribute("stroke",m.fontBorderColor||"none"),b.setAttribute("x",Math.floor(n.x-1)),b.setAttribute("y",Math.floor(n.y-1)),b.setAttribute("width",Math.ceil(n.width+2)),b.setAttribute("height",Math.ceil(n.height)),m=null!=m.fontBorderColor?Math.max(1,this.format(m.scale)):0,b.setAttribute("stroke-width",m),this.root.ownerDocument==document&&1==mxUtils.mod(m,2)&&b.setAttribute("transform","translate(0.5, 0.5)"),a.insertBefore(b,
+a.firstChild))}};mxSvgCanvas2D.prototype.stroke=function(){this.addNode(!1,!0)};mxSvgCanvas2D.prototype.fill=function(){this.addNode(!0,!1)};mxSvgCanvas2D.prototype.fillAndStroke=function(){this.addNode(!0,!0)};var mxVmlCanvas2D=function(a){mxAbstractCanvas2D.call(this);this.root=a};mxUtils.extend(mxVmlCanvas2D,mxAbstractCanvas2D);mxVmlCanvas2D.prototype.node=null;mxVmlCanvas2D.prototype.textEnabled=!0;mxVmlCanvas2D.prototype.moveOp="m";mxVmlCanvas2D.prototype.lineOp="l";
+mxVmlCanvas2D.prototype.curveOp="c";mxVmlCanvas2D.prototype.closeOp="x";mxVmlCanvas2D.prototype.rotatedHtmlBackground="";mxVmlCanvas2D.prototype.vmlScale=1;mxVmlCanvas2D.prototype.createElement=function(a){return document.createElement(a)};mxVmlCanvas2D.prototype.createVmlElement=function(a){return this.createElement(mxClient.VML_PREFIX+":"+a)};
+mxVmlCanvas2D.prototype.addNode=function(a,b){var c=this.node,d=this.state;if(null!=c){if("shape"==c.nodeName)if(null!=this.path&&0<this.path.length)c.path=this.path.join(" ")+" e",c.style.width=this.root.style.width,c.style.height=this.root.style.height,c.coordsize=parseInt(c.style.width)+" "+parseInt(c.style.height);else return;c.strokeweight=this.format(Math.max(1,d.strokeWidth*d.scale/this.vmlScale))+"px";d.shadow&&this.root.appendChild(this.createShadow(c,a&&null!=d.fillColor,b&&null!=d.strokeColor));
+b&&null!=d.strokeColor?(c.stroked="true",c.strokecolor=d.strokeColor):c.stroked="false";c.appendChild(this.createStroke());a&&null!=d.fillColor?c.appendChild(this.createFill()):!this.pointerEvents||"shape"==c.nodeName&&this.path[this.path.length-1]!=this.closeOp?c.filled="false":c.appendChild(this.createTransparentFill());this.root.appendChild(c)}};
+mxVmlCanvas2D.prototype.createTransparentFill=function(){var a=this.createVmlElement("fill");a.src=mxClient.imageBasePath+"/transparent.gif";a.type="tile";return a};
+mxVmlCanvas2D.prototype.createFill=function(){var a=this.state,b=this.createVmlElement("fill");b.color=a.fillColor;if(null!=a.gradientColor){b.type="gradient";b.method="none";b.color2=a.gradientColor;var c=180-a.rotation,c=a.gradientDirection==mxConstants.DIRECTION_WEST?c-(90+("x"==this.root.style.flip?180:0)):a.gradientDirection==mxConstants.DIRECTION_EAST?c+(90+("x"==this.root.style.flip?180:0)):a.gradientDirection==mxConstants.DIRECTION_NORTH?c-(180+("y"==this.root.style.flip?-180:0)):c+("y"==
+this.root.style.flip?-180:0);if("x"==this.root.style.flip||"y"==this.root.style.flip)c*=-1;b.angle=mxUtils.mod(c,360);b.opacity=a.alpha*a.gradientFillAlpha*100+"%";b.setAttribute(mxClient.OFFICE_PREFIX+":opacity2",a.alpha*a.gradientAlpha*100+"%")}else if(1>a.alpha||1>a.fillAlpha)b.opacity=a.alpha*a.fillAlpha*100+"%";return b};
+mxVmlCanvas2D.prototype.createStroke=function(){var a=this.state,b=this.createVmlElement("stroke");b.endcap=a.lineCap||"flat";b.joinstyle=a.lineJoin||"miter";b.miterlimit=a.miterLimit||"10";if(1>a.alpha||1>a.strokeAlpha)b.opacity=a.alpha*a.strokeAlpha*100+"%";a.dashed&&(b.dashstyle=this.getVmlDashStyle());return b};mxVmlCanvas2D.prototype.getVmlDashStyle=function(){var a="dash";if("string"===typeof this.state.dashPattern){var b=this.state.dashPattern.split(" ");0<b.length&&1==b[0]&&(a="0 2")}return a};
+mxVmlCanvas2D.prototype.createShadow=function(a,b,c){var d=this.state,e=Math.PI/180*-d.rotation,f=Math.cos(e),e=Math.sin(e),g=d.shadowDx*d.scale,k=d.shadowDy*d.scale;"x"==this.root.style.flip?g*=-1:"y"==this.root.style.flip&&(k*=-1);var l=a.cloneNode(!0);l.style.marginLeft=Math.round(g*f-k*e)+"px";l.style.marginTop=Math.round(g*e+k*f)+"px";8==document.documentMode&&(l.strokeweight=a.strokeweight,"shape"==a.nodeName&&(l.path=this.path.join(" ")+" e",l.style.width=this.root.style.width,l.style.height=
+this.root.style.height,l.coordsize=parseInt(a.style.width)+" "+parseInt(a.style.height)));c?(l.strokecolor=d.shadowColor,l.appendChild(this.createShadowStroke())):l.stroked="false";b?l.appendChild(this.createShadowFill()):l.filled="false";return l};mxVmlCanvas2D.prototype.createShadowFill=function(){var a=this.createVmlElement("fill");a.color=this.state.shadowColor;a.opacity=this.state.alpha*this.state.shadowAlpha*100+"%";return a};
+mxVmlCanvas2D.prototype.createShadowStroke=function(){var a=this.createStroke();a.opacity=this.state.alpha*this.state.shadowAlpha*100+"%";return a};mxVmlCanvas2D.prototype.rotate=function(a,b,c,d,e){b&&c?a+=180:b?this.root.style.flip="x":c&&(this.root.style.flip="y");if(b?!c:c)a*=-1;this.root.style.rotation=a;this.state.rotation+=a;this.state.rotationCx=d;this.state.rotationCy=e};
+mxVmlCanvas2D.prototype.begin=function(){mxAbstractCanvas2D.prototype.begin.apply(this,arguments);this.node=this.createVmlElement("shape");this.node.style.position="absolute"};
+mxVmlCanvas2D.prototype.quadTo=function(a,b,c,d){var e=this.state,f=(this.lastX+e.dx)*e.scale,g=(this.lastY+e.dy)*e.scale;a=(a+e.dx)*e.scale;b=(b+e.dy)*e.scale;c=(c+e.dx)*e.scale;d=(d+e.dy)*e.scale;var g=g+2/3*(b-g),k=c+2/3*(a-c);b=d+2/3*(b-d);this.path.push("c "+this.format(f+2/3*(a-f))+" "+this.format(g)+" "+this.format(k)+" "+this.format(b)+" "+this.format(c)+" "+this.format(d));this.lastX=c/e.scale-e.dx;this.lastY=d/e.scale-e.dy};
+mxVmlCanvas2D.prototype.createRect=function(a,b,c,d,e){var f=this.state;a=this.createVmlElement(a);a.style.position="absolute";a.style.left=this.format((b+f.dx)*f.scale)+"px";a.style.top=this.format((c+f.dy)*f.scale)+"px";a.style.width=this.format(d*f.scale)+"px";a.style.height=this.format(e*f.scale)+"px";return a};mxVmlCanvas2D.prototype.rect=function(a,b,c,d){this.node=this.createRect("rect",a,b,c,d)};
+mxVmlCanvas2D.prototype.roundrect=function(a,b,c,d,e,f){this.node=this.createRect("roundrect",a,b,c,d);this.node.setAttribute("arcsize",Math.max(100*e/c,100*f/d)+"%")};mxVmlCanvas2D.prototype.ellipse=function(a,b,c,d){this.node=this.createRect("oval",a,b,c,d)};
+mxVmlCanvas2D.prototype.image=function(a,b,c,d,e,f,g,k){f?(a=this.createRect("rect",a,b,c,d),a.stroked="false",b=this.createVmlElement("fill"),b.aspect=f?"atmost":"ignore",b.rotate="true",b.type="frame",b.src=e,a.appendChild(b)):(a=this.createRect("image",a,b,c,d),a.src=e);g&&k?a.style.rotation="180":g?a.style.flip="x":k&&(a.style.flip="y");if(1>this.state.alpha||1>this.state.fillAlpha)a.style.filter+="alpha(opacity="+this.state.alpha*this.state.fillAlpha*100+")";this.root.appendChild(a)};
+mxVmlCanvas2D.prototype.createDiv=function(a,b,c,d){c=this.createElement("div");var e=this.state,f="";null!=e.fontBackgroundColor&&(f+="background-color:"+e.fontBackgroundColor+";");null!=e.fontBorderColor&&(f+="border:1px solid "+e.fontBorderColor+";");mxUtils.isNode(a)?c.appendChild(a):"fill"!=d&&"width"!=d?(d=this.createElement("div"),d.style.cssText=f,d.style.display=mxClient.IS_QUIRKS?"inline":"inline-block",d.style.zoom="1",d.style.textDecoration="inherit",d.innerHTML=a,c.appendChild(d)):(c.style.cssText=
+f,c.innerHTML=a);a=c.style;a.fontSize=e.fontSize/this.vmlScale+"px";a.fontFamily=e.fontFamily;a.color=e.fontColor;a.verticalAlign="top";a.textAlign=b||"left";a.lineHeight=mxConstants.ABSOLUTE_LINE_HEIGHT?e.fontSize*mxConstants.LINE_HEIGHT/this.vmlScale+"px":mxConstants.LINE_HEIGHT;(e.fontStyle&mxConstants.FONT_BOLD)==mxConstants.FONT_BOLD&&(a.fontWeight="bold");(e.fontStyle&mxConstants.FONT_ITALIC)==mxConstants.FONT_ITALIC&&(a.fontStyle="italic");(e.fontStyle&mxConstants.FONT_UNDERLINE)==mxConstants.FONT_UNDERLINE&&
+(a.textDecoration="underline");return c};
+mxVmlCanvas2D.prototype.text=function(a,b,c,d,e,f,g,k,l,m,n,p,q){if(this.textEnabled&&null!=e){var r=this.state;if("html"==l){null!=r.rotation&&(b=this.rotatePoint(a,b,r.rotation,r.rotationCx,r.rotationCy),a=b.x,b=b.y);8!=document.documentMode||mxClient.IS_EM?(a*=r.scale,b*=r.scale):(a+=r.dx,b+=r.dy,"fill"!=m&&g==mxConstants.ALIGN_TOP&&--b);l=8!=document.documentMode||mxClient.IS_EM?this.createElement("div"):this.createVmlElement("group");l.style.position="absolute";l.style.display="inline";l.style.left=
+this.format(a)+"px";l.style.top=this.format(b)+"px";l.style.zoom=r.scale;var t=this.createElement("div");t.style.position="relative";t.style.display="inline";var u=mxUtils.getAlignmentAsPoint(f,g),x=u.x,u=u.y;e=this.createDiv(e,f,g,m);f=this.createElement("div");null!=q&&e.setAttribute("dir",q);if(k&&0<c){if(n||(e.style.width=Math.round(c)+"px"),e.style.wordWrap=mxConstants.WORD_WRAP,e.style.whiteSpace="normal","break-word"==e.style.wordWrap){var y=e;null!=y.firstChild&&"DIV"==y.firstChild.nodeName&&
+(y.firstChild.style.width="100%")}}else e.style.whiteSpace="nowrap";p=r.rotation+(p||0);this.rotateHtml&&0!=p?(f.style.display="inline",f.style.zoom="1",f.appendChild(e),8!=document.documentMode||mxClient.IS_EM||"DIV"==this.root.nodeName?l.appendChild(f):(t.appendChild(f),l.appendChild(t))):8!=document.documentMode||mxClient.IS_EM?(e.style.display="inline",l.appendChild(e)):(t.appendChild(e),l.appendChild(t));"DIV"!=this.root.nodeName?(q=this.createVmlElement("rect"),q.stroked="false",q.filled="false",
+q.appendChild(l),this.root.appendChild(q)):this.root.appendChild(l);n?(e.style.overflow="hidden",e.style.width=Math.round(c)+"px",mxClient.IS_QUIRKS||(e.style.maxHeight=Math.round(d)+"px")):"fill"==m?(e.style.overflow="hidden",e.style.width=Math.max(0,c)+1+"px",e.style.height=Math.max(0,d)+1+"px"):"width"==m&&(e.style.overflow="hidden",e.style.width=Math.max(0,c)+1+"px",e.style.maxHeight=Math.max(0,d)+1+"px");if(this.rotateHtml&&0!=p){y=Math.PI/180*p;p=parseFloat(parseFloat(Math.cos(y)).toFixed(8));
+q=parseFloat(parseFloat(Math.sin(-y)).toFixed(8));y%=2*Math.PI;0>y&&(y+=2*Math.PI);y%=Math.PI;y>Math.PI/2&&(y=Math.PI-y);g=Math.cos(y);var A=Math.sin(y);8!=document.documentMode||mxClient.IS_EM||(e.style.display="inline-block",f.style.display="inline-block",t.style.display="inline-block");e.style.visibility="hidden";e.style.position="absolute";document.body.appendChild(e);t=e;null!=t.firstChild&&"DIV"==t.firstChild.nodeName&&(t=t.firstChild);y=t.offsetWidth+3;t=t.offsetHeight;n?(c=Math.min(c,y),t=
+Math.min(t,d)):c=y;k&&(e.style.width=c+"px");mxClient.IS_QUIRKS&&(n||"width"==m)&&t>d&&(t=d,e.style.height=t+"px");d=t;n=(d-d*g+c*-A)/2-q*c*(x+.5)+p*d*(u+.5);k=(c-c*g+d*-A)/2+p*c*(x+.5)+q*d*(u+.5);"group"==l.nodeName&&"DIV"==this.root.nodeName?(m=this.createElement("div"),m.style.display="inline-block",m.style.position="absolute",m.style.left=this.format(a+(k-c/2)*r.scale)+"px",m.style.top=this.format(b+(n-d/2)*r.scale)+"px",l.parentNode.appendChild(m),m.appendChild(l)):(r=8!=document.documentMode||
+mxClient.IS_EM?r.scale:1,l.style.left=this.format(a+(k-c/2)*r)+"px",l.style.top=this.format(b+(n-d/2)*r)+"px");f.style.filter="progid:DXImageTransform.Microsoft.Matrix(M11="+p+", M12="+q+", M21="+-q+", M22="+p+", sizingMethod='auto expand')";f.style.backgroundColor=this.rotatedHtmlBackground;1>this.state.alpha&&(f.style.filter+="alpha(opacity="+100*this.state.alpha+")");f.appendChild(e);e.style.position="";e.style.visibility=""}else 8!=document.documentMode||mxClient.IS_EM?(e.style.verticalAlign=
+"top",1>this.state.alpha&&(l.style.filter="alpha(opacity="+100*this.state.alpha+")"),r=e.parentNode,e.style.visibility="hidden",document.body.appendChild(e),c=e.offsetWidth,t=e.offsetHeight,mxClient.IS_QUIRKS&&n&&t>d&&(t=d,e.style.height=t+"px"),d=t,e.style.visibility="",r.appendChild(e),l.style.left=this.format(a+c*x*this.state.scale)+"px",l.style.top=this.format(b+d*u*this.state.scale)+"px"):(1>this.state.alpha&&(e.style.filter="alpha(opacity="+100*this.state.alpha+")"),t.style.left=100*x+"%",t.style.top=
+100*u+"%")}else this.plainText(a,b,c,d,mxUtils.htmlEntities(e,!1),f,g,k,l,m,n,p,q)}};
+mxVmlCanvas2D.prototype.plainText=function(a,b,c,d,e,f,g,k,l,m,n,p,q){k=this.state;a=(a+k.dx)*k.scale;b=(b+k.dy)*k.scale;c=this.createVmlElement("shape");c.style.width="1px";c.style.height="1px";c.stroked="false";d=this.createVmlElement("fill");d.color=k.fontColor;d.opacity=100*k.alpha+"%";c.appendChild(d);d=this.createVmlElement("path");d.textpathok="true";d.v="m "+this.format(0)+" "+this.format(0)+" l "+this.format(1)+" "+this.format(0);c.appendChild(d);d=this.createVmlElement("textpath");d.style.cssText=
+"v-text-align:"+f;d.style.align=f;d.style.fontFamily=k.fontFamily;d.string=e;d.on="true";f=k.fontSize*k.scale/this.vmlScale;d.style.fontSize=f+"px";(k.fontStyle&mxConstants.FONT_BOLD)==mxConstants.FONT_BOLD&&(d.style.fontWeight="bold");(k.fontStyle&mxConstants.FONT_ITALIC)==mxConstants.FONT_ITALIC&&(d.style.fontStyle="italic");(k.fontStyle&mxConstants.FONT_UNDERLINE)==mxConstants.FONT_UNDERLINE&&(d.style.textDecoration="underline");e=e.split("\n");k=f+(e.length-1)*f*mxConstants.LINE_HEIGHT;f=e=0;
+g==mxConstants.ALIGN_BOTTOM?f=-k/2:g!=mxConstants.ALIGN_MIDDLE&&(f=k/2);null!=p&&(c.style.rotation=p,g=Math.PI/180*p,e=Math.sin(g)*f,f*=Math.cos(g));c.appendChild(d);c.style.left=this.format(a-e)+"px";c.style.top=this.format(b+f)+"px";this.root.appendChild(c)};mxVmlCanvas2D.prototype.stroke=function(){this.addNode(!1,!0)};mxVmlCanvas2D.prototype.fill=function(){this.addNode(!0,!1)};mxVmlCanvas2D.prototype.fillAndStroke=function(){this.addNode(!0,!0)};
+function mxGuide(a,b){this.graph=a;this.setStates(b)}mxGuide.prototype.graph=null;mxGuide.prototype.states=null;mxGuide.prototype.horizontal=!0;mxGuide.prototype.vertical=!0;mxGuide.prototype.guideX=null;mxGuide.prototype.guideY=null;mxGuide.prototype.setStates=function(a){this.states=a};mxGuide.prototype.isEnabledForEvent=function(a){return!0};mxGuide.prototype.getGuideTolerance=function(){return this.graph.gridSize/2};
+mxGuide.prototype.createGuideShape=function(a){a=new mxPolyline([],mxConstants.GUIDE_COLOR,mxConstants.GUIDE_STROKEWIDTH);a.isDashed=!0;return a};
+mxGuide.prototype.move=function(a,b,c){if(null!=this.states&&(this.horizontal||this.vertical)&&null!=a&&null!=b){var d=function(b){b+=this.graph.panDy;var c=!1;Math.abs(b-F)<y?(l=b-a.getCenterY(),y=Math.abs(b-F),c=!0):Math.abs(b-B)<y?(l=b-a.y,y=Math.abs(b-B),c=!0):Math.abs(b-C)<y&&(l=b-a.y-a.height,y=Math.abs(b-C),c=!0);c&&(r=D,t=Math.round(b-this.graph.panDy),null==this.guideY&&(this.guideY=this.createGuideShape(!1),this.guideY.dialect=this.graph.dialect!=mxConstants.DIALECT_SVG?mxConstants.DIALECT_VML:
+mxConstants.DIALECT_SVG,this.guideY.pointerEvents=!1,this.guideY.init(this.graph.getView().getOverlayPane())));q=q||c},e=function(b,c){b+=this.graph.panDx;var d=!1;Math.abs(b-v)<x?(k=b-a.getCenterX(),x=Math.abs(b-v),d=!0):Math.abs(b-A)<x?(k=b-a.x,x=Math.abs(b-A),d=!0):Math.abs(b-z)<x&&(k=b-a.x-a.width,x=Math.abs(b-z),d=!0);d&&(n=c,p=Math.round(b-this.graph.panDx),null==this.guideX&&(this.guideX=this.createGuideShape(!0),this.guideX.dialect=this.graph.dialect!=mxConstants.DIALECT_SVG?mxConstants.DIALECT_VML:
+mxConstants.DIALECT_SVG,this.guideX.pointerEvents=!1,this.guideX.init(this.graph.getView().getOverlayPane())));m=m||d},f=this.graph.getView().translate,g=this.graph.getView().scale,k=b.x,l=b.y,m=!1,n=null,p=null,q=!1,r=null,t=null,u=this.getGuideTolerance(),x=u,y=u,u=a.clone();u.x+=b.x;u.y+=b.y;var A=u.x,z=u.x+u.width,v=u.getCenterX(),B=u.y,C=u.y+u.height,F=u.getCenterY();for(b=0;b<this.states.length;b++){var D=this.states[b];null!=D&&(this.horizontal&&(e.call(this,D.getCenterX(),D),e.call(this,D.x,
+D),e.call(this,D.x+D.width,D)),this.vertical&&(d.call(this,D.getCenterY(),D),d.call(this,D.y,D),d.call(this,D.y+D.height,D)))}c&&(m||(c=a.x-(this.graph.snap(a.x/g-f.x)+f.x)*g,k=this.graph.snap(k/g)*g-c),q||(f=a.y-(this.graph.snap(a.y/g-f.y)+f.y)*g,l=this.graph.snap(l/g)*g-f));g=this.graph.container;m||null==this.guideX?null!=this.guideX&&(null!=n&&null!=a&&(minY=Math.min(a.y+l-this.graph.panDy,n.y),maxY=Math.max(a.y+a.height+l-this.graph.panDy,n.y+n.height)),this.guideX.points=null!=minY&&null!=maxY?
+[new mxPoint(p,minY),new mxPoint(p,maxY)]:[new mxPoint(p,-this.graph.panDy),new mxPoint(p,g.scrollHeight-3-this.graph.panDy)],this.guideX.stroke=this.getGuideColor(n,!0),this.guideX.node.style.visibility="visible",this.guideX.redraw()):this.guideX.node.style.visibility="hidden";q||null==this.guideY?null!=this.guideY&&(null!=r&&null!=a&&(minX=Math.min(a.x+k-this.graph.panDx,r.x),maxX=Math.max(a.x+a.width+k-this.graph.panDx,r.x+r.width)),this.guideY.points=null!=minX&&null!=maxX?[new mxPoint(minX,t),
+new mxPoint(maxX,t)]:[new mxPoint(-this.graph.panDx,t),new mxPoint(g.scrollWidth-3-this.graph.panDx,t)],this.guideY.stroke=this.getGuideColor(r,!1),this.guideY.node.style.visibility="visible",this.guideY.redraw()):this.guideY.node.style.visibility="hidden";b=new mxPoint(k,l)}return b};mxGuide.prototype.getGuideColor=function(a,b){return mxConstants.GUIDE_COLOR};mxGuide.prototype.hide=function(){this.setVisible(!1)};
+mxGuide.prototype.setVisible=function(a){null!=this.guideX&&(this.guideX.node.style.visibility=a?"visible":"hidden");null!=this.guideY&&(this.guideY.node.style.visibility=a?"visible":"hidden")};mxGuide.prototype.destroy=function(){null!=this.guideX&&(this.guideX.destroy(),this.guideX=null);null!=this.guideY&&(this.guideY.destroy(),this.guideY=null)};function mxStencil(a){this.desc=a;this.parseDescription();this.parseConstraints()}mxStencil.defaultLocalized=!1;mxStencil.allowEval=!1;
+mxStencil.prototype.desc=null;mxStencil.prototype.constraints=null;mxStencil.prototype.aspect=null;mxStencil.prototype.w0=null;mxStencil.prototype.h0=null;mxStencil.prototype.bgNode=null;mxStencil.prototype.fgNode=null;mxStencil.prototype.strokewidth=null;
+mxStencil.prototype.parseDescription=function(){this.fgNode=this.desc.getElementsByTagName("foreground")[0];this.bgNode=this.desc.getElementsByTagName("background")[0];this.w0=Number(this.desc.getAttribute("w")||100);this.h0=Number(this.desc.getAttribute("h")||100);var a=this.desc.getAttribute("aspect");this.aspect=null!=a?a:"variable";a=this.desc.getAttribute("strokewidth");this.strokewidth=null!=a?a:"1"};
+mxStencil.prototype.parseConstraints=function(){var a=this.desc.getElementsByTagName("connections")[0];if(null!=a&&(a=mxUtils.getChildNodes(a),null!=a&&0<a.length)){this.constraints=[];for(var b=0;b<a.length;b++)this.constraints.push(this.parseConstraint(a[b]))}};mxStencil.prototype.parseConstraint=function(a){var b=Number(a.getAttribute("x")),c=Number(a.getAttribute("y")),d="1"==a.getAttribute("perimeter");a=a.getAttribute("name");return new mxConnectionConstraint(new mxPoint(b,c),d,a)};
+mxStencil.prototype.evaluateTextAttribute=function(a,b,c){b=this.evaluateAttribute(a,b,c);a=a.getAttribute("localized");if(mxStencil.defaultLocalized&&null==a||"1"==a)b=mxResources.get(b);return b};mxStencil.prototype.evaluateAttribute=function(a,b,c){b=a.getAttribute(b);null==b&&(a=mxUtils.getTextContent(a),null!=a&&mxStencil.allowEval&&(a=mxUtils.eval(a),"function"==typeof a&&(b=a(c))));return b};
+mxStencil.prototype.drawShape=function(a,b,c,d,e,f){var g=mxUtils.getValue(b.style,mxConstants.STYLE_DIRECTION,null),g=this.computeAspect(b.style,c,d,e,f,g),k=Math.min(g.width,g.height),k="inherit"==this.strokewidth?Number(mxUtils.getNumber(b.style,mxConstants.STYLE_STROKEWIDTH,1)):Number(this.strokewidth)*k;a.setStrokeWidth(k);this.drawChildren(a,b,c,d,e,f,this.bgNode,g,!1);this.drawChildren(a,b,c,d,e,f,this.fgNode,g,!0)};
+mxStencil.prototype.drawChildren=function(a,b,c,d,e,f,g,k,l){if(null!=g&&0<e&&0<f)for(c=g.firstChild;null!=c;)c.nodeType==mxConstants.NODETYPE_ELEMENT&&this.drawNode(a,b,c,k,l),c=c.nextSibling};
+mxStencil.prototype.computeAspect=function(a,b,c,d,e,f){a=b;b=d/this.w0;var g=e/this.h0;if(f=f==mxConstants.DIRECTION_NORTH||f==mxConstants.DIRECTION_SOUTH){g=d/this.h0;b=e/this.w0;var k=(d-e)/2;a+=k;c-=k}"fixed"==this.aspect&&(b=g=Math.min(b,g),f?(a+=(e-this.w0*b)/2,c+=(d-this.h0*g)/2):(a+=(d-this.w0*b)/2,c+=(e-this.h0*g)/2));return new mxRectangle(a,c,b,g)};
+mxStencil.prototype.drawNode=function(a,b,c,d,e){var f=c.nodeName,g=d.x,k=d.y,l=d.width,m=d.height,n=Math.min(l,m);if("save"==f)a.save();else if("restore"==f)a.restore();else if("path"==f)for(a.begin(),c=c.firstChild;null!=c;)c.nodeType==mxConstants.NODETYPE_ELEMENT&&this.drawNode(a,b,c,d,e),c=c.nextSibling;else if("close"==f)a.close();else if("move"==f)a.moveTo(g+Number(c.getAttribute("x"))*l,k+Number(c.getAttribute("y"))*m);else if("line"==f)a.lineTo(g+Number(c.getAttribute("x"))*l,k+Number(c.getAttribute("y"))*
+m);else if("quad"==f)a.quadTo(g+Number(c.getAttribute("x1"))*l,k+Number(c.getAttribute("y1"))*m,g+Number(c.getAttribute("x2"))*l,k+Number(c.getAttribute("y2"))*m);else if("curve"==f)a.curveTo(g+Number(c.getAttribute("x1"))*l,k+Number(c.getAttribute("y1"))*m,g+Number(c.getAttribute("x2"))*l,k+Number(c.getAttribute("y2"))*m,g+Number(c.getAttribute("x3"))*l,k+Number(c.getAttribute("y3"))*m);else if("arc"==f)a.arcTo(Number(c.getAttribute("rx"))*l,Number(c.getAttribute("ry"))*m,Number(c.getAttribute("x-axis-rotation")),
+Number(c.getAttribute("large-arc-flag")),Number(c.getAttribute("sweep-flag")),g+Number(c.getAttribute("x"))*l,k+Number(c.getAttribute("y"))*m);else if("rect"==f)a.rect(g+Number(c.getAttribute("x"))*l,k+Number(c.getAttribute("y"))*m,Number(c.getAttribute("w"))*l,Number(c.getAttribute("h"))*m);else if("roundrect"==f)b=Number(c.getAttribute("arcsize")),0==b&&(b=100*mxConstants.RECTANGLE_ROUNDING_FACTOR),n=Number(c.getAttribute("w"))*l,d=Number(c.getAttribute("h"))*m,b=Number(b)/100,b=Math.min(n*b,d*
+b),a.roundrect(g+Number(c.getAttribute("x"))*l,k+Number(c.getAttribute("y"))*m,n,d,b,b);else if("ellipse"==f)a.ellipse(g+Number(c.getAttribute("x"))*l,k+Number(c.getAttribute("y"))*m,Number(c.getAttribute("w"))*l,Number(c.getAttribute("h"))*m);else if("image"==f)b.outline||(b=this.evaluateAttribute(c,"src",b),a.image(g+Number(c.getAttribute("x"))*l,k+Number(c.getAttribute("y"))*m,Number(c.getAttribute("w"))*l,Number(c.getAttribute("h"))*m,b,!1,"1"==c.getAttribute("flipH"),"1"==c.getAttribute("flipV")));
+else if("text"==f){if(!b.outline){n=this.evaluateTextAttribute(c,"str",b);d="1"==c.getAttribute("vertical")?-90:0;if("0"==c.getAttribute("align-shape")){var p=b.rotation,q=1==mxUtils.getValue(b.style,mxConstants.STYLE_FLIPH,0);b=1==mxUtils.getValue(b.style,mxConstants.STYLE_FLIPV,0);d=q&&b?d-p:q||b?d+p:d-p}d-=c.getAttribute("rotation");a.text(g+Number(c.getAttribute("x"))*l,k+Number(c.getAttribute("y"))*m,0,0,n,c.getAttribute("align")||"left",c.getAttribute("valign")||"top",!1,"",null,!1,d)}}else if("include-shape"==
+f)p=mxStencilRegistry.getStencil(c.getAttribute("name")),null!=p&&(g+=Number(c.getAttribute("x"))*l,k+=Number(c.getAttribute("y"))*m,n=Number(c.getAttribute("w"))*l,d=Number(c.getAttribute("h"))*m,p.drawShape(a,b,g,k,n,d));else if("fillstroke"==f)a.fillAndStroke();else if("fill"==f)a.fill();else if("stroke"==f)a.stroke();else if("strokewidth"==f)l="1"==c.getAttribute("fixed")?1:n,a.setStrokeWidth(Number(c.getAttribute("width"))*l);else if("dashed"==f)a.setDashed("1"==c.getAttribute("dashed"));else if("dashpattern"==
+f){if(c=c.getAttribute("pattern"),null!=c){c=c.split(" ");l=[];for(m=0;m<c.length;m++)0<c[m].length&&l.push(Number(c[m])*n);c=l.join(" ");a.setDashPattern(c)}}else"strokecolor"==f?a.setStrokeColor(c.getAttribute("color")):"linecap"==f?a.setLineCap(c.getAttribute("cap")):"linejoin"==f?a.setLineJoin(c.getAttribute("join")):"miterlimit"==f?a.setMiterLimit(Number(c.getAttribute("limit"))):"fillcolor"==f?a.setFillColor(c.getAttribute("color")):"alpha"==f?a.setAlpha(c.getAttribute("alpha")):"fontcolor"==
+f?a.setFontColor(c.getAttribute("color")):"fontstyle"==f?a.setFontStyle(c.getAttribute("style")):"fontfamily"==f?a.setFontFamily(c.getAttribute("family")):"fontsize"==f&&a.setFontSize(Number(c.getAttribute("size"))*n);!e||"fillstroke"!=f&&"fill"!=f&&"stroke"!=f||a.setShadow(!1)};function mxShape(a){this.stencil=a;this.initStyles()}mxShape.prototype.dialect=null;mxShape.prototype.scale=1;mxShape.prototype.antiAlias=!0;mxShape.prototype.minSvgStrokeWidth=1;mxShape.prototype.bounds=null;
+mxShape.prototype.points=null;mxShape.prototype.node=null;mxShape.prototype.state=null;mxShape.prototype.style=null;mxShape.prototype.boundingBox=null;mxShape.prototype.stencil=null;mxShape.prototype.svgStrokeTolerance=8;mxShape.prototype.pointerEvents=!0;mxShape.prototype.svgPointerEvents="all";mxShape.prototype.shapePointerEvents=!1;mxShape.prototype.stencilPointerEvents=!1;mxShape.prototype.vmlScale=1;mxShape.prototype.outline=!1;mxShape.prototype.visible=!0;
+mxShape.prototype.useSvgBoundingBox=!1;mxShape.prototype.init=function(a){null==this.node&&(this.node=this.create(a),null!=a&&a.appendChild(this.node))};mxShape.prototype.initStyles=function(a){this.strokewidth=1;this.rotation=0;this.strokeOpacity=this.fillOpacity=this.opacity=100;this.flipV=this.flipH=!1};mxShape.prototype.isParseVml=function(){return!0};mxShape.prototype.isHtmlAllowed=function(){return!1};
+mxShape.prototype.getSvgScreenOffset=function(){return 1==mxUtils.mod(Math.max(1,Math.round((this.stencil&&"inherit"!=this.stencil.strokewidth?Number(this.stencil.strokewidth):this.strokewidth)*this.scale)),2)?.5:0};mxShape.prototype.create=function(a){return null!=a&&null!=a.ownerSVGElement?this.createSvg(a):8==document.documentMode||!mxClient.IS_VML||this.dialect!=mxConstants.DIALECT_VML&&this.isHtmlAllowed()?this.createHtml(a):this.createVml(a)};
+mxShape.prototype.createSvg=function(){return document.createElementNS(mxConstants.NS_SVG,"g")};mxShape.prototype.createVml=function(){var a=document.createElement(mxClient.VML_PREFIX+":group");a.style.position="absolute";return a};mxShape.prototype.createHtml=function(){var a=document.createElement("div");a.style.position="absolute";return a};mxShape.prototype.reconfigure=function(){this.redraw()};
+mxShape.prototype.redraw=function(){this.updateBoundsFromPoints();this.visible&&this.checkBounds()?(this.node.style.visibility="visible",this.clear(),"DIV"!=this.node.nodeName||!this.isHtmlAllowed()&&mxClient.IS_VML?this.redrawShape():this.redrawHtmlShape(),this.updateBoundingBox()):(this.node.style.visibility="hidden",this.boundingBox=null)};
+mxShape.prototype.clear=function(){if(null!=this.node.ownerSVGElement)for(;null!=this.node.lastChild;)this.node.removeChild(this.node.lastChild);else this.node.style.cssText="position:absolute;"+(null!=this.cursor?"cursor:"+this.cursor+";":""),this.node.innerHTML=""};
+mxShape.prototype.updateBoundsFromPoints=function(){var a=this.points;if(null!=a&&0<a.length&&null!=a[0]){this.bounds=new mxRectangle(Number(a[0].x),Number(a[0].y),1,1);for(var b=1;b<this.points.length;b++)null!=a[b]&&this.bounds.add(new mxRectangle(Number(a[b].x),Number(a[b].y),1,1))}};
+mxShape.prototype.getLabelBounds=function(a){var b=mxUtils.getValue(this.style,mxConstants.STYLE_DIRECTION,mxConstants.DIRECTION_EAST),c=a;b!=mxConstants.DIRECTION_SOUTH&&b!=mxConstants.DIRECTION_NORTH&&null!=this.state&&null!=this.state.text&&this.state.text.isPaintBoundsInverted()&&(c=c.clone(),b=c.width,c.width=c.height,c.height=b);c=this.getLabelMargins(c);if(null!=c){var d="1"==mxUtils.getValue(this.style,mxConstants.STYLE_FLIPH,!1),e="1"==mxUtils.getValue(this.style,mxConstants.STYLE_FLIPV,
+!1);null!=this.state&&null!=this.state.text&&this.state.text.isPaintBoundsInverted()&&(b=c.x,c.x=c.height,c.height=c.width,c.width=c.y,c.y=b,b=d,d=e,e=b);return mxUtils.getDirectedBounds(a,c,this.style,d,e)}return a};mxShape.prototype.getLabelMargins=function(a){return null};
+mxShape.prototype.checkBounds=function(){return!isNaN(this.scale)&&isFinite(this.scale)&&0<this.scale&&null!=this.bounds&&!isNaN(this.bounds.x)&&!isNaN(this.bounds.y)&&!isNaN(this.bounds.width)&&!isNaN(this.bounds.height)&&0<this.bounds.width&&0<this.bounds.height};mxShape.prototype.createVmlGroup=function(){var a=document.createElement(mxClient.VML_PREFIX+":group");a.style.position="absolute";a.style.width=this.node.style.width;a.style.height=this.node.style.height;return a};
+mxShape.prototype.redrawShape=function(){var a=this.createCanvas();null!=a&&(a.pointerEvents=this.pointerEvents,this.paint(a),this.node!=a.root&&this.node.insertAdjacentHTML("beforeend",a.root.outerHTML),"DIV"==this.node.nodeName&&8==document.documentMode&&(this.node.style.filter="",mxUtils.addTransparentBackgroundFilter(this.node)),this.destroyCanvas(a))};
+mxShape.prototype.createCanvas=function(){var a=null;null!=this.node.ownerSVGElement?a=this.createSvgCanvas():mxClient.IS_VML&&(this.updateVmlContainer(),a=this.createVmlCanvas());null!=a&&this.outline&&(a.setStrokeWidth(this.strokewidth),a.setStrokeColor(this.stroke),null!=this.isDashed&&a.setDashed(this.isDashed),a.setStrokeWidth=function(){},a.setStrokeColor=function(){},a.setFillColor=function(){},a.setGradient=function(){},a.setDashed=function(){});return a};
+mxShape.prototype.createSvgCanvas=function(){var a=new mxSvgCanvas2D(this.node,!1);a.strokeTolerance=this.pointerEvents?this.svgStrokeTolerance:0;a.pointerEventsValue=this.svgPointerEvents;a.blockImagePointerEvents=mxClient.IS_FF;var b=this.getSvgScreenOffset();0!=b?this.node.setAttribute("transform","translate("+b+","+b+")"):this.node.removeAttribute("transform");a.minStrokeWidth=this.minSvgStrokeWidth;this.antiAlias||(a.format=function(a){return Math.round(parseFloat(a))});return a};
+mxShape.prototype.createVmlCanvas=function(){var a=8==document.documentMode&&this.isParseVml()?this.createVmlGroup():this.node,b=new mxVmlCanvas2D(a,!1);""!=a.tagUrn&&(a.coordsize=Math.max(1,Math.round(this.bounds.width))*this.vmlScale+","+Math.max(1,Math.round(this.bounds.height))*this.vmlScale,b.scale(this.vmlScale),b.vmlScale=this.vmlScale);a=this.scale;b.translate(-Math.round(this.bounds.x/a),-Math.round(this.bounds.y/a));return b};
+mxShape.prototype.updateVmlContainer=function(){this.node.style.left=Math.round(this.bounds.x)+"px";this.node.style.top=Math.round(this.bounds.y)+"px";var a=Math.max(1,Math.round(this.bounds.height));this.node.style.width=Math.max(1,Math.round(this.bounds.width))+"px";this.node.style.height=a+"px";this.node.style.overflow="visible"};mxShape.prototype.redrawHtmlShape=function(){this.updateHtmlBounds(this.node);this.updateHtmlFilters(this.node);this.updateHtmlColors(this.node)};
+mxShape.prototype.updateHtmlFilters=function(a){var b="";100>this.opacity&&(b+="alpha(opacity="+this.opacity+")");this.isShadow&&(b+="progid:DXImageTransform.Microsoft.dropShadow (OffX='"+Math.round(mxConstants.SHADOW_OFFSET_X*this.scale)+"', OffY='"+Math.round(mxConstants.SHADOW_OFFSET_Y*this.scale)+"', Color='"+mxConstants.VML_SHADOWCOLOR+"')");if(null!=this.fill&&this.fill!=mxConstants.NONE&&this.gradient&&this.gradient!=mxConstants.NONE){var c=this.fill,d=this.gradient,e="0",f={east:0,south:1,
+west:2,north:3},g=null!=this.direction?f[this.direction]:0;null!=this.gradientDirection&&(g=mxUtils.mod(g+f[this.gradientDirection]-1,4));1==g?(e="1",f=c,c=d,d=f):2==g?(f=c,c=d,d=f):3==g&&(e="1");b+="progid:DXImageTransform.Microsoft.gradient(startColorStr='"+c+"', endColorStr='"+d+"', gradientType='"+e+"')"}a.style.filter=b};
+mxShape.prototype.updateHtmlColors=function(a){var b=this.stroke;null!=b&&b!=mxConstants.NONE?(a.style.borderColor=b,this.isDashed?a.style.borderStyle="dashed":0<this.strokewidth&&(a.style.borderStyle="solid"),a.style.borderWidth=Math.max(1,Math.ceil(this.strokewidth*this.scale))+"px"):a.style.borderWidth="0px";b=this.outline?null:this.fill;null!=b&&b!=mxConstants.NONE?(a.style.backgroundColor=b,a.style.backgroundImage="none"):this.pointerEvents?a.style.backgroundColor="transparent":8==document.documentMode?
+mxUtils.addTransparentBackgroundFilter(a):this.setTransparentBackgroundImage(a)};
+mxShape.prototype.updateHtmlBounds=function(a){var b=9<=document.documentMode?0:Math.ceil(this.strokewidth*this.scale);a.style.borderWidth=Math.max(1,b)+"px";a.style.overflow="hidden";a.style.left=Math.round(this.bounds.x-b/2)+"px";a.style.top=Math.round(this.bounds.y-b/2)+"px";"CSS1Compat"==document.compatMode&&(b=-b);a.style.width=Math.round(Math.max(0,this.bounds.width+b))+"px";a.style.height=Math.round(Math.max(0,this.bounds.height+b))+"px"};
+mxShape.prototype.destroyCanvas=function(a){if(a instanceof mxSvgCanvas2D){for(var b in a.gradients){var c=a.gradients[b];null!=c&&(c.mxRefCount=(c.mxRefCount||0)+1)}this.releaseSvgGradients(this.oldGradients);this.oldGradients=a.gradients}};
+mxShape.prototype.paint=function(a){var b=!1;if(null!=a&&this.outline){var c=a.stroke;a.stroke=function(){b=!0;c.apply(this,arguments)};var d=a.fillAndStroke;a.fillAndStroke=function(){b=!0;d.apply(this,arguments)}}var e=this.scale,f=this.bounds.x/e,g=this.bounds.y/e,k=this.bounds.width/e,l=this.bounds.height/e;if(this.isPaintBoundsInverted())var m=(k-l)/2,f=f+m,g=g-m,m=k,k=l,l=m;this.updateTransform(a,f,g,k,l);this.configureCanvas(a,f,g,k,l);m=null;if(null==this.stencil&&null==this.points&&this.shapePointerEvents||
+null!=this.stencil&&this.stencilPointerEvents){var n=this.createBoundingBox();this.dialect==mxConstants.DIALECT_SVG?(m=this.createTransparentSvgRectangle(n.x,n.y,n.width,n.height),this.node.appendChild(m)):(n=a.createRect("rect",n.x/e,n.y/e,n.width/e,n.height/e),n.appendChild(a.createTransparentFill()),n.stroked="false",a.root.appendChild(n))}if(null!=this.stencil)this.stencil.drawShape(a,this,f,g,k,l);else if(a.setStrokeWidth(this.strokewidth),null!=this.points){for(var n=[],p=0;p<this.points.length;p++)null!=
+this.points[p]&&n.push(new mxPoint(this.points[p].x/e,this.points[p].y/e));this.paintEdgeShape(a,n)}else this.paintVertexShape(a,f,g,k,l);null!=m&&null!=a.state&&null!=a.state.transform&&m.setAttribute("transform",a.state.transform);null!=a&&this.outline&&!b&&(a.rect(f,g,k,l),a.stroke())};
+mxShape.prototype.configureCanvas=function(a,b,c,d,e){var f=null;null!=this.style&&(f=this.style.dashPattern);a.setAlpha(this.opacity/100);a.setFillAlpha(this.fillOpacity/100);a.setStrokeAlpha(this.strokeOpacity/100);null!=this.isShadow&&a.setShadow(this.isShadow);null!=this.isDashed&&a.setDashed(this.isDashed,null!=this.style?1==mxUtils.getValue(this.style,mxConstants.STYLE_FIX_DASH,!1):!1);null!=f&&a.setDashPattern(f);null!=this.fill&&this.fill!=mxConstants.NONE&&this.gradient&&this.gradient!=mxConstants.NONE?
+(b=this.getGradientBounds(a,b,c,d,e),a.setGradient(this.fill,this.gradient,b.x,b.y,b.width,b.height,this.gradientDirection)):a.setFillColor(this.fill);a.setStrokeColor(this.stroke)};mxShape.prototype.getGradientBounds=function(a,b,c,d,e){return new mxRectangle(b,c,d,e)};mxShape.prototype.updateTransform=function(a,b,c,d,e){a.scale(this.scale);a.rotate(this.getShapeRotation(),this.flipH,this.flipV,b+d/2,c+e/2)};
+mxShape.prototype.paintVertexShape=function(a,b,c,d,e){this.paintBackground(a,b,c,d,e);a.setShadow(!1);this.paintForeground(a,b,c,d,e)};mxShape.prototype.paintBackground=function(a,b,c,d,e){};mxShape.prototype.paintForeground=function(a,b,c,d,e){};mxShape.prototype.paintEdgeShape=function(a,b){};
+mxShape.prototype.getArcSize=function(a,b){var c;"1"==mxUtils.getValue(this.style,mxConstants.STYLE_ABSOLUTE_ARCSIZE,0)?c=Math.min(a/2,Math.min(b/2,mxUtils.getValue(this.style,mxConstants.STYLE_ARCSIZE,mxConstants.LINE_ARCSIZE)/2)):(c=mxUtils.getValue(this.style,mxConstants.STYLE_ARCSIZE,100*mxConstants.RECTANGLE_ROUNDING_FACTOR)/100,c=Math.min(a*c,b*c));return c};
+mxShape.prototype.paintGlassEffect=function(a,b,c,d,e,f){var g=Math.ceil(this.strokewidth/2);a.setGradient("#ffffff","#ffffff",b,c,d,.6*e,"south",.9,.1);a.begin();f+=2*g;this.isRounded?(a.moveTo(b-g+f,c-g),a.quadTo(b-g,c-g,b-g,c-g+f),a.lineTo(b-g,c+.4*e),a.quadTo(b+.5*d,c+.7*e,b+d+g,c+.4*e),a.lineTo(b+d+g,c-g+f),a.quadTo(b+d+g,c-g,b+d+g-f,c-g)):(a.moveTo(b-g,c-g),a.lineTo(b-g,c+.4*e),a.quadTo(b+.5*d,c+.7*e,b+d+g,c+.4*e),a.lineTo(b+d+g,c-g));a.close();a.fill()};
+mxShape.prototype.addPoints=function(a,b,c,d,e,f,g){if(null!=b&&0<b.length){g=null!=g?g:!0;var k=b[b.length-1];if(e&&c){b=b.slice();var l=b[0],l=new mxPoint(k.x+(l.x-k.x)/2,k.y+(l.y-k.y)/2);b.splice(0,0,l)}var m=b[0],l=1;for(g?a.moveTo(m.x,m.y):a.lineTo(m.x,m.y);l<(e?b.length:b.length-1);){g=b[mxUtils.mod(l,b.length)];var n=m.x-g.x,m=m.y-g.y;if(c&&(0!=n||0!=m)&&(null==f||0>mxUtils.indexOf(f,l-1))){var p=Math.sqrt(n*n+m*m);a.lineTo(g.x+n*Math.min(d,p/2)/p,g.y+m*Math.min(d,p/2)/p);for(m=b[mxUtils.mod(l+
+1,b.length)];l<b.length-2&&0==Math.round(m.x-g.x)&&0==Math.round(m.y-g.y);)m=b[mxUtils.mod(l+2,b.length)],l++;n=m.x-g.x;m=m.y-g.y;p=Math.max(1,Math.sqrt(n*n+m*m));n=g.x+n*Math.min(d,p/2)/p;m=g.y+m*Math.min(d,p/2)/p;a.quadTo(g.x,g.y,n,m);g=new mxPoint(n,m)}else a.lineTo(g.x,g.y);m=g;l++}e?a.close():a.lineTo(k.x,k.y)}};
+mxShape.prototype.resetStyles=function(){this.initStyles();this.spacing=0;delete this.fill;delete this.gradient;delete this.gradientDirection;delete this.stroke;delete this.startSize;delete this.endSize;delete this.startArrow;delete this.endArrow;delete this.direction;delete this.isShadow;delete this.isDashed;delete this.isRounded;delete this.glass};
+mxShape.prototype.apply=function(a){this.state=a;this.style=a.style;if(null!=this.style){this.fill=mxUtils.getValue(this.style,mxConstants.STYLE_FILLCOLOR,this.fill);this.gradient=mxUtils.getValue(this.style,mxConstants.STYLE_GRADIENTCOLOR,this.gradient);this.gradientDirection=mxUtils.getValue(this.style,mxConstants.STYLE_GRADIENT_DIRECTION,this.gradientDirection);this.opacity=mxUtils.getValue(this.style,mxConstants.STYLE_OPACITY,this.opacity);this.fillOpacity=mxUtils.getValue(this.style,mxConstants.STYLE_FILL_OPACITY,
+this.fillOpacity);this.strokeOpacity=mxUtils.getValue(this.style,mxConstants.STYLE_STROKE_OPACITY,this.strokeOpacity);this.stroke=mxUtils.getValue(this.style,mxConstants.STYLE_STROKECOLOR,this.stroke);this.strokewidth=mxUtils.getNumber(this.style,mxConstants.STYLE_STROKEWIDTH,this.strokewidth);this.spacing=mxUtils.getValue(this.style,mxConstants.STYLE_SPACING,this.spacing);this.startSize=mxUtils.getNumber(this.style,mxConstants.STYLE_STARTSIZE,this.startSize);this.endSize=mxUtils.getNumber(this.style,
+mxConstants.STYLE_ENDSIZE,this.endSize);this.startArrow=mxUtils.getValue(this.style,mxConstants.STYLE_STARTARROW,this.startArrow);this.endArrow=mxUtils.getValue(this.style,mxConstants.STYLE_ENDARROW,this.endArrow);this.rotation=mxUtils.getValue(this.style,mxConstants.STYLE_ROTATION,this.rotation);this.direction=mxUtils.getValue(this.style,mxConstants.STYLE_DIRECTION,this.direction);this.flipH=1==mxUtils.getValue(this.style,mxConstants.STYLE_FLIPH,0);this.flipV=1==mxUtils.getValue(this.style,mxConstants.STYLE_FLIPV,
+0);null!=this.stencil&&(this.flipH=1==mxUtils.getValue(this.style,"stencilFlipH",0)||this.flipH,this.flipV=1==mxUtils.getValue(this.style,"stencilFlipV",0)||this.flipV);if(this.direction==mxConstants.DIRECTION_NORTH||this.direction==mxConstants.DIRECTION_SOUTH)a=this.flipH,this.flipH=this.flipV,this.flipV=a;this.isShadow=1==mxUtils.getValue(this.style,mxConstants.STYLE_SHADOW,this.isShadow);this.isDashed=1==mxUtils.getValue(this.style,mxConstants.STYLE_DASHED,this.isDashed);this.isRounded=1==mxUtils.getValue(this.style,
+mxConstants.STYLE_ROUNDED,this.isRounded);this.glass=1==mxUtils.getValue(this.style,mxConstants.STYLE_GLASS,this.glass);this.fill==mxConstants.NONE&&(this.fill=null);this.gradient==mxConstants.NONE&&(this.gradient=null);this.stroke==mxConstants.NONE&&(this.stroke=null)}};mxShape.prototype.setCursor=function(a){null==a&&(a="");this.cursor=a;null!=this.node&&(this.node.style.cursor=a)};mxShape.prototype.getCursor=function(){return this.cursor};
+mxShape.prototype.updateBoundingBox=function(){if(this.useSvgBoundingBox&&null!=this.node&&null!=this.node.ownerSVGElement)try{var a=this.node.getBBox();if(0<a.width&&0<a.height){this.boundingBox=new mxRectangle(a.x,a.y,a.width,a.height);this.boundingBox.grow(this.strokewidth*this.scale/2);return}}catch(c){}if(null!=this.bounds){a=this.createBoundingBox();if(null!=a){this.augmentBoundingBox(a);var b=this.getShapeRotation();0!=b&&(a=mxUtils.getBoundingBox(a,b))}this.boundingBox=a}};
+mxShape.prototype.createBoundingBox=function(){var a=this.bounds.clone();(null!=this.stencil&&(this.direction==mxConstants.DIRECTION_NORTH||this.direction==mxConstants.DIRECTION_SOUTH)||this.isPaintBoundsInverted())&&a.rotate90();return a};mxShape.prototype.augmentBoundingBox=function(a){this.isShadow&&(a.width+=Math.ceil(mxConstants.SHADOW_OFFSET_X*this.scale),a.height+=Math.ceil(mxConstants.SHADOW_OFFSET_Y*this.scale));a.grow(this.strokewidth*this.scale/2)};
+mxShape.prototype.isPaintBoundsInverted=function(){return null==this.stencil&&(this.direction==mxConstants.DIRECTION_NORTH||this.direction==mxConstants.DIRECTION_SOUTH)};mxShape.prototype.getRotation=function(){return null!=this.rotation?this.rotation:0};mxShape.prototype.getTextRotation=function(){var a=this.getRotation();1!=mxUtils.getValue(this.style,mxConstants.STYLE_HORIZONTAL,1)&&(a+=mxText.prototype.verticalTextRotation);return a};
+mxShape.prototype.getShapeRotation=function(){var a=this.getRotation();null!=this.direction&&(this.direction==mxConstants.DIRECTION_NORTH?a+=270:this.direction==mxConstants.DIRECTION_WEST?a+=180:this.direction==mxConstants.DIRECTION_SOUTH&&(a+=90));return a};
+mxShape.prototype.createTransparentSvgRectangle=function(a,b,c,d){var e=document.createElementNS(mxConstants.NS_SVG,"rect");e.setAttribute("x",a);e.setAttribute("y",b);e.setAttribute("width",c);e.setAttribute("height",d);e.setAttribute("fill","none");e.setAttribute("stroke","none");e.setAttribute("pointer-events","all");return e};mxShape.prototype.setTransparentBackgroundImage=function(a){a.style.backgroundImage="url('"+mxClient.imageBasePath+"/transparent.gif')"};
+mxShape.prototype.releaseSvgGradients=function(a){if(null!=a)for(var b in a){var c=a[b];null!=c&&(c.mxRefCount=(c.mxRefCount||0)-1,0==c.mxRefCount&&null!=c.parentNode&&c.parentNode.removeChild(c))}};mxShape.prototype.destroy=function(){null!=this.node&&(mxEvent.release(this.node),null!=this.node.parentNode&&this.node.parentNode.removeChild(this.node),this.node=null);this.releaseSvgGradients(this.oldGradients);this.oldGradients=null};
+var mxStencilRegistry={stencils:{},addStencil:function(a,b){mxStencilRegistry.stencils[a]=b},getStencil:function(a){return mxStencilRegistry.stencils[a]}},mxMarker={markers:[],addMarker:function(a,b){mxMarker.markers[a]=b},createMarker:function(a,b,c,d,e,f,g,k,l,m){var n=mxMarker.markers[c];return null!=n?n(a,b,c,d,e,f,g,k,l,m):null}};
+(function(){function a(a){a=null!=a?a:2;return function(b,c,d,k,l,m,n,p,q,r){c=l*q*1.118;p=m*q*1.118;l*=n+q;m*=n+q;var e=k.clone();e.x-=c;e.y-=p;n=d!=mxConstants.ARROW_CLASSIC&&d!=mxConstants.ARROW_CLASSIC_THIN?1:.75;k.x+=-l*n-c;k.y+=-m*n-p;return function(){b.begin();b.moveTo(e.x,e.y);b.lineTo(e.x-l-m/a,e.y-m+l/a);d!=mxConstants.ARROW_CLASSIC&&d!=mxConstants.ARROW_CLASSIC_THIN||b.lineTo(e.x-3*l/4,e.y-3*m/4);b.lineTo(e.x+m/a-l,e.y-m-l/a);b.close();r?b.fillAndStroke():b.stroke()}}}function b(a){a=
+null!=a?a:2;return function(b,c,d,k,l,m,n,p,q,r){c=l*q*1.118;d=m*q*1.118;l*=n+q;m*=n+q;var e=k.clone();e.x-=c;e.y-=d;k.x+=2*-c;k.y+=2*-d;return function(){b.begin();b.moveTo(e.x-l-m/a,e.y-m+l/a);b.lineTo(e.x,e.y);b.lineTo(e.x+m/a-l,e.y-m-l/a);b.stroke()}}}function c(a,b,c,g,k,l,m,n,p,q){n=c==mxConstants.ARROW_DIAMOND?.7071:.9862;b=k*p*n;n*=l*p;k*=m+p;l*=m+p;var d=g.clone();d.x-=b;d.y-=n;g.x+=-k-b;g.y+=-l-n;var e=c==mxConstants.ARROW_DIAMOND?2:3.4;return function(){a.begin();a.moveTo(d.x,d.y);a.lineTo(d.x-
+k/2-l/e,d.y+k/e-l/2);a.lineTo(d.x-k,d.y-l);a.lineTo(d.x-k/2+l/e,d.y-l/2-k/e);a.close();q?a.fillAndStroke():a.stroke()}}mxMarker.addMarker("classic",a(2));mxMarker.addMarker("classicThin",a(3));mxMarker.addMarker("block",a(2));mxMarker.addMarker("blockThin",a(3));mxMarker.addMarker("open",b(2));mxMarker.addMarker("openThin",b(3));mxMarker.addMarker("oval",function(a,b,c,g,k,l,m,n,p,q){var d=m/2,e=g.clone();g.x-=k*d;g.y-=l*d;return function(){a.ellipse(e.x-d,e.y-d,m,m);q?a.fillAndStroke():a.stroke()}});
+mxMarker.addMarker("diamond",c);mxMarker.addMarker("diamondThin",c)})();function mxActor(a,b,c,d){mxShape.call(this);this.bounds=a;this.fill=b;this.stroke=c;this.strokewidth=null!=d?d:1}mxUtils.extend(mxActor,mxShape);mxActor.prototype.paintVertexShape=function(a,b,c,d,e){a.translate(b,c);a.begin();this.redrawPath(a,b,c,d,e);a.fillAndStroke()};
+mxActor.prototype.redrawPath=function(a,b,c,d,e){b=d/3;a.moveTo(0,e);a.curveTo(0,3*e/5,0,2*e/5,d/2,2*e/5);a.curveTo(d/2-b,2*e/5,d/2-b,0,d/2,0);a.curveTo(d/2+b,0,d/2+b,2*e/5,d/2,2*e/5);a.curveTo(d,2*e/5,d,3*e/5,d,e);a.close()};function mxCloud(a,b,c,d){mxActor.call(this);this.bounds=a;this.fill=b;this.stroke=c;this.strokewidth=null!=d?d:1}mxUtils.extend(mxCloud,mxActor);
+mxCloud.prototype.redrawPath=function(a,b,c,d,e){a.moveTo(.25*d,.25*e);a.curveTo(.05*d,.25*e,0,.5*e,.16*d,.55*e);a.curveTo(0,.66*e,.18*d,.9*e,.31*d,.8*e);a.curveTo(.4*d,e,.7*d,e,.8*d,.8*e);a.curveTo(d,.8*e,d,.6*e,.875*d,.5*e);a.curveTo(d,.3*e,.8*d,.1*e,.625*d,.2*e);a.curveTo(.5*d,.05*e,.3*d,.05*e,.25*d,.25*e);a.close()};function mxRectangleShape(a,b,c,d){mxShape.call(this);this.bounds=a;this.fill=b;this.stroke=c;this.strokewidth=null!=d?d:1}mxUtils.extend(mxRectangleShape,mxShape);
+mxRectangleShape.prototype.isHtmlAllowed=function(){var a=!0;null!=this.style&&(a="1"==mxUtils.getValue(this.style,mxConstants.STYLE_POINTER_EVENTS,"1"));return!this.isRounded&&!this.glass&&0==this.rotation&&(a||null!=this.fill&&this.fill!=mxConstants.NONE)};
+mxRectangleShape.prototype.paintBackground=function(a,b,c,d,e){var f=!0;null!=this.style&&(f="1"==mxUtils.getValue(this.style,mxConstants.STYLE_POINTER_EVENTS,"1"));if(f||null!=this.fill&&this.fill!=mxConstants.NONE||null!=this.stroke&&this.stroke!=mxConstants.NONE)f||null!=this.fill&&this.fill!=mxConstants.NONE||(a.pointerEvents=!1),this.isRounded?("1"==mxUtils.getValue(this.style,mxConstants.STYLE_ABSOLUTE_ARCSIZE,0)?f=Math.min(d/2,Math.min(e/2,mxUtils.getValue(this.style,mxConstants.STYLE_ARCSIZE,
+mxConstants.LINE_ARCSIZE)/2)):(f=mxUtils.getValue(this.style,mxConstants.STYLE_ARCSIZE,100*mxConstants.RECTANGLE_ROUNDING_FACTOR)/100,f=Math.min(d*f,e*f)),a.roundrect(b,c,d,e,f,f)):a.rect(b,c,d,e),a.fillAndStroke()};mxRectangleShape.prototype.paintForeground=function(a,b,c,d,e){this.glass&&!this.outline&&null!=this.fill&&this.fill!=mxConstants.NONE&&this.paintGlassEffect(a,b,c,d,e,this.getArcSize(d+this.strokewidth,e+this.strokewidth))};
+function mxEllipse(a,b,c,d){mxShape.call(this);this.bounds=a;this.fill=b;this.stroke=c;this.strokewidth=null!=d?d:1}mxUtils.extend(mxEllipse,mxShape);mxEllipse.prototype.paintVertexShape=function(a,b,c,d,e){a.ellipse(b,c,d,e);a.fillAndStroke()};function mxDoubleEllipse(a,b,c,d){mxShape.call(this);this.bounds=a;this.fill=b;this.stroke=c;this.strokewidth=null!=d?d:1}mxUtils.extend(mxDoubleEllipse,mxShape);mxDoubleEllipse.prototype.vmlScale=10;
+mxDoubleEllipse.prototype.paintBackground=function(a,b,c,d,e){a.ellipse(b,c,d,e);a.fillAndStroke()};mxDoubleEllipse.prototype.paintForeground=function(a,b,c,d,e){if(!this.outline){var f=mxUtils.getValue(this.style,mxConstants.STYLE_MARGIN,Math.min(3+this.strokewidth,Math.min(d/5,e/5)));d-=2*f;e-=2*f;0<d&&0<e&&a.ellipse(b+f,c+f,d,e);a.stroke()}};
+mxDoubleEllipse.prototype.getLabelBounds=function(a){var b=mxUtils.getValue(this.style,mxConstants.STYLE_MARGIN,Math.min(3+this.strokewidth,Math.min(a.width/5/this.scale,a.height/5/this.scale)))*this.scale;return new mxRectangle(a.x+b,a.y+b,a.width-2*b,a.height-2*b)};function mxRhombus(a,b,c,d){mxShape.call(this);this.bounds=a;this.fill=b;this.stroke=c;this.strokewidth=null!=d?d:1}mxUtils.extend(mxRhombus,mxShape);
+mxRhombus.prototype.paintVertexShape=function(a,b,c,d,e){var f=d/2,g=e/2,k=mxUtils.getValue(this.style,mxConstants.STYLE_ARCSIZE,mxConstants.LINE_ARCSIZE)/2;a.begin();this.addPoints(a,[new mxPoint(b+f,c),new mxPoint(b+d,c+g),new mxPoint(b+f,c+e),new mxPoint(b,c+g)],this.isRounded,k,!0);a.fillAndStroke()};function mxPolyline(a,b,c){mxShape.call(this);this.points=a;this.stroke=b;this.strokewidth=null!=c?c:1}mxUtils.extend(mxPolyline,mxShape);mxPolyline.prototype.getRotation=function(){return 0};
+mxPolyline.prototype.getShapeRotation=function(){return 0};mxPolyline.prototype.isPaintBoundsInverted=function(){return!1};mxPolyline.prototype.paintEdgeShape=function(a,b){null==this.style||1!=this.style[mxConstants.STYLE_CURVED]?this.paintLine(a,b,this.isRounded):this.paintCurvedLine(a,b)};mxPolyline.prototype.paintLine=function(a,b,c){var d=mxUtils.getValue(this.style,mxConstants.STYLE_ARCSIZE,mxConstants.LINE_ARCSIZE)/2;a.begin();this.addPoints(a,b,c,d,!1);a.stroke()};
+mxPolyline.prototype.paintCurvedLine=function(a,b){a.begin();var c=b[0],d=b.length;a.moveTo(c.x,c.y);for(c=1;c<d-2;c++){var e=b[c],f=b[c+1];a.quadTo(e.x,e.y,(e.x+f.x)/2,(e.y+f.y)/2)}e=b[d-2];f=b[d-1];a.quadTo(e.x,e.y,f.x,f.y);a.stroke()};
+function mxArrow(a,b,c,d,e,f,g){mxShape.call(this);this.points=a;this.fill=b;this.stroke=c;this.strokewidth=null!=d?d:1;this.arrowWidth=null!=e?e:mxConstants.ARROW_WIDTH;this.spacing=null!=f?f:mxConstants.ARROW_SPACING;this.endSize=null!=g?g:mxConstants.ARROW_SIZE}mxUtils.extend(mxArrow,mxShape);mxArrow.prototype.augmentBoundingBox=function(a){mxShape.prototype.augmentBoundingBox.apply(this,arguments);a.grow((Math.max(this.arrowWidth,this.endSize)/2+this.strokewidth)*this.scale)};
+mxArrow.prototype.paintEdgeShape=function(a,b){var c=mxConstants.ARROW_SPACING,d=mxConstants.ARROW_WIDTH,e=b[0],f=b[b.length-1],g=f.x-e.x,k=f.y-e.y,l=Math.sqrt(g*g+k*k),m=l-2*c-mxConstants.ARROW_SIZE,g=g/l,k=k/l,l=d*k/3,d=-d*g/3,n=e.x-l/2+c*g,e=e.y-d/2+c*k,p=n+l,q=e+d,r=p+m*g,m=q+m*k,t=r+l,u=m+d,x=t-3*l,y=u-3*d;a.begin();a.moveTo(n,e);a.lineTo(p,q);a.lineTo(r,m);a.lineTo(t,u);a.lineTo(f.x-c*g,f.y-c*k);a.lineTo(x,y);a.lineTo(x+l,y+d);a.close();a.fillAndStroke()};
+function mxArrowConnector(a,b,c,d,e,f,g){mxShape.call(this);this.points=a;this.fill=b;this.stroke=c;this.strokewidth=null!=d?d:1;this.arrowWidth=null!=e?e:mxConstants.ARROW_WIDTH;this.arrowSpacing=null!=f?f:mxConstants.ARROW_SPACING;this.startSize=mxConstants.ARROW_SIZE/5;this.endSize=mxConstants.ARROW_SIZE/5}mxUtils.extend(mxArrowConnector,mxShape);mxArrowConnector.prototype.useSvgBoundingBox=!0;
+mxArrowConnector.prototype.resetStyles=function(){mxShape.prototype.resetStyles.apply(this,arguments);this.arrowSpacing=mxConstants.ARROW_SPACING};mxArrowConnector.prototype.apply=function(a){mxShape.prototype.apply.apply(this,arguments);null!=this.style&&(this.startSize=3*mxUtils.getNumber(this.style,mxConstants.STYLE_STARTSIZE,mxConstants.ARROW_SIZE/5),this.endSize=3*mxUtils.getNumber(this.style,mxConstants.STYLE_ENDSIZE,mxConstants.ARROW_SIZE/5))};
+mxArrowConnector.prototype.augmentBoundingBox=function(a){mxShape.prototype.augmentBoundingBox.apply(this,arguments);var b=this.getEdgeWidth();this.isMarkerStart()&&(b=Math.max(b,this.getStartArrowWidth()));this.isMarkerEnd()&&(b=Math.max(b,this.getEndArrowWidth()));a.grow((b/2+this.strokewidth)*this.scale)};
+mxArrowConnector.prototype.paintEdgeShape=function(a,b){var c=this.strokewidth;this.outline&&(c=Math.max(1,mxUtils.getNumber(this.style,mxConstants.STYLE_STROKEWIDTH,this.strokewidth)));for(var d=this.getStartArrowWidth()+c,e=this.getEndArrowWidth()+c,f=this.outline?this.getEdgeWidth()+c:this.getEdgeWidth(),g=this.isOpenEnded(),k=this.isMarkerStart(),l=this.isMarkerEnd(),m=g?0:this.arrowSpacing+c/2,n=this.startSize+c,c=this.endSize+c,p=this.isArrowRounded(),q=b[b.length-1],r=1;r<b.length-1&&b[r].x==
+b[0].x&&b[r].y==b[0].y;)r++;var t=b[r].x-b[0].x,r=b[r].y-b[0].y,u=Math.sqrt(t*t+r*r);if(0!=u){var x=t/u,y,A=x,z=r/u,v,B=z,u=f*z,C=-f*x,F=[];p?a.setLineJoin("round"):2<b.length&&a.setMiterLimit(1.42);a.begin();t=x;r=z;if(k&&!g)this.paintMarker(a,b[0].x,b[0].y,x,z,n,d,f,m,!0);else{y=b[0].x+u/2+m*x;v=b[0].y+C/2+m*z;var D=b[0].x-u/2+m*x,I=b[0].y-C/2+m*z;g?(a.moveTo(y,v),F.push(function(){a.lineTo(D,I)})):(a.moveTo(D,I),a.lineTo(y,v))}for(var E=v=y=0,u=0;u<b.length-2;u++)if(C=mxUtils.relativeCcw(b[u].x,
+b[u].y,b[u+1].x,b[u+1].y,b[u+2].x,b[u+2].y),y=b[u+2].x-b[u+1].x,v=b[u+2].y-b[u+1].y,E=Math.sqrt(y*y+v*v),0!=E&&(A=y/E,B=v/E,tmp=Math.max(Math.sqrt((x*A+z*B+1)/2),.04),y=x+A,v=z+B,E=Math.sqrt(y*y+v*v),0!=E)){y/=E;v/=E;var E=Math.max(tmp,Math.min(this.strokewidth/200+.04,.35)),E=0!=C&&p?Math.max(.1,E):Math.max(tmp,.06),G=b[u+1].x+v*f/2/E,H=b[u+1].y-y*f/2/E;v=b[u+1].x-v*f/2/E;y=b[u+1].y+y*f/2/E;0!=C&&p?-1==C?(C=v+B*f,E=y-A*f,a.lineTo(v+z*f,y-x*f),a.quadTo(G,H,C,E),function(b,c){F.push(function(){a.lineTo(b,
+c)})}(v,y)):(a.lineTo(G,H),function(b,c){var d=G-z*f,e=H+x*f,g=G-B*f,k=H+A*f;F.push(function(){a.quadTo(b,c,d,e)});F.push(function(){a.lineTo(g,k)})}(v,y)):(a.lineTo(G,H),function(b,c){F.push(function(){a.lineTo(b,c)})}(v,y));x=A;z=B}u=f*B;C=-f*A;if(l&&!g)this.paintMarker(a,q.x,q.y,-x,-z,c,e,f,m,!1);else{a.lineTo(q.x-m*A+u/2,q.y-m*B+C/2);var J=q.x-m*A-u/2,K=q.y-m*B-C/2;g?(a.moveTo(J,K),F.splice(0,0,function(){a.moveTo(J,K)})):a.lineTo(J,K)}for(u=F.length-1;0<=u;u--)F[u]();g?(a.end(),a.stroke()):(a.close(),
+a.fillAndStroke());a.setShadow(!1);a.setMiterLimit(4);p&&a.setLineJoin("flat");2<b.length&&(a.setMiterLimit(4),k&&!g&&(a.begin(),this.paintMarker(a,b[0].x,b[0].y,t,r,n,d,f,m,!0),a.stroke(),a.end()),l&&!g&&(a.begin(),this.paintMarker(a,q.x,q.y,-x,-z,c,e,f,m,!0),a.stroke(),a.end()))}};
+mxArrowConnector.prototype.paintMarker=function(a,b,c,d,e,f,g,k,l,m){g=k/g;var n=k*e/2;k=-k*d/2;var p=(l+f)*d;f=(l+f)*e;m?a.moveTo(b-n+p,c-k+f):a.lineTo(b-n+p,c-k+f);a.lineTo(b-n/g+p,c-k/g+f);a.lineTo(b+l*d,c+l*e);a.lineTo(b+n/g+p,c+k/g+f);a.lineTo(b+n+p,c+k+f)};mxArrowConnector.prototype.isArrowRounded=function(){return this.isRounded};mxArrowConnector.prototype.getStartArrowWidth=function(){return mxConstants.ARROW_WIDTH};mxArrowConnector.prototype.getEndArrowWidth=function(){return mxConstants.ARROW_WIDTH};
+mxArrowConnector.prototype.getEdgeWidth=function(){return mxConstants.ARROW_WIDTH/3};mxArrowConnector.prototype.isOpenEnded=function(){return!1};mxArrowConnector.prototype.isMarkerStart=function(){return mxUtils.getValue(this.style,mxConstants.STYLE_STARTARROW,mxConstants.NONE)!=mxConstants.NONE};mxArrowConnector.prototype.isMarkerEnd=function(){return mxUtils.getValue(this.style,mxConstants.STYLE_ENDARROW,mxConstants.NONE)!=mxConstants.NONE};
+function mxText(a,b,c,d,e,f,g,k,l,m,n,p,q,r,t,u,x,y,A,z,v){mxShape.call(this);this.value=a;this.bounds=b;this.color=null!=e?e:"black";this.align=null!=c?c:mxConstants.ALIGN_CENTER;this.valign=null!=d?d:mxConstants.ALIGN_MIDDLE;this.family=null!=f?f:mxConstants.DEFAULT_FONTFAMILY;this.size=null!=g?g:mxConstants.DEFAULT_FONTSIZE;this.fontStyle=null!=k?k:mxConstants.DEFAULT_FONTSTYLE;this.spacing=parseInt(l||2);this.spacingTop=this.spacing+parseInt(m||0);this.spacingRight=this.spacing+parseInt(n||0);
+this.spacingBottom=this.spacing+parseInt(p||0);this.spacingLeft=this.spacing+parseInt(q||0);this.horizontal=null!=r?r:!0;this.background=t;this.border=u;this.wrap=null!=x?x:!1;this.clipped=null!=y?y:!1;this.overflow=null!=A?A:"visible";this.labelPadding=null!=z?z:0;this.textDirection=v;this.rotation=0;this.updateMargin()}mxUtils.extend(mxText,mxShape);mxText.prototype.baseSpacingTop=0;mxText.prototype.baseSpacingBottom=0;mxText.prototype.baseSpacingLeft=0;mxText.prototype.baseSpacingRight=0;
+mxText.prototype.replaceLinefeeds=!0;mxText.prototype.verticalTextRotation=-90;mxText.prototype.ignoreClippedStringSize=!0;mxText.prototype.ignoreStringSize=!1;mxText.prototype.textWidthPadding=8!=document.documentMode||mxClient.IS_EM?3:4;mxText.prototype.lastValue=null;mxText.prototype.cacheEnabled=!0;mxText.prototype.isParseVml=function(){return!1};mxText.prototype.isHtmlAllowed=function(){return 8!=document.documentMode||mxClient.IS_EM};mxText.prototype.getSvgScreenOffset=function(){return 0};
+mxText.prototype.checkBounds=function(){return!isNaN(this.scale)&&isFinite(this.scale)&&0<this.scale&&null!=this.bounds&&!isNaN(this.bounds.x)&&!isNaN(this.bounds.y)&&!isNaN(this.bounds.width)&&!isNaN(this.bounds.height)};
+mxText.prototype.paint=function(a,b){var c=this.scale,d=this.bounds.x/c,e=this.bounds.y/c,f=this.bounds.width/c,c=this.bounds.height/c;this.updateTransform(a,d,e,f,c);this.configureCanvas(a,d,e,f,c);var g=null!=this.state?this.state.unscaledWidth:null;if(b)null==this.node.firstChild||null!=g&&this.lastUnscaledWidth==g||a.invalidateCachedOffsetSize(this.node),a.updateText(d,e,f,c,this.align,this.valign,this.wrap,this.overflow,this.clipped,this.getTextRotation(),this.node);else{var k=mxUtils.isNode(this.value)||
+this.dialect==mxConstants.DIALECT_STRICTHTML,l=k||a instanceof mxVmlCanvas2D?"html":"",m=this.value;k||"html"!=l||(m=mxUtils.htmlEntities(m,!1));"html"!=l||mxUtils.isNode(this.value)||(m=mxUtils.replaceTrailingNewlines(m,"<div><br></div>"));var m=!mxUtils.isNode(this.value)&&this.replaceLinefeeds&&"html"==l?m.replace(/\n/g,"<br/>"):m,n=this.textDirection;n!=mxConstants.TEXT_DIRECTION_AUTO||k||(n=this.getAutoDirection());n!=mxConstants.TEXT_DIRECTION_LTR&&n!=mxConstants.TEXT_DIRECTION_RTL&&(n=null);
+a.text(d,e,f,c,m,this.align,this.valign,this.wrap,l,this.overflow,this.clipped,this.getTextRotation(),n)}this.lastUnscaledWidth=g};
+mxText.prototype.redraw=function(){if(this.visible&&this.checkBounds()&&this.cacheEnabled&&this.lastValue==this.value&&(mxUtils.isNode(this.value)||this.dialect==mxConstants.DIALECT_STRICTHTML))if("DIV"!=this.node.nodeName||!this.isHtmlAllowed()&&mxClient.IS_VML){var a=this.createCanvas();null!=a&&null!=a.updateText&&null!=a.invalidateCachedOffsetSize?(this.paint(a,!0),this.destroyCanvas(a),this.updateBoundingBox()):mxShape.prototype.redraw.apply(this,arguments)}else this.updateSize(this.node,null==
+this.state||null==this.state.view.textDiv),mxClient.IS_IE&&(null==document.documentMode||8>=document.documentMode)?this.updateHtmlFilter():this.updateHtmlTransform(),this.updateBoundingBox();else mxShape.prototype.redraw.apply(this,arguments),mxUtils.isNode(this.value)||this.dialect==mxConstants.DIALECT_STRICTHTML?this.lastValue=this.value:this.lastValue=null};
+mxText.prototype.resetStyles=function(){mxShape.prototype.resetStyles.apply(this,arguments);this.color="black";this.align=mxConstants.ALIGN_CENTER;this.valign=mxConstants.ALIGN_MIDDLE;this.family=mxConstants.DEFAULT_FONTFAMILY;this.size=mxConstants.DEFAULT_FONTSIZE;this.fontStyle=mxConstants.DEFAULT_FONTSTYLE;this.spacingLeft=this.spacingBottom=this.spacingRight=this.spacingTop=this.spacing=2;this.horizontal=!0;delete this.background;delete this.border;this.textDirection=mxConstants.DEFAULT_TEXT_DIRECTION;
+delete this.margin};
+mxText.prototype.apply=function(a){var b=this.spacing;mxShape.prototype.apply.apply(this,arguments);null!=this.style&&(this.fontStyle=mxUtils.getValue(this.style,mxConstants.STYLE_FONTSTYLE,this.fontStyle),this.family=mxUtils.getValue(this.style,mxConstants.STYLE_FONTFAMILY,this.family),this.size=mxUtils.getValue(this.style,mxConstants.STYLE_FONTSIZE,this.size),this.color=mxUtils.getValue(this.style,mxConstants.STYLE_FONTCOLOR,this.color),this.align=mxUtils.getValue(this.style,mxConstants.STYLE_ALIGN,
+this.align),this.valign=mxUtils.getValue(this.style,mxConstants.STYLE_VERTICAL_ALIGN,this.valign),this.spacing=parseInt(mxUtils.getValue(this.style,mxConstants.STYLE_SPACING,this.spacing)),this.spacingTop=parseInt(mxUtils.getValue(this.style,mxConstants.STYLE_SPACING_TOP,this.spacingTop-b))+this.spacing,this.spacingRight=parseInt(mxUtils.getValue(this.style,mxConstants.STYLE_SPACING_RIGHT,this.spacingRight-b))+this.spacing,this.spacingBottom=parseInt(mxUtils.getValue(this.style,mxConstants.STYLE_SPACING_BOTTOM,
+this.spacingBottom-b))+this.spacing,this.spacingLeft=parseInt(mxUtils.getValue(this.style,mxConstants.STYLE_SPACING_LEFT,this.spacingLeft-b))+this.spacing,this.horizontal=mxUtils.getValue(this.style,mxConstants.STYLE_HORIZONTAL,this.horizontal),this.background=mxUtils.getValue(this.style,mxConstants.STYLE_LABEL_BACKGROUNDCOLOR,this.background),this.border=mxUtils.getValue(this.style,mxConstants.STYLE_LABEL_BORDERCOLOR,this.border),this.textDirection=mxUtils.getValue(this.style,mxConstants.STYLE_TEXT_DIRECTION,
+mxConstants.DEFAULT_TEXT_DIRECTION),this.opacity=mxUtils.getValue(this.style,mxConstants.STYLE_TEXT_OPACITY,100),this.updateMargin());this.flipH=this.flipV=null};mxText.prototype.getAutoDirection=function(){var a=/[A-Za-z\u05d0-\u065f\u066a-\u06ef\u06fa-\u07ff\ufb1d-\ufdff\ufe70-\ufefc]/.exec(this.value);return null!=a&&0<a.length&&"z"<a[0]?mxConstants.TEXT_DIRECTION_RTL:mxConstants.TEXT_DIRECTION_LTR};
+mxText.prototype.updateBoundingBox=function(){var a=this.node;this.boundingBox=this.bounds.clone();var b=this.getTextRotation(),c=null!=this.style?mxUtils.getValue(this.style,mxConstants.STYLE_LABEL_POSITION,mxConstants.ALIGN_CENTER):null,d=null!=this.style?mxUtils.getValue(this.style,mxConstants.STYLE_VERTICAL_LABEL_POSITION,mxConstants.ALIGN_MIDDLE):null;if(!(this.ignoreStringSize||null==a||"fill"==this.overflow||this.clipped&&this.ignoreClippedStringSize&&c==mxConstants.ALIGN_CENTER&&d==mxConstants.ALIGN_MIDDLE)){d=
+c=null;if(null!=a.ownerSVGElement)if(null!=a.firstChild&&null!=a.firstChild.firstChild&&"foreignObject"==a.firstChild.firstChild.nodeName)a=a.firstChild.firstChild,c=parseInt(a.getAttribute("width"))*this.scale,d=parseInt(a.getAttribute("height"))*this.scale;else try{var e=a.getBBox();"string"==typeof this.value&&0==mxUtils.trim(this.value)?this.boundingBox=null:this.boundingBox=0==e.width&&0==e.height?null:new mxRectangle(e.x,e.y,e.width,e.height);return}catch(f){}else{c=null!=this.state?this.state.view.textDiv:
+null;if(null==this.offsetWidth||null==this.offsetHeight)null!=c&&(this.updateFont(c),this.updateSize(c,!1),this.updateInnerHtml(c),a=c),e=a,8!=document.documentMode||mxClient.IS_EM?null!=e.firstChild&&"DIV"==e.firstChild.nodeName&&(e=e.firstChild):(d=Math.round(this.bounds.width/this.scale),this.wrap&&0<d?(a.style.wordWrap=mxConstants.WORD_WRAP,a.style.whiteSpace="normal","break-word"!=a.style.wordWrap&&(a=e.getElementsByTagName("div"),0<a.length&&(e=a[a.length-1]),c=e.offsetWidth+2,a=this.node.getElementsByTagName("div"),
+this.clipped&&(c=Math.min(d,c)),1<a.length&&(a[a.length-2].style.width=c+"px"))):a.style.whiteSpace="nowrap"),this.offsetWidth=e.offsetWidth+this.textWidthPadding,this.offsetHeight=e.offsetHeight;c=this.offsetWidth*this.scale;d=this.offsetHeight*this.scale}null!=c&&null!=d&&(this.boundingBox=new mxRectangle(this.bounds.x,this.bounds.y,c,d))}null!=this.boundingBox&&(0!=b?(b=mxUtils.getBoundingBox(new mxRectangle(this.margin.x*this.boundingBox.width,this.margin.y*this.boundingBox.height,this.boundingBox.width,
+this.boundingBox.height),b,new mxPoint(0,0)),this.unrotatedBoundingBox=mxRectangle.fromRectangle(this.boundingBox),this.unrotatedBoundingBox.x+=this.margin.x*this.unrotatedBoundingBox.width,this.unrotatedBoundingBox.y+=this.margin.y*this.unrotatedBoundingBox.height,this.boundingBox.x+=b.x,this.boundingBox.y+=b.y,this.boundingBox.width=b.width,this.boundingBox.height=b.height):(this.boundingBox.x+=this.margin.x*this.boundingBox.width,this.boundingBox.y+=this.margin.y*this.boundingBox.height,this.unrotatedBoundingBox=
+null))};mxText.prototype.getShapeRotation=function(){return 0};mxText.prototype.getTextRotation=function(){return null!=this.state&&null!=this.state.shape?this.state.shape.getTextRotation():0};mxText.prototype.isPaintBoundsInverted=function(){return!this.horizontal&&null!=this.state&&this.state.view.graph.model.isVertex(this.state.cell)};
+mxText.prototype.configureCanvas=function(a,b,c,d,e){mxShape.prototype.configureCanvas.apply(this,arguments);a.setFontColor(this.color);a.setFontBackgroundColor(this.background);a.setFontBorderColor(this.border);a.setFontFamily(this.family);a.setFontSize(this.size);a.setFontStyle(this.fontStyle)};
+mxText.prototype.updateVmlContainer=function(){this.node.style.left=Math.round(this.bounds.x)+"px";this.node.style.top=Math.round(this.bounds.y)+"px";this.node.style.width="1px";this.node.style.height="1px";this.node.style.overflow="visible"};
+mxText.prototype.redrawHtmlShape=function(){var a=this.node.style;a.whiteSpace="normal";a.overflow="";a.width="";a.height="";this.updateValue();this.updateFont(this.node);this.updateSize(this.node,null==this.state||null==this.state.view.textDiv);this.offsetHeight=this.offsetWidth=null;mxClient.IS_IE&&(null==document.documentMode||8>=document.documentMode)?this.updateHtmlFilter():this.updateHtmlTransform()};
+mxText.prototype.updateHtmlTransform=function(){var a=this.getTextRotation(),b=this.node.style,c=this.margin.x,d=this.margin.y;0!=a?(mxUtils.setPrefixedStyle(b,"transformOrigin",100*-c+"% "+100*-d+"%"),mxUtils.setPrefixedStyle(b,"transform","translate("+100*c+"%,"+100*d+"%)scale("+this.scale+") rotate("+a+"deg)")):(mxUtils.setPrefixedStyle(b,"transformOrigin","0% 0%"),mxUtils.setPrefixedStyle(b,"transform","scale("+this.scale+")translate("+100*c+"%,"+100*d+"%)"));b.left=Math.round(this.bounds.x-Math.ceil(c*
+("fill"!=this.overflow&&"width"!=this.overflow?3:1)))+"px";b.top=Math.round(this.bounds.y-d*("fill"!=this.overflow?3:1))+"px";b.opacity=100>this.opacity?this.opacity/100:""};
+mxText.prototype.updateInnerHtml=function(a){if(mxUtils.isNode(this.value))a.innerHTML=this.value.outerHTML;else{var b=this.value;this.dialect!=mxConstants.DIALECT_STRICTHTML&&(b=mxUtils.htmlEntities(b,!1));b=mxUtils.replaceTrailingNewlines(b,"<div>&nbsp;</div>");b=this.replaceLinefeeds?b.replace(/\n/g,"<br/>"):b;a.innerHTML='<div style="display:inline-block;_display:inline;">'+b+"</div>"}};
+mxText.prototype.updateHtmlFilter=function(){var a=this.node.style,b=this.margin.x,c=this.margin.y,d=this.scale;mxUtils.setOpacity(this.node,this.opacity);var e,f=0,g=null!=this.state?this.state.view.textDiv:null,k=this.node;if(null!=g){g.style.overflow="";g.style.height="";g.style.width="";this.updateFont(g);this.updateSize(g,!1);this.updateInnerHtml(g);var l=Math.round(this.bounds.width/this.scale);this.wrap&&0<l?(g.style.whiteSpace="normal",g.style.wordWrap=mxConstants.WORD_WRAP,e=l,this.clipped&&
+(e=Math.min(e,this.bounds.width)),g.style.width=e+"px"):g.style.whiteSpace="nowrap";k=g;null!=k.firstChild&&"DIV"==k.firstChild.nodeName&&(k=k.firstChild,this.wrap&&"break-word"==g.style.wordWrap&&(k.style.width="100%"));!this.clipped&&this.wrap&&0<l&&(e=k.offsetWidth+this.textWidthPadding,g.style.width=e+"px");f=k.offsetHeight+2;mxClient.IS_QUIRKS&&null!=this.border&&this.border!=mxConstants.NONE&&(f+=3)}else null!=k.firstChild&&"DIV"==k.firstChild.nodeName&&(k=k.firstChild,f=k.offsetHeight);e=k.offsetWidth+
+this.textWidthPadding;this.clipped&&(f=Math.min(f,this.bounds.height));l=this.bounds.width/d;g=this.bounds.height/d;"fill"==this.overflow?(f=g,e=l):"width"==this.overflow&&(f=k.scrollHeight,e=l);this.offsetWidth=e;this.offsetHeight=f;mxClient.IS_QUIRKS&&(this.clipped||"width"==this.overflow&&0<g)?(g=Math.min(g,f),a.height=Math.round(g)+"px"):g=f;"fill"!=this.overflow&&"width"!=this.overflow&&(this.clipped&&(e=Math.min(l,e)),l=e,mxClient.IS_QUIRKS&&this.clipped||this.wrap)&&(a.width=Math.round(l)+
+"px");var g=g*d,l=l*d,m=this.getTextRotation()*(Math.PI/180);e=parseFloat(parseFloat(Math.cos(m)).toFixed(8));f=parseFloat(parseFloat(Math.sin(-m)).toFixed(8));m%=2*Math.PI;0>m&&(m+=2*Math.PI);m%=Math.PI;m>Math.PI/2&&(m=Math.PI-m);var k=Math.cos(m),n=Math.sin(-m),b=l*-(b+.5),p=g*-(c+.5);0!=m&&(c="progid:DXImageTransform.Microsoft.Matrix(M11="+e+", M12="+f+", M21="+-f+", M22="+e+", sizingMethod='auto expand')",a.filter=null!=a.filter&&0<a.filter.length?a.filter+(" "+c):c);c=0;"fill"!=this.overflow&&
+mxClient.IS_QUIRKS&&(c=this.valign==mxConstants.ALIGN_TOP?c-1:this.valign==mxConstants.ALIGN_BOTTOM?c+2:c+1);a.zoom=d;a.left=Math.round(this.bounds.x+((l-l*k+g*n)/2-e*b-f*p)-l/2)+"px";a.top=Math.round(this.bounds.y+((g-g*k+l*n)/2+f*b-e*p)-g/2+c)+"px"};
+mxText.prototype.updateValue=function(){if(mxUtils.isNode(this.value))this.node.innerHTML="",this.node.appendChild(this.value);else{var a=this.value;this.dialect!=mxConstants.DIALECT_STRICTHTML&&(a=mxUtils.htmlEntities(a,!1));var a=mxUtils.replaceTrailingNewlines(a,"<div><br></div>"),a=this.replaceLinefeeds?a.replace(/\n/g,"<br/>"):a,b=null!=this.background&&this.background!=mxConstants.NONE?this.background:null,c=null!=this.border&&this.border!=mxConstants.NONE?this.border:null;if("fill"==this.overflow||
+"width"==this.overflow)null!=b&&(this.node.style.backgroundColor=b),null!=c&&(this.node.style.border="1px solid "+c);else{var d="";null!=b&&(d+="background-color:"+b+";");null!=c&&(d+="border:1px solid "+c+";");a='<div style="zoom:1;'+d+"display:inline-block;_display:inline;text-decoration:inherit;padding-bottom:1px;padding-right:1px;line-height:"+(mxConstants.ABSOLUTE_LINE_HEIGHT?this.size*mxConstants.LINE_HEIGHT+"px":mxConstants.LINE_HEIGHT)+'">'+a+"</div>"}this.node.innerHTML=a;a=this.node.getElementsByTagName("div");
+0<a.length&&(b=this.textDirection,b==mxConstants.TEXT_DIRECTION_AUTO&&this.dialect!=mxConstants.DIALECT_STRICTHTML&&(b=this.getAutoDirection()),b==mxConstants.TEXT_DIRECTION_LTR||b==mxConstants.TEXT_DIRECTION_RTL?a[a.length-1].setAttribute("dir",b):a[a.length-1].removeAttribute("dir"))}};
+mxText.prototype.updateFont=function(a){a=a.style;a.lineHeight=mxConstants.ABSOLUTE_LINE_HEIGHT?this.size*mxConstants.LINE_HEIGHT+"px":mxConstants.LINE_HEIGHT;a.fontSize=this.size+"px";a.fontFamily=this.family;a.verticalAlign="top";a.color=this.color;a.fontWeight=(this.fontStyle&mxConstants.FONT_BOLD)==mxConstants.FONT_BOLD?"bold":"";a.fontStyle=(this.fontStyle&mxConstants.FONT_ITALIC)==mxConstants.FONT_ITALIC?"italic":"";a.textDecoration=(this.fontStyle&mxConstants.FONT_UNDERLINE)==mxConstants.FONT_UNDERLINE?
+"underline":"";a.textAlign=this.align==mxConstants.ALIGN_CENTER?"center":this.align==mxConstants.ALIGN_RIGHT?"right":"left"};
+mxText.prototype.updateSize=function(a,b){var c=Math.max(0,Math.round(this.bounds.width/this.scale)),d=Math.max(0,Math.round(this.bounds.height/this.scale)),e=a.style;this.clipped?(e.overflow="hidden",mxClient.IS_QUIRKS?e.width=c+"px":(e.maxHeight=d+"px",e.maxWidth=c+"px")):"fill"==this.overflow?(e.width=c+1+"px",e.height=d+1+"px",e.overflow="hidden"):"width"==this.overflow&&(e.width=c+1+"px",e.maxHeight=d+1+"px",e.overflow="hidden");if(this.wrap&&0<c){if(e.wordWrap=mxConstants.WORD_WRAP,e.whiteSpace=
+"normal",e.width=c+"px",b&&"fill"!=this.overflow&&"width"!=this.overflow){d=a;null!=d.firstChild&&"DIV"==d.firstChild.nodeName&&(d=d.firstChild,"break-word"==a.style.wordWrap&&(d.style.width="100%"));var f=d.offsetWidth;if(0==f){var g=a.parentNode;a.style.visibility="hidden";document.body.appendChild(a);f=d.offsetWidth;a.style.visibility="";g.appendChild(a)}f+=3;this.clipped&&(f=Math.min(f,c));e.width=f+"px"}}else e.whiteSpace="nowrap"};
+mxText.prototype.updateMargin=function(){this.margin=mxUtils.getAlignmentAsPoint(this.align,this.valign)};
+mxText.prototype.getSpacing=function(){return new mxPoint(this.align==mxConstants.ALIGN_CENTER?(this.spacingLeft-this.spacingRight)/2:this.align==mxConstants.ALIGN_RIGHT?-this.spacingRight-this.baseSpacingRight:this.spacingLeft+this.baseSpacingLeft,this.valign==mxConstants.ALIGN_MIDDLE?(this.spacingTop-this.spacingBottom)/2:this.valign==mxConstants.ALIGN_BOTTOM?-this.spacingBottom-this.baseSpacingBottom:this.spacingTop+this.baseSpacingTop)};function mxTriangle(){mxActor.call(this)}
+mxUtils.extend(mxTriangle,mxActor);mxTriangle.prototype.redrawPath=function(a,b,c,d,e){b=mxUtils.getValue(this.style,mxConstants.STYLE_ARCSIZE,mxConstants.LINE_ARCSIZE)/2;this.addPoints(a,[new mxPoint(0,0),new mxPoint(d,.5*e),new mxPoint(0,e)],this.isRounded,b,!0)};function mxHexagon(){mxActor.call(this)}mxUtils.extend(mxHexagon,mxActor);
+mxHexagon.prototype.redrawPath=function(a,b,c,d,e){b=mxUtils.getValue(this.style,mxConstants.STYLE_ARCSIZE,mxConstants.LINE_ARCSIZE)/2;this.addPoints(a,[new mxPoint(.25*d,0),new mxPoint(.75*d,0),new mxPoint(d,.5*e),new mxPoint(.75*d,e),new mxPoint(.25*d,e),new mxPoint(0,.5*e)],this.isRounded,b,!0)};function mxLine(a,b,c){mxShape.call(this);this.bounds=a;this.stroke=b;this.strokewidth=null!=c?c:1}mxUtils.extend(mxLine,mxShape);
+mxLine.prototype.paintVertexShape=function(a,b,c,d,e){c+=e/2;a.begin();a.moveTo(b,c);a.lineTo(b+d,c);a.stroke()};function mxImageShape(a,b,c,d,e){mxShape.call(this);this.bounds=a;this.image=b;this.fill=c;this.stroke=d;this.strokewidth=null!=e?e:1;this.shadow=!1}mxUtils.extend(mxImageShape,mxRectangleShape);mxImageShape.prototype.preserveImageAspect=!0;mxImageShape.prototype.getSvgScreenOffset=function(){return 0};
+mxImageShape.prototype.apply=function(a){mxShape.prototype.apply.apply(this,arguments);this.gradient=this.stroke=this.fill=null;null!=this.style&&(this.preserveImageAspect=1==mxUtils.getNumber(this.style,mxConstants.STYLE_IMAGE_ASPECT,1),this.flipH=this.flipH||1==mxUtils.getValue(this.style,"imageFlipH",0),this.flipV=this.flipV||1==mxUtils.getValue(this.style,"imageFlipV",0))};mxImageShape.prototype.isHtmlAllowed=function(){return!this.preserveImageAspect};
+mxImageShape.prototype.createHtml=function(){var a=document.createElement("div");a.style.position="absolute";return a};
+mxImageShape.prototype.paintVertexShape=function(a,b,c,d,e){if(null!=this.image){var f=mxUtils.getValue(this.style,mxConstants.STYLE_IMAGE_BACKGROUND,null),g=mxUtils.getValue(this.style,mxConstants.STYLE_IMAGE_BORDER,null);null!=f&&(a.setFillColor(f),a.setStrokeColor(g),a.rect(b,c,d,e),a.fillAndStroke());a.image(b,c,d,e,this.image,this.preserveImageAspect,!1,!1);g=mxUtils.getValue(this.style,mxConstants.STYLE_IMAGE_BORDER,null);null!=g&&(a.setShadow(!1),a.setStrokeColor(g),a.rect(b,c,d,e),a.stroke())}else mxRectangleShape.prototype.paintBackground.apply(this,
+arguments)};
+mxImageShape.prototype.redrawHtmlShape=function(){this.node.style.left=Math.round(this.bounds.x)+"px";this.node.style.top=Math.round(this.bounds.y)+"px";this.node.style.width=Math.max(0,Math.round(this.bounds.width))+"px";this.node.style.height=Math.max(0,Math.round(this.bounds.height))+"px";this.node.innerHTML="";if(null!=this.image){var a=mxUtils.getValue(this.style,mxConstants.STYLE_IMAGE_BACKGROUND,""),b=mxUtils.getValue(this.style,mxConstants.STYLE_IMAGE_BORDER,"");this.node.style.backgroundColor=a;
+this.node.style.borderColor=b;a=document.createElement(mxClient.IS_IE6||(null==document.documentMode||8>=document.documentMode)&&0!=this.rotation?mxClient.VML_PREFIX+":image":"img");a.setAttribute("border","0");a.style.position="absolute";a.src=this.image;b=100>this.opacity?"alpha(opacity="+this.opacity+")":"";this.node.style.filter=b;this.flipH&&this.flipV?b+="progid:DXImageTransform.Microsoft.BasicImage(rotation=2)":this.flipH?b+="progid:DXImageTransform.Microsoft.BasicImage(mirror=1)":this.flipV&&
+(b+="progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)");a.style.filter!=b&&(a.style.filter=b);"image"==a.nodeName?a.style.rotation=this.rotation:0!=this.rotation?mxUtils.setPrefixedStyle(a.style,"transform","rotate("+this.rotation+"deg)"):mxUtils.setPrefixedStyle(a.style,"transform","");a.style.width=this.node.style.width;a.style.height=this.node.style.height;this.node.style.backgroundImage="";this.node.appendChild(a)}else this.setTransparentBackgroundImage(this.node)};
+function mxLabel(a,b,c,d){mxRectangleShape.call(this,a,b,c,d)}mxUtils.extend(mxLabel,mxRectangleShape);mxLabel.prototype.imageSize=mxConstants.DEFAULT_IMAGESIZE;mxLabel.prototype.spacing=2;mxLabel.prototype.indicatorSize=10;mxLabel.prototype.indicatorSpacing=2;mxLabel.prototype.init=function(a){mxShape.prototype.init.apply(this,arguments);null!=this.indicatorShape&&(this.indicator=new this.indicatorShape,this.indicator.dialect=this.dialect,this.indicator.init(this.node))};
+mxLabel.prototype.redraw=function(){null!=this.indicator&&(this.indicator.fill=this.indicatorColor,this.indicator.stroke=this.indicatorStrokeColor,this.indicator.gradient=this.indicatorGradientColor,this.indicator.direction=this.indicatorDirection);mxShape.prototype.redraw.apply(this,arguments)};mxLabel.prototype.isHtmlAllowed=function(){return mxRectangleShape.prototype.isHtmlAllowed.apply(this,arguments)&&null==this.indicatorColor&&null==this.indicatorShape};
+mxLabel.prototype.paintForeground=function(a,b,c,d,e){this.paintImage(a,b,c,d,e);this.paintIndicator(a,b,c,d,e);mxRectangleShape.prototype.paintForeground.apply(this,arguments)};mxLabel.prototype.paintImage=function(a,b,c,d,e){null!=this.image&&(b=this.getImageBounds(b,c,d,e),a.image(b.x,b.y,b.width,b.height,this.image,!1,!1,!1))};
+mxLabel.prototype.getImageBounds=function(a,b,c,d){var e=mxUtils.getValue(this.style,mxConstants.STYLE_IMAGE_ALIGN,mxConstants.ALIGN_LEFT),f=mxUtils.getValue(this.style,mxConstants.STYLE_IMAGE_VERTICAL_ALIGN,mxConstants.ALIGN_MIDDLE),g=mxUtils.getNumber(this.style,mxConstants.STYLE_IMAGE_WIDTH,mxConstants.DEFAULT_IMAGESIZE),k=mxUtils.getNumber(this.style,mxConstants.STYLE_IMAGE_HEIGHT,mxConstants.DEFAULT_IMAGESIZE),l=mxUtils.getNumber(this.style,mxConstants.STYLE_SPACING,this.spacing)+5;a=e==mxConstants.ALIGN_CENTER?
+a+(c-g)/2:e==mxConstants.ALIGN_RIGHT?a+(c-g-l):a+l;b=f==mxConstants.ALIGN_TOP?b+l:f==mxConstants.ALIGN_BOTTOM?b+(d-k-l):b+(d-k)/2;return new mxRectangle(a,b,g,k)};mxLabel.prototype.paintIndicator=function(a,b,c,d,e){null!=this.indicator?(this.indicator.bounds=this.getIndicatorBounds(b,c,d,e),this.indicator.paint(a)):null!=this.indicatorImage&&(b=this.getIndicatorBounds(b,c,d,e),a.image(b.x,b.y,b.width,b.height,this.indicatorImage,!1,!1,!1))};
+mxLabel.prototype.getIndicatorBounds=function(a,b,c,d){var e=mxUtils.getValue(this.style,mxConstants.STYLE_IMAGE_ALIGN,mxConstants.ALIGN_LEFT),f=mxUtils.getValue(this.style,mxConstants.STYLE_IMAGE_VERTICAL_ALIGN,mxConstants.ALIGN_MIDDLE),g=mxUtils.getNumber(this.style,mxConstants.STYLE_INDICATOR_WIDTH,this.indicatorSize),k=mxUtils.getNumber(this.style,mxConstants.STYLE_INDICATOR_HEIGHT,this.indicatorSize),l=this.spacing+5;a=e==mxConstants.ALIGN_RIGHT?a+(c-g-l):e==mxConstants.ALIGN_CENTER?a+(c-g)/
+2:a+l;b=f==mxConstants.ALIGN_BOTTOM?b+(d-k-l):f==mxConstants.ALIGN_TOP?b+l:b+(d-k)/2;return new mxRectangle(a,b,g,k)};
+mxLabel.prototype.redrawHtmlShape=function(){for(mxRectangleShape.prototype.redrawHtmlShape.apply(this,arguments);this.node.hasChildNodes();)this.node.removeChild(this.node.lastChild);if(null!=this.image){var a=document.createElement("img");a.style.position="relative";a.setAttribute("border","0");var b=this.getImageBounds(this.bounds.x,this.bounds.y,this.bounds.width,this.bounds.height);b.x-=this.bounds.x;b.y-=this.bounds.y;a.style.left=Math.round(b.x)+"px";a.style.top=Math.round(b.y)+"px";a.style.width=
+Math.round(b.width)+"px";a.style.height=Math.round(b.height)+"px";a.src=this.image;this.node.appendChild(a)}};function mxCylinder(a,b,c,d){mxShape.call(this);this.bounds=a;this.fill=b;this.stroke=c;this.strokewidth=null!=d?d:1}mxUtils.extend(mxCylinder,mxShape);mxCylinder.prototype.maxHeight=40;mxCylinder.prototype.svgStrokeTolerance=0;
+mxCylinder.prototype.paintVertexShape=function(a,b,c,d,e){a.translate(b,c);a.begin();this.redrawPath(a,b,c,d,e,!1);a.fillAndStroke();a.setShadow(!1);a.begin();this.redrawPath(a,b,c,d,e,!0);a.stroke()};
+mxCylinder.prototype.redrawPath=function(a,b,c,d,e,f){b=Math.min(this.maxHeight,Math.round(e/5));if(f&&null!=this.fill||!f&&null==this.fill)a.moveTo(0,b),a.curveTo(0,2*b,d,2*b,d,b),f||(a.stroke(),a.begin());f||(a.moveTo(0,b),a.curveTo(0,-b/3,d,-b/3,d,b),a.lineTo(d,e-b),a.curveTo(d,e+b/3,0,e+b/3,0,e-b),a.close())};function mxConnector(a,b,c){mxPolyline.call(this,a,b,c)}mxUtils.extend(mxConnector,mxPolyline);
+mxConnector.prototype.updateBoundingBox=function(){this.useSvgBoundingBox=null!=this.style&&1==this.style[mxConstants.STYLE_CURVED];mxShape.prototype.updateBoundingBox.apply(this,arguments)};mxConnector.prototype.paintEdgeShape=function(a,b){var c=this.createMarker(a,b,!0),d=this.createMarker(a,b,!1);mxPolyline.prototype.paintEdgeShape.apply(this,arguments);a.setFillColor(this.stroke);a.setShadow(!1);a.setDashed(!1);null!=c&&c();null!=d&&d()};
+mxConnector.prototype.createMarker=function(a,b,c){var d=null,e=b.length,f=mxUtils.getValue(this.style,c?mxConstants.STYLE_STARTARROW:mxConstants.STYLE_ENDARROW),g=c?b[1]:b[e-2],k=c?b[0]:b[e-1];if(null!=f&&null!=g&&null!=k){for(d=1;d<e-1&&0==Math.round(g.x-k.x)&&0==Math.round(g.y-k.y);)g=c?b[1+d]:b[e-2-d],d++;b=k.x-g.x;e=k.y-g.y;d=Math.max(1,Math.sqrt(b*b+e*e));g=b/d;b=e/d;e=mxUtils.getNumber(this.style,c?mxConstants.STYLE_STARTSIZE:mxConstants.STYLE_ENDSIZE,mxConstants.DEFAULT_MARKERSIZE);d=mxMarker.createMarker(a,
+this,f,k,g,b,e,c,this.strokewidth,0!=this.style[c?mxConstants.STYLE_STARTFILL:mxConstants.STYLE_ENDFILL])}return d};
+mxConnector.prototype.augmentBoundingBox=function(a){mxShape.prototype.augmentBoundingBox.apply(this,arguments);var b=0;mxUtils.getValue(this.style,mxConstants.STYLE_STARTARROW,mxConstants.NONE)!=mxConstants.NONE&&(b=mxUtils.getNumber(this.style,mxConstants.STYLE_STARTSIZE,mxConstants.DEFAULT_MARKERSIZE)+1);mxUtils.getValue(this.style,mxConstants.STYLE_ENDARROW,mxConstants.NONE)!=mxConstants.NONE&&(b=Math.max(b,mxUtils.getNumber(this.style,mxConstants.STYLE_ENDSIZE,mxConstants.DEFAULT_MARKERSIZE))+
+1);a.grow(b*this.scale)};function mxSwimlane(a,b,c,d){mxShape.call(this);this.bounds=a;this.fill=b;this.stroke=c;this.strokewidth=null!=d?d:1}mxUtils.extend(mxSwimlane,mxShape);mxSwimlane.prototype.imageSize=16;mxSwimlane.prototype.getTitleSize=function(){return Math.max(0,mxUtils.getValue(this.style,mxConstants.STYLE_STARTSIZE,mxConstants.DEFAULT_STARTSIZE))};
+mxSwimlane.prototype.getLabelBounds=function(a){var b=this.getTitleSize();a=new mxRectangle(a.x,a.y,a.width,a.height);var c=this.isHorizontal(),d=1==mxUtils.getValue(this.style,mxConstants.STYLE_FLIPH,0),e=1==mxUtils.getValue(this.style,mxConstants.STYLE_FLIPV,0),f=this.direction==mxConstants.DIRECTION_NORTH||this.direction==mxConstants.DIRECTION_SOUTH,c=c==!f,d=!c&&d!=(this.direction==mxConstants.DIRECTION_SOUTH||this.direction==mxConstants.DIRECTION_WEST),e=c&&e!=(this.direction==mxConstants.DIRECTION_SOUTH||
+this.direction==mxConstants.DIRECTION_WEST);if(f){b=Math.min(a.width,b*this.scale);if(d||e)a.x+=a.width-b;a.width=b}else{b=Math.min(a.height,b*this.scale);if(d||e)a.y+=a.height-b;a.height=b}return a};mxSwimlane.prototype.getGradientBounds=function(a,b,c,d,e){a=this.getTitleSize();if(this.isHorizontal())return a=Math.min(a,e),new mxRectangle(b,c,d,a);a=Math.min(a,d);return new mxRectangle(b,c,a,e)};
+mxSwimlane.prototype.getArcSize=function(a,b,c){a=mxUtils.getValue(this.style,mxConstants.STYLE_ARCSIZE,100*mxConstants.RECTANGLE_ROUNDING_FACTOR)/100;return c*a*3};mxSwimlane.prototype.isHorizontal=function(){return 1==mxUtils.getValue(this.style,mxConstants.STYLE_HORIZONTAL,1)};
+mxSwimlane.prototype.paintVertexShape=function(a,b,c,d,e){var f=this.getTitleSize(),g=mxUtils.getValue(this.style,mxConstants.STYLE_SWIMLANE_FILLCOLOR,mxConstants.NONE),k=1==mxUtils.getValue(this.style,mxConstants.STYLE_SWIMLANE_LINE,1),l=0,f=this.isHorizontal()?Math.min(f,e):Math.min(f,d);a.translate(b,c);this.isRounded?(l=this.getArcSize(d,e,f),this.paintRoundedSwimlane(a,b,c,d,e,f,l,g,k)):this.paintSwimlane(a,b,c,d,e,f,g,k);g=mxUtils.getValue(this.style,mxConstants.STYLE_SEPARATORCOLOR,mxConstants.NONE);
+this.paintSeparator(a,b,c,d,e,f,g);null!=this.image&&(e=this.getImageBounds(b,c,d,e),a.image(e.x-b,e.y-c,e.width,e.height,this.image,!1,!1,!1));this.glass&&(a.setShadow(!1),this.paintGlassEffect(a,0,0,d,f,l))};
+mxSwimlane.prototype.paintSwimlane=function(a,b,c,d,e,f,g,k){g!=mxConstants.NONE&&(a.save(),a.setFillColor(g),a.rect(0,0,d,e),a.fillAndStroke(),a.restore(),a.setShadow(!1));a.begin();0==f&&(f=e);this.isHorizontal()?(a.moveTo(0,f),a.lineTo(0,0),a.lineTo(d,0),a.lineTo(d,f),(k||f>=e)&&a.close(),a.fillAndStroke(),f<e&&g==mxConstants.NONE&&(a.pointerEvents=!1,a.begin(),a.moveTo(0,f),a.lineTo(0,e),a.lineTo(d,e),a.lineTo(d,f),a.stroke())):(a.moveTo(f,0),a.lineTo(0,0),a.lineTo(0,e),a.lineTo(f,e),(k||f>=d)&&
+a.close(),a.fillAndStroke(),f<d&&g==mxConstants.NONE&&(a.pointerEvents=!1,a.begin(),a.moveTo(f,0),a.lineTo(d,0),a.lineTo(d,e),a.lineTo(f,e),a.stroke()))};
+mxSwimlane.prototype.paintRoundedSwimlane=function(a,b,c,d,e,f,g,k,l){g=Math.min(e-f,Math.min(f,g));k!=mxConstants.NONE&&(a.save(),a.setFillColor(k),a.roundrect(0,0,d,e,g,g),a.fillAndStroke(),a.restore(),a.setShadow(!1));a.begin();this.isHorizontal()?(a.moveTo(d,f),a.lineTo(d,g),a.quadTo(d,0,d-Math.min(d/2,g),0),a.lineTo(Math.min(d/2,g),0),a.quadTo(0,0,0,g),a.lineTo(0,f),(l||f>=e)&&a.close(),a.fillAndStroke(),f<e&&k==mxConstants.NONE&&(a.pointerEvents=!1,a.begin(),a.moveTo(0,f),a.lineTo(0,e-g),a.quadTo(0,
+e,Math.min(d/2,g),e),a.lineTo(d-Math.min(d/2,g),e),a.quadTo(d,e,d,e-g),a.lineTo(d,f),a.stroke())):(a.moveTo(f,0),a.lineTo(g,0),a.quadTo(0,0,0,Math.min(e/2,g)),a.lineTo(0,e-Math.min(e/2,g)),a.quadTo(0,e,g,e),a.lineTo(f,e),(l||f>=d)&&a.close(),a.fillAndStroke(),f<d&&k==mxConstants.NONE&&(a.pointerEvents=!1,a.begin(),a.moveTo(f,e),a.lineTo(d-g,e),a.quadTo(d,e,d,e-Math.min(e/2,g)),a.lineTo(d,Math.min(e/2,g)),a.quadTo(d,0,d-g,0),a.lineTo(f,0),a.stroke()))};
+mxSwimlane.prototype.paintSeparator=function(a,b,c,d,e,f,g){g!=mxConstants.NONE&&(a.setStrokeColor(g),a.setDashed(!0),a.begin(),this.isHorizontal()?(a.moveTo(d,f),a.lineTo(d,e)):(a.moveTo(f,0),a.lineTo(d,0)),a.stroke(),a.setDashed(!1))};mxSwimlane.prototype.getImageBounds=function(a,b,c,d){return this.isHorizontal()?new mxRectangle(a+c-this.imageSize,b,this.imageSize,this.imageSize):new mxRectangle(a,b,this.imageSize,this.imageSize)};function mxGraphLayout(a){this.graph=a}
+mxGraphLayout.prototype.graph=null;mxGraphLayout.prototype.useBoundingBox=!0;mxGraphLayout.prototype.parent=null;mxGraphLayout.prototype.moveCell=function(a,b,c){};mxGraphLayout.prototype.execute=function(a){};mxGraphLayout.prototype.getGraph=function(){return this.graph};mxGraphLayout.prototype.getConstraint=function(a,b,c,d){c=this.graph.view.getState(b);b=null!=c?c.style:this.graph.getCellStyle(b);return null!=b?b[a]:null};
+mxGraphLayout.traverse=function(a,b,c,d,e){if(null!=c&&null!=a&&(b=null!=b?b:!0,e=e||new mxDictionary,!e.get(a)&&(e.put(a,!0),d=c(a,d),null==d||d))&&(d=this.graph.model.getEdgeCount(a),0<d))for(var f=0;f<d;f++){var g=this.graph.model.getEdgeAt(a,f),k=this.graph.model.getTerminal(g,!0)==a;if(!b||k)k=this.graph.view.getVisibleTerminal(g,!k),this.traverse(k,b,c,g,e)}};
+mxGraphLayout.prototype.isAncestor=function(a,b,c){if(!c)return this.graph.model.getParent(cell)==a;if(b==a)return!1;for(;null!=b&&b!=a;)b=this.graph.model.getParent(b);return b==a};mxGraphLayout.prototype.isVertexMovable=function(a){return this.graph.isCellMovable(a)};mxGraphLayout.prototype.isVertexIgnored=function(a){return!this.graph.getModel().isVertex(a)||!this.graph.isCellVisible(a)};
+mxGraphLayout.prototype.isEdgeIgnored=function(a){var b=this.graph.getModel();return!b.isEdge(a)||!this.graph.isCellVisible(a)||null==b.getTerminal(a,!0)||null==b.getTerminal(a,!1)};mxGraphLayout.prototype.setEdgeStyleEnabled=function(a,b){this.graph.setCellStyles(mxConstants.STYLE_NOEDGESTYLE,b?"0":"1",[a])};mxGraphLayout.prototype.setOrthogonalEdge=function(a,b){this.graph.setCellStyles(mxConstants.STYLE_ORTHOGONAL,b?"1":"0",[a])};
+mxGraphLayout.prototype.getParentOffset=function(a){var b=new mxPoint;if(null!=a&&a!=this.parent){var c=this.graph.getModel();if(c.isAncestor(this.parent,a))for(var d=c.getGeometry(a);a!=this.parent;)b.x+=d.x,b.y+=d.y,a=c.getParent(a),d=c.getGeometry(a)}return b};
+mxGraphLayout.prototype.setEdgePoints=function(a,b){if(null!=a){var c=this.graph.model,d=c.getGeometry(a);null==d?(d=new mxGeometry,d.setRelative(!0)):d=d.clone();if(null!=this.parent&&null!=b)for(var e=c.getParent(a),e=this.getParentOffset(e),f=0;f<b.length;f++)b[f].x-=e.x,b[f].y-=e.y;d.points=b;c.setGeometry(a,d)}};
+mxGraphLayout.prototype.setVertexLocation=function(a,b,c){var d=this.graph.getModel(),e=d.getGeometry(a),f=null;if(null!=e){f=new mxRectangle(b,c,e.width,e.height);if(this.useBoundingBox){var g=this.graph.getView().getState(a);if(null!=g&&null!=g.text&&null!=g.text.boundingBox){var k=this.graph.getView().scale,l=g.text.boundingBox;g.text.boundingBox.x<g.x&&(b+=(g.x-l.x)/k,f.width=l.width);g.text.boundingBox.y<g.y&&(c+=(g.y-l.y)/k,f.height=l.height)}}null!=this.parent&&(g=d.getParent(a),null!=g&&g!=
+this.parent&&(g=this.getParentOffset(g),b-=g.x,c-=g.y));if(e.x!=b||e.y!=c)e=e.clone(),e.x=b,e.y=c,d.setGeometry(a,e)}return f};
+mxGraphLayout.prototype.getVertexBounds=function(a){var b=this.graph.getModel().getGeometry(a);if(this.useBoundingBox){var c=this.graph.getView().getState(a);if(null!=c&&null!=c.text&&null!=c.text.boundingBox)var d=this.graph.getView().scale,e=c.text.boundingBox,f=Math.max(c.x-e.x,0)/d,g=Math.max(c.y-e.y,0)/d,b=new mxRectangle(b.x-f,b.y-g,b.width+f+Math.max(e.x+e.width-(c.x+c.width),0)/d,b.height+g+Math.max(e.y+e.height-(c.y+c.height),0)/d)}null!=this.parent&&(a=this.graph.getModel().getParent(a),
+b=b.clone(),null!=a&&a!=this.parent&&(a=this.getParentOffset(a),b.x+=a.x,b.y+=a.y));return new mxRectangle(b.x,b.y,b.width,b.height)};mxGraphLayout.prototype.arrangeGroups=function(a,b,c,d,e,f){return this.graph.updateGroupBounds(a,b,!0,c,d,e,f)};function WeightedCellSorter(a,b){this.cell=a;this.weightedValue=b}WeightedCellSorter.prototype.weightedValue=0;WeightedCellSorter.prototype.nudge=!1;WeightedCellSorter.prototype.visited=!1;WeightedCellSorter.prototype.rankIndex=null;
+WeightedCellSorter.prototype.cell=null;WeightedCellSorter.prototype.compare=function(a,b){return null!=a&&null!=b?b.weightedValue>a.weightedValue?-1:b.weightedValue<a.weightedValue?1:b.nudge?-1:1:0};function mxStackLayout(a,b,c,d,e,f){mxGraphLayout.call(this,a);this.horizontal=null!=b?b:!0;this.spacing=null!=c?c:0;this.x0=null!=d?d:0;this.y0=null!=e?e:0;this.border=null!=f?f:0}mxStackLayout.prototype=new mxGraphLayout;mxStackLayout.prototype.constructor=mxStackLayout;
+mxStackLayout.prototype.horizontal=null;mxStackLayout.prototype.spacing=null;mxStackLayout.prototype.x0=null;mxStackLayout.prototype.y0=null;mxStackLayout.prototype.border=0;mxStackLayout.prototype.marginTop=0;mxStackLayout.prototype.marginLeft=0;mxStackLayout.prototype.marginRight=0;mxStackLayout.prototype.marginBottom=0;mxStackLayout.prototype.keepFirstLocation=!1;mxStackLayout.prototype.fill=!1;mxStackLayout.prototype.resizeParent=!1;mxStackLayout.prototype.resizeParentMax=!1;
+mxStackLayout.prototype.resizeLast=!1;mxStackLayout.prototype.wrap=null;mxStackLayout.prototype.borderCollapse=!0;mxStackLayout.prototype.isHorizontal=function(){return this.horizontal};
+mxStackLayout.prototype.moveCell=function(a,b,c){var d=this.graph.getModel(),e=d.getParent(a),f=this.isHorizontal();if(null!=a&&null!=e){var g=0,k=d.getChildCount(e);c=f?b:c;b=this.graph.getView().getState(e);null!=b&&(c-=f?b.x:b.y);c/=this.graph.view.scale;for(b=0;b<k;b++){var l=d.getChildAt(e,b);if(l!=a&&(l=d.getGeometry(l),null!=l)){l=f?l.x+l.width/2:l.y+l.height/2;if(g<=c&&l>c)break;g=l}}f=e.getIndex(a);f=Math.max(0,b-(b>f?1:0));d.add(e,a,f)}};
+mxStackLayout.prototype.getParentSize=function(a){var b=this.graph.getModel(),c=b.getGeometry(a);null!=this.graph.container&&(null==c&&b.isLayer(a)||a==this.graph.getView().currentRoot)&&(c=new mxRectangle(0,0,this.graph.container.offsetWidth-1,this.graph.container.offsetHeight-1));return c};
+mxStackLayout.prototype.execute=function(a){if(null!=a){var b=this.getParentSize(a),c=this.isHorizontal(),d=this.graph.getModel(),e=null;null!=b&&(e=c?b.height-this.marginTop-this.marginBottom:b.width-this.marginLeft-this.marginRight);var e=e-2*this.border,f=this.x0+this.border+this.marginLeft,g=this.y0+this.border+this.marginTop;if(this.graph.isSwimlane(a)){var k=this.graph.getCellStyle(a),l=mxUtils.getNumber(k,mxConstants.STYLE_STARTSIZE,mxConstants.DEFAULT_STARTSIZE),k=1==mxUtils.getValue(k,mxConstants.STYLE_HORIZONTAL,
+!0);null!=b&&(l=k?Math.min(l,b.height):Math.min(l,b.width));c==k&&(e-=l);k?g+=l:f+=l}d.beginUpdate();try{for(var l=0,k=null,m=0,n=null,p=d.getChildCount(a),q=0;q<p;q++){var r=d.getChildAt(a,q);if(!this.isVertexIgnored(r)&&this.isVertexMovable(r)){var t=d.getGeometry(r);if(null!=t){t=t.clone();null!=this.wrap&&null!=k&&(c&&k.x+k.width+t.width+2*this.spacing>this.wrap||!c&&k.y+k.height+t.height+2*this.spacing>this.wrap)&&(k=null,c?g+=l+this.spacing:f+=l+this.spacing,l=0);var l=Math.max(l,c?t.height:
+t.width),u=0;if(!this.borderCollapse)var x=this.graph.getCellStyle(r),u=mxUtils.getNumber(x,mxConstants.STYLE_STROKEWIDTH,1);null!=k?c?t.x=m+this.spacing+Math.floor(u/2):t.y=m+this.spacing+Math.floor(u/2):this.keepFirstLocation||(c?t.x=f:t.y=g);c?t.y=g:t.x=f;this.fill&&null!=e&&(c?t.height=e:t.width=e);this.setChildGeometry(r,t);n=r;k=t;m=c?k.x+k.width+Math.floor(u/2):k.y+k.height+Math.floor(u/2)}}}this.resizeParent&&null!=b&&null!=k&&!this.graph.isCellCollapsed(a)?this.updateParentGeometry(a,b,k):
+this.resizeLast&&null!=b&&null!=k&&null!=n&&(c?k.width=b.width-k.x-this.spacing-this.marginRight-this.marginLeft:k.height=b.height-k.y-this.spacing-this.marginBottom,this.setChildGeometry(n,k))}finally{d.endUpdate()}}};mxStackLayout.prototype.setChildGeometry=function(a,b){var c=this.graph.getCellGeometry(a);null!=c&&b.x==c.x&&b.y==c.y&&b.width==c.width&&b.height==c.height||this.graph.getModel().setGeometry(a,b)};
+mxStackLayout.prototype.updateParentGeometry=function(a,b,c){var d=this.isHorizontal(),e=this.graph.getModel(),f=b.clone();d?(c=c.x+c.width+this.marginRight+this.border,f.width=this.resizeParentMax?Math.max(f.width,c):c):(c=c.y+c.height+this.marginBottom+this.border,f.height=this.resizeParentMax?Math.max(f.height,c):c);b.x==f.x&&b.y==f.y&&b.width==f.width&&b.height==f.height||e.setGeometry(a,f)};
+function mxPartitionLayout(a,b,c,d){mxGraphLayout.call(this,a);this.horizontal=null!=b?b:!0;this.spacing=c||0;this.border=d||0}mxPartitionLayout.prototype=new mxGraphLayout;mxPartitionLayout.prototype.constructor=mxPartitionLayout;mxPartitionLayout.prototype.horizontal=null;mxPartitionLayout.prototype.spacing=null;mxPartitionLayout.prototype.border=null;mxPartitionLayout.prototype.resizeVertices=!0;mxPartitionLayout.prototype.isHorizontal=function(){return this.horizontal};
+mxPartitionLayout.prototype.moveCell=function(a,b,c){c=this.graph.getModel();var d=c.getParent(a);if(null!=a&&null!=d){var e,f=0,g=c.getChildCount(d);for(e=0;e<g;e++){var k=c.getChildAt(d,e),k=this.getVertexBounds(k);if(null!=k){k=k.x+k.width/2;if(f<b&&k>b)break;f=k}}b=d.getIndex(a);b=Math.max(0,e-(e>b?1:0));c.add(d,a,b)}};
+mxPartitionLayout.prototype.execute=function(a){var b=this.isHorizontal(),c=this.graph.getModel(),d=c.getGeometry(a);null!=this.graph.container&&(null==d&&c.isLayer(a)||a==this.graph.getView().currentRoot)&&(d=new mxRectangle(0,0,this.graph.container.offsetWidth-1,this.graph.container.offsetHeight-1));if(null!=d){for(var e=[],f=c.getChildCount(a),g=0;g<f;g++){var k=c.getChildAt(a,g);!this.isVertexIgnored(k)&&this.isVertexMovable(k)&&e.push(k)}f=e.length;if(0<f){var l=this.border,m=this.border,n=b?
+d.height:d.width,n=n-2*this.border;a=this.graph.isSwimlane(a)?this.graph.getStartSize(a):new mxRectangle;n-=b?a.height:a.width;l+=a.width;m+=a.height;a=this.border+(f-1)*this.spacing;d=b?(d.width-l-a)/f:(d.height-m-a)/f;if(0<d){c.beginUpdate();try{for(g=0;g<f;g++){var k=e[g],p=c.getGeometry(k);null!=p&&(p=p.clone(),p.x=l,p.y=m,b?(this.resizeVertices&&(p.width=d,p.height=n),l+=d+this.spacing):(this.resizeVertices&&(p.height=d,p.width=n),m+=d+this.spacing),c.setGeometry(k,p))}}finally{c.endUpdate()}}}}};
+function mxCompactTreeLayout(a,b,c){mxGraphLayout.call(this,a);this.horizontal=null!=b?b:!0;this.invert=null!=c?c:!1}mxCompactTreeLayout.prototype=new mxGraphLayout;mxCompactTreeLayout.prototype.constructor=mxCompactTreeLayout;mxCompactTreeLayout.prototype.horizontal=null;mxCompactTreeLayout.prototype.invert=null;mxCompactTreeLayout.prototype.resizeParent=!0;mxCompactTreeLayout.prototype.maintainParentLocation=!1;mxCompactTreeLayout.prototype.groupPadding=10;
+mxCompactTreeLayout.prototype.groupPaddingTop=0;mxCompactTreeLayout.prototype.groupPaddingRight=0;mxCompactTreeLayout.prototype.groupPaddingBottom=0;mxCompactTreeLayout.prototype.groupPaddingLeft=0;mxCompactTreeLayout.prototype.parentsChanged=null;mxCompactTreeLayout.prototype.moveTree=!1;mxCompactTreeLayout.prototype.visited=null;mxCompactTreeLayout.prototype.levelDistance=10;mxCompactTreeLayout.prototype.nodeDistance=20;mxCompactTreeLayout.prototype.resetEdges=!0;
+mxCompactTreeLayout.prototype.prefHozEdgeSep=5;mxCompactTreeLayout.prototype.prefVertEdgeOff=4;mxCompactTreeLayout.prototype.minEdgeJetty=8;mxCompactTreeLayout.prototype.channelBuffer=4;mxCompactTreeLayout.prototype.edgeRouting=!0;mxCompactTreeLayout.prototype.sortEdges=!1;mxCompactTreeLayout.prototype.alignRanks=!1;mxCompactTreeLayout.prototype.maxRankHeight=null;mxCompactTreeLayout.prototype.root=null;mxCompactTreeLayout.prototype.node=null;
+mxCompactTreeLayout.prototype.isVertexIgnored=function(a){return mxGraphLayout.prototype.isVertexIgnored.apply(this,arguments)||0==this.graph.getConnections(a).length};mxCompactTreeLayout.prototype.isHorizontal=function(){return this.horizontal};
+mxCompactTreeLayout.prototype.execute=function(a,b){this.parent=a;var c=this.graph.getModel();if(null==b)if(0<this.graph.getEdges(a,c.getParent(a),this.invert,!this.invert,!1).length)this.root=a;else{var d=this.graph.findTreeRoots(a,!0,this.invert);if(0<d.length)for(var e=0;e<d.length;e++)if(!this.isVertexIgnored(d[e])&&0<this.graph.getEdges(d[e],null,this.invert,!this.invert,!1).length){this.root=d[e];break}}else this.root=b;if(null!=this.root){this.parentsChanged=this.resizeParent?{}:null;this.parentY=
+this.parentX=null;if(a!=this.root&&null!=c.isVertex(a)&&this.maintainParentLocation){var f=this.graph.getCellGeometry(a);null!=f&&(this.parentX=f.x,this.parentY=f.y)}c.beginUpdate();try{if(this.visited={},this.node=this.dfs(this.root,a),this.alignRanks&&(this.maxRankHeight=[],this.findRankHeights(this.node,0),this.setCellHeights(this.node,0)),null!=this.node){this.layout(this.node);var g=this.graph.gridSize,d=g;if(!this.moveTree){var k=this.getVertexBounds(this.root);null!=k&&(g=k.x,d=k.y)}k=null;
+k=this.isHorizontal()?this.horizontalLayout(this.node,g,d):this.verticalLayout(this.node,null,g,d);if(null!=k){var l=e=0;0>k.x&&(e=Math.abs(g-k.x));0>k.y&&(l=Math.abs(d-k.y));0==e&&0==l||this.moveNode(this.node,e,l);this.resizeParent&&this.adjustParents();this.edgeRouting&&this.localEdgeProcessing(this.node)}null!=this.parentX&&null!=this.parentY&&(f=this.graph.getCellGeometry(a),null!=f&&(f=f.clone(),f.x=this.parentX,f.y=this.parentY,c.setGeometry(a,f)))}}finally{c.endUpdate()}}};
+mxCompactTreeLayout.prototype.moveNode=function(a,b,c){a.x+=b;a.y+=c;this.apply(a);for(a=a.child;null!=a;)this.moveNode(a,b,c),a=a.next};
+mxCompactTreeLayout.prototype.sortOutgoingEdges=function(a,b){var c=new mxDictionary;b.sort(function(b,e){var d=b.getTerminal(b.getTerminal(!1)==a),g=c.get(d);null==g&&(g=mxCellPath.create(d).split(mxCellPath.PATH_SEPARATOR),c.put(d,g));var d=e.getTerminal(e.getTerminal(!1)==a),k=c.get(d);null==k&&(k=mxCellPath.create(d).split(mxCellPath.PATH_SEPARATOR),c.put(d,k));return mxCellPath.compare(g,k)})};
+mxCompactTreeLayout.prototype.findRankHeights=function(a,b){if(null==this.maxRankHeight[b]||this.maxRankHeight[b]<a.height)this.maxRankHeight[b]=a.height;for(var c=a.child;null!=c;)this.findRankHeights(c,b+1),c=c.next};mxCompactTreeLayout.prototype.setCellHeights=function(a,b){null!=this.maxRankHeight[b]&&this.maxRankHeight[b]>a.height&&(a.height=this.maxRankHeight[b]);for(var c=a.child;null!=c;)this.setCellHeights(c,b+1),c=c.next};
+mxCompactTreeLayout.prototype.dfs=function(a,b){var c=mxCellPath.create(a),d=null;if(null!=a&&null==this.visited[c]&&!this.isVertexIgnored(a)){this.visited[c]=a;var d=this.createNode(a),c=this.graph.getModel(),e=null,f=this.graph.getEdges(a,b,this.invert,!this.invert,!1,!0),g=this.graph.getView();this.sortEdges&&this.sortOutgoingEdges(a,f);for(var k=0;k<f.length;k++){var l=f[k];if(!this.isEdgeIgnored(l)){this.resetEdges&&this.setEdgePoints(l,null);this.edgeRouting&&(this.setEdgeStyleEnabled(l,!1),
+this.setEdgePoints(l,null));var m=g.getState(l),l=null!=m?m.getVisibleTerminal(this.invert):g.getVisibleTerminal(l,this.invert),m=this.dfs(l,b);null!=m&&null!=c.getGeometry(l)&&(null==e?d.child=m:e.next=m,e=m)}}}return d};mxCompactTreeLayout.prototype.layout=function(a){if(null!=a){for(var b=a.child;null!=b;)this.layout(b),b=b.next;null!=a.child?this.attachParent(a,this.join(a)):this.layoutLeaf(a)}};
+mxCompactTreeLayout.prototype.horizontalLayout=function(a,b,c,d){a.x+=b+a.offsetX;a.y+=c+a.offsetY;d=this.apply(a,d);b=a.child;if(null!=b){d=this.horizontalLayout(b,a.x,a.y,d);c=a.y+b.offsetY;for(var e=b.next;null!=e;)d=this.horizontalLayout(e,a.x+b.offsetX,c,d),c+=e.offsetY,e=e.next}return d};
+mxCompactTreeLayout.prototype.verticalLayout=function(a,b,c,d,e){a.x+=c+a.offsetY;a.y+=d+a.offsetX;e=this.apply(a,e);b=a.child;if(null!=b)for(e=this.verticalLayout(b,a,a.x,a.y,e),c=a.x+b.offsetY,d=b.next;null!=d;)e=this.verticalLayout(d,a,c,a.y+b.offsetX,e),c+=d.offsetY,d=d.next;return e};
+mxCompactTreeLayout.prototype.attachParent=function(a,b){var c=this.nodeDistance+this.levelDistance,d=(b-a.width)/2-this.nodeDistance,e=d+a.width+2*this.nodeDistance-b;a.child.offsetX=c+a.height;a.child.offsetY=e;a.contour.upperHead=this.createLine(a.height,0,this.createLine(c,e,a.contour.upperHead));a.contour.lowerHead=this.createLine(a.height,0,this.createLine(c,d,a.contour.lowerHead))};
+mxCompactTreeLayout.prototype.layoutLeaf=function(a){var b=2*this.nodeDistance;a.contour.upperTail=this.createLine(a.height+b,0);a.contour.upperHead=a.contour.upperTail;a.contour.lowerTail=this.createLine(0,-a.width-b);a.contour.lowerHead=this.createLine(a.height+b,0,a.contour.lowerTail)};
+mxCompactTreeLayout.prototype.join=function(a){var b=2*this.nodeDistance,c=a.child;a.contour=c.contour;for(var d=c.width+b,e=d,c=c.next;null!=c;){var f=this.merge(a.contour,c.contour);c.offsetY=f+d;c.offsetX=0;d=c.width+b;e+=f+d;c=c.next}return e};
+mxCompactTreeLayout.prototype.merge=function(a,b){for(var c=0,d=0,e=0,f=a.lowerHead,g=b.upperHead;null!=g&&null!=f;){var k=this.offset(c,d,g.dx,g.dy,f.dx,f.dy),d=d+k,e=e+k;c+g.dx<=f.dx?(c+=g.dx,d+=g.dy,g=g.next):(c-=f.dx,d-=f.dy,f=f.next)}null!=g?(c=this.bridge(a.upperTail,0,0,g,c,d),a.upperTail=null!=c.next?b.upperTail:c,a.lowerTail=b.lowerTail):(c=this.bridge(b.lowerTail,c,d,f,0,0),null==c.next&&(a.lowerTail=c));a.lowerHead=b.lowerHead;return e};
+mxCompactTreeLayout.prototype.offset=function(a,b,c,d,e,f){if(e<=a||0>=a+c)return 0;a=0<e*d-c*f?0>a?a*d/c-b:0<a?a*f/e-b:-b:e<a+c?f-(b+(e-a)*d/c):e>a+c?(c+a)*f/e-(b+d):f-(b+d);return 0<a?a:0};mxCompactTreeLayout.prototype.bridge=function(a,b,c,d,e,f){b=e+d.dx-b;0==d.dx?e=d.dy:(e=b*d.dy,e/=d.dx);b=this.createLine(b,e,d.next);a.next=this.createLine(0,f+d.dy-e-c,b);return b};
+mxCompactTreeLayout.prototype.createNode=function(a){var b={};b.cell=a;b.x=0;b.y=0;b.width=0;b.height=0;a=this.getVertexBounds(a);null!=a&&(this.isHorizontal()?(b.width=a.height,b.height=a.width):(b.width=a.width,b.height=a.height));b.offsetX=0;b.offsetY=0;b.contour={};return b};
+mxCompactTreeLayout.prototype.apply=function(a,b){var c=this.graph.getModel(),d=a.cell,e=c.getGeometry(d);null!=d&&null!=e&&(this.isVertexMovable(d)&&(e=this.setVertexLocation(d,a.x,a.y),this.resizeParent&&(c=c.getParent(d),d=mxCellPath.create(c),null==this.parentsChanged[d]&&(this.parentsChanged[d]=c))),b=null==b?new mxRectangle(e.x,e.y,e.width,e.height):new mxRectangle(Math.min(b.x,e.x),Math.min(b.y,e.y),Math.max(b.x+b.width,e.x+e.width),Math.max(b.y+b.height,e.y+e.height)));return b};
+mxCompactTreeLayout.prototype.createLine=function(a,b,c){var d={};d.dx=a;d.dy=b;d.next=c;return d};mxCompactTreeLayout.prototype.adjustParents=function(){var a=[],b;for(b in this.parentsChanged)a.push(this.parentsChanged[b]);this.arrangeGroups(mxUtils.sortCells(a,!0),this.groupPadding,this.groupPaddingTop,this.groupPaddingRight,this.groupPaddingBottom,this.groupPaddingLeft)};
+mxCompactTreeLayout.prototype.localEdgeProcessing=function(a){this.processNodeOutgoing(a);for(a=a.child;null!=a;)this.localEdgeProcessing(a),a=a.next};
+mxCompactTreeLayout.prototype.processNodeOutgoing=function(a){for(var b=a.child,c=a.cell,d=0,e=[];null!=b;){d++;var f=b.x;this.horizontal&&(f=b.y);e.push(new WeightedCellSorter(b,f));b=b.next}e.sort(WeightedCellSorter.prototype.compare);var f=a.width,g=(d+1)*this.prefHozEdgeSep;f>g+2*this.prefHozEdgeSep&&(f-=2*this.prefHozEdgeSep);a=f/d;b=a/2;f>g+2*this.prefHozEdgeSep&&(b+=this.prefHozEdgeSep);for(var f=this.minEdgeJetty-this.prefVertEdgeOff,g=this.getVertexBounds(c),k=0;k<e.length;k++){for(var l=
+e[k].cell.cell,m=this.getVertexBounds(l),l=this.graph.getEdgesBetween(c,l,!1),n=[],p,q,r=0;r<l.length;r++)this.horizontal?(p=g.x+g.width,q=g.y+b,n.push(new mxPoint(p,q)),p=g.x+g.width+f,n.push(new mxPoint(p,q)),q=m.y+m.height/2):(p=g.x+b,q=g.y+g.height,n.push(new mxPoint(p,q)),q=g.y+g.height+f,n.push(new mxPoint(p,q)),p=m.x+m.width/2),n.push(new mxPoint(p,q)),this.setEdgePoints(l[r],n);k<d/2?f+=this.prefVertEdgeOff:k>d/2&&(f-=this.prefVertEdgeOff);b+=a}};
+function mxRadialTreeLayout(a){mxCompactTreeLayout.call(this,a,!1)}mxUtils.extend(mxRadialTreeLayout,mxCompactTreeLayout);mxRadialTreeLayout.prototype.angleOffset=.5;mxRadialTreeLayout.prototype.rootx=0;mxRadialTreeLayout.prototype.rooty=0;mxRadialTreeLayout.prototype.levelDistance=120;mxRadialTreeLayout.prototype.nodeDistance=10;mxRadialTreeLayout.prototype.autoRadius=!1;mxRadialTreeLayout.prototype.sortEdges=!1;mxRadialTreeLayout.prototype.rowMinX=[];mxRadialTreeLayout.prototype.rowMaxX=[];
+mxRadialTreeLayout.prototype.rowMinCenX=[];mxRadialTreeLayout.prototype.rowMaxCenX=[];mxRadialTreeLayout.prototype.rowRadi=[];mxRadialTreeLayout.prototype.row=[];mxRadialTreeLayout.prototype.isVertexIgnored=function(a){return mxGraphLayout.prototype.isVertexIgnored.apply(this,arguments)||0==this.graph.getConnections(a).length};
+mxRadialTreeLayout.prototype.execute=function(a,b){this.parent=a;this.edgeRouting=this.useBoundingBox=!1;mxCompactTreeLayout.prototype.execute.apply(this,arguments);var c=null,d=this.getVertexBounds(this.root);this.centerX=d.x+d.width/2;this.centerY=d.y+d.height/2;for(var e in this.visited){var f=this.getVertexBounds(this.visited[e]),c=null!=c?c:f.clone();c.add(f)}this.calcRowDims([this.node],0);for(var g=0,k=0,c=0;c<this.row.length;c++)e=(this.rowMaxX[c]-this.centerX-this.nodeDistance)/this.rowRadi[c],
+g=Math.max(g,(this.centerX-this.rowMinX[c]-this.nodeDistance)/this.rowRadi[c]),k=Math.max(k,e);for(c=0;c<this.row.length;c++){var l=this.centerX-this.nodeDistance-g*this.rowRadi[c],m=this.centerX+this.nodeDistance+k*this.rowRadi[c]-l;for(e=0;e<this.row[c].length;e++)f=this.row[c],d=f[e],f=this.getVertexBounds(d.cell),d.theta=(f.x+f.width/2-l)/m*Math.PI*2}for(c=this.row.length-2;0<=c;c--)for(f=this.row[c],e=0;e<f.length;e++){d=f[e];g=d.child;for(l=k=0;null!=g;)l+=g.theta,k++,g=g.next;0<k&&(g=l/k,g>
+d.theta&&e<f.length-1?d.theta=Math.min(g,f[e+1].theta-Math.PI/10):g<d.theta&&0<e&&(d.theta=Math.max(g,f[e-1].theta+Math.PI/10)))}for(c=0;c<this.row.length;c++)for(e=0;e<this.row[c].length;e++)f=this.row[c],d=f[e],f=this.getVertexBounds(d.cell),this.setVertexLocation(d.cell,this.centerX-f.width/2+this.rowRadi[c]*Math.cos(d.theta),this.centerY-f.height/2+this.rowRadi[c]*Math.sin(d.theta))};
+mxRadialTreeLayout.prototype.calcRowDims=function(a,b){if(null!=a&&0!=a.length){this.rowMinX[b]=this.centerX;this.rowMaxX[b]=this.centerX;this.rowMinCenX[b]=this.centerX;this.rowMaxCenX[b]=this.centerX;this.row[b]=[];for(var c=!1,d=0;d<a.length;d++)for(var e=null!=a[d]?a[d].child:null;null!=e;){var f=this.getVertexBounds(e.cell);this.rowMinX[b]=Math.min(f.x,this.rowMinX[b]);this.rowMaxX[b]=Math.max(f.x+f.width,this.rowMaxX[b]);this.rowMinCenX[b]=Math.min(f.x+f.width/2,this.rowMinCenX[b]);this.rowMaxCenX[b]=
+Math.max(f.x+f.width/2,this.rowMaxCenX[b]);this.rowRadi[b]=f.y-this.getVertexBounds(this.root).y;null!=e.child&&(c=!0);this.row[b].push(e);e=e.next}c&&this.calcRowDims(this.row[b],b+1)}};function mxFastOrganicLayout(a){mxGraphLayout.call(this,a)}mxFastOrganicLayout.prototype=new mxGraphLayout;mxFastOrganicLayout.prototype.constructor=mxFastOrganicLayout;mxFastOrganicLayout.prototype.useInputOrigin=!0;mxFastOrganicLayout.prototype.resetEdges=!0;mxFastOrganicLayout.prototype.disableEdgeStyle=!0;
+mxFastOrganicLayout.prototype.forceConstant=50;mxFastOrganicLayout.prototype.forceConstantSquared=0;mxFastOrganicLayout.prototype.minDistanceLimit=2;mxFastOrganicLayout.prototype.maxDistanceLimit=500;mxFastOrganicLayout.prototype.minDistanceLimitSquared=4;mxFastOrganicLayout.prototype.initialTemp=200;mxFastOrganicLayout.prototype.temperature=0;mxFastOrganicLayout.prototype.maxIterations=0;mxFastOrganicLayout.prototype.iteration=0;mxFastOrganicLayout.prototype.allowedToRun=!0;
+mxFastOrganicLayout.prototype.isVertexIgnored=function(a){return mxGraphLayout.prototype.isVertexIgnored.apply(this,arguments)||0==this.graph.getConnections(a).length};
+mxFastOrganicLayout.prototype.execute=function(a){var b=this.graph.getModel();this.vertexArray=[];for(var c=this.graph.getChildVertices(a),d=0;d<c.length;d++)this.isVertexIgnored(c[d])||this.vertexArray.push(c[d]);var e=this.useInputOrigin?this.graph.getBoundingBoxFromGeometry(this.vertexArray):null,f=this.vertexArray.length;this.indices=[];this.dispX=[];this.dispY=[];this.cellLocation=[];this.isMoveable=[];this.neighbours=[];this.radius=[];this.radiusSquared=[];.001>this.forceConstant&&(this.forceConstant=
+.001);this.forceConstantSquared=this.forceConstant*this.forceConstant;for(d=0;d<this.vertexArray.length;d++){var g=this.vertexArray[d];this.cellLocation[d]=[];var k=mxObjectIdentity.get(g);this.indices[k]=d;var l=this.getVertexBounds(g),m=l.width,n=l.height,p=l.x,q=l.y;this.cellLocation[d][0]=p+m/2;this.cellLocation[d][1]=q+n/2;this.radius[d]=Math.min(m,n);this.radiusSquared[d]=this.radius[d]*this.radius[d]}b.beginUpdate();try{for(d=0;d<f;d++){this.dispX[d]=0;this.dispY[d]=0;this.isMoveable[d]=this.isVertexMovable(this.vertexArray[d]);
+var r=this.graph.getConnections(this.vertexArray[d],a),c=this.graph.getOpposites(r,this.vertexArray[d]);this.neighbours[d]=[];for(m=0;m<c.length;m++){this.resetEdges&&this.graph.resetEdge(r[m]);this.disableEdgeStyle&&this.setEdgeStyleEnabled(r[m],!1);var k=mxObjectIdentity.get(c[m]),t=this.indices[k];this.neighbours[d][m]=null!=t?t:d}}this.temperature=this.initialTemp;0==this.maxIterations&&(this.maxIterations=20*Math.sqrt(f));for(this.iteration=0;this.iteration<this.maxIterations;this.iteration++){if(!this.allowedToRun)return;
+this.calcRepulsion();this.calcAttraction();this.calcPositions();this.reduceTemperature()}a=c=null;for(d=0;d<this.vertexArray.length;d++)g=this.vertexArray[d],this.isVertexMovable(g)&&(l=this.getVertexBounds(g),null!=l&&(this.cellLocation[d][0]-=l.width/2,this.cellLocation[d][1]-=l.height/2,p=this.graph.snap(Math.round(this.cellLocation[d][0])),q=this.graph.snap(Math.round(this.cellLocation[d][1])),this.setVertexLocation(g,p,q),c=null==c?p:Math.min(c,p),a=null==a?q:Math.min(a,q)));d=-(c||0)+1;g=-(a||
+0)+1;null!=e&&(d+=e.x,g+=e.y);this.graph.moveCells(this.vertexArray,d,g)}finally{b.endUpdate()}};mxFastOrganicLayout.prototype.calcPositions=function(){for(var a=0;a<this.vertexArray.length;a++)if(this.isMoveable[a]){var b=Math.sqrt(this.dispX[a]*this.dispX[a]+this.dispY[a]*this.dispY[a]);.001>b&&(b=.001);var c=this.dispX[a]/b*Math.min(b,this.temperature),b=this.dispY[a]/b*Math.min(b,this.temperature);this.dispX[a]=0;this.dispY[a]=0;this.cellLocation[a][0]+=c;this.cellLocation[a][1]+=b}};
+mxFastOrganicLayout.prototype.calcAttraction=function(){for(var a=0;a<this.vertexArray.length;a++)for(var b=0;b<this.neighbours[a].length;b++){var c=this.neighbours[a][b];if(a!=c&&this.isMoveable[a]&&this.isMoveable[c]){var d=this.cellLocation[a][0]-this.cellLocation[c][0],e=this.cellLocation[a][1]-this.cellLocation[c][1],f=d*d+e*e-this.radiusSquared[a]-this.radiusSquared[c];f<this.minDistanceLimitSquared&&(f=this.minDistanceLimitSquared);var g=Math.sqrt(f),f=f/this.forceConstant,d=d/g*f,e=e/g*f;
+this.dispX[a]-=d;this.dispY[a]-=e;this.dispX[c]+=d;this.dispY[c]+=e}}};
+mxFastOrganicLayout.prototype.calcRepulsion=function(){for(var a=this.vertexArray.length,b=0;b<a;b++)for(var c=b;c<a;c++){if(!this.allowedToRun)return;if(c!=b&&this.isMoveable[b]&&this.isMoveable[c]){var d=this.cellLocation[b][0]-this.cellLocation[c][0],e=this.cellLocation[b][1]-this.cellLocation[c][1];0==d&&(d=.01+Math.random());0==e&&(e=.01+Math.random());var f=Math.sqrt(d*d+e*e),g=f-this.radius[b]-this.radius[c];g>this.maxDistanceLimit||(g<this.minDistanceLimit&&(g=this.minDistanceLimit),g=this.forceConstantSquared/
+g,d=d/f*g,e=e/f*g,this.dispX[b]+=d,this.dispY[b]+=e,this.dispX[c]-=d,this.dispY[c]-=e)}}};mxFastOrganicLayout.prototype.reduceTemperature=function(){this.temperature=this.initialTemp*(1-this.iteration/this.maxIterations)};function mxCircleLayout(a,b){mxGraphLayout.call(this,a);this.radius=null!=b?b:100}mxCircleLayout.prototype=new mxGraphLayout;mxCircleLayout.prototype.constructor=mxCircleLayout;mxCircleLayout.prototype.radius=null;mxCircleLayout.prototype.moveCircle=!1;
+mxCircleLayout.prototype.x0=0;mxCircleLayout.prototype.y0=0;mxCircleLayout.prototype.resetEdges=!0;mxCircleLayout.prototype.disableEdgeStyle=!0;
+mxCircleLayout.prototype.execute=function(a){var b=this.graph.getModel();b.beginUpdate();try{for(var c=0,d=null,e=null,f=[],g=b.getChildCount(a),k=0;k<g;k++){var l=b.getChildAt(a,k);if(this.isVertexIgnored(l))this.isEdgeIgnored(l)||(this.resetEdges&&this.graph.resetEdge(l),this.disableEdgeStyle&&this.setEdgeStyleEnabled(l,!1));else{f.push(l);var m=this.getVertexBounds(l),d=null==d?m.y:Math.min(d,m.y),e=null==e?m.x:Math.min(e,m.x),c=Math.max(c,Math.max(m.width,m.height))}}var n=this.getRadius(f.length,
+c);this.moveCircle&&(e=this.x0,d=this.y0);this.circle(f,n,e,d)}finally{b.endUpdate()}};mxCircleLayout.prototype.getRadius=function(a,b){return Math.max(a*b/Math.PI,this.radius)};mxCircleLayout.prototype.circle=function(a,b,c,d){for(var e=a.length,f=2*Math.PI/e,g=0;g<e;g++)this.isVertexMovable(a[g])&&this.setVertexLocation(a[g],Math.round(c+b+b*Math.sin(g*f)),Math.round(d+b+b*Math.cos(g*f)))};function mxParallelEdgeLayout(a){mxGraphLayout.call(this,a)}mxParallelEdgeLayout.prototype=new mxGraphLayout;
+mxParallelEdgeLayout.prototype.constructor=mxParallelEdgeLayout;mxParallelEdgeLayout.prototype.spacing=20;mxParallelEdgeLayout.prototype.execute=function(a){a=this.findParallels(a);this.graph.model.beginUpdate();try{for(var b in a){var c=a[b];1<c.length&&this.layout(c)}}finally{this.graph.model.endUpdate()}};
+mxParallelEdgeLayout.prototype.findParallels=function(a){for(var b=this.graph.getModel(),c=[],d=b.getChildCount(a),e=0;e<d;e++){var f=b.getChildAt(a,e);if(!this.isEdgeIgnored(f)){var g=this.getEdgeId(f);null!=g&&(null==c[g]&&(c[g]=[]),c[g].push(f))}}return c};mxParallelEdgeLayout.prototype.getEdgeId=function(a){var b=this.graph.getView(),c=b.getVisibleTerminal(a,!0);a=b.getVisibleTerminal(a,!1);return null!=c&&null!=a?(c=mxObjectIdentity.get(c),a=mxObjectIdentity.get(a),c>a?a+"-"+c:c+"-"+a):null};
+mxParallelEdgeLayout.prototype.layout=function(a){var b=a[0],c=this.graph.getView(),d=this.graph.getModel(),e=d.getGeometry(c.getVisibleTerminal(b,!0)),d=d.getGeometry(c.getVisibleTerminal(b,!1));if(e==d)for(var b=e.x+e.width+this.spacing,c=e.y+e.height/2,f=0;f<a.length;f++)this.route(a[f],b,c),b+=this.spacing;else if(null!=e&&null!=d){var b=e.x+e.width/2,c=e.y+e.height/2,f=d.x+d.width/2-b,g=d.y+d.height/2-c,d=Math.sqrt(f*f+g*g);if(0<d)for(e=g*this.spacing/d,d=f*this.spacing/d,b=b+f/2+e*(a.length-
+1)/2,c=c+g/2-d*(a.length-1)/2,f=0;f<a.length;f++)this.route(a[f],b,c),b-=e,c+=d}};mxParallelEdgeLayout.prototype.route=function(a,b,c){this.graph.isCellMovable(a)&&this.setEdgePoints(a,[new mxPoint(b,c)])};function mxCompositeLayout(a,b,c){mxGraphLayout.call(this,a);this.layouts=b;this.master=c}mxCompositeLayout.prototype=new mxGraphLayout;mxCompositeLayout.prototype.constructor=mxCompositeLayout;mxCompositeLayout.prototype.layouts=null;mxCompositeLayout.prototype.master=null;
+mxCompositeLayout.prototype.moveCell=function(a,b,c){null!=this.master?this.master.move.apply(this.master,arguments):this.layouts[0].move.apply(this.layouts[0],arguments)};mxCompositeLayout.prototype.execute=function(a){var b=this.graph.getModel();b.beginUpdate();try{for(var c=0;c<this.layouts.length;c++)this.layouts[c].execute.apply(this.layouts[c],arguments)}finally{b.endUpdate()}};function mxEdgeLabelLayout(a,b){mxGraphLayout.call(this,a)}mxEdgeLabelLayout.prototype=new mxGraphLayout;
+mxEdgeLabelLayout.prototype.constructor=mxEdgeLabelLayout;mxEdgeLabelLayout.prototype.execute=function(a){for(var b=this.graph.view,c=this.graph.getModel(),d=[],e=[],f=c.getChildCount(a),g=0;g<f;g++){var k=c.getChildAt(a,g),l=b.getState(k);null!=l&&(this.isVertexIgnored(k)?this.isEdgeIgnored(k)||d.push(l):e.push(l))}this.placeLabels(e,d)};
+mxEdgeLabelLayout.prototype.placeLabels=function(a,b){var c=this.graph.getModel();c.beginUpdate();try{for(var d=0;d<b.length;d++){var e=b[d];if(null!=e&&null!=e.text&&null!=e.text.boundingBox)for(var f=0;f<a.length;f++){var g=a[f];null!=g&&this.avoid(e,g)}}}finally{c.endUpdate()}};
+mxEdgeLabelLayout.prototype.avoid=function(a,b){var c=this.graph.getModel(),d=a.text.boundingBox;if(mxUtils.intersects(d,b)){var e=-d.y-d.height+b.y,f=-d.y+b.y+b.height,e=Math.abs(e)<Math.abs(f)?e:f,f=-d.x-d.width+b.x,d=-d.x+b.x+b.width,d=Math.abs(f)<Math.abs(d)?f:d;Math.abs(d)<Math.abs(e)?e=0:d=0;f=c.getGeometry(a.cell);null!=f&&(f=f.clone(),null!=f.offset?(f.offset.x+=d,f.offset.y+=e):f.offset=new mxPoint(d,e),c.setGeometry(a.cell,f))}};
+function mxGraphAbstractHierarchyCell(){this.x=[];this.y=[];this.temp=[]}mxGraphAbstractHierarchyCell.prototype.maxRank=-1;mxGraphAbstractHierarchyCell.prototype.minRank=-1;mxGraphAbstractHierarchyCell.prototype.x=null;mxGraphAbstractHierarchyCell.prototype.y=null;mxGraphAbstractHierarchyCell.prototype.width=0;mxGraphAbstractHierarchyCell.prototype.height=0;mxGraphAbstractHierarchyCell.prototype.nextLayerConnectedCells=null;mxGraphAbstractHierarchyCell.prototype.previousLayerConnectedCells=null;
+mxGraphAbstractHierarchyCell.prototype.temp=null;mxGraphAbstractHierarchyCell.prototype.getNextLayerConnectedCells=function(a){return null};mxGraphAbstractHierarchyCell.prototype.getPreviousLayerConnectedCells=function(a){return null};mxGraphAbstractHierarchyCell.prototype.isEdge=function(){return!1};mxGraphAbstractHierarchyCell.prototype.isVertex=function(){return!1};mxGraphAbstractHierarchyCell.prototype.getGeneralPurposeVariable=function(a){return null};
+mxGraphAbstractHierarchyCell.prototype.setGeneralPurposeVariable=function(a,b){return null};mxGraphAbstractHierarchyCell.prototype.setX=function(a,b){this.isVertex()?this.x[0]=b:this.isEdge()&&(this.x[a-this.minRank-1]=b)};mxGraphAbstractHierarchyCell.prototype.getX=function(a){return this.isVertex()?this.x[0]:this.isEdge()?this.x[a-this.minRank-1]:0};mxGraphAbstractHierarchyCell.prototype.setY=function(a,b){this.isVertex()?this.y[0]=b:this.isEdge()&&(this.y[a-this.minRank-1]=b)};
+function mxGraphHierarchyNode(a){mxGraphAbstractHierarchyCell.apply(this,arguments);this.cell=a;this.id=mxObjectIdentity.get(a);this.connectsAsTarget=[];this.connectsAsSource=[]}mxGraphHierarchyNode.prototype=new mxGraphAbstractHierarchyCell;mxGraphHierarchyNode.prototype.constructor=mxGraphHierarchyNode;mxGraphHierarchyNode.prototype.cell=null;mxGraphHierarchyNode.prototype.id=null;mxGraphHierarchyNode.prototype.connectsAsTarget=null;mxGraphHierarchyNode.prototype.connectsAsSource=null;
+mxGraphHierarchyNode.prototype.hashCode=!1;mxGraphHierarchyNode.prototype.getRankValue=function(a){return this.maxRank};mxGraphHierarchyNode.prototype.getNextLayerConnectedCells=function(a){if(null==this.nextLayerConnectedCells){this.nextLayerConnectedCells=[];this.nextLayerConnectedCells[0]=[];for(var b=0;b<this.connectsAsTarget.length;b++){var c=this.connectsAsTarget[b];-1==c.maxRank||c.maxRank==a+1?this.nextLayerConnectedCells[0].push(c.source):this.nextLayerConnectedCells[0].push(c)}}return this.nextLayerConnectedCells[0]};
+mxGraphHierarchyNode.prototype.getPreviousLayerConnectedCells=function(a){if(null==this.previousLayerConnectedCells){this.previousLayerConnectedCells=[];this.previousLayerConnectedCells[0]=[];for(var b=0;b<this.connectsAsSource.length;b++){var c=this.connectsAsSource[b];-1==c.minRank||c.minRank==a-1?this.previousLayerConnectedCells[0].push(c.target):this.previousLayerConnectedCells[0].push(c)}}return this.previousLayerConnectedCells[0]};mxGraphHierarchyNode.prototype.isVertex=function(){return!0};
+mxGraphHierarchyNode.prototype.getGeneralPurposeVariable=function(a){return this.temp[0]};mxGraphHierarchyNode.prototype.setGeneralPurposeVariable=function(a,b){this.temp[0]=b};mxGraphHierarchyNode.prototype.isAncestor=function(a){if(null!=a&&null!=this.hashCode&&null!=a.hashCode&&this.hashCode.length<a.hashCode.length){if(this.hashCode==a.hashCode)return!0;if(null==this.hashCode||null==this.hashCode)return!1;for(var b=0;b<this.hashCode.length;b++)if(this.hashCode[b]!=a.hashCode[b])return!1;return!0}return!1};
+mxGraphHierarchyNode.prototype.getCoreCell=function(){return this.cell};function mxGraphHierarchyEdge(a){mxGraphAbstractHierarchyCell.apply(this,arguments);this.edges=a;this.ids=[];for(var b=0;b<a.length;b++)this.ids.push(mxObjectIdentity.get(a[b]))}mxGraphHierarchyEdge.prototype=new mxGraphAbstractHierarchyCell;mxGraphHierarchyEdge.prototype.constructor=mxGraphHierarchyEdge;mxGraphHierarchyEdge.prototype.edges=null;mxGraphHierarchyEdge.prototype.ids=null;mxGraphHierarchyEdge.prototype.source=null;
+mxGraphHierarchyEdge.prototype.target=null;mxGraphHierarchyEdge.prototype.isReversed=!1;mxGraphHierarchyEdge.prototype.invert=function(a){a=this.source;this.source=this.target;this.target=a;this.isReversed=!this.isReversed};
+mxGraphHierarchyEdge.prototype.getNextLayerConnectedCells=function(a){if(null==this.nextLayerConnectedCells){this.nextLayerConnectedCells=[];for(var b=0;b<this.temp.length;b++)this.nextLayerConnectedCells[b]=[],b==this.temp.length-1?this.nextLayerConnectedCells[b].push(this.source):this.nextLayerConnectedCells[b].push(this)}return this.nextLayerConnectedCells[a-this.minRank-1]};
+mxGraphHierarchyEdge.prototype.getPreviousLayerConnectedCells=function(a){if(null==this.previousLayerConnectedCells){this.previousLayerConnectedCells=[];for(var b=0;b<this.temp.length;b++)this.previousLayerConnectedCells[b]=[],0==b?this.previousLayerConnectedCells[b].push(this.target):this.previousLayerConnectedCells[b].push(this)}return this.previousLayerConnectedCells[a-this.minRank-1]};mxGraphHierarchyEdge.prototype.isEdge=function(){return!0};
+mxGraphHierarchyEdge.prototype.getGeneralPurposeVariable=function(a){return this.temp[a-this.minRank-1]};mxGraphHierarchyEdge.prototype.setGeneralPurposeVariable=function(a,b){this.temp[a-this.minRank-1]=b};mxGraphHierarchyEdge.prototype.getCoreCell=function(){return null!=this.edges&&0<this.edges.length?this.edges[0]:null};
+function mxGraphHierarchyModel(a,b,c,d,e){a.getGraph();this.tightenToSource=e;this.roots=c;this.parent=d;this.vertexMapper=new mxDictionary;this.edgeMapper=new mxDictionary;this.maxRank=0;c=[];null==b&&(b=this.graph.getChildVertices(d));this.maxRank=this.SOURCESCANSTARTRANK;this.createInternalCells(a,b,c);for(d=0;d<b.length;d++){e=c[d].connectsAsSource;for(var f=0;f<e.length;f++){var g=e[f],k=g.edges;if(null!=k&&0<k.length){var k=k[0],l=a.getVisibleTerminal(k,!1),l=this.vertexMapper.get(l);c[d]==
+l&&(l=a.getVisibleTerminal(k,!0),l=this.vertexMapper.get(l));null!=l&&c[d]!=l&&(g.target=l,0==l.connectsAsTarget.length&&(l.connectsAsTarget=[]),0>mxUtils.indexOf(l.connectsAsTarget,g)&&l.connectsAsTarget.push(g))}}c[d].temp[0]=1}}mxGraphHierarchyModel.prototype.maxRank=null;mxGraphHierarchyModel.prototype.vertexMapper=null;mxGraphHierarchyModel.prototype.edgeMapper=null;mxGraphHierarchyModel.prototype.ranks=null;mxGraphHierarchyModel.prototype.roots=null;mxGraphHierarchyModel.prototype.parent=null;
+mxGraphHierarchyModel.prototype.dfsCount=0;mxGraphHierarchyModel.prototype.SOURCESCANSTARTRANK=1E8;mxGraphHierarchyModel.prototype.tightenToSource=!1;
+mxGraphHierarchyModel.prototype.createInternalCells=function(a,b,c){for(var d=a.getGraph(),e=0;e<b.length;e++){c[e]=new mxGraphHierarchyNode(b[e]);this.vertexMapper.put(b[e],c[e]);var f=a.getEdges(b[e]);c[e].connectsAsSource=[];for(var g=0;g<f.length;g++){var k=a.getVisibleTerminal(f[g],!1);if(k!=b[e]&&a.graph.model.isVertex(k)&&!a.isVertexIgnored(k)){var l=a.getEdgesBetween(b[e],k,!1),k=a.getEdgesBetween(b[e],k,!0);if(null!=l&&0<l.length&&null==this.edgeMapper.get(l[0])&&2*k.length>=l.length){for(var k=
+new mxGraphHierarchyEdge(l),m=0;m<l.length;m++){var n=l[m];this.edgeMapper.put(n,k);d.resetEdge(n);a.disableEdgeStyle&&(a.setEdgeStyleEnabled(n,!1),a.setOrthogonalEdge(n,!0))}k.source=c[e];0>mxUtils.indexOf(c[e].connectsAsSource,k)&&c[e].connectsAsSource.push(k)}}}c[e].temp[0]=0}};
+mxGraphHierarchyModel.prototype.initialRank=function(){var a=[];if(null!=this.roots)for(var b=0;b<this.roots.length;b++){var c=this.vertexMapper.get(this.roots[b]);null!=c&&a.push(c)}for(var d=this.vertexMapper.getValues(),b=0;b<d.length;b++)d[b].temp[0]=-1;for(var e=a.slice();0<a.length;){var c=a[0],f,g;f=c.connectsAsTarget;g=c.connectsAsSource;for(var k=!0,l=this.SOURCESCANSTARTRANK,b=0;b<f.length;b++){var m=f[b];if(5270620==m.temp[0])m=m.source,l=Math.min(l,m.temp[0]-1);else{k=!1;break}}if(k){c.temp[0]=
+l;this.maxRank=Math.min(this.maxRank,l);if(null!=g)for(b=0;b<g.length;b++)m=g[b],m.temp[0]=5270620,m=m.target,-1==m.temp[0]&&(a.push(m),m.temp[0]=-2);a.shift()}else if(b=a.shift(),a.push(c),b==c&&1==a.length)break}for(b=0;b<d.length;b++)d[b].temp[0]-=this.maxRank;for(b=0;b<e.length;b++)for(c=e[b],a=0,f=c.connectsAsSource,d=0;d<f.length;d++)m=f[d],m=m.target,c.temp[0]=Math.max(a,m.temp[0]+1),a=c.temp[0];this.maxRank=this.SOURCESCANSTARTRANK-this.maxRank};
+mxGraphHierarchyModel.prototype.fixRanks=function(){var a=[];this.ranks=[];for(var b=0;b<this.maxRank+1;b++)a[b]=[],this.ranks[b]=a[b];var c=null;if(null!=this.roots)for(var d=this.roots,c=[],b=0;b<d.length;b++){var e=this.vertexMapper.get(d[b]);c[b]=e}this.visit(function(b,c,d,e,m){0==m&&0>c.maxRank&&0>c.minRank&&(a[c.temp[0]].push(c),c.maxRank=c.temp[0],c.minRank=c.temp[0],c.temp[0]=a[c.maxRank].length-1);if(null!=b&&null!=d&&1<b.maxRank-c.maxRank)for(d.maxRank=b.maxRank,d.minRank=c.maxRank,d.temp=
+[],d.x=[],d.y=[],b=d.minRank+1;b<d.maxRank;b++)a[b].push(d),d.setGeneralPurposeVariable(b,a[b].length-1)},c,!1,null)};mxGraphHierarchyModel.prototype.visit=function(a,b,c,d){if(null!=b){for(var e=0;e<b.length;e++){var f=b[e];null!=f&&(null==d&&(d={}),c?(f.hashCode=[],f.hashCode[0]=this.dfsCount,f.hashCode[1]=e,this.extendedDfs(null,f,null,a,d,f.hashCode,e,0)):this.dfs(null,f,null,a,d,0))}this.dfsCount++}};
+mxGraphHierarchyModel.prototype.dfs=function(a,b,c,d,e,f){if(null!=b){var g=b.id;if(null==e[g])for(e[g]=b,d(a,b,c,f,0),a=b.connectsAsSource.slice(),c=0;c<a.length;c++)g=a[c],this.dfs(b,g.target,g,d,e,f+1);else d(a,b,c,f,1)}};
+mxGraphHierarchyModel.prototype.extendedDfs=function(a,b,c,d,e,f,g,k){if(null!=b)if(null==a||null!=b.hashCode&&b.hashCode[0]==a.hashCode[0]||(f=a.hashCode.length+1,b.hashCode=a.hashCode.slice(),b.hashCode[f-1]=g),g=b.id,null==e[g])for(e[g]=b,d(a,b,c,k,0),a=b.connectsAsSource.slice(),c=0;c<a.length;c++)g=a[c],this.extendedDfs(b,g.target,g,d,e,b.hashCode,c,k+1);else d(a,b,c,k,1)};
+function mxSwimlaneModel(a,b,c,d,e){a.getGraph();this.tightenToSource=e;this.roots=c;this.parent=d;this.vertexMapper=new mxDictionary;this.edgeMapper=new mxDictionary;this.maxRank=0;c=[];null==b&&(b=this.graph.getChildVertices(d));this.maxRank=this.SOURCESCANSTARTRANK;this.createInternalCells(a,b,c);for(d=0;d<b.length;d++){e=c[d].connectsAsSource;for(var f=0;f<e.length;f++){var g=e[f],k=g.edges;if(null!=k&&0<k.length){var k=k[0],l=a.getVisibleTerminal(k,!1),l=this.vertexMapper.get(l);c[d]==l&&(l=
+a.getVisibleTerminal(k,!0),l=this.vertexMapper.get(l));null!=l&&c[d]!=l&&(g.target=l,0==l.connectsAsTarget.length&&(l.connectsAsTarget=[]),0>mxUtils.indexOf(l.connectsAsTarget,g)&&l.connectsAsTarget.push(g))}}c[d].temp[0]=1}}mxSwimlaneModel.prototype.maxRank=null;mxSwimlaneModel.prototype.vertexMapper=null;mxSwimlaneModel.prototype.edgeMapper=null;mxSwimlaneModel.prototype.ranks=null;mxSwimlaneModel.prototype.roots=null;mxSwimlaneModel.prototype.parent=null;mxSwimlaneModel.prototype.dfsCount=0;
+mxSwimlaneModel.prototype.SOURCESCANSTARTRANK=1E8;mxSwimlaneModel.prototype.tightenToSource=!1;mxSwimlaneModel.prototype.ranksPerGroup=null;
+mxSwimlaneModel.prototype.createInternalCells=function(a,b,c){for(var d=a.getGraph(),e=a.swimlanes,f=0;f<b.length;f++){c[f]=new mxGraphHierarchyNode(b[f]);this.vertexMapper.put(b[f],c[f]);c[f].swimlaneIndex=-1;for(var g=0;g<e.length;g++)if(d.model.getParent(b[f])==e[g]){c[f].swimlaneIndex=g;break}g=a.getEdges(b[f]);c[f].connectsAsSource=[];for(var k=0;k<g.length;k++){var l=a.getVisibleTerminal(g[k],!1);if(l!=b[f]&&a.graph.model.isVertex(l)&&!a.isVertexIgnored(l)){var m=a.getEdgesBetween(b[f],l,!1),
+l=a.getEdgesBetween(b[f],l,!0);if(null!=m&&0<m.length&&null==this.edgeMapper.get(m[0])&&2*l.length>=m.length){for(var l=new mxGraphHierarchyEdge(m),n=0;n<m.length;n++){var p=m[n];this.edgeMapper.put(p,l);d.resetEdge(p);a.disableEdgeStyle&&(a.setEdgeStyleEnabled(p,!1),a.setOrthogonalEdge(p,!0))}l.source=c[f];0>mxUtils.indexOf(c[f].connectsAsSource,l)&&c[f].connectsAsSource.push(l)}}}c[f].temp[0]=0}};
+mxSwimlaneModel.prototype.initialRank=function(){this.ranksPerGroup=[];var a=[],b={};if(null!=this.roots)for(var c=0;c<this.roots.length;c++){var d=this.vertexMapper.get(this.roots[c]);this.maxChainDfs(null,d,null,b,0);null!=d&&a.push(d)}d=[];b=[];for(c=this.ranksPerGroup.length-1;0<=c;c--)d[c]=c==this.ranksPerGroup.length-1?0:b[c+1]+1,b[c]=d[c]+this.ranksPerGroup[c];this.maxRank=b[0];d=this.vertexMapper.getValues();for(c=0;c<d.length;c++)d[c].temp[0]=-1;for(a.slice();0<a.length;){var d=a[0],e,f;
+e=d.connectsAsTarget;f=d.connectsAsSource;for(var g=!0,k=b[0],c=0;c<e.length;c++){var l=e[c];if(5270620==l.temp[0])l=l.source,k=Math.min(k,l.temp[0]-1);else{g=!1;break}}if(g){k>b[d.swimlaneIndex]&&(k=b[d.swimlaneIndex]);d.temp[0]=k;if(null!=f)for(c=0;c<f.length;c++)l=f[c],l.temp[0]=5270620,l=l.target,-1==l.temp[0]&&(a.push(l),l.temp[0]=-2);a.shift()}else if(c=a.shift(),a.push(d),c==d&&1==a.length)break}};
+mxSwimlaneModel.prototype.maxChainDfs=function(a,b,c,d,e){if(null!=b&&(a=mxCellPath.create(b.cell),null==d[a])){d[a]=b;a=b.swimlaneIndex;if(null==this.ranksPerGroup[a]||this.ranksPerGroup[a]<e)this.ranksPerGroup[a]=e;a=b.connectsAsSource.slice();for(c=0;c<a.length;c++){var f=a[c],g=f.target;b.swimlaneIndex<g.swimlaneIndex?this.maxChainDfs(b,g,f,mxUtils.clone(d,null,!0),0):b.swimlaneIndex==g.swimlaneIndex&&this.maxChainDfs(b,g,f,mxUtils.clone(d,null,!0),e+1)}}};
+mxSwimlaneModel.prototype.fixRanks=function(){var a=[];this.ranks=[];for(var b=0;b<this.maxRank+1;b++)a[b]=[],this.ranks[b]=a[b];var c=null;if(null!=this.roots)for(var d=this.roots,c=[],b=0;b<d.length;b++){var e=this.vertexMapper.get(d[b]);c[b]=e}this.visit(function(b,c,d,e,m){0==m&&0>c.maxRank&&0>c.minRank&&(a[c.temp[0]].push(c),c.maxRank=c.temp[0],c.minRank=c.temp[0],c.temp[0]=a[c.maxRank].length-1);if(null!=b&&null!=d&&1<b.maxRank-c.maxRank)for(d.maxRank=b.maxRank,d.minRank=c.maxRank,d.temp=[],
+d.x=[],d.y=[],b=d.minRank+1;b<d.maxRank;b++)a[b].push(d),d.setGeneralPurposeVariable(b,a[b].length-1)},c,!1,null)};mxSwimlaneModel.prototype.visit=function(a,b,c,d){if(null!=b){for(var e=0;e<b.length;e++){var f=b[e];null!=f&&(null==d&&(d={}),c?(f.hashCode=[],f.hashCode[0]=this.dfsCount,f.hashCode[1]=e,this.extendedDfs(null,f,null,a,d,f.hashCode,e,0)):this.dfs(null,f,null,a,d,0))}this.dfsCount++}};
+mxSwimlaneModel.prototype.dfs=function(a,b,c,d,e,f){if(null!=b){var g=b.id;if(null==e[g])for(e[g]=b,d(a,b,c,f,0),a=b.connectsAsSource.slice(),c=0;c<a.length;c++)g=a[c],this.dfs(b,g.target,g,d,e,f+1);else d(a,b,c,f,1)}};
+mxSwimlaneModel.prototype.extendedDfs=function(a,b,c,d,e,f,g,k){if(null!=b)if(null==a||null!=b.hashCode&&b.hashCode[0]==a.hashCode[0]||(f=a.hashCode.length+1,b.hashCode=a.hashCode.slice(),b.hashCode[f-1]=g),g=b.id,null==e[g]){e[g]=b;d(a,b,c,k,0);a=b.connectsAsSource.slice();c=b.connectsAsTarget.slice();for(g=0;g<a.length;g++){f=a[g];var l=f.target;b.swimlaneIndex<=l.swimlaneIndex&&this.extendedDfs(b,l,f,d,e,b.hashCode,g,k+1)}for(g=0;g<c.length;g++)f=c[g],l=f.source,b.swimlaneIndex<l.swimlaneIndex&&
+this.extendedDfs(b,l,f,d,e,b.hashCode,g,k+1)}else d(a,b,c,k,1)};function mxHierarchicalLayoutStage(){}mxHierarchicalLayoutStage.prototype.execute=function(a){};function mxMedianHybridCrossingReduction(a){this.layout=a}mxMedianHybridCrossingReduction.prototype=new mxHierarchicalLayoutStage;mxMedianHybridCrossingReduction.prototype.constructor=mxMedianHybridCrossingReduction;mxMedianHybridCrossingReduction.prototype.layout=null;mxMedianHybridCrossingReduction.prototype.maxIterations=24;
+mxMedianHybridCrossingReduction.prototype.nestedBestRanks=null;mxMedianHybridCrossingReduction.prototype.currentBestCrossings=0;mxMedianHybridCrossingReduction.prototype.iterationsWithoutImprovement=0;mxMedianHybridCrossingReduction.prototype.maxNoImprovementIterations=2;
+mxMedianHybridCrossingReduction.prototype.execute=function(a){a=this.layout.getModel();this.nestedBestRanks=[];for(var b=0;b<a.ranks.length;b++)this.nestedBestRanks[b]=a.ranks[b].slice();for(var c=0,d=this.calculateCrossings(a),b=0;b<this.maxIterations&&c<this.maxNoImprovementIterations;b++){this.weightedMedian(b,a);this.transpose(b,a);var e=this.calculateCrossings(a);if(e<d)for(d=e,e=c=0;e<this.nestedBestRanks.length;e++)for(var f=a.ranks[e],g=0;g<f.length;g++){var k=f[g];this.nestedBestRanks[e][k.getGeneralPurposeVariable(e)]=
+k}else for(c++,e=0;e<this.nestedBestRanks.length;e++)for(f=a.ranks[e],g=0;g<f.length;g++)k=f[g],k.setGeneralPurposeVariable(e,g);if(0==d)break}c=[];d=[];for(b=0;b<a.maxRank+1;b++)d[b]=[],c[b]=d[b];for(b=0;b<this.nestedBestRanks.length;b++)for(e=0;e<this.nestedBestRanks[b].length;e++)d[b].push(this.nestedBestRanks[b][e]);a.ranks=c};mxMedianHybridCrossingReduction.prototype.calculateCrossings=function(a){for(var b=a.ranks.length,c=0,d=1;d<b;d++)c+=this.calculateRankCrossing(d,a);return c};
+mxMedianHybridCrossingReduction.prototype.calculateRankCrossing=function(a,b){for(var c=0,d=b.ranks[a],e=b.ranks[a-1],f=[],g=0;g<d.length;g++){for(var k=d[g],l=k.getGeneralPurposeVariable(a),k=k.getPreviousLayerConnectedCells(a),m=[],n=0;n<k.length;n++){var p=k[n].getGeneralPurposeVariable(a-1);m.push(p)}m.sort(function(a,b){return a-b});f[l]=m}d=[];for(g=0;g<f.length;g++)d=d.concat(f[g]);for(f=1;f<e.length;)f<<=1;l=2*f-1;--f;e=[];for(g=0;g<l;++g)e[g]=0;for(g=0;g<d.length;g++)for(l=d[g]+f,++e[l];0<
+l;)l%2&&(c+=e[l+1]),l=l-1>>1,++e[l];return c};
+mxMedianHybridCrossingReduction.prototype.transpose=function(a,b){for(var c=!0,d=0;c&&10>d++;)for(var e=1==a%2&&1==d%2,c=!1,f=0;f<b.ranks.length;f++){for(var g=b.ranks[f],k=[],l=0;l<g.length;l++){var m=g[l],n=m.getGeneralPurposeVariable(f);0>n&&(n=l);k[n]=m}for(var p=null,q=null,r,t,u=null,x=null,y,A=null,l=0;l<g.length-1;l++){if(0==l){y=k[l];m=y.getNextLayerConnectedCells(f);n=y.getPreviousLayerConnectedCells(f);r=[];t=[];for(var z=0;z<m.length;z++)r[z]=m[z].getGeneralPurposeVariable(f+1);for(z=
+0;z<n.length;z++)t[z]=n[z].getGeneralPurposeVariable(f-1)}else m=p,n=q,r=u,t=x,y=A;A=k[l+1];p=A.getNextLayerConnectedCells(f);q=A.getPreviousLayerConnectedCells(f);u=[];x=[];for(z=0;z<p.length;z++)u[z]=p[z].getGeneralPurposeVariable(f+1);for(z=0;z<q.length;z++)x[z]=q[z].getGeneralPurposeVariable(f-1);for(var v=0,B=0,z=0;z<r.length;z++)for(var C=0;C<u.length;C++)r[z]>u[C]&&v++,r[z]<u[C]&&B++;for(z=0;z<t.length;z++)for(C=0;C<x.length;C++)t[z]>x[C]&&v++,t[z]<x[C]&&B++;if(B<v||B==v&&e)p=y.getGeneralPurposeVariable(f),
+y.setGeneralPurposeVariable(f,A.getGeneralPurposeVariable(f)),A.setGeneralPurposeVariable(f,p),p=m,q=n,u=r,x=t,A=y,e||(c=!0)}}};mxMedianHybridCrossingReduction.prototype.weightedMedian=function(a,b){var c=0==a%2;if(c)for(var d=b.maxRank-1;0<=d;d--)this.medianRank(d,c);else for(d=1;d<b.maxRank;d++)this.medianRank(d,c)};
+mxMedianHybridCrossingReduction.prototype.medianRank=function(a,b){for(var c=this.nestedBestRanks[a].length,d=[],e=[],f=0;f<c;f++){var g=this.nestedBestRanks[a][f],k=new MedianCellSorter;k.cell=g;var l;l=b?g.getNextLayerConnectedCells(a):g.getPreviousLayerConnectedCells(a);var m;m=b?a+1:a-1;null!=l&&0!=l.length?(k.medianValue=this.medianValue(l,m),d.push(k)):e[g.getGeneralPurposeVariable(a)]=!0}d.sort(MedianCellSorter.prototype.compare);for(f=0;f<c;f++)null==e[f]&&(g=d.shift().cell,g.setGeneralPurposeVariable(a,
+f))};mxMedianHybridCrossingReduction.prototype.medianValue=function(a,b){for(var c=[],d=0,e=0;e<a.length;e++){var f=a[e];c[d++]=f.getGeneralPurposeVariable(b)}c.sort(function(a,b){return a-b});if(1==d%2)return c[Math.floor(d/2)];if(2==d)return(c[0]+c[1])/2;e=d/2;f=c[e-1]-c[0];d=c[d-1]-c[e];return(c[e-1]*d+c[e]*f)/(f+d)};function MedianCellSorter(){}MedianCellSorter.prototype.medianValue=0;MedianCellSorter.prototype.cell=!1;
+MedianCellSorter.prototype.compare=function(a,b){return null!=a&&null!=b?b.medianValue>a.medianValue?-1:b.medianValue<a.medianValue?1:0:0};function mxMinimumCycleRemover(a){this.layout=a}mxMinimumCycleRemover.prototype=new mxHierarchicalLayoutStage;mxMinimumCycleRemover.prototype.constructor=mxMinimumCycleRemover;mxMinimumCycleRemover.prototype.layout=null;
+mxMinimumCycleRemover.prototype.execute=function(a){a=this.layout.getModel();for(var b={},c=a.vertexMapper.getValues(),d={},e=0;e<c.length;e++)d[c[e].id]=c[e];c=null;if(null!=a.roots)for(var f=a.roots,c=[],e=0;e<f.length;e++)c[e]=a.vertexMapper.get(f[e]);a.visit(function(a,c,e,f,n){c.isAncestor(a)&&(e.invert(),mxUtils.remove(e,a.connectsAsSource),a.connectsAsTarget.push(e),mxUtils.remove(e,c.connectsAsTarget),c.connectsAsSource.push(e));b[c.id]=c;delete d[c.id]},c,!0,null);e=mxUtils.clone(b,null,
+!0);a.visit(function(a,c,e,f,n){c.isAncestor(a)&&(e.invert(),mxUtils.remove(e,a.connectsAsSource),c.connectsAsSource.push(e),a.connectsAsTarget.push(e),mxUtils.remove(e,c.connectsAsTarget));b[c.id]=c;delete d[c.id]},d,!0,e)};function mxCoordinateAssignment(a,b,c,d,e,f){this.layout=a;this.intraCellSpacing=b;this.interRankCellSpacing=c;this.orientation=d;this.initialX=e;this.parallelEdgeSpacing=f}mxCoordinateAssignment.prototype=new mxHierarchicalLayoutStage;
+mxCoordinateAssignment.prototype.constructor=mxCoordinateAssignment;mxCoordinateAssignment.prototype.layout=null;mxCoordinateAssignment.prototype.intraCellSpacing=30;mxCoordinateAssignment.prototype.interRankCellSpacing=100;mxCoordinateAssignment.prototype.parallelEdgeSpacing=10;mxCoordinateAssignment.prototype.maxIterations=8;mxCoordinateAssignment.prototype.prefHozEdgeSep=5;mxCoordinateAssignment.prototype.prefVertEdgeOff=2;mxCoordinateAssignment.prototype.minEdgeJetty=12;
+mxCoordinateAssignment.prototype.channelBuffer=4;mxCoordinateAssignment.prototype.jettyPositions=null;mxCoordinateAssignment.prototype.orientation=mxConstants.DIRECTION_NORTH;mxCoordinateAssignment.prototype.initialX=null;mxCoordinateAssignment.prototype.limitX=null;mxCoordinateAssignment.prototype.currentXDelta=null;mxCoordinateAssignment.prototype.widestRank=null;mxCoordinateAssignment.prototype.rankTopY=null;mxCoordinateAssignment.prototype.rankBottomY=null;
+mxCoordinateAssignment.prototype.widestRankValue=null;mxCoordinateAssignment.prototype.rankWidths=null;mxCoordinateAssignment.prototype.rankY=null;mxCoordinateAssignment.prototype.fineTuning=!0;mxCoordinateAssignment.prototype.nextLayerConnectedCache=null;mxCoordinateAssignment.prototype.previousLayerConnectedCache=null;mxCoordinateAssignment.prototype.groupPadding=10;
+mxCoordinateAssignment.prototype.printStatus=function(){var a=this.layout.getModel();mxLog.show();mxLog.writeln("======Coord assignment debug=======");for(var b=0;b<a.ranks.length;b++){mxLog.write("Rank ",b," : ");for(var c=a.ranks[b],d=0;d<c.length;d++)mxLog.write(c[d].getGeneralPurposeVariable(b)," ");mxLog.writeln()}mxLog.writeln("====================================")};
+mxCoordinateAssignment.prototype.execute=function(a){this.jettyPositions={};a=this.layout.getModel();this.currentXDelta=0;this.initialCoords(this.layout.getGraph(),a);this.fineTuning&&this.minNode(a);var b=1E8;if(this.fineTuning)for(var c=0;c<this.maxIterations;c++){0!=c&&(this.medianPos(c,a),this.minNode(a));if(this.currentXDelta<b){for(var d=0;d<a.ranks.length;d++)for(var e=a.ranks[d],f=0;f<e.length;f++){var g=e[f];g.setX(d,g.getGeneralPurposeVariable(d))}b=this.currentXDelta}else for(d=0;d<a.ranks.length;d++)for(e=
+a.ranks[d],f=0;f<e.length;f++)g=e[f],g.setGeneralPurposeVariable(d,g.getX(d));this.minPath(this.layout.getGraph(),a);this.currentXDelta=0}this.setCellLocations(this.layout.getGraph(),a)};
+mxCoordinateAssignment.prototype.minNode=function(a){for(var b=[],c=new mxDictionary,d=[],e=0;e<=a.maxRank;e++){d[e]=a.ranks[e];for(var f=0;f<d[e].length;f++){var g=d[e][f],k=new WeightedCellSorter(g,e);k.rankIndex=f;k.visited=!0;b.push(k);c.put(g,k)}}a=10*b.length;for(f=0;0<b.length&&f<=a;){var g=b.shift(),e=g.cell,l=g.weightedValue,m=parseInt(g.rankIndex),k=e.getNextLayerConnectedCells(l),n=e.getPreviousLayerConnectedCells(l),p=k.length,q=n.length,r=this.medianXValue(k,l+1),t=this.medianXValue(n,
+l-1),u=p+q,x=e.getGeneralPurposeVariable(l),y=x;0<u&&(y=(r*p+t*q)/u);p=!1;y<x-1?0==m?(e.setGeneralPurposeVariable(l,y),p=!0):(m=d[l][m-1],x=m.getGeneralPurposeVariable(l),x=x+m.width/2+this.intraCellSpacing+e.width/2,x<y?(e.setGeneralPurposeVariable(l,y),p=!0):x<e.getGeneralPurposeVariable(l)-1&&(e.setGeneralPurposeVariable(l,x),p=!0)):y>x+1&&(m==d[l].length-1?(e.setGeneralPurposeVariable(l,y),p=!0):(m=d[l][m+1],x=m.getGeneralPurposeVariable(l),x=x-m.width/2-this.intraCellSpacing-e.width/2,x>y?(e.setGeneralPurposeVariable(l,
+y),p=!0):x>e.getGeneralPurposeVariable(l)+1&&(e.setGeneralPurposeVariable(l,x),p=!0)));if(p){for(e=0;e<k.length;e++)l=k[e],l=c.get(l),null!=l&&0==l.visited&&(l.visited=!0,b.push(l));for(e=0;e<n.length;e++)l=n[e],l=c.get(l),null!=l&&0==l.visited&&(l.visited=!0,b.push(l))}g.visited=!1;f++}};mxCoordinateAssignment.prototype.medianPos=function(a,b){if(0==a%2)for(var c=b.maxRank;0<c;c--)this.rankMedianPosition(c-1,b,c);else for(c=0;c<b.maxRank-1;c++)this.rankMedianPosition(c+1,b,c)};
+mxCoordinateAssignment.prototype.rankMedianPosition=function(a,b,c){b=b.ranks[a];for(var d=[],e={},f=0;f<b.length;f++){var g=b[f];d[f]=new WeightedCellSorter;d[f].cell=g;d[f].rankIndex=f;e[g.id]=d[f];var k;k=c<a?g.getPreviousLayerConnectedCells(a):g.getNextLayerConnectedCells(a);d[f].weightedValue=this.calculatedWeightedValue(g,k)}d.sort(WeightedCellSorter.prototype.compare);for(f=0;f<d.length;f++){var l,g=d[f].cell;l=0;k=c<a?g.getPreviousLayerConnectedCells(a).slice():g.getNextLayerConnectedCells(a).slice();
+null!=k&&(l=k.length,l=0<l?this.medianXValue(k,c):g.getGeneralPurposeVariable(a));var m=0;k=-1E8;for(var n=d[f].rankIndex-1;0<=n;){var p=e[b[n].id];if(null!=p){var q=p.cell;p.visited?(k=q.getGeneralPurposeVariable(a)+q.width/2+this.intraCellSpacing+m+g.width/2,n=-1):(m+=q.width+this.intraCellSpacing,n--)}}m=0;q=1E8;for(n=d[f].rankIndex+1;n<d.length;)if(p=e[b[n].id],null!=p){var r=p.cell;p.visited?(q=r.getGeneralPurposeVariable(a)-r.width/2-this.intraCellSpacing-m-g.width/2,n=d.length):(m+=r.width+
+this.intraCellSpacing,n++)}l>=k&&l<=q?g.setGeneralPurposeVariable(a,l):l<k?(g.setGeneralPurposeVariable(a,k),this.currentXDelta+=k-l):l>q&&(g.setGeneralPurposeVariable(a,q),this.currentXDelta+=l-q);d[f].visited=!0}};mxCoordinateAssignment.prototype.calculatedWeightedValue=function(a,b){for(var c=0,d=0;d<b.length;d++){var e=b[d];a.isVertex()&&e.isVertex()?c++:c=a.isEdge()&&e.isEdge()?c+8:c+2}return c};
+mxCoordinateAssignment.prototype.medianXValue=function(a,b){if(0==a.length)return 0;for(var c=[],d=0;d<a.length;d++)c[d]=a[d].getGeneralPurposeVariable(b);c.sort(function(a,b){return a-b});if(1==a.length%2)return c[Math.floor(a.length/2)];d=a.length/2;return(c[d-1]+c[d])/2};
+mxCoordinateAssignment.prototype.initialCoords=function(a,b){this.calculateWidestRank(a,b);for(var c=this.widestRank;0<=c;c--)c<b.maxRank&&this.rankCoordinates(c,a,b);for(c=this.widestRank+1;c<=b.maxRank;c++)0<c&&this.rankCoordinates(c,a,b)};
+mxCoordinateAssignment.prototype.rankCoordinates=function(a,b,c){b=c.ranks[a];c=this.initialX+(this.widestRankValue-this.rankWidths[a])/2;for(var d=!1,e=0;e<b.length;e++){var f=b[e];if(f.isVertex()){var g=this.layout.getVertexBounds(f.cell);null!=g?this.orientation==mxConstants.DIRECTION_NORTH||this.orientation==mxConstants.DIRECTION_SOUTH?(f.width=g.width,f.height=g.height):(f.width=g.height,f.height=g.width):d=!0}else f.isEdge()&&(g=1,null!=f.edges?g=f.edges.length:mxLog.warn("edge.edges is null"),
+f.width=(g-1)*this.parallelEdgeSpacing);c+=f.width/2;f.setX(a,c);f.setGeneralPurposeVariable(a,c);c+=f.width/2;c+=this.intraCellSpacing}1==d&&mxLog.warn("At least one cell has no bounds")};
+mxCoordinateAssignment.prototype.calculateWidestRank=function(a,b){var c=-this.interRankCellSpacing,d=0;this.rankWidths=[];this.rankY=[];for(var e=b.maxRank;0<=e;e--){for(var f=0,g=b.ranks[e],k=this.initialX,l=!1,m=0;m<g.length;m++){var n=g[m];if(n.isVertex()){var p=this.layout.getVertexBounds(n.cell);null!=p?this.orientation==mxConstants.DIRECTION_NORTH||this.orientation==mxConstants.DIRECTION_SOUTH?(n.width=p.width,n.height=p.height):(n.width=p.height,n.height=p.width):l=!0;f=Math.max(f,n.height)}else n.isEdge()&&
+(p=1,null!=n.edges?p=n.edges.length:mxLog.warn("edge.edges is null"),n.width=(p-1)*this.parallelEdgeSpacing);k+=n.width/2;n.setX(e,k);n.setGeneralPurposeVariable(e,k);k+=n.width/2;k+=this.intraCellSpacing;k>this.widestRankValue&&(this.widestRankValue=k,this.widestRank=e);this.rankWidths[e]=k}1==l&&mxLog.warn("At least one cell has no bounds");this.rankY[e]=c;k=f/2+d/2+this.interRankCellSpacing;d=f;c=this.orientation==mxConstants.DIRECTION_NORTH||this.orientation==mxConstants.DIRECTION_WEST?c+k:c-
+k;for(m=0;m<g.length;m++)g[m].setY(e,c)}};
+mxCoordinateAssignment.prototype.minPath=function(a,b){for(var c=b.edgeMapper.getValues(),d=0;d<c.length;d++){var e=c[d];if(!(1>e.maxRank-e.minRank-1)){for(var f=e.getGeneralPurposeVariable(e.minRank+1),g=!0,k=0,l=e.minRank+2;l<e.maxRank;l++){var m=e.getGeneralPurposeVariable(l);f!=m?(g=!1,f=m):k++}if(!g){for(var g=f=0,m=[],n=[],p=e.getGeneralPurposeVariable(e.minRank+1),l=e.minRank+1;l<e.maxRank-1;l++){var q=e.getX(l+1);p==q?(m[l-e.minRank-1]=p,f++):this.repositionValid(b,e,l+1,p)?(m[l-e.minRank-
+1]=p,f++):p=m[l-e.minRank-1]=q}p=e.getX(l);for(l=e.maxRank-1;l>e.minRank+1;l--)q=e.getX(l-1),p==q?(n[l-e.minRank-2]=p,g++):this.repositionValid(b,e,l-1,p)?(n[l-e.minRank-2]=p,g++):(n[l-e.minRank-2]=e.getX(l-1),p=q);if(g>k||f>k)if(g>=f)for(l=e.maxRank-2;l>e.minRank;l--)e.setX(l,n[l-e.minRank-1]);else if(f>g)for(l=e.minRank+2;l<e.maxRank;l++)e.setX(l,m[l-e.minRank-2])}}}};
+mxCoordinateAssignment.prototype.repositionValid=function(a,b,c,d){a=a.ranks[c];for(var e=-1,f=0;f<a.length;f++)if(b==a[f]){e=f;break}if(0>e)return!1;f=b.getGeneralPurposeVariable(c);if(d<f){if(0==e)return!0;a=a[e-1];c=a.getGeneralPurposeVariable(c);c=c+a.width/2+this.intraCellSpacing+b.width/2;if(!(c<=d))return!1}else if(d>f){if(e==a.length-1)return!0;a=a[e+1];c=a.getGeneralPurposeVariable(c);c=c-a.width/2-this.intraCellSpacing-b.width/2;if(!(c>=d))return!1}return!0};
+mxCoordinateAssignment.prototype.setCellLocations=function(a,b){this.rankTopY=[];this.rankBottomY=[];for(var c=0;c<b.ranks.length;c++)this.rankTopY[c]=Number.MAX_VALUE,this.rankBottomY[c]=-Number.MAX_VALUE;for(var d=b.vertexMapper.getValues(),c=0;c<d.length;c++)this.setVertexLocation(d[c]);this.layout.edgeStyle!=mxHierarchicalEdgeStyle.ORTHOGONAL&&this.layout.edgeStyle!=mxHierarchicalEdgeStyle.POLYLINE&&this.layout.edgeStyle!=mxHierarchicalEdgeStyle.CURVE||this.localEdgeProcessing(b);d=b.edgeMapper.getValues();
+for(c=0;c<d.length;c++)this.setEdgePosition(d[c])};
+mxCoordinateAssignment.prototype.localEdgeProcessing=function(a){for(var b=0;b<a.ranks.length;b++)for(var c=a.ranks[b],d=0;d<c.length;d++){var e=c[d];if(e.isVertex())for(var f=e.getPreviousLayerConnectedCells(b),g=b-1,k=0;2>k;k++){if(-1<g&&g<a.ranks.length&&null!=f&&0<f.length){for(var l=[],m=0;m<f.length;m++){var n=new WeightedCellSorter(f[m],f[m].getX(g));l.push(n)}l.sort(WeightedCellSorter.prototype.compare);for(var n=e.x[0]-e.width/2,p=n+e.width,q=f=0,g=[],m=0;m<l.length;m++){var r=l[m].cell,
+t;if(r.isVertex()){t=0==k?e.connectsAsSource:e.connectsAsTarget;for(var u=0;u<t.length;u++)if(t[u].source==r||t[u].target==r)f+=t[u].edges.length,q++,g.push(t[u])}else f+=r.edges.length,q++,g.push(r)}e.width>(f+1)*this.prefHozEdgeSep+2*this.prefHozEdgeSep&&(n+=this.prefHozEdgeSep,p-=this.prefHozEdgeSep);l=(p-n)/f;n+=l/2;p=this.minEdgeJetty-this.prefVertEdgeOff;for(m=0;m<g.length;m++)for(q=g[m].edges.length,r=this.jettyPositions[g[m].ids[0]],null==r&&(r=[],this.jettyPositions[g[m].ids[0]]=r),m<f/2?
+p+=this.prefVertEdgeOff:m>f/2&&(p-=this.prefVertEdgeOff),t=0;t<q;t++)r[4*t+2*k]=n,n+=l,r[4*t+2*k+1]=p}f=e.getNextLayerConnectedCells(b);g=b+1}}};
+mxCoordinateAssignment.prototype.setEdgePosition=function(a){var b=0;if(101207!=a.temp[0]){var c=a.maxRank,d=a.minRank;c==d&&(c=a.source.maxRank,d=a.target.minRank);for(var e=0,f=this.jettyPositions[a.ids[0]],g=a.isReversed?a.target.cell:a.source.cell,k=this.layout.graph,l=this.orientation==mxConstants.DIRECTION_EAST||this.orientation==mxConstants.DIRECTION_SOUTH,m=0;m<a.edges.length;m++){var n=a.edges[m],p=this.layout.getVisibleTerminal(n,!0),q=[],r=a.isReversed;p!=g&&(r=!r);if(null!=f){var t=r?
+2:0,u=r?l?this.rankBottomY[d]:this.rankTopY[d]:l?this.rankTopY[c]:this.rankBottomY[c],x=f[4*e+1+t];r!=l&&(x=-x);var u=u+x,t=f[4*e+t],y=k.model.getTerminal(n,!0);this.layout.isPort(y)&&k.model.getParent(y)==p&&(t=k.view.getState(y),t=null!=t?t.x:p.geometry.x+a.source.width*y.geometry.x);this.orientation==mxConstants.DIRECTION_NORTH||this.orientation==mxConstants.DIRECTION_SOUTH?(q.push(new mxPoint(t,u)),this.layout.edgeStyle==mxHierarchicalEdgeStyle.CURVE&&q.push(new mxPoint(t,u+x))):(q.push(new mxPoint(u,
+t)),this.layout.edgeStyle==mxHierarchicalEdgeStyle.CURVE&&q.push(new mxPoint(u+x,t)))}t=a.x.length-1;u=x=-1;p=a.maxRank-1;r&&(t=0,x=a.x.length,u=1,p=a.minRank+1);for(;a.maxRank!=a.minRank&&t!=x;t+=u){var y=a.x[t]+b,A=(this.rankTopY[p]+this.rankBottomY[p+1])/2,z=(this.rankTopY[p-1]+this.rankBottomY[p])/2;if(r)var v=A,A=z,z=v;this.orientation==mxConstants.DIRECTION_NORTH||this.orientation==mxConstants.DIRECTION_SOUTH?(q.push(new mxPoint(y,A)),q.push(new mxPoint(y,z))):(q.push(new mxPoint(A,y)),q.push(new mxPoint(z,
+y)));this.limitX=Math.max(this.limitX,y);p+=u}null!=f&&(t=r?2:0,u=r?l?this.rankTopY[c]:this.rankBottomY[c]:l?this.rankBottomY[d]:this.rankTopY[d],x=f[4*e+3-t],r!=l&&(x=-x),u-=x,t=f[4*e+2-t],r=k.model.getTerminal(n,!1),p=this.layout.getVisibleTerminal(n,!1),this.layout.isPort(r)&&k.model.getParent(r)==p&&(t=k.view.getState(r),t=null!=t?t.x:p.geometry.x+a.target.width*r.geometry.x),this.orientation==mxConstants.DIRECTION_NORTH||this.orientation==mxConstants.DIRECTION_SOUTH?(this.layout.edgeStyle==mxHierarchicalEdgeStyle.CURVE&&
+q.push(new mxPoint(t,u-x)),q.push(new mxPoint(t,u))):(this.layout.edgeStyle==mxHierarchicalEdgeStyle.CURVE&&q.push(new mxPoint(u-x,t)),q.push(new mxPoint(u,t))));a.isReversed&&this.processReversedEdge(a,n);this.layout.setEdgePoints(n,q);b=0==b?this.parallelEdgeSpacing:0<b?-b:-b+this.parallelEdgeSpacing;e++}a.temp[0]=101207}};
+mxCoordinateAssignment.prototype.setVertexLocation=function(a){var b=a.cell,c=a.x[0]-a.width/2,d=a.y[0]-a.height/2;this.rankTopY[a.minRank]=Math.min(this.rankTopY[a.minRank],d);this.rankBottomY[a.minRank]=Math.max(this.rankBottomY[a.minRank],d+a.height);this.orientation==mxConstants.DIRECTION_NORTH||this.orientation==mxConstants.DIRECTION_SOUTH?this.layout.setVertexLocation(b,c,d):this.layout.setVertexLocation(b,d,c);this.limitX=Math.max(this.limitX,c+a.width)};
+mxCoordinateAssignment.prototype.processReversedEdge=function(a,b){};function mxSwimlaneOrdering(a){this.layout=a}mxSwimlaneOrdering.prototype=new mxHierarchicalLayoutStage;mxSwimlaneOrdering.prototype.constructor=mxSwimlaneOrdering;mxSwimlaneOrdering.prototype.layout=null;
+mxSwimlaneOrdering.prototype.execute=function(a){a=this.layout.getModel();var b=mxUtils.clone(a.vertexMapper,null,!0),c=null;if(null!=a.roots)for(var d=a.roots,c=[],e=0;e<d.length;e++)mxCellPath.create(d[e]),c[e]=a.vertexMapper.get(d[e]);a.visit(function(a,c,d,e,m){e=null!=a&&a.swimlaneIndex==c.swimlaneIndex&&c.isAncestor(a);m=null!=a&&null!=d&&a.swimlaneIndex<c.swimlaneIndex&&d.source==c;e?(d.invert(),mxUtils.remove(d,a.connectsAsSource),c.connectsAsSource.push(d),a.connectsAsTarget.push(d),mxUtils.remove(d,
+c.connectsAsTarget)):m&&(d.invert(),mxUtils.remove(d,a.connectsAsTarget),c.connectsAsTarget.push(d),a.connectsAsSource.push(d),mxUtils.remove(d,c.connectsAsSource));a=mxCellPath.create(c.cell);delete b[a]},c,!0,null)};function mxHierarchicalLayout(a,b,c){mxGraphLayout.call(this,a);this.orientation=null!=b?b:mxConstants.DIRECTION_NORTH;this.deterministic=null!=c?c:!0}var mxHierarchicalEdgeStyle={ORTHOGONAL:1,POLYLINE:2,STRAIGHT:3,CURVE:4};mxHierarchicalLayout.prototype=new mxGraphLayout;
+mxHierarchicalLayout.prototype.constructor=mxHierarchicalLayout;mxHierarchicalLayout.prototype.roots=null;mxHierarchicalLayout.prototype.resizeParent=!1;mxHierarchicalLayout.prototype.maintainParentLocation=!1;mxHierarchicalLayout.prototype.moveParent=!1;mxHierarchicalLayout.prototype.parentBorder=0;mxHierarchicalLayout.prototype.intraCellSpacing=30;mxHierarchicalLayout.prototype.interRankCellSpacing=100;mxHierarchicalLayout.prototype.interHierarchySpacing=60;
+mxHierarchicalLayout.prototype.parallelEdgeSpacing=10;mxHierarchicalLayout.prototype.orientation=mxConstants.DIRECTION_NORTH;mxHierarchicalLayout.prototype.fineTuning=!0;mxHierarchicalLayout.prototype.tightenToSource=!0;mxHierarchicalLayout.prototype.disableEdgeStyle=!0;mxHierarchicalLayout.prototype.traverseAncestors=!0;mxHierarchicalLayout.prototype.model=null;mxHierarchicalLayout.prototype.edgesCache=null;mxHierarchicalLayout.prototype.edgeSourceTermCache=null;
+mxHierarchicalLayout.prototype.edgesTargetTermCache=null;mxHierarchicalLayout.prototype.edgeStyle=mxHierarchicalEdgeStyle.POLYLINE;mxHierarchicalLayout.prototype.getModel=function(){return this.model};
+mxHierarchicalLayout.prototype.execute=function(a,b){this.parent=a;var c=this.graph.model;this.edgesCache=new mxDictionary;this.edgeSourceTermCache=new mxDictionary;this.edgesTargetTermCache=new mxDictionary;null==b||b instanceof Array||(b=[b]);if(null!=b||null!=a){this.parentY=this.parentX=null;if(a!=this.root&&null!=c.isVertex(a)&&this.maintainParentLocation){var d=this.graph.getCellGeometry(a);null!=d&&(this.parentX=d.x,this.parentY=d.y)}if(null!=b){for(var e=[],f=0;f<b.length;f++)(null!=a?c.isAncestor(a,
+b[f]):1)&&c.isVertex(b[f])&&e.push(b[f]);this.roots=e}c.beginUpdate();try{this.run(a),this.resizeParent&&!this.graph.isCellCollapsed(a)&&this.graph.updateGroupBounds([a],this.parentBorder,this.moveParent),null!=this.parentX&&null!=this.parentY&&(d=this.graph.getCellGeometry(a),null!=d&&(d=d.clone(),d.x=this.parentX,d.y=this.parentY,c.setGeometry(a,d)))}finally{c.endUpdate()}}};
+mxHierarchicalLayout.prototype.findRoots=function(a,b){var c=[];if(null!=a&&null!=b){var d=this.graph.model,e=null,f=-1E5,g;for(g in b){var k=b[g];if(d.isVertex(k)&&this.graph.isCellVisible(k)){for(var l=this.getEdges(k),m=0,n=0,p=0;p<l.length;p++)this.getVisibleTerminal(l[p],!0)==k?m++:n++;0==n&&0<m&&c.push(k);l=m-n;l>f&&(f=l,e=k)}}0==c.length&&null!=e&&c.push(e)}return c};
+mxHierarchicalLayout.prototype.getEdges=function(a){var b=this.edgesCache.get(a);if(null!=b)return b;for(var c=this.graph.model,b=[],d=this.graph.isCellCollapsed(a),e=c.getChildCount(a),f=0;f<e;f++){var g=c.getChildAt(a,f);if(this.isPort(g))b=b.concat(c.getEdges(g,!0,!0));else if(d||!this.graph.isCellVisible(g))b=b.concat(c.getEdges(g,!0,!0))}b=b.concat(c.getEdges(a,!0,!0));c=[];for(f=0;f<b.length;f++)d=this.getVisibleTerminal(b[f],!0),e=this.getVisibleTerminal(b[f],!1),(d==e||d!=e&&(e==a&&(null==
+this.parent||this.isAncestor(this.parent,d,this.traverseAncestors))||d==a&&(null==this.parent||this.isAncestor(this.parent,e,this.traverseAncestors))))&&c.push(b[f]);this.edgesCache.put(a,c);return c};
+mxHierarchicalLayout.prototype.getVisibleTerminal=function(a,b){var c=this.edgesTargetTermCache;b&&(c=this.edgeSourceTermCache);var d=c.get(a);if(null!=d)return d;var d=this.graph.view.getState(a),e=null!=d?d.getVisibleTerminal(b):this.graph.view.getVisibleTerminal(a,b);null==e&&(e=null!=d?d.getVisibleTerminal(b):this.graph.view.getVisibleTerminal(a,b));null!=e&&(this.isPort(e)&&(e=this.graph.model.getParent(e)),c.put(a,e));return e};
+mxHierarchicalLayout.prototype.run=function(a){var b=[],c=[];if(null==this.roots&&null!=a){var d={};this.filterDescendants(a,d);this.roots=[];var e=!0,f;for(f in d)if(null!=d[f]){e=!1;break}for(;!e;){for(var g=this.findRoots(a,d),e=0;e<g.length;e++){var k={};b.push(k);this.traverse(g[e],!0,null,c,k,b,d)}for(e=0;e<g.length;e++)this.roots.push(g[e]);e=!0;for(f in d)if(null!=d[f]){e=!1;break}}}else for(e=0;e<this.roots.length;e++)k={},b.push(k),this.traverse(this.roots[e],!0,null,c,k,b,null);for(e=c=
+0;e<b.length;e++){k=b[e];d=[];for(f in k)d.push(k[f]);this.model=new mxGraphHierarchyModel(this,d,this.roots,a,this.tightenToSource);this.cycleStage(a);this.layeringStage();this.crossingStage(a);c=this.placementStage(c,a)}};
+mxHierarchicalLayout.prototype.filterDescendants=function(a,b){var c=this.graph.model;c.isVertex(a)&&a!=this.parent&&this.graph.isCellVisible(a)&&(b[mxObjectIdentity.get(a)]=a);if(this.traverseAncestors||a==this.parent&&this.graph.isCellVisible(a))for(var d=c.getChildCount(a),e=0;e<d;e++){var f=c.getChildAt(a,e);this.isPort(f)||this.filterDescendants(f,b)}};mxHierarchicalLayout.prototype.isPort=function(a){return a.geometry.relative?!0:!1};
+mxHierarchicalLayout.prototype.getEdgesBetween=function(a,b,c){c=null!=c?c:!1;for(var d=this.getEdges(a),e=[],f=0;f<d.length;f++){var g=this.getVisibleTerminal(d[f],!0),k=this.getVisibleTerminal(d[f],!1);(g==a&&k==b||!c&&g==b&&k==a)&&e.push(d[f])}return e};
+mxHierarchicalLayout.prototype.traverse=function(a,b,c,d,e,f,g){if(null!=a&&null!=d){var k=mxObjectIdentity.get(a);if(null==d[k]&&(null==g||null!=g[k])){null==e[k]&&(e[k]=a);null==d[k]&&(d[k]=a);null!==g&&delete g[k];var l=this.getEdges(a),k=[];for(c=0;c<l.length;c++)k[c]=this.getVisibleTerminal(l[c],!0)==a;for(c=0;c<l.length;c++)if(!b||k[c]){a=this.getVisibleTerminal(l[c],!k[c]);for(var m=1,n=0;n<l.length;n++)if(n!=c){var p=k[n];this.getVisibleTerminal(l[n],!p)==a&&(p?m++:m--)}0<=m&&(e=this.traverse(a,
+b,l[c],d,e,f,g))}}else if(null==e[k])for(c=0;c<f.length;c++)if(b=f[c],null!=b[k]){for(l in b)e[l]=b[l];f.splice(c,1);break}}return e};mxHierarchicalLayout.prototype.cycleStage=function(a){(new mxMinimumCycleRemover(this)).execute(a)};mxHierarchicalLayout.prototype.layeringStage=function(){this.model.initialRank();this.model.fixRanks()};mxHierarchicalLayout.prototype.crossingStage=function(a){(new mxMedianHybridCrossingReduction(this)).execute(a)};
+mxHierarchicalLayout.prototype.placementStage=function(a,b){var c=new mxCoordinateAssignment(this,this.intraCellSpacing,this.interRankCellSpacing,this.orientation,a,this.parallelEdgeSpacing);c.fineTuning=this.fineTuning;c.execute(b);return c.limitX+this.interHierarchySpacing};function mxSwimlaneLayout(a,b,c){mxGraphLayout.call(this,a);this.orientation=null!=b?b:mxConstants.DIRECTION_NORTH;this.deterministic=null!=c?c:!0}mxSwimlaneLayout.prototype=new mxGraphLayout;
+mxSwimlaneLayout.prototype.constructor=mxSwimlaneLayout;mxSwimlaneLayout.prototype.roots=null;mxSwimlaneLayout.prototype.swimlanes=null;mxSwimlaneLayout.prototype.dummyVertices=null;mxSwimlaneLayout.prototype.dummyVertexWidth=50;mxSwimlaneLayout.prototype.resizeParent=!1;mxSwimlaneLayout.prototype.maintainParentLocation=!1;mxSwimlaneLayout.prototype.moveParent=!1;mxSwimlaneLayout.prototype.parentBorder=30;mxSwimlaneLayout.prototype.intraCellSpacing=30;
+mxSwimlaneLayout.prototype.interRankCellSpacing=100;mxSwimlaneLayout.prototype.interHierarchySpacing=60;mxSwimlaneLayout.prototype.parallelEdgeSpacing=10;mxSwimlaneLayout.prototype.orientation=mxConstants.DIRECTION_NORTH;mxSwimlaneLayout.prototype.fineTuning=!0;mxSwimlaneLayout.prototype.tightenToSource=!0;mxSwimlaneLayout.prototype.disableEdgeStyle=!0;mxSwimlaneLayout.prototype.traverseAncestors=!0;mxSwimlaneLayout.prototype.model=null;mxSwimlaneLayout.prototype.edgesCache=null;
+mxHierarchicalLayout.prototype.edgeSourceTermCache=null;mxHierarchicalLayout.prototype.edgesTargetTermCache=null;mxHierarchicalLayout.prototype.edgeStyle=mxHierarchicalEdgeStyle.POLYLINE;mxSwimlaneLayout.prototype.getModel=function(){return this.model};
+mxSwimlaneLayout.prototype.execute=function(a,b){this.parent=a;var c=this.graph.model;this.edgesCache=new mxDictionary;this.edgeSourceTermCache=new mxDictionary;this.edgesTargetTermCache=new mxDictionary;if(!(null==b||1>b.length)){null==a&&(a=c.getParent(b[0]));this.parentY=this.parentX=null;if(a!=this.root&&null!=c.isVertex(a)&&this.maintainParentLocation){var d=this.graph.getCellGeometry(a);null!=d&&(this.parentX=d.x,this.parentY=d.y)}this.swimlanes=b;this.dummyVertices=[];for(var e=0;e<b.length;e++){var f=
+this.graph.getChildCells(b[e]);if(null==f||0==f.length)f=this.graph.insertVertex(b[e],null,null,0,0,this.dummyVertexWidth,0),this.dummyVertices.push(f)}c.beginUpdate();try{this.run(a),this.resizeParent&&!this.graph.isCellCollapsed(a)&&this.graph.updateGroupBounds([a],this.parentBorder,this.moveParent),null!=this.parentX&&null!=this.parentY&&(d=this.graph.getCellGeometry(a),null!=d&&(d=d.clone(),d.x=this.parentX,d.y=this.parentY,c.setGeometry(a,d))),this.graph.removeCells(this.dummyVertices)}finally{c.endUpdate()}}};
+mxSwimlaneLayout.prototype.updateGroupBounds=function(){var a=[],b=this.model,c;for(c in b.edgeMapper)for(var d=b.edgeMapper[c],e=0;e<d.edges.length;e++)a.push(d.edges[e]);a=this.graph.getBoundingBoxFromGeometry(a,!0);b=[];for(e=0;e<this.swimlanes.length;e++){var f=this.swimlanes[e];c=this.graph.getCellGeometry(f);if(null!=c){var g=this.graph.getChildCells(f),d=this.graph.isSwimlane(f)?this.graph.getStartSize(f):new mxRectangle,f=this.graph.getBoundingBoxFromGeometry(g);b[e]=f;d=f.y+c.y-d.height-
+this.parentBorder;c=f.y+c.y+f.height;null==a?a=new mxRectangle(0,d,0,c-d):(a.y=Math.min(a.y,d),a.height=Math.max(a.y+a.height,c)-a.y)}}for(e=0;e<this.swimlanes.length;e++)if(f=this.swimlanes[e],c=this.graph.getCellGeometry(f),null!=c){var g=this.graph.getChildCells(f),d=this.graph.isSwimlane(f)?this.graph.getStartSize(f):new mxRectangle,k=c.clone(),l=0==e?this.parentBorder:this.interRankCellSpacing/2;k.x+=b[e].x-d.width-l;k.y=k.y+a.y-c.y-this.parentBorder;k.width=b[e].width+d.width+this.interRankCellSpacing/
+2+l;k.height=a.height+d.height+2*this.parentBorder;this.graph.model.setGeometry(f,k);this.graph.moveCells(g,-b[e].x+d.width+l,c.y-a.y+this.parentBorder)}};
+mxSwimlaneLayout.prototype.findRoots=function(a,b){var c=[];if(null!=a&&null!=b){var d=this.graph.model,e=null,f=-1E5,g;for(g in b){var k=b[g];if(null!=k&&d.isVertex(k)&&this.graph.isCellVisible(k)&&d.isAncestor(a,k)){for(var l=this.getEdges(k),m=0,n=0,p=0;p<l.length;p++){var q=this.getVisibleTerminal(l[p],!0);q==k?(q=this.getVisibleTerminal(l[p],!1),d.isAncestor(a,q)&&m++):d.isAncestor(a,q)&&n++}0==n&&0<m&&c.push(k);l=m-n;l>f&&(f=l,e=k)}}0==c.length&&null!=e&&c.push(e)}return c};
+mxSwimlaneLayout.prototype.getEdges=function(a){var b=this.edgesCache.get(a);if(null!=b)return b;for(var c=this.graph.model,b=[],d=this.graph.isCellCollapsed(a),e=c.getChildCount(a),f=0;f<e;f++){var g=c.getChildAt(a,f);if(this.isPort(g))b=b.concat(c.getEdges(g,!0,!0));else if(d||!this.graph.isCellVisible(g))b=b.concat(c.getEdges(g,!0,!0))}b=b.concat(c.getEdges(a,!0,!0));c=[];for(f=0;f<b.length;f++)d=this.getVisibleTerminal(b[f],!0),e=this.getVisibleTerminal(b[f],!1),(d==e||d!=e&&(e==a&&(null==this.parent||
+this.graph.isValidAncestor(d,this.parent,this.traverseAncestors))||d==a&&(null==this.parent||this.graph.isValidAncestor(e,this.parent,this.traverseAncestors))))&&c.push(b[f]);this.edgesCache.put(a,c);return c};
+mxSwimlaneLayout.prototype.getVisibleTerminal=function(a,b){var c=this.edgesTargetTermCache;b&&(c=this.edgeSourceTermCache);var d=c.get(a);if(null!=d)return d;var d=this.graph.view.getState(a),e=null!=d?d.getVisibleTerminal(b):this.graph.view.getVisibleTerminal(a,b);null==e&&(e=null!=d?d.getVisibleTerminal(b):this.graph.view.getVisibleTerminal(a,b));null!=e&&(this.isPort(e)&&(e=this.graph.model.getParent(e)),c.put(a,e));return e};
+mxSwimlaneLayout.prototype.run=function(a){var b=[],c=[];if(null!=this.swimlanes&&0<this.swimlanes.length&&null!=a){for(var d={},e=0;e<this.swimlanes.length;e++)this.filterDescendants(this.swimlanes[e],d);this.roots=[];var e=!0,f;for(f in d)if(null!=d[f]){e=!1;break}for(var g=0;!e&&g<this.swimlanes.length;){var k=this.findRoots(this.swimlanes[g],d);if(0==k.length)g++;else{for(e=0;e<k.length;e++){var l={};b.push(l);this.traverse(k[e],!0,null,c,l,b,d,g)}for(e=0;e<k.length;e++)this.roots.push(k[e]);
+e=!0;for(f in d)if(null!=d[f]){e=!1;break}}}}else for(e=0;e<this.roots.length;e++)l={},b.push(l),this.traverse(this.roots[e],!0,null,c,l,b,null);b=[];for(f in c)b.push(c[f]);this.model=new mxSwimlaneModel(this,b,this.roots,a,this.tightenToSource);this.cycleStage(a);this.layeringStage();this.crossingStage(a);initialX=this.placementStage(0,a)};
+mxSwimlaneLayout.prototype.filterDescendants=function(a,b){var c=this.graph.model;c.isVertex(a)&&a!=this.parent&&c.getParent(a)!=this.parent&&this.graph.isCellVisible(a)&&(b[mxObjectIdentity.get(a)]=a);if(this.traverseAncestors||a==this.parent&&this.graph.isCellVisible(a))for(var d=c.getChildCount(a),e=0;e<d;e++){var f=c.getChildAt(a,e);this.isPort(f)||this.filterDescendants(f,b)}};mxSwimlaneLayout.prototype.isPort=function(a){return a.geometry.relative?!0:!1};
+mxSwimlaneLayout.prototype.getEdgesBetween=function(a,b,c){c=null!=c?c:!1;for(var d=this.getEdges(a),e=[],f=0;f<d.length;f++){var g=this.getVisibleTerminal(d[f],!0),k=this.getVisibleTerminal(d[f],!1);(g==a&&k==b||!c&&g==b&&k==a)&&e.push(d[f])}return e};
+mxSwimlaneLayout.prototype.traverse=function(a,b,c,d,e,f,g,k){if(null!=a&&null!=d){var l=mxObjectIdentity.get(a);if(null==d[l]&&(null==g||null!=g[l])){null==e[l]&&(e[l]=a);null==d[l]&&(d[l]=a);null!==g&&delete g[l];var m=this.getEdges(a),l=this.graph.model;for(c=0;c<m.length;c++){var n=this.getVisibleTerminal(m[c],!0),p=n==a;p&&(n=this.getVisibleTerminal(m[c],!1));var q;for(q=0;q<this.swimlanes.length&&!l.isAncestor(this.swimlanes[q],n);q++);q>=this.swimlanes.length||!(q>k||(!b||p)&&q==k)||(e=this.traverse(n,
+b,m[c],d,e,f,g,q))}}else if(null==e[l])for(c=0;c<f.length;c++)if(a=f[c],null!=a[l]){for(m in a)e[m]=a[m];f.splice(c,1);break}}return e};mxSwimlaneLayout.prototype.cycleStage=function(a){(new mxSwimlaneOrdering(this)).execute(a)};mxSwimlaneLayout.prototype.layeringStage=function(){this.model.initialRank();this.model.fixRanks()};mxSwimlaneLayout.prototype.crossingStage=function(a){(new mxMedianHybridCrossingReduction(this)).execute(a)};
+mxSwimlaneLayout.prototype.placementStage=function(a,b){var c=new mxCoordinateAssignment(this,this.intraCellSpacing,this.interRankCellSpacing,this.orientation,a,this.parallelEdgeSpacing);c.fineTuning=this.fineTuning;c.execute(b);return c.limitX+this.interHierarchySpacing};function mxGraphModel(a){this.currentEdit=this.createUndoableEdit();null!=a?this.setRoot(a):this.clear()}mxGraphModel.prototype=new mxEventSource;mxGraphModel.prototype.constructor=mxGraphModel;mxGraphModel.prototype.root=null;
+mxGraphModel.prototype.cells=null;mxGraphModel.prototype.maintainEdgeParent=!0;mxGraphModel.prototype.ignoreRelativeEdgeParent=!0;mxGraphModel.prototype.createIds=!0;mxGraphModel.prototype.prefix="";mxGraphModel.prototype.postfix="";mxGraphModel.prototype.nextId=0;mxGraphModel.prototype.currentEdit=null;mxGraphModel.prototype.updateLevel=0;mxGraphModel.prototype.endingUpdate=!1;mxGraphModel.prototype.clear=function(){this.setRoot(this.createRoot())};mxGraphModel.prototype.isCreateIds=function(){return this.createIds};
+mxGraphModel.prototype.setCreateIds=function(a){this.createIds=a};mxGraphModel.prototype.createRoot=function(){var a=new mxCell;a.insert(new mxCell);return a};mxGraphModel.prototype.getCell=function(a){return null!=this.cells?this.cells[a]:null};mxGraphModel.prototype.filterCells=function(a,b){var c=null;if(null!=a)for(var c=[],d=0;d<a.length;d++)b(a[d])&&c.push(a[d]);return c};mxGraphModel.prototype.getDescendants=function(a){return this.filterDescendants(null,a)};
+mxGraphModel.prototype.filterDescendants=function(a,b){var c=[];b=b||this.getRoot();(null==a||a(b))&&c.push(b);for(var d=this.getChildCount(b),e=0;e<d;e++)var f=this.getChildAt(b,e),c=c.concat(this.filterDescendants(a,f));return c};mxGraphModel.prototype.getRoot=function(a){var b=a||this.root;if(null!=a)for(;null!=a;)b=a,a=this.getParent(a);return b};mxGraphModel.prototype.setRoot=function(a){this.execute(new mxRootChange(this,a));return a};
+mxGraphModel.prototype.rootChanged=function(a){var b=this.root;this.root=a;this.nextId=0;this.cells=null;this.cellAdded(a);return b};mxGraphModel.prototype.isRoot=function(a){return null!=a&&this.root==a};mxGraphModel.prototype.isLayer=function(a){return this.isRoot(this.getParent(a))};mxGraphModel.prototype.isAncestor=function(a,b){for(;null!=b&&b!=a;)b=this.getParent(b);return b==a};mxGraphModel.prototype.contains=function(a){return this.isAncestor(this.root,a)};
+mxGraphModel.prototype.getParent=function(a){return null!=a?a.getParent():null};mxGraphModel.prototype.add=function(a,b,c){if(b!=a&&null!=a&&null!=b){null==c&&(c=this.getChildCount(a));var d=a!=this.getParent(b);this.execute(new mxChildChange(this,a,b,c));this.maintainEdgeParent&&d&&this.updateEdgeParents(b)}return b};
+mxGraphModel.prototype.cellAdded=function(a){if(null!=a){null==a.getId()&&this.createIds&&a.setId(this.createId(a));if(null!=a.getId()){var b=this.getCell(a.getId());if(b!=a){for(;null!=b;)a.setId(this.createId(a)),b=this.getCell(a.getId());null==this.cells&&(this.cells={});this.cells[a.getId()]=a}}mxUtils.isNumeric(a.getId())&&(this.nextId=Math.max(this.nextId,a.getId()));for(var b=this.getChildCount(a),c=0;c<b;c++)this.cellAdded(this.getChildAt(a,c))}};
+mxGraphModel.prototype.createId=function(a){a=this.nextId;this.nextId++;return this.prefix+a+this.postfix};mxGraphModel.prototype.updateEdgeParents=function(a,b){b=b||this.getRoot(a);for(var c=this.getChildCount(a),d=0;d<c;d++){var e=this.getChildAt(a,d);this.updateEdgeParents(e,b)}e=this.getEdgeCount(a);c=[];for(d=0;d<e;d++)c.push(this.getEdgeAt(a,d));for(d=0;d<c.length;d++)e=c[d],this.isAncestor(b,e)&&this.updateEdgeParent(e,b)};
+mxGraphModel.prototype.updateEdgeParent=function(a,b){for(var c=this.getTerminal(a,!0),d=this.getTerminal(a,!1);null!=c&&!this.isEdge(c)&&null!=c.geometry&&c.geometry.relative;)c=this.getParent(c);for(;null!=d&&this.ignoreRelativeEdgeParent&&!this.isEdge(d)&&null!=d.geometry&&d.geometry.relative;)d=this.getParent(d);if(this.isAncestor(b,c)&&this.isAncestor(b,d)&&(c=c==d?this.getParent(c):this.getNearestCommonAncestor(c,d),null!=c&&(this.getParent(c)!=this.root||this.isAncestor(c,a))&&this.getParent(a)!=
+c)){d=this.getGeometry(a);if(null!=d){var e=this.getOrigin(this.getParent(a)),f=this.getOrigin(c),g=f.x-e.x,e=f.y-e.y,d=d.clone();d.translate(-g,-e);this.setGeometry(a,d)}this.add(c,a,this.getChildCount(c))}};mxGraphModel.prototype.getOrigin=function(a){var b;null!=a?(b=this.getOrigin(this.getParent(a)),this.isEdge(a)||(a=this.getGeometry(a),null!=a&&(b.x+=a.x,b.y+=a.y))):b=new mxPoint;return b};
+mxGraphModel.prototype.getNearestCommonAncestor=function(a,b){if(null!=a&&null!=b){var c=mxCellPath.create(b);if(null!=c&&0<c.length){var d=a,e=mxCellPath.create(d);if(c.length<e.length)var d=b,f=e,e=c,c=f;for(;null!=d;){f=this.getParent(d);if(0==c.indexOf(e+mxCellPath.PATH_SEPARATOR)&&null!=f)return d;e=mxCellPath.getParentPath(e);d=f}}}return null};mxGraphModel.prototype.remove=function(a){a==this.root?this.setRoot(null):null!=this.getParent(a)&&this.execute(new mxChildChange(this,null,a));return a};
+mxGraphModel.prototype.cellRemoved=function(a){if(null!=a&&null!=this.cells){for(var b=this.getChildCount(a)-1;0<=b;b--)this.cellRemoved(this.getChildAt(a,b));null!=this.cells&&null!=a.getId()&&delete this.cells[a.getId()]}};mxGraphModel.prototype.parentForCellChanged=function(a,b,c){var d=this.getParent(a);null!=b?b==d&&d.getIndex(a)==c||b.insert(a,c):null!=d&&(c=d.getIndex(a),d.remove(c));this.contains(d)||null==b?null==b&&this.cellRemoved(a):this.cellAdded(a);return d};
+mxGraphModel.prototype.getChildCount=function(a){return null!=a?a.getChildCount():0};mxGraphModel.prototype.getChildAt=function(a,b){return null!=a?a.getChildAt(b):null};mxGraphModel.prototype.getChildren=function(a){return null!=a?a.children:null};mxGraphModel.prototype.getChildVertices=function(a){return this.getChildCells(a,!0,!1)};mxGraphModel.prototype.getChildEdges=function(a){return this.getChildCells(a,!1,!0)};
+mxGraphModel.prototype.getChildCells=function(a,b,c){b=null!=b?b:!1;c=null!=c?c:!1;for(var d=this.getChildCount(a),e=[],f=0;f<d;f++){var g=this.getChildAt(a,f);(!c&&!b||c&&this.isEdge(g)||b&&this.isVertex(g))&&e.push(g)}return e};mxGraphModel.prototype.getTerminal=function(a,b){return null!=a?a.getTerminal(b):null};
+mxGraphModel.prototype.setTerminal=function(a,b,c){var d=b!=this.getTerminal(a,c);this.execute(new mxTerminalChange(this,a,b,c));this.maintainEdgeParent&&d&&this.updateEdgeParent(a,this.getRoot());return b};mxGraphModel.prototype.setTerminals=function(a,b,c){this.beginUpdate();try{this.setTerminal(a,b,!0),this.setTerminal(a,c,!1)}finally{this.endUpdate()}};
+mxGraphModel.prototype.terminalForCellChanged=function(a,b,c){var d=this.getTerminal(a,c);null!=b?b.insertEdge(a,c):null!=d&&d.removeEdge(a,c);return d};mxGraphModel.prototype.getEdgeCount=function(a){return null!=a?a.getEdgeCount():0};mxGraphModel.prototype.getEdgeAt=function(a,b){return null!=a?a.getEdgeAt(b):null};mxGraphModel.prototype.getDirectedEdgeCount=function(a,b,c){for(var d=0,e=this.getEdgeCount(a),f=0;f<e;f++){var g=this.getEdgeAt(a,f);g!=c&&this.getTerminal(g,b)==a&&d++}return d};
+mxGraphModel.prototype.getConnections=function(a){return this.getEdges(a,!0,!0,!1)};mxGraphModel.prototype.getIncomingEdges=function(a){return this.getEdges(a,!0,!1,!1)};mxGraphModel.prototype.getOutgoingEdges=function(a){return this.getEdges(a,!1,!0,!1)};
+mxGraphModel.prototype.getEdges=function(a,b,c,d){b=null!=b?b:!0;c=null!=c?c:!0;d=null!=d?d:!0;for(var e=this.getEdgeCount(a),f=[],g=0;g<e;g++){var k=this.getEdgeAt(a,g),l=this.getTerminal(k,!0),m=this.getTerminal(k,!1);(d&&l==m||l!=m&&(b&&m==a||c&&l==a))&&f.push(k)}return f};
+mxGraphModel.prototype.getEdgesBetween=function(a,b,c){c=null!=c?c:!1;var d=this.getEdgeCount(a),e=this.getEdgeCount(b),f=a,g=d;e<d&&(g=e,f=b);d=[];for(e=0;e<g;e++){var k=this.getEdgeAt(f,e),l=this.getTerminal(k,!0),m=this.getTerminal(k,!1),n=m==a&&l==b;(l==a&&m==b||!c&&n)&&d.push(k)}return d};
+mxGraphModel.prototype.getOpposites=function(a,b,c,d){c=null!=c?c:!0;d=null!=d?d:!0;var e=[];if(null!=a)for(var f=0;f<a.length;f++){var g=this.getTerminal(a[f],!0),k=this.getTerminal(a[f],!1);g==b&&null!=k&&k!=b&&d?e.push(k):k==b&&null!=g&&g!=b&&c&&e.push(g)}return e};
+mxGraphModel.prototype.getTopmostCells=function(a){for(var b=new mxDictionary,c=[],d=0;d<a.length;d++)b.put(a[d],!0);for(d=0;d<a.length;d++){for(var e=a[d],f=!0,g=this.getParent(e);null!=g;){if(b.get(g)){f=!1;break}g=this.getParent(g)}f&&c.push(e)}return c};mxGraphModel.prototype.isVertex=function(a){return null!=a?a.isVertex():!1};mxGraphModel.prototype.isEdge=function(a){return null!=a?a.isEdge():!1};mxGraphModel.prototype.isConnectable=function(a){return null!=a?a.isConnectable():!1};
+mxGraphModel.prototype.getValue=function(a){return null!=a?a.getValue():null};mxGraphModel.prototype.setValue=function(a,b){this.execute(new mxValueChange(this,a,b));return b};mxGraphModel.prototype.valueForCellChanged=function(a,b){return a.valueChanged(b)};mxGraphModel.prototype.getGeometry=function(a){return null!=a?a.getGeometry():null};mxGraphModel.prototype.setGeometry=function(a,b){b!=this.getGeometry(a)&&this.execute(new mxGeometryChange(this,a,b));return b};
+mxGraphModel.prototype.geometryForCellChanged=function(a,b){var c=this.getGeometry(a);a.setGeometry(b);return c};mxGraphModel.prototype.getStyle=function(a){return null!=a?a.getStyle():null};mxGraphModel.prototype.setStyle=function(a,b){b!=this.getStyle(a)&&this.execute(new mxStyleChange(this,a,b));return b};mxGraphModel.prototype.styleForCellChanged=function(a,b){var c=this.getStyle(a);a.setStyle(b);return c};mxGraphModel.prototype.isCollapsed=function(a){return null!=a?a.isCollapsed():!1};
+mxGraphModel.prototype.setCollapsed=function(a,b){b!=this.isCollapsed(a)&&this.execute(new mxCollapseChange(this,a,b));return b};mxGraphModel.prototype.collapsedStateForCellChanged=function(a,b){var c=this.isCollapsed(a);a.setCollapsed(b);return c};mxGraphModel.prototype.isVisible=function(a){return null!=a?a.isVisible():!1};mxGraphModel.prototype.setVisible=function(a,b){b!=this.isVisible(a)&&this.execute(new mxVisibleChange(this,a,b));return b};
+mxGraphModel.prototype.visibleStateForCellChanged=function(a,b){var c=this.isVisible(a);a.setVisible(b);return c};mxGraphModel.prototype.execute=function(a){a.execute();this.beginUpdate();this.currentEdit.add(a);this.fireEvent(new mxEventObject(mxEvent.EXECUTE,"change",a));this.fireEvent(new mxEventObject(mxEvent.EXECUTED,"change",a));this.endUpdate()};mxGraphModel.prototype.beginUpdate=function(){this.updateLevel++;this.fireEvent(new mxEventObject(mxEvent.BEGIN_UPDATE));1==this.updateLevel&&this.fireEvent(new mxEventObject(mxEvent.START_EDIT))};
+mxGraphModel.prototype.endUpdate=function(){this.updateLevel--;0==this.updateLevel&&this.fireEvent(new mxEventObject(mxEvent.END_EDIT));if(!this.endingUpdate){this.endingUpdate=0==this.updateLevel;this.fireEvent(new mxEventObject(mxEvent.END_UPDATE,"edit",this.currentEdit));try{if(this.endingUpdate&&!this.currentEdit.isEmpty()){this.fireEvent(new mxEventObject(mxEvent.BEFORE_UNDO,"edit",this.currentEdit));var a=this.currentEdit;this.currentEdit=this.createUndoableEdit();a.notify();this.fireEvent(new mxEventObject(mxEvent.UNDO,
+"edit",a))}}finally{this.endingUpdate=!1}}};mxGraphModel.prototype.createUndoableEdit=function(a){var b=new mxUndoableEdit(this,null!=a?a:!0);b.notify=function(){b.source.fireEvent(new mxEventObject(mxEvent.CHANGE,"edit",b,"changes",b.changes));b.source.fireEvent(new mxEventObject(mxEvent.NOTIFY,"edit",b,"changes",b.changes))};return b};
+mxGraphModel.prototype.mergeChildren=function(a,b,c){c=null!=c?c:!0;this.beginUpdate();try{var d={};this.mergeChildrenImpl(a,b,c,d);for(var e in d){var f=d[e],g=this.getTerminal(f,!0);null!=g&&(g=d[mxCellPath.create(g)],this.setTerminal(f,g,!0));g=this.getTerminal(f,!1);null!=g&&(g=d[mxCellPath.create(g)],this.setTerminal(f,g,!1))}}finally{this.endUpdate()}};
+mxGraphModel.prototype.mergeChildrenImpl=function(a,b,c,d){this.beginUpdate();try{for(var e=a.getChildCount(),f=0;f<e;f++){var g=a.getChildAt(f);if("function"==typeof g.getId){var k=g.getId(),l=null==k||this.isEdge(g)&&c?null:this.getCell(k);if(null==l){var m=g.clone();m.setId(k);m.setTerminal(g.getTerminal(!0),!0);m.setTerminal(g.getTerminal(!1),!1);l=b.insert(m);this.cellAdded(l)}d[mxCellPath.create(g)]=l;this.mergeChildrenImpl(g,l,c,d)}}}finally{this.endUpdate()}};
+mxGraphModel.prototype.getParents=function(a){var b=[];if(null!=a)for(var c=new mxDictionary,d=0;d<a.length;d++){var e=this.getParent(a[d]);null==e||c.get(e)||(c.put(e,!0),b.push(e))}return b};mxGraphModel.prototype.cloneCell=function(a){return null!=a?this.cloneCells([a],!0)[0]:null};
+mxGraphModel.prototype.cloneCells=function(a,b,c){c=null!=c?c:{};for(var d=[],e=0;e<a.length;e++)null!=a[e]?d.push(this.cloneCellImpl(a[e],c,b)):d.push(null);for(e=0;e<d.length;e++)null!=d[e]&&this.restoreClone(d[e],a[e],c);return d};mxGraphModel.prototype.cloneCellImpl=function(a,b,c){var d=this.cellCloned(a);b[mxObjectIdentity.get(a)]=d;if(c){c=this.getChildCount(a);for(var e=0;e<c;e++){var f=this.cloneCellImpl(this.getChildAt(a,e),b,!0);d.insert(f)}}return d};
+mxGraphModel.prototype.cellCloned=function(a){return a.clone()};mxGraphModel.prototype.restoreClone=function(a,b,c){var d=this.getTerminal(b,!0);null!=d&&(d=c[mxObjectIdentity.get(d)],null!=d&&d.insertEdge(a,!0));d=this.getTerminal(b,!1);null!=d&&(d=c[mxObjectIdentity.get(d)],null!=d&&d.insertEdge(a,!1));for(var d=this.getChildCount(a),e=0;e<d;e++)this.restoreClone(this.getChildAt(a,e),this.getChildAt(b,e),c)};function mxRootChange(a,b){this.model=a;this.previous=this.root=b}
+mxRootChange.prototype.execute=function(){this.root=this.previous;this.previous=this.model.rootChanged(this.previous)};function mxChildChange(a,b,c,d){this.model=a;this.previous=this.parent=b;this.child=c;this.previousIndex=this.index=d}
+mxChildChange.prototype.execute=function(){if(null!=this.child){var a=this.model.getParent(this.child),b=null!=a?a.getIndex(this.child):0;null==this.previous&&this.connect(this.child,!1);a=this.model.parentForCellChanged(this.child,this.previous,this.previousIndex);null!=this.previous&&this.connect(this.child,!0);this.parent=this.previous;this.previous=a;this.index=this.previousIndex;this.previousIndex=b}};
+mxChildChange.prototype.connect=function(a,b){b=null!=b?b:!0;var c=a.getTerminal(!0),d=a.getTerminal(!1);null!=c&&(b?this.model.terminalForCellChanged(a,c,!0):this.model.terminalForCellChanged(a,null,!0));null!=d&&(b?this.model.terminalForCellChanged(a,d,!1):this.model.terminalForCellChanged(a,null,!1));a.setTerminal(c,!0);a.setTerminal(d,!1);c=this.model.getChildCount(a);for(d=0;d<c;d++)this.connect(this.model.getChildAt(a,d),b)};
+function mxTerminalChange(a,b,c,d){this.model=a;this.cell=b;this.previous=this.terminal=c;this.source=d}mxTerminalChange.prototype.execute=function(){null!=this.cell&&(this.terminal=this.previous,this.previous=this.model.terminalForCellChanged(this.cell,this.previous,this.source))};function mxValueChange(a,b,c){this.model=a;this.cell=b;this.previous=this.value=c}
+mxValueChange.prototype.execute=function(){null!=this.cell&&(this.value=this.previous,this.previous=this.model.valueForCellChanged(this.cell,this.previous))};function mxStyleChange(a,b,c){this.model=a;this.cell=b;this.previous=this.style=c}mxStyleChange.prototype.execute=function(){null!=this.cell&&(this.style=this.previous,this.previous=this.model.styleForCellChanged(this.cell,this.previous))};function mxGeometryChange(a,b,c){this.model=a;this.cell=b;this.previous=this.geometry=c}
+mxGeometryChange.prototype.execute=function(){null!=this.cell&&(this.geometry=this.previous,this.previous=this.model.geometryForCellChanged(this.cell,this.previous))};function mxCollapseChange(a,b,c){this.model=a;this.cell=b;this.previous=this.collapsed=c}mxCollapseChange.prototype.execute=function(){null!=this.cell&&(this.collapsed=this.previous,this.previous=this.model.collapsedStateForCellChanged(this.cell,this.previous))};
+function mxVisibleChange(a,b,c){this.model=a;this.cell=b;this.previous=this.visible=c}mxVisibleChange.prototype.execute=function(){null!=this.cell&&(this.visible=this.previous,this.previous=this.model.visibleStateForCellChanged(this.cell,this.previous))};function mxCellAttributeChange(a,b,c){this.cell=a;this.attribute=b;this.previous=this.value=c}
+mxCellAttributeChange.prototype.execute=function(){if(null!=this.cell){var a=this.cell.getAttribute(this.attribute);null==this.previous?this.cell.value.removeAttribute(this.attribute):this.cell.setAttribute(this.attribute,this.previous);this.previous=a}};function mxCell(a,b,c){this.value=a;this.setGeometry(b);this.setStyle(c);if(null!=this.onInit)this.onInit()}mxCell.prototype.id=null;mxCell.prototype.value=null;mxCell.prototype.geometry=null;mxCell.prototype.style=null;mxCell.prototype.vertex=!1;
+mxCell.prototype.edge=!1;mxCell.prototype.connectable=!0;mxCell.prototype.visible=!0;mxCell.prototype.collapsed=!1;mxCell.prototype.parent=null;mxCell.prototype.source=null;mxCell.prototype.target=null;mxCell.prototype.children=null;mxCell.prototype.edges=null;mxCell.prototype.mxTransient="id value parent source target children edges".split(" ");mxCell.prototype.getId=function(){return this.id};mxCell.prototype.setId=function(a){this.id=a};mxCell.prototype.getValue=function(){return this.value};
+mxCell.prototype.setValue=function(a){this.value=a};mxCell.prototype.valueChanged=function(a){var b=this.getValue();this.setValue(a);return b};mxCell.prototype.getGeometry=function(){return this.geometry};mxCell.prototype.setGeometry=function(a){this.geometry=a};mxCell.prototype.getStyle=function(){return this.style};mxCell.prototype.setStyle=function(a){this.style=a};mxCell.prototype.isVertex=function(){return 0!=this.vertex};mxCell.prototype.setVertex=function(a){this.vertex=a};
+mxCell.prototype.isEdge=function(){return 0!=this.edge};mxCell.prototype.setEdge=function(a){this.edge=a};mxCell.prototype.isConnectable=function(){return 0!=this.connectable};mxCell.prototype.setConnectable=function(a){this.connectable=a};mxCell.prototype.isVisible=function(){return 0!=this.visible};mxCell.prototype.setVisible=function(a){this.visible=a};mxCell.prototype.isCollapsed=function(){return 0!=this.collapsed};mxCell.prototype.setCollapsed=function(a){this.collapsed=a};
+mxCell.prototype.getParent=function(){return this.parent};mxCell.prototype.setParent=function(a){this.parent=a};mxCell.prototype.getTerminal=function(a){return a?this.source:this.target};mxCell.prototype.setTerminal=function(a,b){b?this.source=a:this.target=a;return a};mxCell.prototype.getChildCount=function(){return null==this.children?0:this.children.length};mxCell.prototype.getIndex=function(a){return mxUtils.indexOf(this.children,a)};
+mxCell.prototype.getChildAt=function(a){return null==this.children?null:this.children[a]};mxCell.prototype.insert=function(a,b){null!=a&&(null==b&&(b=this.getChildCount(),a.getParent()==this&&b--),a.removeFromParent(),a.setParent(this),null==this.children?(this.children=[],this.children.push(a)):this.children.splice(b,0,a));return a};mxCell.prototype.remove=function(a){var b=null;null!=this.children&&0<=a&&(b=this.getChildAt(a),null!=b&&(this.children.splice(a,1),b.setParent(null)));return b};
+mxCell.prototype.removeFromParent=function(){if(null!=this.parent){var a=this.parent.getIndex(this);this.parent.remove(a)}};mxCell.prototype.getEdgeCount=function(){return null==this.edges?0:this.edges.length};mxCell.prototype.getEdgeIndex=function(a){return mxUtils.indexOf(this.edges,a)};mxCell.prototype.getEdgeAt=function(a){return null==this.edges?null:this.edges[a]};
+mxCell.prototype.insertEdge=function(a,b){null!=a&&(a.removeFromTerminal(b),a.setTerminal(this,b),null==this.edges||a.getTerminal(!b)!=this||0>mxUtils.indexOf(this.edges,a))&&(null==this.edges&&(this.edges=[]),this.edges.push(a));return a};mxCell.prototype.removeEdge=function(a,b){if(null!=a){if(a.getTerminal(!b)!=this&&null!=this.edges){var c=this.getEdgeIndex(a);0<=c&&this.edges.splice(c,1)}a.setTerminal(null,b)}return a};
+mxCell.prototype.removeFromTerminal=function(a){var b=this.getTerminal(a);null!=b&&b.removeEdge(this,a)};mxCell.prototype.hasAttribute=function(a){var b=this.getValue();return null!=b&&b.nodeType==mxConstants.NODETYPE_ELEMENT&&b.hasAttribute?b.hasAttribute(a):null!=b.getAttribute(a)};mxCell.prototype.getAttribute=function(a,b){var c=this.getValue();return(null!=c&&c.nodeType==mxConstants.NODETYPE_ELEMENT?c.getAttribute(a):null)||b};
+mxCell.prototype.setAttribute=function(a,b){var c=this.getValue();null!=c&&c.nodeType==mxConstants.NODETYPE_ELEMENT&&c.setAttribute(a,b)};mxCell.prototype.clone=function(){var a=mxUtils.clone(this,this.mxTransient);a.setValue(this.cloneValue());return a};mxCell.prototype.cloneValue=function(){var a=this.getValue();null!=a&&("function"==typeof a.clone?a=a.clone():isNaN(a.nodeType)||(a=a.cloneNode(!0)));return a};function mxGeometry(a,b,c,d){mxRectangle.call(this,a,b,c,d)}mxGeometry.prototype=new mxRectangle;
+mxGeometry.prototype.constructor=mxGeometry;mxGeometry.prototype.TRANSLATE_CONTROL_POINTS=!0;mxGeometry.prototype.alternateBounds=null;mxGeometry.prototype.sourcePoint=null;mxGeometry.prototype.targetPoint=null;mxGeometry.prototype.points=null;mxGeometry.prototype.offset=null;mxGeometry.prototype.relative=!1;
+mxGeometry.prototype.swap=function(){if(null!=this.alternateBounds){var a=new mxRectangle(this.x,this.y,this.width,this.height);this.x=this.alternateBounds.x;this.y=this.alternateBounds.y;this.width=this.alternateBounds.width;this.height=this.alternateBounds.height;this.alternateBounds=a}};mxGeometry.prototype.getTerminalPoint=function(a){return a?this.sourcePoint:this.targetPoint};mxGeometry.prototype.setTerminalPoint=function(a,b){b?this.sourcePoint=a:this.targetPoint=a;return a};
+mxGeometry.prototype.rotate=function(a,b){var c=mxUtils.toRadians(a),d=Math.cos(c),c=Math.sin(c);if(!this.relative){var e=new mxPoint(this.getCenterX(),this.getCenterY()),e=mxUtils.getRotatedPoint(e,d,c,b);this.x=Math.round(e.x-this.width/2);this.y=Math.round(e.y-this.height/2)}null!=this.sourcePoint&&(e=mxUtils.getRotatedPoint(this.sourcePoint,d,c,b),this.sourcePoint.x=Math.round(e.x),this.sourcePoint.y=Math.round(e.y));null!=this.targetPoint&&(e=mxUtils.getRotatedPoint(this.targetPoint,d,c,b),this.targetPoint.x=
+Math.round(e.x),this.targetPoint.y=Math.round(e.y));if(null!=this.points)for(var f=0;f<this.points.length;f++)null!=this.points[f]&&(e=mxUtils.getRotatedPoint(this.points[f],d,c,b),this.points[f].x=Math.round(e.x),this.points[f].y=Math.round(e.y))};
+mxGeometry.prototype.translate=function(a,b){a=parseFloat(a);b=parseFloat(b);this.relative||(this.x=parseFloat(this.x)+a,this.y=parseFloat(this.y)+b);null!=this.sourcePoint&&(this.sourcePoint.x=parseFloat(this.sourcePoint.x)+a,this.sourcePoint.y=parseFloat(this.sourcePoint.y)+b);null!=this.targetPoint&&(this.targetPoint.x=parseFloat(this.targetPoint.x)+a,this.targetPoint.y=parseFloat(this.targetPoint.y)+b);if(this.TRANSLATE_CONTROL_POINTS&&null!=this.points)for(var c=0;c<this.points.length;c++)null!=
+this.points[c]&&(this.points[c].x=parseFloat(this.points[c].x)+a,this.points[c].y=parseFloat(this.points[c].y)+b)};
+mxGeometry.prototype.scale=function(a,b,c){a=parseFloat(a);b=parseFloat(b);null!=this.sourcePoint&&(this.sourcePoint.x=parseFloat(this.sourcePoint.x)*a,this.sourcePoint.y=parseFloat(this.sourcePoint.y)*b);null!=this.targetPoint&&(this.targetPoint.x=parseFloat(this.targetPoint.x)*a,this.targetPoint.y=parseFloat(this.targetPoint.y)*b);if(null!=this.points)for(var d=0;d<this.points.length;d++)null!=this.points[d]&&(this.points[d].x=parseFloat(this.points[d].x)*a,this.points[d].y=parseFloat(this.points[d].y)*
+b);this.relative||(this.x=parseFloat(this.x)*a,this.y=parseFloat(this.y)*b,c&&(b=a=Math.min(a,b)),this.width=parseFloat(this.width)*a,this.height=parseFloat(this.height)*b)};
+mxGeometry.prototype.equals=function(a){return mxRectangle.prototype.equals.apply(this,arguments)&&this.relative==a.relative&&(null==this.sourcePoint&&null==a.sourcePoint||null!=this.sourcePoint&&this.sourcePoint.equals(a.sourcePoint))&&(null==this.targetPoint&&null==a.targetPoint||null!=this.targetPoint&&this.targetPoint.equals(a.targetPoint))&&(null==this.points&&null==a.points||null!=this.points&&mxUtils.equalPoints(this.points,a.points))&&(null==this.alternateBounds&&null==a.alternateBounds||
+null!=this.alternateBounds&&this.alternateBounds.equals(a.alternateBounds))&&(null==this.offset&&null==a.offset||null!=this.offset&&this.offset.equals(a.offset))};
+var mxCellPath={PATH_SEPARATOR:".",create:function(a){var b="";if(null!=a)for(var c=a.getParent();null!=c;)b=c.getIndex(a)+mxCellPath.PATH_SEPARATOR+b,a=c,c=a.getParent();a=b.length;1<a&&(b=b.substring(0,a-1));return b},getParentPath:function(a){if(null!=a){var b=a.lastIndexOf(mxCellPath.PATH_SEPARATOR);if(0<=b)return a.substring(0,b);if(0<a.length)return""}return null},resolve:function(a,b){var c=a;if(null!=b)for(var d=b.split(mxCellPath.PATH_SEPARATOR),e=0;e<d.length;e++)c=c.getChildAt(parseInt(d[e]));
+return c},compare:function(a,b){for(var c=Math.min(a.length,b.length),d=0,e=0;e<c;e++)if(a[e]!=b[e]){0==a[e].length||0==b[e].length?d=a[e]==b[e]?0:a[e]>b[e]?1:-1:(c=parseInt(a[e]),e=parseInt(b[e]),d=c==e?0:c>e?1:-1);break}0==d&&(c=a.length,e=b.length,c!=e&&(d=c>e?1:-1));return d}},mxPerimeter={RectanglePerimeter:function(a,b,c,d){b=a.getCenterX();var e=a.getCenterY(),f=Math.atan2(c.y-e,c.x-b),g=new mxPoint(0,0),k=Math.PI,l=Math.PI/2-f,m=Math.atan2(a.height,a.width);f<-k+m||f>k-m?(g.x=a.x,g.y=e-a.width*
+Math.tan(f)/2):f<-m?(g.y=a.y,g.x=b-a.height*Math.tan(l)/2):f<m?(g.x=a.x+a.width,g.y=e+a.width*Math.tan(f)/2):(g.y=a.y+a.height,g.x=b+a.height*Math.tan(l)/2);d&&(c.x>=a.x&&c.x<=a.x+a.width?g.x=c.x:c.y>=a.y&&c.y<=a.y+a.height&&(g.y=c.y),c.x<a.x?g.x=a.x:c.x>a.x+a.width&&(g.x=a.x+a.width),c.y<a.y?g.y=a.y:c.y>a.y+a.height&&(g.y=a.y+a.height));return g},EllipsePerimeter:function(a,b,c,d){var e=a.x,f=a.y,g=a.width/2,k=a.height/2,l=e+g,m=f+k;b=c.x;c=c.y;var n=parseInt(b-l),p=parseInt(c-m);if(0==n&&0!=p)return new mxPoint(l,
+m+k*p/Math.abs(p));if(0==n&&0==p)return new mxPoint(b,c);if(d){if(c>=f&&c<=f+a.height)return a=c-m,a=Math.sqrt(g*g*(1-a*a/(k*k)))||0,b<=e&&(a=-a),new mxPoint(l+a,c);if(b>=e&&b<=e+a.width)return a=b-l,a=Math.sqrt(k*k*(1-a*a/(g*g)))||0,c<=f&&(a=-a),new mxPoint(b,m+a)}e=p/n;m-=e*l;f=g*g*e*e+k*k;a=-2*l*f;k=Math.sqrt(a*a-4*f*(g*g*e*e*l*l+k*k*l*l-g*g*k*k));g=(-a+k)/(2*f);l=(-a-k)/(2*f);k=e*g+m;m=e*l+m;Math.sqrt(Math.pow(g-b,2)+Math.pow(k-c,2))<Math.sqrt(Math.pow(l-b,2)+Math.pow(m-c,2))?(b=g,c=k):(b=l,c=
+m);return new mxPoint(b,c)},RhombusPerimeter:function(a,b,c,d){b=a.x;var e=a.y,f=a.width;a=a.height;var g=b+f/2,k=e+a/2,l=c.x;c=c.y;if(g==l)return k>c?new mxPoint(g,e):new mxPoint(g,e+a);if(k==c)return g>l?new mxPoint(b,k):new mxPoint(b+f,k);var m=g,n=k;d&&(l>=b&&l<=b+f?m=l:c>=e&&c<=e+a&&(n=c));return l<g?c<k?mxUtils.intersection(l,c,m,n,g,e,b,k):mxUtils.intersection(l,c,m,n,g,e+a,b,k):c<k?mxUtils.intersection(l,c,m,n,g,e,b+f,k):mxUtils.intersection(l,c,m,n,g,e+a,b+f,k)},TrianglePerimeter:function(a,
+b,c,d){b=null!=b?b.style[mxConstants.STYLE_DIRECTION]:null;var e=b==mxConstants.DIRECTION_NORTH||b==mxConstants.DIRECTION_SOUTH,f=a.x,g=a.y,k=a.width,l=a.height;a=f+k/2;var m=g+l/2,n=new mxPoint(f,g),p=new mxPoint(f+k,m),q=new mxPoint(f,g+l);b==mxConstants.DIRECTION_NORTH?(n=q,p=new mxPoint(a,g),q=new mxPoint(f+k,g+l)):b==mxConstants.DIRECTION_SOUTH?(p=new mxPoint(a,g+l),q=new mxPoint(f+k,g)):b==mxConstants.DIRECTION_WEST&&(n=new mxPoint(f+k,g),p=new mxPoint(f,m),q=new mxPoint(f+k,g+l));var r=c.x-
+a,t=c.y-m,r=e?Math.atan2(r,t):Math.atan2(t,r),t=e?Math.atan2(k,l):Math.atan2(l,k);(b==mxConstants.DIRECTION_NORTH||b==mxConstants.DIRECTION_WEST?r>-t&&r<t:r<-Math.PI+t||r>Math.PI-t)?c=d&&(e&&c.x>=n.x&&c.x<=q.x||!e&&c.y>=n.y&&c.y<=q.y)?e?new mxPoint(c.x,n.y):new mxPoint(n.x,c.y):b==mxConstants.DIRECTION_NORTH?new mxPoint(f+k/2+l*Math.tan(r)/2,g+l):b==mxConstants.DIRECTION_SOUTH?new mxPoint(f+k/2-l*Math.tan(r)/2,g):b==mxConstants.DIRECTION_WEST?new mxPoint(f+k,g+l/2+k*Math.tan(r)/2):new mxPoint(f,g+
+l/2-k*Math.tan(r)/2):(d&&(d=new mxPoint(a,m),c.y>=g&&c.y<=g+l?(d.x=e?a:b==mxConstants.DIRECTION_WEST?f+k:f,d.y=c.y):c.x>=f&&c.x<=f+k&&(d.x=c.x,d.y=e?b==mxConstants.DIRECTION_NORTH?g+l:g:m),a=d.x,m=d.y),c=e&&c.x<=f+k/2||!e&&c.y<=g+l/2?mxUtils.intersection(c.x,c.y,a,m,n.x,n.y,p.x,p.y):mxUtils.intersection(c.x,c.y,a,m,p.x,p.y,q.x,q.y));null==c&&(c=new mxPoint(a,m));return c},HexagonPerimeter:function(a,b,c,d){var e=a.x,f=a.y,g=a.width,k=a.height,l=a.getCenterX();a=a.getCenterY();var m=c.x,n=c.y,p=-Math.atan2(n-
+a,m-l),q=Math.PI,r=Math.PI/2;new mxPoint(l,a);b=null!=b?mxUtils.getValue(b.style,mxConstants.STYLE_DIRECTION,mxConstants.DIRECTION_EAST):mxConstants.DIRECTION_EAST;var t=b==mxConstants.DIRECTION_NORTH||b==mxConstants.DIRECTION_SOUTH;b=new mxPoint;var u=new mxPoint;if(m<e&&n<f||m<e&&n>f+k||m>e+g&&n<f||m>e+g&&n>f+k)d=!1;if(d){if(t){if(m==l){if(n<=f)return new mxPoint(l,f);if(n>=f+k)return new mxPoint(l,f+k)}else if(m<e){if(n==f+k/4)return new mxPoint(e,f+k/4);if(n==f+3*k/4)return new mxPoint(e,f+3*
+k/4)}else if(m>e+g){if(n==f+k/4)return new mxPoint(e+g,f+k/4);if(n==f+3*k/4)return new mxPoint(e+g,f+3*k/4)}else if(m==e){if(n<a)return new mxPoint(e,f+k/4);if(n>a)return new mxPoint(e,f+3*k/4)}else if(m==e+g){if(n<a)return new mxPoint(e+g,f+k/4);if(n>a)return new mxPoint(e+g,f+3*k/4)}if(n==f)return new mxPoint(l,f);if(n==f+k)return new mxPoint(l,f+k);m<l?n>f+k/4&&n<f+3*k/4?(b=new mxPoint(e,f),u=new mxPoint(e,f+k)):n<f+k/4?(b=new mxPoint(e-Math.floor(.5*g),f+Math.floor(.5*k)),u=new mxPoint(e+g,f-
+Math.floor(.25*k))):n>f+3*k/4&&(b=new mxPoint(e-Math.floor(.5*g),f+Math.floor(.5*k)),u=new mxPoint(e+g,f+Math.floor(1.25*k))):m>l&&(n>f+k/4&&n<f+3*k/4?(b=new mxPoint(e+g,f),u=new mxPoint(e+g,f+k)):n<f+k/4?(b=new mxPoint(e,f-Math.floor(.25*k)),u=new mxPoint(e+Math.floor(1.5*g),f+Math.floor(.5*k))):n>f+3*k/4&&(b=new mxPoint(e+Math.floor(1.5*g),f+Math.floor(.5*k)),u=new mxPoint(e,f+Math.floor(1.25*k))))}else{if(n==a){if(m<=e)return new mxPoint(e,f+k/2);if(m>=e+g)return new mxPoint(e+g,f+k/2)}else if(n<
+f){if(m==e+g/4)return new mxPoint(e+g/4,f);if(m==e+3*g/4)return new mxPoint(e+3*g/4,f)}else if(n>f+k){if(m==e+g/4)return new mxPoint(e+g/4,f+k);if(m==e+3*g/4)return new mxPoint(e+3*g/4,f+k)}else if(n==f){if(m<l)return new mxPoint(e+g/4,f);if(m>l)return new mxPoint(e+3*g/4,f)}else if(n==f+k){if(m<l)return new mxPoint(e+g/4,f+k);if(n>a)return new mxPoint(e+3*g/4,f+k)}if(m==e)return new mxPoint(e,a);if(m==e+g)return new mxPoint(e+g,a);n<a?m>e+g/4&&m<e+3*g/4?(b=new mxPoint(e,f),u=new mxPoint(e+g,f)):
+m<e+g/4?(b=new mxPoint(e-Math.floor(.25*g),f+k),u=new mxPoint(e+Math.floor(.5*g),f-Math.floor(.5*k))):m>e+3*g/4&&(b=new mxPoint(e+Math.floor(.5*g),f-Math.floor(.5*k)),u=new mxPoint(e+Math.floor(1.25*g),f+k)):n>a&&(m>e+g/4&&m<e+3*g/4?(b=new mxPoint(e,f+k),u=new mxPoint(e+g,f+k)):m<e+g/4?(b=new mxPoint(e-Math.floor(.25*g),f),u=new mxPoint(e+Math.floor(.5*g),f+Math.floor(1.5*k))):m>e+3*g/4&&(b=new mxPoint(e+Math.floor(.5*g),f+Math.floor(1.5*k)),u=new mxPoint(e+Math.floor(1.25*g),f)))}d=l;p=a;m>=e&&m<=
+e+g?(d=m,p=n<a?f+k:f):n>=f&&n<=f+k&&(p=n,d=m<l?e+g:e);c=mxUtils.intersection(d,p,c.x,c.y,b.x,b.y,u.x,u.y)}else{if(t){m=Math.atan2(k/4,g/2);if(p==m)return new mxPoint(e+g,f+Math.floor(.25*k));if(p==r)return new mxPoint(e+Math.floor(.5*g),f);if(p==q-m)return new mxPoint(e,f+Math.floor(.25*k));if(p==-m)return new mxPoint(e+g,f+Math.floor(.75*k));if(p==-r)return new mxPoint(e+Math.floor(.5*g),f+k);if(p==-q+m)return new mxPoint(e,f+Math.floor(.75*k));p<m&&p>-m?(b=new mxPoint(e+g,f),u=new mxPoint(e+g,f+
+k)):p>m&&p<r?(b=new mxPoint(e,f-Math.floor(.25*k)),u=new mxPoint(e+Math.floor(1.5*g),f+Math.floor(.5*k))):p>r&&p<q-m?(b=new mxPoint(e-Math.floor(.5*g),f+Math.floor(.5*k)),u=new mxPoint(e+g,f-Math.floor(.25*k))):p>q-m&&p<=q||p<-q+m&&p>=-q?(b=new mxPoint(e,f),u=new mxPoint(e,f+k)):p<-m&&p>-r?(b=new mxPoint(e+Math.floor(1.5*g),f+Math.floor(.5*k)),u=new mxPoint(e,f+Math.floor(1.25*k))):p<-r&&p>-q+m&&(b=new mxPoint(e-Math.floor(.5*g),f+Math.floor(.5*k)),u=new mxPoint(e+g,f+Math.floor(1.25*k)))}else{m=
+Math.atan2(k/2,g/4);if(p==m)return new mxPoint(e+Math.floor(.75*g),f);if(p==q-m)return new mxPoint(e+Math.floor(.25*g),f);if(p==q||p==-q)return new mxPoint(e,f+Math.floor(.5*k));if(0==p)return new mxPoint(e+g,f+Math.floor(.5*k));if(p==-m)return new mxPoint(e+Math.floor(.75*g),f+k);if(p==-q+m)return new mxPoint(e+Math.floor(.25*g),f+k);0<p&&p<m?(b=new mxPoint(e+Math.floor(.5*g),f-Math.floor(.5*k)),u=new mxPoint(e+Math.floor(1.25*g),f+k)):p>m&&p<q-m?(b=new mxPoint(e,f),u=new mxPoint(e+g,f)):p>q-m&&
+p<q?(b=new mxPoint(e-Math.floor(.25*g),f+k),u=new mxPoint(e+Math.floor(.5*g),f-Math.floor(.5*k))):0>p&&p>-m?(b=new mxPoint(e+Math.floor(.5*g),f+Math.floor(1.5*k)),u=new mxPoint(e+Math.floor(1.25*g),f)):p<-m&&p>-q+m?(b=new mxPoint(e,f+k),u=new mxPoint(e+g,f+k)):p<-q+m&&p>-q&&(b=new mxPoint(e-Math.floor(.25*g),f),u=new mxPoint(e+Math.floor(.5*g),f+Math.floor(1.5*k)))}c=mxUtils.intersection(l,a,c.x,c.y,b.x,b.y,u.x,u.y)}return null==c?new mxPoint(l,a):c}};
+function mxPrintPreview(a,b,c,d,e,f,g,k,l){this.graph=a;this.scale=null!=b?b:1/a.pageScale;this.border=null!=d?d:0;this.pageFormat=mxRectangle.fromRectangle(null!=c?c:a.pageFormat);this.title=null!=k?k:"Printer-friendly version";this.x0=null!=e?e:0;this.y0=null!=f?f:0;this.borderColor=g;this.pageSelector=null!=l?l:!0}mxPrintPreview.prototype.graph=null;mxPrintPreview.prototype.pageFormat=null;mxPrintPreview.prototype.scale=null;mxPrintPreview.prototype.border=0;
+mxPrintPreview.prototype.marginTop=0;mxPrintPreview.prototype.marginBottom=0;mxPrintPreview.prototype.x0=0;mxPrintPreview.prototype.y0=0;mxPrintPreview.prototype.autoOrigin=!0;mxPrintPreview.prototype.printOverlays=!1;mxPrintPreview.prototype.printControls=!1;mxPrintPreview.prototype.printBackgroundImage=!1;mxPrintPreview.prototype.backgroundColor="#ffffff";mxPrintPreview.prototype.borderColor=null;mxPrintPreview.prototype.title=null;mxPrintPreview.prototype.pageSelector=null;
+mxPrintPreview.prototype.wnd=null;mxPrintPreview.prototype.targetWindow=null;mxPrintPreview.prototype.pageCount=0;mxPrintPreview.prototype.clipping=!0;mxPrintPreview.prototype.getWindow=function(){return this.wnd};
+mxPrintPreview.prototype.getDoctype=function(){var a="";5==document.documentMode?a='<meta http-equiv="X-UA-Compatible" content="IE=5">':8==document.documentMode?a='<meta http-equiv="X-UA-Compatible" content="IE=8">':8<document.documentMode&&(a='\x3c!--[if IE]><meta http-equiv="X-UA-Compatible" content="IE=edge"><![endif]--\x3e');return a};mxPrintPreview.prototype.appendGraph=function(a,b,c,d,e,f){this.graph=a;this.scale=null!=b?b:1/a.pageScale;this.x0=c;this.y0=d;this.open(null,null,e,f)};
+mxPrintPreview.prototype.open=function(a,b,c,d){var e=this.graph.cellRenderer.initializeOverlay,f=null;try{this.printOverlays&&(this.graph.cellRenderer.initializeOverlay=function(a,b){b.init(a.view.getDrawPane())});this.printControls&&(this.graph.cellRenderer.initControl=function(a,b,c,d){b.dialect=a.view.graph.dialect;b.init(a.view.getDrawPane())});this.wnd=null!=b?b:this.wnd;var g=!1;null==this.wnd&&(g=!0,this.wnd=window.open());var k=this.wnd.document;if(g){var l=this.getDoctype();null!=l&&0<l.length&&
+k.writeln(l);mxClient.IS_VML?k.writeln('<html xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office">'):("CSS1Compat"===document.compatMode&&k.writeln("<!DOCTYPE html>"),k.writeln("<html>"));k.writeln("<head>");this.writeHead(k,a);k.writeln("</head>");k.writeln('<body class="mxPage">')}var m=this.graph.getGraphBounds().clone(),n=this.graph.getView().getScale(),p=n/this.scale,q=this.graph.getView().getTranslate();this.autoOrigin||(this.x0-=q.x*this.scale,this.y0-=
+q.y*this.scale,m.width+=m.x,m.height+=m.y,m.x=0,this.border=m.y=0);var r=this.pageFormat.width-2*this.border,t=this.pageFormat.height-2*this.border;this.pageFormat.height+=this.marginTop+this.marginBottom;m.width/=p;m.height/=p;var u=Math.max(1,Math.ceil((m.width+this.x0)/r)),x=Math.max(1,Math.ceil((m.height+this.y0)/t));this.pageCount=u*x;var y=mxUtils.bind(this,function(){if(this.pageSelector&&(1<x||1<u)){var a=this.createPageSelector(x,u);k.body.appendChild(a);if(mxClient.IS_IE&&null==k.documentMode||
+5==k.documentMode||8==k.documentMode||7==k.documentMode){a.style.position="absolute";var b=function(){a.style.top=(k.body.scrollTop||k.documentElement.scrollTop)+10+"px"};mxEvent.addListener(this.wnd,"scroll",function(a){b()});mxEvent.addListener(this.wnd,"resize",function(a){b()})}}}),A=mxUtils.bind(this,function(a,b){null!=this.borderColor&&(a.style.borderColor=this.borderColor,a.style.borderStyle="solid",a.style.borderWidth="1px");a.style.background=this.backgroundColor;if(c||b)a.style.pageBreakAfter=
+"always";g&&(mxClient.IS_IE||11<=document.documentMode||mxClient.IS_EDGE)?(k.writeln(a.outerHTML),a.parentNode.removeChild(a)):(a.parentNode.removeChild(a),k.body.appendChild(a));(c||b)&&this.addPageBreak(k)}),z=this.getCoverPages(this.pageFormat.width,this.pageFormat.height);if(null!=z)for(var v=0;v<z.length;v++)A(z[v],!0);for(var B=this.getAppendices(this.pageFormat.width,this.pageFormat.height),v=0;v<x;v++){var C=v*t/this.scale-this.y0/this.scale+(m.y-q.y*n)/n;for(a=0;a<u;a++){if(null==this.wnd)return null;
+var F=a*r/this.scale-this.x0/this.scale+(m.x-q.x*n)/n,D=v*u+a+1,I=new mxRectangle(F,C,r,t),f=this.renderPage(this.pageFormat.width,this.pageFormat.height,0,0,mxUtils.bind(this,function(a){this.addGraphFragment(-F,-C,this.scale,D,a,I);this.printBackgroundImage&&this.insertBackgroundImage(a,-F,-C)}),D);f.setAttribute("id","mxPage-"+D);A(f,null!=B||v<x-1||a<u-1)}}if(null!=B)for(v=0;v<B.length;v++)A(B[v],v<B.length-1);g&&!d&&(this.closeDocument(),y());this.wnd.focus()}catch(E){null!=f&&null!=f.parentNode&&
+f.parentNode.removeChild(f)}finally{this.graph.cellRenderer.initializeOverlay=e}return this.wnd};mxPrintPreview.prototype.addPageBreak=function(a){var b=a.createElement("hr");b.className="mxPageBreak";a.body.appendChild(b)};mxPrintPreview.prototype.closeDocument=function(){if(null!=this.wnd&&null!=this.wnd.document){var a=this.wnd.document;this.writePostfix(a);a.writeln("</body>");a.writeln("</html>");a.close();mxEvent.release(a.body)}};
+mxPrintPreview.prototype.writeHead=function(a,b){null!=this.title&&a.writeln("<title>"+this.title+"</title>");mxClient.IS_VML&&a.writeln('<style type="text/css">v\\:*{behavior:url(#default#VML)}o\\:*{behavior:url(#default#VML)}</style>');mxClient.link("stylesheet",mxClient.basePath+"/css/common.css",a);a.writeln('<style type="text/css">');a.writeln("@media print {");a.writeln(" table.mxPageSelector { display: none; }");a.writeln(" hr.mxPageBreak { display: none; }");a.writeln("}");a.writeln("@media screen {");
+a.writeln(" table.mxPageSelector { position: fixed; right: 10px; top: 10px;font-family: Arial; font-size:10pt; border: solid 1px darkgray;background: white; border-collapse:collapse; }");a.writeln(" table.mxPageSelector td { border: solid 1px gray; padding:4px; }");a.writeln(" body.mxPage { background: gray; }");a.writeln("}");null!=b&&a.writeln(b);a.writeln("</style>")};mxPrintPreview.prototype.writePostfix=function(a){};
+mxPrintPreview.prototype.createPageSelector=function(a,b){var c=this.wnd.document,d=c.createElement("table");d.className="mxPageSelector";d.setAttribute("border","0");for(var e=c.createElement("tbody"),f=0;f<a;f++){for(var g=c.createElement("tr"),k=0;k<b;k++){var l=f*b+k+1,m=c.createElement("td"),n=c.createElement("a");n.setAttribute("href","#mxPage-"+l);!mxClient.IS_NS||mxClient.IS_SF||mxClient.IS_GC||n.setAttribute("onclick","var page = document.getElementById('mxPage-"+l+"');page.scrollIntoView(true);event.preventDefault();");
+mxUtils.write(n,l,c);m.appendChild(n);g.appendChild(m)}e.appendChild(g)}d.appendChild(e);return d};
+mxPrintPreview.prototype.renderPage=function(a,b,c,d,e,f){f=this.wnd.document;var g=document.createElement("div"),k=null;try{if(0!=c||0!=d){g.style.position="relative";g.style.width=a+"px";g.style.height=b+"px";g.style.pageBreakInside="avoid";var l=document.createElement("div");l.style.position="relative";l.style.top=this.border+"px";l.style.left=this.border+"px";l.style.width=a-2*this.border+"px";l.style.height=b-2*this.border+"px";l.style.overflow="hidden";var m=document.createElement("div");m.style.position=
+"relative";m.style.marginLeft=c+"px";m.style.marginTop=d+"px";8==f.documentMode&&(l.style.position="absolute",m.style.position="absolute");10==f.documentMode&&(m.style.width="100%",m.style.height="100%");l.appendChild(m);g.appendChild(l);document.body.appendChild(g);k=m}else g.style.width=a+"px",g.style.height=b+"px",g.style.overflow="hidden",g.style.pageBreakInside="avoid",8==f.documentMode&&(g.style.position="relative"),l=document.createElement("div"),l.style.width=a-2*this.border+"px",l.style.height=
+b-2*this.border+"px",l.style.overflow="hidden",!mxClient.IS_IE||null!=f.documentMode&&5!=f.documentMode&&8!=f.documentMode&&7!=f.documentMode?(l.style.top=this.border+"px",l.style.left=this.border+"px"):(l.style.marginTop=this.border+"px",l.style.marginLeft=this.border+"px"),this.graph.dialect==mxConstants.DIALECT_VML&&(l.style.position="absolute"),g.appendChild(l),document.body.appendChild(g),k=l}catch(n){throw g.parentNode.removeChild(g),n;}e(k);return g};
+mxPrintPreview.prototype.getRoot=function(){var a=this.graph.view.currentRoot;null==a&&(a=this.graph.getModel().getRoot());return a};
+mxPrintPreview.prototype.addGraphFragment=function(a,b,c,d,e,f){var g=this.graph.getView();d=this.graph.container;this.graph.container=e;var k=g.getCanvas(),l=g.getBackgroundPane(),m=g.getDrawPane(),n=g.getOverlayPane();this.graph.dialect==mxConstants.DIALECT_SVG?g.createSvg():this.graph.dialect==mxConstants.DIALECT_VML?g.createVml():g.createHtml();var p=g.isEventsEnabled();g.setEventsEnabled(!1);var q=this.graph.isEnabled();this.graph.setEnabled(!1);var r=g.getTranslate();g.translate=new mxPoint(a,
+b);var t=this.graph.cellRenderer.redraw,u=g.states;a=g.scale;if(this.clipping){var x=new mxRectangle((f.x+r.x)*a,(f.y+r.y)*a,f.width*a/c,f.height*a/c);this.graph.cellRenderer.redraw=function(a,b,c){if(null!=a){var d=u.get(a.cell);if(null!=d&&(d=g.getBoundingBox(d,!1),null!=d&&!mxUtils.intersects(x,d)))return}t.apply(this,arguments)}}a=null;try{var y=[this.getRoot()];a=new mxTemporaryCellStates(g,c,y,null,mxUtils.bind(this,function(a){return this.getLinkForCellState(a)}))}finally{if(mxClient.IS_IE)g.overlayPane.innerHTML=
+"",g.canvas.style.overflow="hidden",g.canvas.style.position="relative",g.canvas.style.top=this.marginTop+"px",g.canvas.style.width=f.width+"px",g.canvas.style.height=f.height+"px";else for(c=e.firstChild;null!=c;)y=c.nextSibling,b=c.nodeName.toLowerCase(),"svg"==b?(c.style.overflow="hidden",c.style.position="relative",c.style.top=this.marginTop+"px",c.setAttribute("width",f.width),c.setAttribute("height",f.height),c.style.width="",c.style.height=""):"default"!=c.style.cursor&&"div"!=b&&c.parentNode.removeChild(c),
+c=y;this.printBackgroundImage&&(e=e.getElementsByTagName("svg"),0<e.length&&(e[0].style.position="absolute"));g.overlayPane.parentNode.removeChild(g.overlayPane);this.graph.setEnabled(q);this.graph.container=d;this.graph.cellRenderer.redraw=t;g.canvas=k;g.backgroundPane=l;g.drawPane=m;g.overlayPane=n;g.translate=r;a.destroy();g.setEventsEnabled(p)}};mxPrintPreview.prototype.getLinkForCellState=function(a){return this.graph.getLinkForCell(a.cell)};
+mxPrintPreview.prototype.insertBackgroundImage=function(a,b,c){var d=this.graph.backgroundImage;if(null!=d){var e=document.createElement("img");e.style.position="absolute";e.style.marginLeft=Math.round(b*this.scale)+"px";e.style.marginTop=Math.round(c*this.scale)+"px";e.setAttribute("width",Math.round(this.scale*d.width));e.setAttribute("height",Math.round(this.scale*d.height));e.src=d.src;a.insertBefore(e,a.firstChild)}};mxPrintPreview.prototype.getCoverPages=function(){return null};
+mxPrintPreview.prototype.getAppendices=function(){return null};mxPrintPreview.prototype.print=function(a){a=this.open(a);null!=a&&a.print()};mxPrintPreview.prototype.close=function(){null!=this.wnd&&(this.wnd.close(),this.wnd=null)};function mxStylesheet(){this.styles={};this.putDefaultVertexStyle(this.createDefaultVertexStyle());this.putDefaultEdgeStyle(this.createDefaultEdgeStyle())}
+mxStylesheet.prototype.createDefaultVertexStyle=function(){var a={};a[mxConstants.STYLE_SHAPE]=mxConstants.SHAPE_RECTANGLE;a[mxConstants.STYLE_PERIMETER]=mxPerimeter.RectanglePerimeter;a[mxConstants.STYLE_VERTICAL_ALIGN]=mxConstants.ALIGN_MIDDLE;a[mxConstants.STYLE_ALIGN]=mxConstants.ALIGN_CENTER;a[mxConstants.STYLE_FILLCOLOR]="#C3D9FF";a[mxConstants.STYLE_STROKECOLOR]="#6482B9";a[mxConstants.STYLE_FONTCOLOR]="#774400";return a};
+mxStylesheet.prototype.createDefaultEdgeStyle=function(){var a={};a[mxConstants.STYLE_SHAPE]=mxConstants.SHAPE_CONNECTOR;a[mxConstants.STYLE_ENDARROW]=mxConstants.ARROW_CLASSIC;a[mxConstants.STYLE_VERTICAL_ALIGN]=mxConstants.ALIGN_MIDDLE;a[mxConstants.STYLE_ALIGN]=mxConstants.ALIGN_CENTER;a[mxConstants.STYLE_STROKECOLOR]="#6482B9";a[mxConstants.STYLE_FONTCOLOR]="#446299";return a};mxStylesheet.prototype.putDefaultVertexStyle=function(a){this.putCellStyle("defaultVertex",a)};
+mxStylesheet.prototype.putDefaultEdgeStyle=function(a){this.putCellStyle("defaultEdge",a)};mxStylesheet.prototype.getDefaultVertexStyle=function(){return this.styles.defaultVertex};mxStylesheet.prototype.getDefaultEdgeStyle=function(){return this.styles.defaultEdge};mxStylesheet.prototype.putCellStyle=function(a,b){this.styles[a]=b};
+mxStylesheet.prototype.getCellStyle=function(a,b){var c=b;if(null!=a&&0<a.length)for(var d=a.split(";"),c=null!=c&&";"!=a.charAt(0)?mxUtils.clone(c):{},e=0;e<d.length;e++){var f=d[e],g=f.indexOf("=");if(0<=g){var k=f.substring(0,g),f=f.substring(g+1);f==mxConstants.NONE?delete c[k]:mxUtils.isNumeric(f)?c[k]=parseFloat(f):c[k]=f}else if(f=this.styles[f],null!=f)for(k in f)c[k]=f[k]}return c};
+function mxCellState(a,b,c){this.view=a;this.cell=b;this.style=c;this.origin=new mxPoint;this.absoluteOffset=new mxPoint}mxCellState.prototype=new mxRectangle;mxCellState.prototype.constructor=mxCellState;mxCellState.prototype.view=null;mxCellState.prototype.cell=null;mxCellState.prototype.style=null;mxCellState.prototype.invalid=!0;mxCellState.prototype.origin=null;mxCellState.prototype.absolutePoints=null;mxCellState.prototype.absoluteOffset=null;mxCellState.prototype.visibleSourceState=null;
+mxCellState.prototype.visibleTargetState=null;mxCellState.prototype.terminalDistance=0;mxCellState.prototype.length=0;mxCellState.prototype.segments=null;mxCellState.prototype.shape=null;mxCellState.prototype.text=null;mxCellState.prototype.unscaledWidth=null;
+mxCellState.prototype.getPerimeterBounds=function(a,b){a=a||0;b=null!=b?b:new mxRectangle(this.x,this.y,this.width,this.height);if(null!=this.shape&&null!=this.shape.stencil&&"fixed"==this.shape.stencil.aspect){var c=this.shape.stencil.computeAspect(this.style,b.x,b.y,b.width,b.height);b.x=c.x;b.y=c.y;b.width=this.shape.stencil.w0*c.width;b.height=this.shape.stencil.h0*c.height}0!=a&&b.grow(a);return b};
+mxCellState.prototype.setAbsoluteTerminalPoint=function(a,b){b?(null==this.absolutePoints&&(this.absolutePoints=[]),0==this.absolutePoints.length?this.absolutePoints.push(a):this.absolutePoints[0]=a):null==this.absolutePoints?(this.absolutePoints=[],this.absolutePoints.push(null),this.absolutePoints.push(a)):1==this.absolutePoints.length?this.absolutePoints.push(a):this.absolutePoints[this.absolutePoints.length-1]=a};
+mxCellState.prototype.setCursor=function(a){null!=this.shape&&this.shape.setCursor(a);null!=this.text&&this.text.setCursor(a)};mxCellState.prototype.getVisibleTerminal=function(a){a=this.getVisibleTerminalState(a);return null!=a?a.cell:null};mxCellState.prototype.getVisibleTerminalState=function(a){return a?this.visibleSourceState:this.visibleTargetState};mxCellState.prototype.setVisibleTerminalState=function(a,b){b?this.visibleSourceState=a:this.visibleTargetState=a};
+mxCellState.prototype.getCellBounds=function(){return this.cellBounds};mxCellState.prototype.getPaintBounds=function(){return this.paintBounds};mxCellState.prototype.updateCachedBounds=function(){var a=this.view.translate,b=this.view.scale;this.cellBounds=new mxRectangle(this.x/b-a.x,this.y/b-a.y,this.width/b,this.height/b);this.paintBounds=mxRectangle.fromRectangle(this.cellBounds);null!=this.shape&&this.shape.isPaintBoundsInverted()&&this.paintBounds.rotate90()};
+mxCellState.prototype.setState=function(a){this.view=a.view;this.cell=a.cell;this.style=a.style;this.absolutePoints=a.absolutePoints;this.origin=a.origin;this.absoluteOffset=a.absoluteOffset;this.boundingBox=a.boundingBox;this.terminalDistance=a.terminalDistance;this.segments=a.segments;this.length=a.length;this.x=a.x;this.y=a.y;this.width=a.width;this.height=a.height;this.unscaledWidth=a.unscaledWidth};
+mxCellState.prototype.clone=function(){var a=new mxCellState(this.view,this.cell,this.style);if(null!=this.absolutePoints){a.absolutePoints=[];for(var b=0;b<this.absolutePoints.length;b++)a.absolutePoints[b]=this.absolutePoints[b].clone()}null!=this.origin&&(a.origin=this.origin.clone());null!=this.absoluteOffset&&(a.absoluteOffset=this.absoluteOffset.clone());null!=this.boundingBox&&(a.boundingBox=this.boundingBox.clone());a.terminalDistance=this.terminalDistance;a.segments=this.segments;a.length=
+this.length;a.x=this.x;a.y=this.y;a.width=this.width;a.height=this.height;a.unscaledWidth=this.unscaledWidth;return a};mxCellState.prototype.destroy=function(){this.view.graph.cellRenderer.destroy(this)};function mxGraphSelectionModel(a){this.graph=a;this.cells=[]}mxGraphSelectionModel.prototype=new mxEventSource;mxGraphSelectionModel.prototype.constructor=mxGraphSelectionModel;mxGraphSelectionModel.prototype.doneResource="none"!=mxClient.language?"done":"";
+mxGraphSelectionModel.prototype.updatingSelectionResource="none"!=mxClient.language?"updatingSelection":"";mxGraphSelectionModel.prototype.graph=null;mxGraphSelectionModel.prototype.singleSelection=!1;mxGraphSelectionModel.prototype.isSingleSelection=function(){return this.singleSelection};mxGraphSelectionModel.prototype.setSingleSelection=function(a){this.singleSelection=a};mxGraphSelectionModel.prototype.isSelected=function(a){return null!=a?0<=mxUtils.indexOf(this.cells,a):!1};
+mxGraphSelectionModel.prototype.isEmpty=function(){return 0==this.cells.length};mxGraphSelectionModel.prototype.clear=function(){this.changeSelection(null,this.cells)};mxGraphSelectionModel.prototype.setCell=function(a){null!=a&&this.setCells([a])};mxGraphSelectionModel.prototype.setCells=function(a){if(null!=a){this.singleSelection&&(a=[this.getFirstSelectableCell(a)]);for(var b=[],c=0;c<a.length;c++)this.graph.isCellSelectable(a[c])&&b.push(a[c]);this.changeSelection(b,this.cells)}};
+mxGraphSelectionModel.prototype.getFirstSelectableCell=function(a){if(null!=a)for(var b=0;b<a.length;b++)if(this.graph.isCellSelectable(a[b]))return a[b];return null};mxGraphSelectionModel.prototype.addCell=function(a){null!=a&&this.addCells([a])};
+mxGraphSelectionModel.prototype.addCells=function(a){if(null!=a){var b=null;this.singleSelection&&(b=this.cells,a=[this.getFirstSelectableCell(a)]);for(var c=[],d=0;d<a.length;d++)!this.isSelected(a[d])&&this.graph.isCellSelectable(a[d])&&c.push(a[d]);this.changeSelection(c,b)}};mxGraphSelectionModel.prototype.removeCell=function(a){null!=a&&this.removeCells([a])};
+mxGraphSelectionModel.prototype.removeCells=function(a){if(null!=a){for(var b=[],c=0;c<a.length;c++)this.isSelected(a[c])&&b.push(a[c]);this.changeSelection(null,b)}};mxGraphSelectionModel.prototype.changeSelection=function(a,b){if(null!=a&&0<a.length&&null!=a[0]||null!=b&&0<b.length&&null!=b[0]){var c=new mxSelectionChange(this,a,b);c.execute();var d=new mxUndoableEdit(this,!1);d.add(c);this.fireEvent(new mxEventObject(mxEvent.UNDO,"edit",d))}};
+mxGraphSelectionModel.prototype.cellAdded=function(a){null==a||this.isSelected(a)||this.cells.push(a)};mxGraphSelectionModel.prototype.cellRemoved=function(a){null!=a&&(a=mxUtils.indexOf(this.cells,a),0<=a&&this.cells.splice(a,1))};function mxSelectionChange(a,b,c){this.selectionModel=a;this.added=null!=b?b.slice():null;this.removed=null!=c?c.slice():null}
+mxSelectionChange.prototype.execute=function(){var a=mxLog.enter("mxSelectionChange.execute");window.status=mxResources.get(this.selectionModel.updatingSelectionResource)||this.selectionModel.updatingSelectionResource;if(null!=this.removed)for(var b=0;b<this.removed.length;b++)this.selectionModel.cellRemoved(this.removed[b]);if(null!=this.added)for(b=0;b<this.added.length;b++)this.selectionModel.cellAdded(this.added[b]);b=this.added;this.added=this.removed;this.removed=b;window.status=mxResources.get(this.selectionModel.doneResource)||
+this.selectionModel.doneResource;mxLog.leave("mxSelectionChange.execute",a);this.selectionModel.fireEvent(new mxEventObject(mxEvent.CHANGE,"added",this.added,"removed",this.removed))};
+function mxCellEditor(a){this.graph=a;this.zoomHandler=mxUtils.bind(this,function(){this.graph.isEditing()&&this.resize()});this.graph.view.addListener(mxEvent.SCALE,this.zoomHandler);this.graph.view.addListener(mxEvent.SCALE_AND_TRANSLATE,this.zoomHandler);this.changeHandler=mxUtils.bind(this,function(a){null!=this.editingCell&&null==this.graph.getView().getState(this.editingCell)&&this.stopEditing(!0)});this.graph.getModel().addListener(mxEvent.CHANGE,this.changeHandler)}
+mxCellEditor.prototype.graph=null;mxCellEditor.prototype.textarea=null;mxCellEditor.prototype.editingCell=null;mxCellEditor.prototype.trigger=null;mxCellEditor.prototype.modified=!1;mxCellEditor.prototype.autoSize=!0;mxCellEditor.prototype.selectText=!0;mxCellEditor.prototype.emptyLabelText=mxClient.IS_FF?"<br>":"";mxCellEditor.prototype.escapeCancelsEditing=!0;mxCellEditor.prototype.textNode="";mxCellEditor.prototype.zIndex=5;mxCellEditor.prototype.minResize=new mxRectangle(0,20);
+mxCellEditor.prototype.wordWrapPadding=mxClient.IS_QUIRKS?2:mxClient.IS_IE11?0:1;mxCellEditor.prototype.blurEnabled=!1;mxCellEditor.prototype.initialValue=null;mxCellEditor.prototype.init=function(){this.textarea=document.createElement("div");this.textarea.className="mxCellEditor mxPlainTextEditor";this.textarea.contentEditable=!0;mxClient.IS_GC&&(this.textarea.style.minHeight="1em");this.textarea.style.position=this.isLegacyEditor()?"absolute":"relative";this.installListeners(this.textarea)};
+mxCellEditor.prototype.applyValue=function(a,b){this.graph.labelChanged(a.cell,b,this.trigger)};mxCellEditor.prototype.getInitialValue=function(a,b){var c=mxUtils.htmlEntities(this.graph.getEditingValue(a.cell,b),!1);mxClient.IS_QUIRKS||8==document.documentMode||9==document.documentMode||10==document.documentMode||(c=mxUtils.replaceTrailingNewlines(c,"<div><br></div>"));return c.replace(/\n/g,"<br>")};mxCellEditor.prototype.getCurrentValue=function(a){return mxUtils.extractTextWithWhitespace(this.textarea.childNodes)};
+mxCellEditor.prototype.isCancelEditingKeyEvent=function(a){return this.escapeCancelsEditing||mxEvent.isShiftDown(a)||mxEvent.isControlDown(a)||mxEvent.isMetaDown(a)};
+mxCellEditor.prototype.installListeners=function(a){mxEvent.addListener(a,"blur",mxUtils.bind(this,function(a){this.blurEnabled&&this.focusLost(a)}));mxEvent.addListener(a,"keydown",mxUtils.bind(this,function(a){mxEvent.isConsumed(a)||(this.isStopEditingEvent(a)?(this.graph.stopEditing(!1),mxEvent.consume(a)):27==a.keyCode&&(this.graph.stopEditing(this.isCancelEditingKeyEvent(a)),mxEvent.consume(a)))}));var b=mxUtils.bind(this,function(b){null!=this.editingCell&&this.clearOnChange&&a.innerHTML==this.getEmptyLabelText()&&
+(!mxClient.IS_FF||8!=b.keyCode&&46!=b.keyCode)&&(this.clearOnChange=!1,a.innerHTML="")});mxEvent.addListener(a,"keypress",b);mxEvent.addListener(a,"paste",b);b=mxUtils.bind(this,function(a){null!=this.editingCell&&(0==this.textarea.innerHTML.length||"<br>"==this.textarea.innerHTML?(this.textarea.innerHTML=this.getEmptyLabelText(),this.clearOnChange=0<this.textarea.innerHTML.length):this.clearOnChange=!1)});mxEvent.addListener(a,mxClient.IS_IE11||mxClient.IS_IE?"keyup":"input",b);mxEvent.addListener(a,
+"cut",b);mxEvent.addListener(a,"paste",b);var b=mxClient.IS_IE11||mxClient.IS_IE?"keydown":"input",c=mxUtils.bind(this,function(a){null!=this.editingCell&&this.autoSize&&!mxEvent.isConsumed(a)&&(null!=this.resizeThread&&window.clearTimeout(this.resizeThread),this.resizeThread=window.setTimeout(mxUtils.bind(this,function(){this.resizeThread=null;this.resize()}),0))});mxEvent.addListener(a,b,c);9<=document.documentMode?(mxEvent.addListener(a,"DOMNodeRemoved",c),mxEvent.addListener(a,"DOMNodeInserted",
+c)):(mxEvent.addListener(a,"cut",c),mxEvent.addListener(a,"paste",c))};mxCellEditor.prototype.isStopEditingEvent=function(a){return 113==a.keyCode||this.graph.isEnterStopsCellEditing()&&13==a.keyCode&&!mxEvent.isControlDown(a)&&!mxEvent.isShiftDown(a)};mxCellEditor.prototype.isEventSource=function(a){return mxEvent.getSource(a)==this.textarea};
+mxCellEditor.prototype.resize=function(){var a=this.graph.getView().getState(this.editingCell);if(null==a)this.stopEditing(!0);else if(null!=this.textarea){var b=this.graph.getModel().isEdge(a.cell),c=this.graph.getView().scale,d=null;if(this.autoSize&&"fill"!=a.style[mxConstants.STYLE_OVERFLOW]){var e=mxUtils.getValue(a.style,mxConstants.STYLE_LABEL_WIDTH,null),d=null!=a.text?a.text.margin:null;null==d&&(d=mxUtils.getAlignmentAsPoint(mxUtils.getValue(a.style,mxConstants.STYLE_ALIGN,mxConstants.ALIGN_CENTER),
+mxUtils.getValue(a.style,mxConstants.STYLE_VERTICAL_ALIGN,mxConstants.ALIGN_MIDDLE)));if(b)this.bounds=new mxRectangle(a.absoluteOffset.x,a.absoluteOffset.y,0,0),null!=e&&(e=(parseFloat(e)+2)*c,this.bounds.width=e,this.bounds.x+=d.x*e);else{var b=mxRectangle.fromRectangle(a),f=mxUtils.getValue(a.style,mxConstants.STYLE_LABEL_POSITION,mxConstants.ALIGN_CENTER),g=mxUtils.getValue(a.style,mxConstants.STYLE_VERTICAL_LABEL_POSITION,mxConstants.ALIGN_MIDDLE),b=null!=a.shape&&f==mxConstants.ALIGN_CENTER&&
+g==mxConstants.ALIGN_MIDDLE?a.shape.getLabelBounds(b):b;null!=e&&(b.width=parseFloat(e)*c);if(!a.view.graph.cellRenderer.legacySpacing||"width"!=a.style[mxConstants.STYLE_OVERFLOW])var f=parseInt(a.style[mxConstants.STYLE_SPACING]||2)*c,k=(parseInt(a.style[mxConstants.STYLE_SPACING_TOP]||0)+mxText.prototype.baseSpacingTop)*c+f,l=(parseInt(a.style[mxConstants.STYLE_SPACING_RIGHT]||0)+mxText.prototype.baseSpacingRight)*c+f,m=(parseInt(a.style[mxConstants.STYLE_SPACING_BOTTOM]||0)+mxText.prototype.baseSpacingBottom)*
+c+f,n=(parseInt(a.style[mxConstants.STYLE_SPACING_LEFT]||0)+mxText.prototype.baseSpacingLeft)*c+f,f=mxUtils.getValue(a.style,mxConstants.STYLE_LABEL_POSITION,mxConstants.ALIGN_CENTER),g=mxUtils.getValue(a.style,mxConstants.STYLE_VERTICAL_LABEL_POSITION,mxConstants.ALIGN_MIDDLE),b=new mxRectangle(b.x+n,b.y+k,b.width-(f==mxConstants.ALIGN_CENTER&&null==e?n+l:0),b.height-(g==mxConstants.ALIGN_MIDDLE?k+m:0));this.bounds=new mxRectangle(b.x+a.absoluteOffset.x,b.y+a.absoluteOffset.y,b.width,b.height)}this.graph.isWrapping(a.cell)&&
+(2<=this.bounds.width||2<=this.bounds.height)&&this.textarea.innerHTML!=this.getEmptyLabelText()?(this.textarea.style.wordWrap=mxConstants.WORD_WRAP,this.textarea.style.whiteSpace="normal",e=Math.round(this.bounds.width/c)+this.wordWrapPadding,"relative"!=this.textarea.style.position?(this.textarea.style.width=e+"px",this.textarea.scrollWidth>e&&(this.textarea.style.width=this.textarea.scrollWidth+"px")):this.textarea.style.maxWidth=e+"px"):(this.textarea.style.whiteSpace="nowrap",this.textarea.style.width=
+"");8==document.documentMode&&(this.textarea.style.zoom="1",this.textarea.style.height="auto");a=this.textarea.scrollWidth;e=this.textarea.scrollHeight;8==document.documentMode?(this.textarea.style.left=Math.max(0,Math.ceil((this.bounds.x-d.x*(this.bounds.width-(a+1)*c)+a*(c-1)*0+2*(d.x+.5))/c))+"px",this.textarea.style.top=Math.max(0,Math.ceil((this.bounds.y-d.y*(this.bounds.height-(e+.5)*c)+e*(c-1)*0+1*Math.abs(d.y+.5))/c))+"px",this.textarea.style.width=Math.round(a*c)+"px",this.textarea.style.height=
+Math.round(e*c)+"px"):mxClient.IS_QUIRKS?(this.textarea.style.left=Math.max(0,Math.ceil(this.bounds.x-d.x*(this.bounds.width-(a+1)*c)+a*(c-1)*0+2*(d.x+.5)))+"px",this.textarea.style.top=Math.max(0,Math.ceil(this.bounds.y-d.y*(this.bounds.height-(e+.5)*c)+e*(c-1)*0+1*Math.abs(d.y+.5)))+"px"):(this.textarea.style.left=Math.max(0,Math.round(this.bounds.x-d.x*(this.bounds.width-2))+1)+"px",this.textarea.style.top=Math.max(0,Math.round(this.bounds.y-d.y*(this.bounds.height-4)+(-1==d.y?3:0))+1)+"px")}else this.bounds=
+this.getEditorBounds(a),this.textarea.style.width=Math.round(this.bounds.width/c)+"px",this.textarea.style.height=Math.round(this.bounds.height/c)+"px",8==document.documentMode||mxClient.IS_QUIRKS?(this.textarea.style.left=Math.round(this.bounds.x)+"px",this.textarea.style.top=Math.round(this.bounds.y)+"px"):(this.textarea.style.left=Math.max(0,Math.round(this.bounds.x+1))+"px",this.textarea.style.top=Math.max(0,Math.round(this.bounds.y+1))+"px"),this.graph.isWrapping(a.cell)&&(2<=this.bounds.width||
+2<=this.bounds.height)&&this.textarea.innerHTML!=this.getEmptyLabelText()?(this.textarea.style.wordWrap=mxConstants.WORD_WRAP,this.textarea.style.whiteSpace="normal","fill"!=a.style[mxConstants.STYLE_OVERFLOW]&&(this.textarea.style.width=Math.round(this.bounds.width/c)+this.wordWrapPadding+"px")):(this.textarea.style.whiteSpace="nowrap","fill"!=a.style[mxConstants.STYLE_OVERFLOW]&&(this.textarea.style.width=""));mxClient.IS_VML?this.textarea.style.zoom=c:(mxUtils.setPrefixedStyle(this.textarea.style,
+"transformOrigin","0px 0px"),mxUtils.setPrefixedStyle(this.textarea.style,"transform","scale("+c+","+c+")"+(null==d?"":" translate("+100*d.x+"%,"+100*d.y+"%)")))}};mxCellEditor.prototype.focusLost=function(){this.stopEditing(!this.graph.isInvokesStopCellEditing())};mxCellEditor.prototype.getBackgroundColor=function(a){return null};
+mxCellEditor.prototype.isLegacyEditor=function(){if(mxClient.IS_VML)return!0;var a=!1;if(mxClient.IS_SVG){var b=this.graph.view.getDrawPane().ownerSVGElement;null!=b&&(a="absolute"==mxUtils.getCurrentStyle(b).position)}return!a};
+mxCellEditor.prototype.startEditing=function(a,b){this.stopEditing(!0);null==this.textarea&&this.init();null!=this.graph.tooltipHandler&&this.graph.tooltipHandler.hideTooltip();var c=this.graph.getView().getState(a);if(null!=c){this.graph.getView();var d=mxUtils.getValue(c.style,mxConstants.STYLE_FONTSIZE,mxConstants.DEFAULT_FONTSIZE),e=mxUtils.getValue(c.style,mxConstants.STYLE_FONTFAMILY,mxConstants.DEFAULT_FONTFAMILY),f=mxUtils.getValue(c.style,mxConstants.STYLE_FONTCOLOR,"black"),g=mxUtils.getValue(c.style,
+mxConstants.STYLE_ALIGN,mxConstants.ALIGN_LEFT),k=(mxUtils.getValue(c.style,mxConstants.STYLE_FONTSTYLE,0)&mxConstants.FONT_BOLD)==mxConstants.FONT_BOLD,l=(mxUtils.getValue(c.style,mxConstants.STYLE_FONTSTYLE,0)&mxConstants.FONT_ITALIC)==mxConstants.FONT_ITALIC,m=(mxUtils.getValue(c.style,mxConstants.STYLE_FONTSTYLE,0)&mxConstants.FONT_UNDERLINE)==mxConstants.FONT_UNDERLINE;this.textarea.style.lineHeight=mxConstants.ABSOLUTE_LINE_HEIGHT?Math.round(d*mxConstants.LINE_HEIGHT)+"px":mxConstants.LINE_HEIGHT;
+this.textarea.style.backgroundColor=this.getBackgroundColor(c);this.textarea.style.textDecoration=m?"underline":"";this.textarea.style.fontWeight=k?"bold":"normal";this.textarea.style.fontStyle=l?"italic":"";this.textarea.style.fontSize=Math.round(d)+"px";this.textarea.style.zIndex=this.zIndex;this.textarea.style.fontFamily=e;this.textarea.style.textAlign=g;this.textarea.style.outline="none";this.textarea.style.color=f;d=this.textDirection=mxUtils.getValue(c.style,mxConstants.STYLE_TEXT_DIRECTION,
+mxConstants.DEFAULT_TEXT_DIRECTION);d==mxConstants.TEXT_DIRECTION_AUTO&&(null==c||null==c.text||c.text.dialect==mxConstants.DIALECT_STRICTHTML||mxUtils.isNode(c.text.value)||(d=c.text.getAutoDirection()));d==mxConstants.TEXT_DIRECTION_LTR||d==mxConstants.TEXT_DIRECTION_RTL?this.textarea.setAttribute("dir",d):this.textarea.removeAttribute("dir");this.textarea.innerHTML=this.getInitialValue(c,b)||"";this.initialValue=this.textarea.innerHTML;0==this.textarea.innerHTML.length||"<br>"==this.textarea.innerHTML?
+(this.textarea.innerHTML=this.getEmptyLabelText(),this.clearOnChange=!0):this.clearOnChange=this.textarea.innerHTML==this.getEmptyLabelText();this.graph.container.appendChild(this.textarea);this.editingCell=a;this.trigger=b;this.textNode=null;null!=c.text&&this.isHideLabel(c)&&(this.textNode=c.text.node,this.textNode.style.visibility="hidden");this.autoSize&&(this.graph.model.isEdge(c.cell)||"fill"!=c.style[mxConstants.STYLE_OVERFLOW])&&window.setTimeout(mxUtils.bind(this,function(){this.resize()}),
+0);this.resize();try{this.textarea.focus(),this.isSelectText()&&0<this.textarea.innerHTML.length&&(this.textarea.innerHTML!=this.getEmptyLabelText()||!this.clearOnChange)&&document.execCommand("selectAll",!1,null)}catch(n){}}};mxCellEditor.prototype.isSelectText=function(){return this.selectText};mxCellEditor.prototype.clearSelection=function(){var a=null;window.getSelection?a=window.getSelection():document.selection&&(a=document.selection);null!=a&&(a.empty?a.empty():a.removeAllRanges&&a.removeAllRanges())};
+mxCellEditor.prototype.stopEditing=function(a){if(null!=this.editingCell){null!=this.textNode&&(this.textNode.style.visibility="visible",this.textNode=null);a=a?null:this.graph.view.getState(this.editingCell);var b=this.initialValue;this.bounds=this.trigger=this.editingCell=this.initialValue=null;this.textarea.blur();this.clearSelection();null!=this.textarea.parentNode&&this.textarea.parentNode.removeChild(this.textarea);this.clearOnChange&&this.textarea.innerHTML==this.getEmptyLabelText()&&(this.textarea.innerHTML=
+"",this.clearOnChange=!1);null!=a&&this.textarea.innerHTML!=b&&(this.prepareTextarea(),b=this.getCurrentValue(a),null!=b&&this.applyValue(a,b));mxEvent.release(this.textarea);this.textarea=null}};mxCellEditor.prototype.prepareTextarea=function(){mxClient.IS_FF&&null!=this.textarea.lastChild&&"BR"==this.textarea.lastChild.nodeName&&this.textarea.removeChild(this.textarea.lastChild)};mxCellEditor.prototype.isHideLabel=function(a){return!0};
+mxCellEditor.prototype.getMinimumSize=function(a){var b=this.graph.getView().scale;return new mxRectangle(0,0,null==a.text?30:a.text.size*b+20,"left"==this.textarea.style.textAlign?120:40)};
+mxCellEditor.prototype.getEditorBounds=function(a){var b=this.graph.getModel().isEdge(a.cell),c=this.graph.getView().scale,d=this.getMinimumSize(a),e=d.width,d=d.height;if(!b&&a.view.graph.cellRenderer.legacySpacing&&"fill"==a.style[mxConstants.STYLE_OVERFLOW])c=a.shape.getLabelBounds(mxRectangle.fromRectangle(a));else{var f=parseInt(a.style[mxConstants.STYLE_SPACING]||0)*c,g=(parseInt(a.style[mxConstants.STYLE_SPACING_TOP]||0)+mxText.prototype.baseSpacingTop)*c+f,k=(parseInt(a.style[mxConstants.STYLE_SPACING_RIGHT]||
+0)+mxText.prototype.baseSpacingRight)*c+f,l=(parseInt(a.style[mxConstants.STYLE_SPACING_BOTTOM]||0)+mxText.prototype.baseSpacingBottom)*c+f,f=(parseInt(a.style[mxConstants.STYLE_SPACING_LEFT]||0)+mxText.prototype.baseSpacingLeft)*c+f,c=new mxRectangle(a.x,a.y,Math.max(e,a.width-f-k),Math.max(d,a.height-g-l)),k=mxUtils.getValue(a.style,mxConstants.STYLE_LABEL_POSITION,mxConstants.ALIGN_CENTER),l=mxUtils.getValue(a.style,mxConstants.STYLE_VERTICAL_LABEL_POSITION,mxConstants.ALIGN_MIDDLE),c=null!=a.shape&&
+k==mxConstants.ALIGN_CENTER&&l==mxConstants.ALIGN_MIDDLE?a.shape.getLabelBounds(c):c;b?(c.x=a.absoluteOffset.x,c.y=a.absoluteOffset.y,null!=a.text&&null!=a.text.boundingBox&&(0<a.text.boundingBox.x&&(c.x=a.text.boundingBox.x),0<a.text.boundingBox.y&&(c.y=a.text.boundingBox.y))):null!=a.text&&null!=a.text.boundingBox&&(c.x=Math.min(c.x,a.text.boundingBox.x),c.y=Math.min(c.y,a.text.boundingBox.y));c.x+=f;c.y+=g;null!=a.text&&null!=a.text.boundingBox&&(b?(c.width=Math.max(e,a.text.boundingBox.width),
+c.height=Math.max(d,a.text.boundingBox.height)):(c.width=Math.max(c.width,a.text.boundingBox.width),c.height=Math.max(c.height,a.text.boundingBox.height)));this.graph.getModel().isVertex(a.cell)&&(b=mxUtils.getValue(a.style,mxConstants.STYLE_LABEL_POSITION,mxConstants.ALIGN_CENTER),b==mxConstants.ALIGN_LEFT?c.x-=a.width:b==mxConstants.ALIGN_RIGHT&&(c.x+=a.width),b=mxUtils.getValue(a.style,mxConstants.STYLE_VERTICAL_LABEL_POSITION,mxConstants.ALIGN_MIDDLE),b==mxConstants.ALIGN_TOP?c.y-=a.height:b==
+mxConstants.ALIGN_BOTTOM&&(c.y+=a.height))}return new mxRectangle(Math.round(c.x),Math.round(c.y),Math.round(c.width),Math.round(c.height))};mxCellEditor.prototype.getEmptyLabelText=function(a){return this.emptyLabelText};mxCellEditor.prototype.getEditingCell=function(){return this.editingCell};
+mxCellEditor.prototype.destroy=function(){null!=this.textarea&&(mxEvent.release(this.textarea),null!=this.textarea.parentNode&&this.textarea.parentNode.removeChild(this.textarea),this.textarea=null);null!=this.changeHandler&&(this.graph.getModel().removeListener(this.changeHandler),this.changeHandler=null);this.zoomHandler&&(this.graph.view.removeListener(this.zoomHandler),this.zoomHandler=null)};function mxCellRenderer(){}mxCellRenderer.defaultShapes={};
+mxCellRenderer.prototype.defaultEdgeShape=mxConnector;mxCellRenderer.prototype.defaultVertexShape=mxRectangleShape;mxCellRenderer.prototype.defaultTextShape=mxText;mxCellRenderer.prototype.legacyControlPosition=!0;mxCellRenderer.prototype.legacySpacing=!0;mxCellRenderer.prototype.antiAlias=!0;mxCellRenderer.prototype.minSvgStrokeWidth=1;mxCellRenderer.prototype.forceControlClickHandler=!1;mxCellRenderer.registerShape=function(a,b){mxCellRenderer.defaultShapes[a]=b};
+mxCellRenderer.registerShape(mxConstants.SHAPE_RECTANGLE,mxRectangleShape);mxCellRenderer.registerShape(mxConstants.SHAPE_ELLIPSE,mxEllipse);mxCellRenderer.registerShape(mxConstants.SHAPE_RHOMBUS,mxRhombus);mxCellRenderer.registerShape(mxConstants.SHAPE_CYLINDER,mxCylinder);mxCellRenderer.registerShape(mxConstants.SHAPE_CONNECTOR,mxConnector);mxCellRenderer.registerShape(mxConstants.SHAPE_ACTOR,mxActor);mxCellRenderer.registerShape(mxConstants.SHAPE_TRIANGLE,mxTriangle);
+mxCellRenderer.registerShape(mxConstants.SHAPE_HEXAGON,mxHexagon);mxCellRenderer.registerShape(mxConstants.SHAPE_CLOUD,mxCloud);mxCellRenderer.registerShape(mxConstants.SHAPE_LINE,mxLine);mxCellRenderer.registerShape(mxConstants.SHAPE_ARROW,mxArrow);mxCellRenderer.registerShape(mxConstants.SHAPE_ARROW_CONNECTOR,mxArrowConnector);mxCellRenderer.registerShape(mxConstants.SHAPE_DOUBLE_ELLIPSE,mxDoubleEllipse);mxCellRenderer.registerShape(mxConstants.SHAPE_SWIMLANE,mxSwimlane);
+mxCellRenderer.registerShape(mxConstants.SHAPE_IMAGE,mxImageShape);mxCellRenderer.registerShape(mxConstants.SHAPE_LABEL,mxLabel);mxCellRenderer.prototype.initializeShape=function(a){a.shape.dialect=a.view.graph.dialect;this.configureShape(a);a.shape.init(a.view.getDrawPane())};mxCellRenderer.prototype.createShape=function(a){var b=null;null!=a.style&&(b=mxStencilRegistry.getStencil(a.style[mxConstants.STYLE_SHAPE]),b=null!=b?new mxShape(b):new (this.getShapeConstructor(a)));return b};
+mxCellRenderer.prototype.createIndicatorShape=function(a){a.shape.indicatorShape=this.getShape(a.view.graph.getIndicatorShape(a))};mxCellRenderer.prototype.getShape=function(a){return null!=a?mxCellRenderer.defaultShapes[a]:null};mxCellRenderer.prototype.getShapeConstructor=function(a){var b=this.getShape(a.style[mxConstants.STYLE_SHAPE]);null==b&&(b=a.view.graph.getModel().isEdge(a.cell)?this.defaultEdgeShape:this.defaultVertexShape);return b};
+mxCellRenderer.prototype.configureShape=function(a){a.shape.apply(a);a.shape.image=a.view.graph.getImage(a);a.shape.indicatorColor=a.view.graph.getIndicatorColor(a);a.shape.indicatorStrokeColor=a.style[mxConstants.STYLE_INDICATOR_STROKECOLOR];a.shape.indicatorGradientColor=a.view.graph.getIndicatorGradientColor(a);a.shape.indicatorDirection=a.style[mxConstants.STYLE_INDICATOR_DIRECTION];a.shape.indicatorImage=a.view.graph.getIndicatorImage(a);this.postConfigureShape(a)};
+mxCellRenderer.prototype.postConfigureShape=function(a){null!=a.shape&&(this.resolveColor(a,"indicatorColor",mxConstants.STYLE_FILLCOLOR),this.resolveColor(a,"indicatorGradientColor",mxConstants.STYLE_GRADIENTCOLOR),this.resolveColor(a,"fill",mxConstants.STYLE_FILLCOLOR),this.resolveColor(a,"stroke",mxConstants.STYLE_STROKECOLOR),this.resolveColor(a,"gradient",mxConstants.STYLE_GRADIENTCOLOR))};
+mxCellRenderer.prototype.checkPlaceholderStyles=function(a){if(null!=a.style)for(var b=["inherit","swimlane","indicated"],c=[mxConstants.STYLE_FILLCOLOR,mxConstants.STYLE_STROKECOLOR,mxConstants.STYLE_GRADIENTCOLOR],d=0;d<c.length;d++)if(0<=mxUtils.indexOf(b,a.style[c[d]]))return!0;return!1};
+mxCellRenderer.prototype.resolveColor=function(a,b,c){var d=a.shape[b],e=a.view.graph,f=null;"inherit"==d?f=e.model.getParent(a.cell):"swimlane"==d?(a.shape[b]=c==mxConstants.STYLE_STROKECOLOR?"#000000":"#ffffff",f=null!=e.model.getTerminal(a.cell,!1)?e.model.getTerminal(a.cell,!1):a.cell,f=e.getSwimlane(f),c=e.swimlaneIndicatorColorAttribute):"indicated"==d&&(a.shape[b]=a.shape.indicatorColor);null!=f&&(d=e.getView().getState(f),a.shape[b]=null,null!=d&&(a.shape[b]=null!=d.shape&&"indicatorColor"!=
+b?d.shape[b]:d.style[c]))};mxCellRenderer.prototype.getLabelValue=function(a){return a.view.graph.getLabel(a.cell)};
+mxCellRenderer.prototype.createLabel=function(a,b){var c=a.view.graph;c.getModel().isEdge(a.cell);if(0<a.style[mxConstants.STYLE_FONTSIZE]||null==a.style[mxConstants.STYLE_FONTSIZE]){var d=c.isHtmlLabel(a.cell)||null!=b&&mxUtils.isNode(b);a.text=new this.defaultTextShape(b,new mxRectangle,a.style[mxConstants.STYLE_ALIGN]||mxConstants.ALIGN_CENTER,c.getVerticalAlign(a),a.style[mxConstants.STYLE_FONTCOLOR],a.style[mxConstants.STYLE_FONTFAMILY],a.style[mxConstants.STYLE_FONTSIZE],a.style[mxConstants.STYLE_FONTSTYLE],
+a.style[mxConstants.STYLE_SPACING],a.style[mxConstants.STYLE_SPACING_TOP],a.style[mxConstants.STYLE_SPACING_RIGHT],a.style[mxConstants.STYLE_SPACING_BOTTOM],a.style[mxConstants.STYLE_SPACING_LEFT],a.style[mxConstants.STYLE_HORIZONTAL],a.style[mxConstants.STYLE_LABEL_BACKGROUNDCOLOR],a.style[mxConstants.STYLE_LABEL_BORDERCOLOR],c.isWrapping(a.cell)&&c.isHtmlLabel(a.cell),c.isLabelClipped(a.cell),a.style[mxConstants.STYLE_OVERFLOW],a.style[mxConstants.STYLE_LABEL_PADDING],mxUtils.getValue(a.style,mxConstants.STYLE_TEXT_DIRECTION,
+mxConstants.DEFAULT_TEXT_DIRECTION));a.text.opacity=mxUtils.getValue(a.style,mxConstants.STYLE_TEXT_OPACITY,100);a.text.dialect=d?mxConstants.DIALECT_STRICTHTML:a.view.graph.dialect;a.text.style=a.style;a.text.state=a;this.initializeLabel(a,a.text);var e=!1,f=function(b){var d=a;if(mxClient.IS_TOUCH||e)d=mxEvent.getClientX(b),b=mxEvent.getClientY(b),b=mxUtils.convertPoint(c.container,d,b),d=c.view.getState(c.getCellAt(b.x,b.y));return d};mxEvent.addGestureListeners(a.text.node,mxUtils.bind(this,function(b){this.isLabelEvent(a,
+b)&&(c.fireMouseEvent(mxEvent.MOUSE_DOWN,new mxMouseEvent(b,a)),e=c.dialect!=mxConstants.DIALECT_SVG&&"IMG"==mxEvent.getSource(b).nodeName)}),mxUtils.bind(this,function(b){this.isLabelEvent(a,b)&&c.fireMouseEvent(mxEvent.MOUSE_MOVE,new mxMouseEvent(b,f(b)))}),mxUtils.bind(this,function(b){this.isLabelEvent(a,b)&&(c.fireMouseEvent(mxEvent.MOUSE_UP,new mxMouseEvent(b,f(b))),e=!1)}));c.nativeDblClickEnabled&&mxEvent.addListener(a.text.node,"dblclick",mxUtils.bind(this,function(b){this.isLabelEvent(a,
+b)&&(c.dblClick(b,a.cell),mxEvent.consume(b))}))}};mxCellRenderer.prototype.initializeLabel=function(a,b){mxClient.IS_SVG&&mxClient.NO_FO&&b.dialect!=mxConstants.DIALECT_SVG?b.init(a.view.graph.container):b.init(a.view.getDrawPane())};
+mxCellRenderer.prototype.createCellOverlays=function(a){var b=a.view.graph.getCellOverlays(a.cell),c=null;if(null!=b)for(var c=new mxDictionary,d=0;d<b.length;d++){var e=null!=a.overlays?a.overlays.remove(b[d]):null;null==e&&(e=new mxImageShape(new mxRectangle,b[d].image.src),e.dialect=a.view.graph.dialect,e.preserveImageAspect=!1,e.overlay=b[d],this.initializeOverlay(a,e),this.installCellOverlayListeners(a,b[d],e),null!=b[d].cursor&&(e.node.style.cursor=b[d].cursor));c.put(b[d],e)}null!=a.overlays&&
+a.overlays.visit(function(a,b){b.destroy()});a.overlays=c};mxCellRenderer.prototype.initializeOverlay=function(a,b){b.init(a.view.getOverlayPane())};
+mxCellRenderer.prototype.installCellOverlayListeners=function(a,b,c){var d=a.view.graph;mxEvent.addListener(c.node,"click",function(c){d.isEditing()&&d.stopEditing(!d.isInvokesStopCellEditing());b.fireEvent(new mxEventObject(mxEvent.CLICK,"event",c,"cell",a.cell))});mxEvent.addGestureListeners(c.node,function(a){mxEvent.consume(a)},function(b){d.fireMouseEvent(mxEvent.MOUSE_MOVE,new mxMouseEvent(b,a))});mxClient.IS_TOUCH&&mxEvent.addListener(c.node,"touchend",function(c){b.fireEvent(new mxEventObject(mxEvent.CLICK,
+"event",c,"cell",a.cell))})};mxCellRenderer.prototype.createControl=function(a){var b=a.view.graph,c=b.getFoldingImage(a);if(b.foldingEnabled&&null!=c){if(null==a.control){var d=new mxRectangle(0,0,c.width,c.height);a.control=new mxImageShape(d,c.src);a.control.preserveImageAspect=!1;a.control.dialect=b.dialect;this.initControl(a,a.control,!0,this.createControlClickHandler(a))}}else null!=a.control&&(a.control.destroy(),a.control=null)};
+mxCellRenderer.prototype.createControlClickHandler=function(a){var b=a.view.graph;return mxUtils.bind(this,function(c){if(this.forceControlClickHandler||b.isEnabled()){var d=!b.isCellCollapsed(a.cell);b.foldCells(d,!1,[a.cell],null,c);mxEvent.consume(c)}})};
+mxCellRenderer.prototype.initControl=function(a,b,c,d){var e=a.view.graph;e.isHtmlLabel(a.cell)&&mxClient.NO_FO&&e.dialect==mxConstants.DIALECT_SVG?(b.dialect=mxConstants.DIALECT_PREFERHTML,b.init(e.container),b.node.style.zIndex=1):b.init(a.view.getOverlayPane());b=b.innerNode||b.node;null==d||mxClient.IS_IOS||(e.isEnabled()&&(b.style.cursor="pointer"),mxEvent.addListener(b,"click",d));if(c){var f=null;mxEvent.addGestureListeners(b,function(b){f=new mxPoint(mxEvent.getClientX(b),mxEvent.getClientY(b));
+e.fireMouseEvent(mxEvent.MOUSE_DOWN,new mxMouseEvent(b,a));mxEvent.consume(b)},function(b){e.fireMouseEvent(mxEvent.MOUSE_MOVE,new mxMouseEvent(b,a))},function(b){e.fireMouseEvent(mxEvent.MOUSE_UP,new mxMouseEvent(b,a));mxEvent.consume(b)});null!=d&&mxClient.IS_IOS&&b.addEventListener("touchend",function(a){if(null!=f){var b=e.tolerance;Math.abs(f.x-mxEvent.getClientX(a))<b&&Math.abs(f.y-mxEvent.getClientY(a))<b&&(d.call(d,a),mxEvent.consume(a))}},!0)}return b};
+mxCellRenderer.prototype.isShapeEvent=function(a,b){return!0};mxCellRenderer.prototype.isLabelEvent=function(a,b){return!0};
+mxCellRenderer.prototype.installListeners=function(a){var b=a.view.graph,c=function(c){var d=a;if(b.dialect!=mxConstants.DIALECT_SVG&&"IMG"==mxEvent.getSource(c).nodeName||mxClient.IS_TOUCH)d=mxEvent.getClientX(c),c=mxEvent.getClientY(c),c=mxUtils.convertPoint(b.container,d,c),d=b.view.getState(b.getCellAt(c.x,c.y));return d};mxEvent.addGestureListeners(a.shape.node,mxUtils.bind(this,function(c){this.isShapeEvent(a,c)&&b.fireMouseEvent(mxEvent.MOUSE_DOWN,new mxMouseEvent(c,a))}),mxUtils.bind(this,
+function(d){this.isShapeEvent(a,d)&&b.fireMouseEvent(mxEvent.MOUSE_MOVE,new mxMouseEvent(d,c(d)))}),mxUtils.bind(this,function(d){this.isShapeEvent(a,d)&&b.fireMouseEvent(mxEvent.MOUSE_UP,new mxMouseEvent(d,c(d)))}));b.nativeDblClickEnabled&&mxEvent.addListener(a.shape.node,"dblclick",mxUtils.bind(this,function(c){this.isShapeEvent(a,c)&&(b.dblClick(c,a.cell),mxEvent.consume(c))}))};
+mxCellRenderer.prototype.redrawLabel=function(a,b){var c=a.view.graph,d=this.getLabelValue(a),e=c.isWrapping(a.cell),f=c.isLabelClipped(a.cell),g=a.view.graph.isHtmlLabel(a.cell)||null!=d&&mxUtils.isNode(d)?mxConstants.DIALECT_STRICTHTML:a.view.graph.dialect,k=a.style[mxConstants.STYLE_OVERFLOW]||"visible";null==a.text||a.text.wrap==e&&a.text.clipped==f&&a.text.overflow==k&&a.text.dialect==g||(a.text.destroy(),a.text=null);null==a.text&&null!=d&&(mxUtils.isNode(d)||0<d.length)?this.createLabel(a,
+d):null==a.text||null!=d&&0!=d.length||(a.text.destroy(),a.text=null);if(null!=a.text){b&&(null!=a.text.lastValue&&this.isTextShapeInvalid(a,a.text)&&(a.text.lastValue=null),a.text.resetStyles(),a.text.apply(a),a.text.valign=c.getVerticalAlign(a));var c=this.getLabelBounds(a),l=this.getTextScale(a);if(b||a.text.value!=d||a.text.isWrapping!=e||a.text.overflow!=k||a.text.isClipping!=f||a.text.scale!=l||a.text.dialect!=g||!a.text.bounds.equals(c))0!=a.text.bounds.width&&null!=a.unscaledWidth&&0!=Math.round(a.text.bounds.width/
+a.text.scale*l-c.width)&&(a.unscaledWidth=null),a.text.dialect=g,a.text.value=d,a.text.bounds=c,a.text.scale=l,a.text.wrap=e,a.text.clipped=f,a.text.overflow=k,d=a.text.node.style.visibility,this.redrawLabelShape(a.text),a.text.node.style.visibility=d}};
+mxCellRenderer.prototype.isTextShapeInvalid=function(a,b){function c(c,e,f){return result="spacingTop"==e||"spacingRight"==e||"spacingBottom"==e||"spacingLeft"==e?parseFloat(b[c])-parseFloat(b.spacing)!=(a.style[e]||f):b[c]!=(a.style[e]||f)}return c("fontStyle",mxConstants.STYLE_FONTSTYLE,mxConstants.DEFAULT_FONTSTYLE)||c("family",mxConstants.STYLE_FONTFAMILY,mxConstants.DEFAULT_FONTFAMILY)||c("size",mxConstants.STYLE_FONTSIZE,mxConstants.DEFAULT_FONTSIZE)||c("color",mxConstants.STYLE_FONTCOLOR,"black")||
+c("align",mxConstants.STYLE_ALIGN,"")||c("valign",mxConstants.STYLE_VERTICAL_ALIGN,"")||c("spacing",mxConstants.STYLE_SPACING,2)||c("spacingTop",mxConstants.STYLE_SPACING_TOP,0)||c("spacingRight",mxConstants.STYLE_SPACING_RIGHT,0)||c("spacingBottom",mxConstants.STYLE_SPACING_BOTTOM,0)||c("spacingLeft",mxConstants.STYLE_SPACING_LEFT,0)||c("horizontal",mxConstants.STYLE_HORIZONTAL,!0)||c("background",mxConstants.STYLE_LABEL_BACKGROUNDCOLOR)||c("border",mxConstants.STYLE_LABEL_BORDERCOLOR)||c("opacity",
+mxConstants.STYLE_TEXT_OPACITY,100)||c("textDirection",mxConstants.STYLE_TEXT_DIRECTION,mxConstants.DEFAULT_TEXT_DIRECTION)};mxCellRenderer.prototype.redrawLabelShape=function(a){a.redraw()};mxCellRenderer.prototype.getTextScale=function(a){return a.view.scale};
+mxCellRenderer.prototype.getLabelBounds=function(a){var b=a.view.graph,c=a.view.scale,d=b.getModel().isEdge(a.cell),e=new mxRectangle(a.absoluteOffset.x,a.absoluteOffset.y);if(d){var f=a.text.getSpacing();e.x+=f.x*c;e.y+=f.y*c;b=b.getCellGeometry(a.cell);null!=b&&(e.width=Math.max(0,b.width*c),e.height=Math.max(0,b.height*c))}else a.text.isPaintBoundsInverted()&&(b=e.x,e.x=e.y,e.y=b),e.x+=a.x,e.y+=a.y,e.width=Math.max(1,a.width),e.height=Math.max(1,a.height),b=mxUtils.getValue(a.style,mxConstants.STYLE_STROKECOLOR,
+mxConstants.NONE),b!=mxConstants.NONE&&""!=b&&(f=parseFloat(mxUtils.getValue(a.style,mxConstants.STYLE_STROKEWIDTH,1))*c,b=1+Math.floor((f-1)/2),f=Math.floor(f+1),e.x+=b,e.y+=b,e.width-=f,e.height-=f);a.text.isPaintBoundsInverted()&&(b=(a.width-a.height)/2,e.x+=b,e.y-=b,b=e.width,e.width=e.height,e.height=b);null!=a.shape&&(b=mxUtils.getValue(a.style,mxConstants.STYLE_LABEL_POSITION,mxConstants.ALIGN_CENTER),f=mxUtils.getValue(a.style,mxConstants.STYLE_VERTICAL_LABEL_POSITION,mxConstants.ALIGN_MIDDLE),
+b==mxConstants.ALIGN_CENTER&&f==mxConstants.ALIGN_MIDDLE&&(e=a.shape.getLabelBounds(e)));b=mxUtils.getValue(a.style,mxConstants.STYLE_LABEL_WIDTH,null);null!=b&&(e.width=parseFloat(b)*c);d||this.rotateLabelBounds(a,e);return e};
+mxCellRenderer.prototype.rotateLabelBounds=function(a,b){b.y-=a.text.margin.y*b.height;b.x-=a.text.margin.x*b.width;if(!this.legacySpacing||"fill"!=a.style[mxConstants.STYLE_OVERFLOW]&&"width"!=a.style[mxConstants.STYLE_OVERFLOW]){var c=a.view.scale,d=a.text.getSpacing();b.x+=d.x*c;b.y+=d.y*c;var d=mxUtils.getValue(a.style,mxConstants.STYLE_LABEL_POSITION,mxConstants.ALIGN_CENTER),e=mxUtils.getValue(a.style,mxConstants.STYLE_VERTICAL_LABEL_POSITION,mxConstants.ALIGN_MIDDLE),f=mxUtils.getValue(a.style,
+mxConstants.STYLE_LABEL_WIDTH,null);b.width=Math.max(0,b.width-(d==mxConstants.ALIGN_CENTER&&null==f?a.text.spacingLeft*c+a.text.spacingRight*c:0));b.height=Math.max(0,b.height-(e==mxConstants.ALIGN_MIDDLE?a.text.spacingTop*c+a.text.spacingBottom*c:0))}e=a.text.getTextRotation();0!=e&&null!=a&&a.view.graph.model.isVertex(a.cell)&&(c=a.getCenterX(),d=a.getCenterY(),b.x!=c||b.y!=d)&&(e*=Math.PI/180,pt=mxUtils.getRotatedPoint(new mxPoint(b.x,b.y),Math.cos(e),Math.sin(e),new mxPoint(c,d)),b.x=pt.x,b.y=
+pt.y)};
+mxCellRenderer.prototype.redrawCellOverlays=function(a,b){this.createCellOverlays(a);if(null!=a.overlays){var c=mxUtils.mod(mxUtils.getValue(a.style,mxConstants.STYLE_ROTATION,0),90),d=mxUtils.toRadians(c),e=Math.cos(d),f=Math.sin(d);a.overlays.visit(function(d,k){var g=k.overlay.getBounds(a);if(!a.view.graph.getModel().isEdge(a.cell)&&null!=a.shape&&0!=c){var m=g.getCenterX(),n=g.getCenterY(),n=mxUtils.getRotatedPoint(new mxPoint(m,n),e,f,new mxPoint(a.getCenterX(),a.getCenterY())),m=n.x,n=n.y;g.x=
+Math.round(m-g.width/2);g.y=Math.round(n-g.height/2)}if(b||null==k.bounds||k.scale!=a.view.scale||!k.bounds.equals(g))k.bounds=g,k.scale=a.view.scale,k.redraw()})}};
+mxCellRenderer.prototype.redrawControl=function(a,b){var c=a.view.graph.getFoldingImage(a);if(null!=a.control&&null!=c){var c=this.getControlBounds(a,c.width,c.height),d=this.legacyControlPosition?mxUtils.getValue(a.style,mxConstants.STYLE_ROTATION,0):a.shape.getTextRotation(),e=a.view.scale;if(b||a.control.scale!=e||!a.control.bounds.equals(c)||a.control.rotation!=d)a.control.rotation=d,a.control.bounds=c,a.control.scale=e,a.control.redraw()}};
+mxCellRenderer.prototype.getControlBounds=function(a,b,c){if(null!=a.control){var d=a.view.scale,e=a.getCenterX(),f=a.getCenterY();if(!a.view.graph.getModel().isEdge(a.cell)&&(e=a.x+b*d,f=a.y+c*d,null!=a.shape)){var g=a.shape.getShapeRotation();if(this.legacyControlPosition)g=mxUtils.getValue(a.style,mxConstants.STYLE_ROTATION,0);else if(a.shape.isPaintBoundsInverted())var k=(a.width-a.height)/2,e=e+k,f=f-k;0!=g&&(k=mxUtils.toRadians(g),g=Math.cos(k),k=Math.sin(k),f=mxUtils.getRotatedPoint(new mxPoint(e,
+f),g,k,new mxPoint(a.getCenterX(),a.getCenterY())),e=f.x,f=f.y)}return a.view.graph.getModel().isEdge(a.cell),new mxRectangle(Math.round(e-b/2*d),Math.round(f-c/2*d),Math.round(b*d),Math.round(c*d))}return null};
+mxCellRenderer.prototype.insertStateAfter=function(a,b,c){for(var d=this.getShapesForState(a),e=0;e<d.length;e++)if(null!=d[e]&&null!=d[e].node){var f=d[e].node.parentNode!=a.view.getDrawPane()&&d[e].node.parentNode!=a.view.getOverlayPane(),g=f?c:b;if(null!=g&&g.nextSibling!=d[e].node)null==g.nextSibling?g.parentNode.appendChild(d[e].node):g.parentNode.insertBefore(d[e].node,g.nextSibling);else if(null==g)if(d[e].node.parentNode==a.view.graph.container){for(g=a.view.canvas;null!=g&&g.parentNode!=
+a.view.graph.container;)g=g.parentNode;null!=g&&null!=g.nextSibling?g.nextSibling!=d[e].node&&d[e].node.parentNode.insertBefore(d[e].node,g.nextSibling):d[e].node.parentNode.appendChild(d[e].node)}else null!=d[e].node.parentNode.firstChild&&d[e].node.parentNode.firstChild!=d[e].node&&d[e].node.parentNode.insertBefore(d[e].node,d[e].node.parentNode.firstChild);f?c=d[e].node:b=d[e].node}return[b,c]};mxCellRenderer.prototype.getShapesForState=function(a){return[a.shape,a.text,a.control]};
+mxCellRenderer.prototype.redraw=function(a,b,c){b=this.redrawShape(a,b,c);null==a.shape||null!=c&&!c||(this.redrawLabel(a,b),this.redrawCellOverlays(a,b),this.redrawControl(a,b))};
+mxCellRenderer.prototype.redrawShape=function(a,b,c){var d=a.view.graph.model,e=!1;null!=a.shape&&null!=a.shape.style&&null!=a.style&&a.shape.style[mxConstants.STYLE_SHAPE]!=a.style[mxConstants.STYLE_SHAPE]&&(a.shape.destroy(),a.shape=null);null==a.shape&&null!=a.view.graph.container&&a.cell!=a.view.currentRoot&&(d.isVertex(a.cell)||d.isEdge(a.cell))?(a.shape=this.createShape(a),null!=a.shape&&(a.shape.minSvgStrokeWidth=this.minSvgStrokeWidth,a.shape.antiAlias=this.antiAlias,this.createIndicatorShape(a),
+this.initializeShape(a),this.createCellOverlays(a),this.installListeners(a),a.view.graph.selectionCellsHandler.updateHandler(a))):b||null==a.shape||mxUtils.equalEntries(a.shape.style,a.style)&&!this.checkPlaceholderStyles(a)||(a.shape.resetStyles(),this.configureShape(a),a.view.graph.selectionCellsHandler.updateHandler(a),b=!0);null!=a.shape&&(this.createControl(a),b||this.isShapeInvalid(a,a.shape))&&(null!=a.absolutePoints?(a.shape.points=a.absolutePoints.slice(),a.shape.bounds=null):(a.shape.points=
+null,a.shape.bounds=new mxRectangle(a.x,a.y,a.width,a.height)),a.shape.scale=a.view.scale,null==c||c?this.doRedrawShape(a):a.shape.updateBoundingBox(),e=!0);return e};mxCellRenderer.prototype.doRedrawShape=function(a){a.shape.redraw()};mxCellRenderer.prototype.isShapeInvalid=function(a,b){return null==b.bounds||b.scale!=a.view.scale||null==a.absolutePoints&&!b.bounds.equals(a)||null!=a.absolutePoints&&!mxUtils.equalPoints(b.points,a.absolutePoints)};
+mxCellRenderer.prototype.destroy=function(a){null!=a.shape&&(null!=a.text&&(a.text.destroy(),a.text=null),null!=a.overlays&&(a.overlays.visit(function(a,c){c.destroy()}),a.overlays=null),null!=a.control&&(a.control.destroy(),a.control=null),a.shape.destroy(),a.shape=null)};
+var mxEdgeStyle={EntityRelation:function(a,b,c,d,e){var f=a.view,g=f.graph;d=mxUtils.getValue(a.style,mxConstants.STYLE_SEGMENT,mxConstants.ENTITY_SEGMENT)*f.scale;var k=a.absolutePoints,l=k[0],m=k[k.length-1],k=!1;if(null!=l)b=new mxCellState,b.x=l.x,b.y=l.y;else if(null!=b){var n=mxUtils.getPortConstraints(b,a,!0,mxConstants.DIRECTION_MASK_NONE);n!=mxConstants.DIRECTION_MASK_NONE&&n!=mxConstants.DIRECTION_MASK_WEST+mxConstants.DIRECTION_MASK_EAST?k=n==mxConstants.DIRECTION_MASK_WEST:(l=g.getCellGeometry(b.cell),
+l.relative?k=.5>=l.x:null!=c&&(k=c.x+c.width<b.x))}else return;l=!0;null!=m?(c=new mxCellState,c.x=m.x,c.y=m.y):null!=c&&(n=mxUtils.getPortConstraints(c,a,!1,mxConstants.DIRECTION_MASK_NONE),n!=mxConstants.DIRECTION_MASK_NONE&&n!=mxConstants.DIRECTION_MASK_WEST+mxConstants.DIRECTION_MASK_EAST?l=n==mxConstants.DIRECTION_MASK_WEST:(a=g.getCellGeometry(c.cell),a.relative?l=.5>=a.x:null!=b&&(l=b.x+b.width<c.x)));null!=b&&null!=c&&(a=k?b.x:b.x+b.width,b=f.getRoutingCenterY(b),g=l?c.x:c.x+c.width,c=f.getRoutingCenterY(c),
+f=new mxPoint(a+(k?-d:d),b),m=new mxPoint(g+(l?-d:d),c),k==l?(d=k?Math.min(a,g)-d:Math.max(a,g)+d,e.push(new mxPoint(d,b)),e.push(new mxPoint(d,c))):(f.x<m.x==k?(d=b+(c-b)/2,e.push(f),e.push(new mxPoint(f.x,d)),e.push(new mxPoint(m.x,d))):e.push(f),e.push(m)))},Loop:function(a,b,c,d,e){c=a.absolutePoints;var f=c[c.length-1];if(null!=c[0]&&null!=f){if(null!=d&&0<d.length)for(b=0;b<d.length;b++)c=d[b],c=a.view.transformControlPoint(a,c),e.push(new mxPoint(c.x,c.y))}else if(null!=b){var f=a.view,g=f.graph;
+c=null!=d&&0<d.length?d[0]:null;null!=c&&(c=f.transformControlPoint(a,c),mxUtils.contains(b,c.x,c.y)&&(c=null));var k=d=0,l=0,m=0,g=mxUtils.getValue(a.style,mxConstants.STYLE_SEGMENT,g.gridSize)*f.scale;a=mxUtils.getValue(a.style,mxConstants.STYLE_DIRECTION,mxConstants.DIRECTION_WEST);a==mxConstants.DIRECTION_NORTH||a==mxConstants.DIRECTION_SOUTH?(d=f.getRoutingCenterX(b),k=g):(l=f.getRoutingCenterY(b),m=g);null==c||c.x<b.x||c.x>b.x+b.width?null!=c?(d=c.x,m=Math.max(Math.abs(l-c.y),m)):a==mxConstants.DIRECTION_NORTH?
+l=b.y-2*k:a==mxConstants.DIRECTION_SOUTH?l=b.y+b.height+2*k:d=a==mxConstants.DIRECTION_EAST?b.x-2*m:b.x+b.width+2*m:null!=c&&(d=f.getRoutingCenterX(b),k=Math.max(Math.abs(d-c.x),m),l=c.y,m=0);e.push(new mxPoint(d-k,l-m));e.push(new mxPoint(d+k,l+m))}},ElbowConnector:function(a,b,c,d,e){var f=null!=d&&0<d.length?d[0]:null,g=!1,k=!1;if(null!=b&&null!=c)if(null!=f)var l=Math.min(b.x,c.x),m=Math.max(b.x+b.width,c.x+c.width),k=Math.min(b.y,c.y),n=Math.max(b.y+b.height,c.y+c.height),f=a.view.transformControlPoint(a,
+f),g=f.y<k||f.y>n,k=f.x<l||f.x>m;else l=Math.max(b.x,c.x),m=Math.min(b.x+b.width,c.x+c.width),g=l==m,g||(k=Math.max(b.y,c.y),n=Math.min(b.y+b.height,c.y+c.height),k=k==n);k||!g&&a.style[mxConstants.STYLE_ELBOW]!=mxConstants.ELBOW_VERTICAL?mxEdgeStyle.SideToSide(a,b,c,d,e):mxEdgeStyle.TopToBottom(a,b,c,d,e)},SideToSide:function(a,b,c,d,e){var f=a.view;d=null!=d&&0<d.length?d[0]:null;var g=a.absolutePoints,k=g[0],g=g[g.length-1];null!=d&&(d=f.transformControlPoint(a,d));null!=k&&(b=new mxCellState,
+b.x=k.x,b.y=k.y);null!=g&&(c=new mxCellState,c.x=g.x,c.y=g.y);null!=b&&null!=c&&(a=Math.max(b.x,c.x),k=Math.min(b.x+b.width,c.x+c.width),a=null!=d?d.x:Math.round(k+(a-k)/2),k=f.getRoutingCenterY(b),f=f.getRoutingCenterY(c),null!=d&&(d.y>=b.y&&d.y<=b.y+b.height&&(k=d.y),d.y>=c.y&&d.y<=c.y+c.height&&(f=d.y)),mxUtils.contains(c,a,k)||mxUtils.contains(b,a,k)||e.push(new mxPoint(a,k)),mxUtils.contains(c,a,f)||mxUtils.contains(b,a,f)||e.push(new mxPoint(a,f)),1==e.length&&(null!=d?mxUtils.contains(c,a,
+d.y)||mxUtils.contains(b,a,d.y)||e.push(new mxPoint(a,d.y)):(f=Math.max(b.y,c.y),e.push(new mxPoint(a,f+(Math.min(b.y+b.height,c.y+c.height)-f)/2)))))},TopToBottom:function(a,b,c,d,e){var f=a.view;d=null!=d&&0<d.length?d[0]:null;var g=a.absolutePoints,k=g[0],g=g[g.length-1];null!=d&&(d=f.transformControlPoint(a,d));null!=k&&(b=new mxCellState,b.x=k.x,b.y=k.y);null!=g&&(c=new mxCellState,c.x=g.x,c.y=g.y);null!=b&&null!=c&&(k=Math.max(b.y,c.y),g=Math.min(b.y+b.height,c.y+c.height),a=f.getRoutingCenterX(b),
+null!=d&&d.x>=b.x&&d.x<=b.x+b.width&&(a=d.x),k=null!=d?d.y:Math.round(g+(k-g)/2),mxUtils.contains(c,a,k)||mxUtils.contains(b,a,k)||e.push(new mxPoint(a,k)),a=null!=d&&d.x>=c.x&&d.x<=c.x+c.width?d.x:f.getRoutingCenterX(c),mxUtils.contains(c,a,k)||mxUtils.contains(b,a,k)||e.push(new mxPoint(a,k)),1==e.length&&(null!=d&&1==e.length?mxUtils.contains(c,d.x,k)||mxUtils.contains(b,d.x,k)||e.push(new mxPoint(d.x,k)):(f=Math.max(b.x,c.x),e.push(new mxPoint(f+(Math.min(b.x+b.width,c.x+c.width)-f)/2,k)))))},
+SegmentConnector:function(a,b,c,d,e){function f(a){if(null==l||Math.abs(l.x-a.x)>=k||Math.abs(l.y-a.y)>=k)e.push(a),l=a;return l}var g=a.absolutePoints,k=Math.max(1,a.view.scale),l=0<e.length?e[0]:null,m=!0,n=null,p=g[0];null==p&&null!=b?p=new mxPoint(a.view.getRoutingCenterX(b),a.view.getRoutingCenterY(b)):null!=p&&(p=p.clone());p.x=Math.round(p.x);p.y=Math.round(p.y);var q=g.length-1;if(null!=d&&0<d.length){for(var n=[],r=0;r<d.length;r++){var t=a.view.transformControlPoint(a,d[r]);null!=t&&(t.x=
+Math.round(t.x),t.y=Math.round(t.y),n.push(t))}if(0==n.length)return;d=n;null!=p&&null!=d[0]&&(Math.abs(d[0].x-p.x)<k&&(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/dashboard/src/templates/account/booking_list.html b/dashboard/src/templates/account/booking_list.html
new file mode 100644
index 0000000..98ab5c8
--- /dev/null
+++ b/dashboard/src/templates/account/booking_list.html
@@ -0,0 +1,136 @@
+{% extends "base.html" %}
+{% block content %}
+<h2>Bookings I Own</h2>
+ <div class="card_container">
+ {% for booking in bookings %}
+ <div class="card">
+ <div class="card-header">
+ <h3>Booking {{booking.id}}</h3>
+ </div>
+ <div class="card-body">
+ <ul class="list-group">
+ <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>
+ <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>
+ {% endfor %}
+ </div>
+<h2>Bookings I Collaborate On</h2>
+ <div class="card_container">
+ {% for booking in collab_bookings %}
+ <div class="card">
+ <div class="card-header">
+ <h3>Booking {{booking.id}}</h3>
+ </div>
+ <div class="card-body">
+ <ul class="list-group">
+ <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>
+ <div class="card-footer d-flex">
+ <a class="btn btn-primary ml-auto" href="/booking/detail/{{booking.id}}/">Details</a>
+ </div>
+ </div>
+ {% endfor %}
+ </div>
+ <h2>Expired Bookings
+ <i class="fa fa-fw fa-caret-down" onclick='toggle_display("expired_bookings");'></i>
+ </h2>
+ <div id="expired_bookings" class="card_container" style="display:none;">
+ {% for booking in expired_bookings %}
+ <div class="card">
+ <div class="card-header">
+ <h3>Booking {{booking.id}}</h3>
+ </div>
+ <div class="card-body">
+ <ul class="list-group">
+ <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>
+ <div class="card-footer d-flex">
+ <a class="btn btn-primary ml-auto" href="/booking/detail/{{booking.id}}/">Details</a>
+ </div>
+ </div>
+ {% endfor %}
+ </div>
+<script>
+ var current_booking_id = -1;
+ function cancel_booking(booking_id) {
+ current_booking_id = booking_id;
+ document.getElementById('modal_warning').style['max-height'] = '0px';
+ }
+
+ 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);
+ }
+
+ function toggle_display(elem_id){
+ var e = document.getElementById(elem_id);
+ if (e.style.display === "none"){
+ e.style.display = "grid";
+ } else {
+ e.style.display = "none";
+ }
+ }
+</script>
+<div class="modal fade" id="resModal" tabindex="-1" role="dialog" aria-labelledby="my_modal" aria-hidden="true">
+ <div class="modal-dialog" style="width: 450px;" role="document">
+ <div class="modal-content">
+ <div class="modal-header">
+ <h4 class="modal-title" id="my_modal" style="display: inline; float: left;">Cancel Booking?</h4>
+ <p>Everthing on your machine(s) will be lost</p>
+ <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">
+ <button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
+ <button type="button" class="btn btn-primary" onclick="document.getElementById('modal_warning').style['max-height'] = '500px';">Cancel Booking</button>
+ </div>
+ <div id="modal_warning" class="modal-footer" style="max-height:0px;" >
+ <div style="text-align:center; margin: 5px">
+ <h3>Are You Sure?</h3>
+ <p>This cannot be undone</p>
+ <button class="btn" onclick="document.getElementById('modal_warning').style['max-height'] = '0px';">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>
+{% endblock %}
diff --git a/dashboard/src/templates/account/configuration_list.html b/dashboard/src/templates/account/configuration_list.html
new file mode 100644
index 0000000..6f7844a
--- /dev/null
+++ b/dashboard/src/templates/account/configuration_list.html
@@ -0,0 +1,72 @@
+{% extends "base.html" %}
+{% block content %}
+<div class="card_container">
+{% for config in configurations %}
+ <div class="card">
+ <div class="card-header">
+ <h3>Configuration {{config.id}}</h3>
+ </div>
+ <div class="card-body">
+ <ul class="list-group">
+ <li class="list-group-item">id: {{config.id}}</li>
+ <li class="list-group-item">name: {{config.name}}</li>
+ <li class="list-group-item">description: {{config.description}}</li>
+ <li class="list-group-item">resource: {{config.bundle}}</li>
+ </ul>
+ </div>
+ <div class="card-footer">
+ <button
+ class="btn btn-danger w-100"
+ onclick='delete_config({{config.id}});'
+ data-toggle="modal"
+ data-target="#configModal"
+ >Delete</button>
+ </div>
+ </div>
+{% endfor %}
+</div>
+<script>
+ var current_config_id = -1;
+ function delete_config(config_id) {
+ current_config_id = config_id;
+ document.getElementById('modal_warning').style['max-height'] = '0px';
+ }
+
+ function submit_delete_form() {
+ var ajaxForm = $("#config_delete_form");
+ var formData = ajaxForm.serialize();
+ req = new XMLHttpRequest();
+ var url = "delete/" + current_config_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="configModal" tabindex="-1" role="dialog" aria-labelledby="my_modal" aria-hidden="true">
+ <div class="modal-dialog" style="width: 450px;" role="document">
+ <div class="modal-content">
+ <div class="modal-header">
+ <h4 class="modal-title" id="my_modal" style="display: 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="config_delete_form">
+ {% csrf_token %}
+ </form>
+ <div class="modal-footer">
+ <button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
+ <button type="button" class="btn btn-primary" onclick="document.getElementById('modal_warning').style['max-height'] = '500px';">Delete</button>
+ </div>
+ <div id="modal_warning" class="modal-footer" style="max-height:0px;" >
+ <div style="text-align:center; margin: 5px">
+ <h3>Are You Sure?</h3>
+ <p>This cannot be undone</p>
+ <button class="btn" onclick="document.getElementById('modal_warning').style['max-height'] = '0px';">Nevermind</button>
+ <button class="btn btn-danger" data-dismiss="modal" onclick="submit_delete_form();">I'm Sure</button>
+ </div>
+ </div>
+ </div>
+</div>
+{% endblock %}
diff --git a/dashboard/src/templates/account/details.html b/dashboard/src/templates/account/details.html
new file mode 100644
index 0000000..3092ad0
--- /dev/null
+++ b/dashboard/src/templates/account/details.html
@@ -0,0 +1,9 @@
+{% 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-configurations' %}">My Configurations</a>
+<a class="btn btn-primary" href="{% url 'account:my-images' %}">My Snapshots</a>
+{% endblock content %}
diff --git a/dashboard/src/templates/account/image_list.html b/dashboard/src/templates/account/image_list.html
new file mode 100644
index 0000000..068e096
--- /dev/null
+++ b/dashboard/src/templates/account/image_list.html
@@ -0,0 +1,123 @@
+{% extends "base.html" %}
+{% block content %}
+<h2>Images I Own</h2>
+<div class="card_container">
+{% for image in images %}
+ <div class="card">
+ <div class="card-header">
+ <h3>Image {{image.id}}</h3>
+ </div>
+ <div class="card-body">
+ <ul class="list-group">
+ <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 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>
+{% endfor %}
+</div>
+<h2>Public Images</h2>
+<div class="card_container">
+ {% for image in public_images %}
+ <div class="card">
+ <div class="card-header">
+ <h3>Image {{image.id}}</h3>
+ </div>
+ <div class="card-body">
+ <ul class="list-group">
+ <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>
+ {% 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;
+ document.getElementById('modal_warning').style['max-height'] = '0px';
+ 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-labelledby="my_modal" aria-hidden="true">
+ <div class="modal-dialog" style="width: 450px;" role="document">
+ <div class="modal-content">
+ <div class="modal-header">
+ <h4 class="modal-title" id="my_modal" style="display: 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">
+ <button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
+ <button type="button" class="btn btn-primary" onclick="document.getElementById('modal_warning').style['max-height'] = '500px';">Delete</button>
+ </div>
+ <div id="modal_warning" class="modal-footer" style="max-height:0px;" >
+ <div style="text-align:center; margin: 5px">
+ <h3 id="warning_header">Are You Sure?</h3>
+ <p id="warning_text">This cannot be undone</p>
+ <button class="btn" onclick="document.getElementById('modal_warning').style['max-height'] = '0px';">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>
+{% endblock %}
diff --git a/dashboard/src/templates/account/resource_list.html b/dashboard/src/templates/account/resource_list.html
new file mode 100644
index 0000000..f92f78e
--- /dev/null
+++ b/dashboard/src/templates/account/resource_list.html
@@ -0,0 +1,118 @@
+{% extends "base.html" %}
+{% block content %}
+<div class="card_container">
+{% for resource in resources %}
+ <div class="card">
+ <div class="card-header">
+ <h3>Resource {{resource.id}}</h3>
+ </div>
+ <div class="card-body p-4">
+ <ul class="list-group">
+ <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>
+ <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>
+{% endfor %}
+</div>
+<script>
+ var grb_mapping = {{grb_mapping|safe|default:"{}"}};
+ var booking_mapping = {{booking_mapping|safe|default:"{}"}};
+ var current_resource_id = -1;
+ function delete_resource(resource_id) {
+ document.getElementById("confirm_delete_button").removeAttribute("disabled");
+ var configs = grb_mapping[resource_id];
+ var warning = document.createTextNode("Are You Sure?");
+ var warning_subtext = document.createTextNode("This cannot be undone");
+ if(booking_mapping[resource_id]){
+ var warning = document.createTextNode("This resource is being 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 if(configs.length > 0) {
+ list_configs(configs);
+ warning_text = "Are You Sure? The following Configurations will also be deleted.";
+ 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);
+ document.getElementById('modal_warning').style['max-height'] = '0px';
+ }
+
+ 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.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-labelledby="my_modal" aria-hidden="true">
+ <div class="modal-dialog" style="width: 450px;" role="document">
+ <div class="modal-content">
+ <div class="modal-header">
+ <h4 class="modal-title" id="my_modal" style="display: 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">
+ <button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
+ <button type="button" class="btn btn-primary" onclick="document.getElementById('modal_warning').style['max-height'] = '500px';">Delete</button>
+ </div>
+ <div id="modal_warning" class="modal-footer" style="max-height:0px;" >
+ <div style="text-align:center; margin: 5px">
+ <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" onclick="document.getElementById('modal_warning').style['max-height'] = '0px';">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>
+
+{% endblock %}
diff --git a/dashboard/src/templates/account/user_list.html b/dashboard/src/templates/account/user_list.html
index 58ddda6..e564524 100644
--- a/dashboard/src/templates/account/user_list.html
+++ b/dashboard/src/templates/account/user_list.html
@@ -47,7 +47,7 @@
<script type="text/javascript">
$(document).ready(function () {
$('#table').DataTable({
- scrollX: true,
+ scrollX: true,
"order": [[0, "asc"]]
});
});
diff --git a/dashboard/src/templates/account/userprofile_update_form.html b/dashboard/src/templates/account/userprofile_update_form.html
index f4bb7b5..6ab8242 100644
--- a/dashboard/src/templates/account/userprofile_update_form.html
+++ b/dashboard/src/templates/account/userprofile_update_form.html
@@ -1,23 +1,18 @@
-{% extends "layout.html" %}
-{% load bootstrap3 %}
+{% extends "base.html" %}
+{% load bootstrap4 %}
-{% block basecontent %}
- <div class="container">
+{% block content %}
+ <div class="container-fluid">
<div class="row">
- <div class="col-md-4 col-md-offset-4">
+ <div class="col-12 col-xl-6">
{% bootstrap_messages %}
<div class="login-panel panel panel-default">
- <div class="panel-heading">
- <h3 class="panel-title">
- {{ title }}
- </h3>
- </div>
<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-default">
+ <a href="{% url 'generate_token' %}" class="btn btn-primary">
Generate
</a>
</p>
@@ -35,4 +30,4 @@
</div>
</div>
</div>
-{% endblock basecontent %}
+{% endblock content %}
diff --git a/dashboard/src/templates/base.html b/dashboard/src/templates/base.html
index c74db05..62a9ed5 100644
--- a/dashboard/src/templates/base.html
+++ b/dashboard/src/templates/base.html
@@ -1,112 +1,236 @@
{% extends "layout.html" %}
-{% load bootstrap3 %}
+{% load bootstrap4 %}
{% load staticfiles %}
+{% block extrahead %}
+
+<!-- Custom CSS -->
+<link href="{% static "css/detail_view.css" %}" rel="stylesheet">
+<link href="{% static "css/base.css" %}" rel="stylesheet">
+<script type="text/javascript">
+ function cwf(type) {
+ $.ajax({
+ type: "POST",
+ url: "/",
+ data: {
+ "create": type
+ },
+ beforeSend: function (request) {
+ request.setRequestHeader("X-CSRFToken",
+ $('input[name="csrfmiddlewaretoken"]').val()
+ );
+ }
+ }).done(function (data) {
+ window.location.replace("/wf/");
+ }).fail(function (jqxHR, textstatus) {
+ alert("Something went wrong...");
+ });
+ }
+
+ function continue_wf() {
+ window.location.replace("/wf/");
+ }
+
+ function toggle_create_drop() {
+ drop_div = document.getElementById("create_drop");
+
+ if (drop_div.style.display === "none") {
+ drop_div.style.display = "inherit";
+ } else {
+ drop_div.style.display = "none";
+ }
+ }
+</script>
+<style>
+ .navbar {
+ min-width: 200px;
+ }
+
+ .create_drop {
+ display: none;
+ width: 100%;
+ }
+
+ .create_drop button {
+ width: 100%;
+ }
+
+ .drop_btn {
+ border-radius: 0px;
+ background-color: CCCCCC
+ }
+
+ .drop_btn:hover {
+ color: #555;
+ border-top: 1px solid #E7E7E7;
+ border-bottom: 1px solid #E7E7E7;
+ }
+
+ #wrapper {
+ height: 100vh;
+ }
+</style>
+
+{% endblock %}
{% block basecontent %}
- <div id="wrapper">
- <!-- Navigation -->
- <nav class="navbar navbar-default navbar-static-top" role="navigation"
- style="margin-bottom: 0">
- <div class="navbar-header">
- <button type="button" class="navbar-toggle" data-toggle="collapse"
- data-target=".navbar-collapse">
- <span class="sr-only">Toggle navigation</span>
- <span class="icon-bar"></span>
- <span class="icon-bar"></span>
- <span class="icon-bar"></span>
- </button>
- <a href="https://www.opnfv.org/" class="navbar-left"><img
- src="{% static "img/opnfv-logo.png" %}"></a>
- <a class="navbar-brand" href={% url 'dashboard:index' %}>Pharos Dashboard</a>
+<div id="wrapper" class="d-flex flex-column">
+ <!-- Navigation -->
+ <nav class="navbar navbar-light bg-light navbar-fixed-top border-bottom py-0" role="navigation" style="margin-bottom: 0">
+ <div class="container-fluid">
+ <div class="col order-2 order-lg-1 text-center text-lg-left">
+ <a href="https://www.opnfv.org/" class="navbar-brand">
+ <img src="{% static "img/opnfv-logo.png" %}">
+ </a>
+ <a class="navbar-brand" href={% url 'dashboard:index' %}>
+ Pharos Dashboard
+ </a>
</div>
<!-- /.navbar-header -->
-
- <ul class="nav navbar-top-links navbar-right">
- <li class="dropdown">
- <a class="dropdown-toggle" data-toggle="dropdown" href="#">
- <i class="fa fa-user fa-fw"></i> <i class="fa fa-caret-down"></i>
- </a>
- <ul class="dropdown-menu dropdown-user">
- {% if user.is_authenticated %}
- <li><a href="{% url 'account:settings' %}"><i
- class="fa fa-gear fa-fw"></i>
- Settings</a>
- </li>
- <li class="divider"></li>
- <li><a href="{% url 'account:logout' %}?next={{ request.path }}"><i
- class="fa fa-sign-out fa-fw"></i>
- Logout</a>
- </li>
- {% else %}
- <li><a href="{% url 'account:login' %}"><i
- class="fa fa-sign-in fa-fw"></i>
- Login with Jira</a>
- <li>
- {% endif %}
- </ul>
- <!-- /.dropdown-user -->
- </li>
- <!-- /.dropdown -->
- </ul>
- <!-- /.navbar-top-links -->
-
- <div class="navbar-default sidebar" role="navigation">
- <div class="sidebar-nav navbar-collapse">
- <ul class="nav" id="side-menu">
- <li>
- <a href="{% url 'dashboard:ci_pods' %}"><i
- class="fa fa-fw"></i>CI-Pods</a>
- </li>
- <li>
- <a href="{% url 'dashboard:dev_pods' %}"><i
- class="fa fa-fw"></i>Development
- Pods</a>
- </li>
- <li>
- <a href="{% url 'dashboard:jenkins_slaves' %}"><i
- class="fa fa-fw"></i>Jenkins
- Slaves</a>
- </li>
- <li>
+ <div class="col-2 order-1 order-lg-3 d-lg-none">
+ <button class="btn border" type="button" data-toggle="collapse" data-target="#sidebar"
+ aria-expanded="false" aria-controls="sidebar">
+ <i class="fas fa-bars"></i>
+ </button>
+ </div>
+ <div class="col-2 order-3">
+ <ul class="nav ml-auto">
+ <li class="dropdown ml-auto">
+ <a class="nav-link p-0 text-dark 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>
+ <div class="dropdown-menu dropdown-menu-right">
{% if user.is_authenticated %}
- <a href="{% url 'account:users' %}"><i
- class="fa fa-fw"></i>User List
+ <a href="{% url 'account:settings' %}" class="text-dark dropdown-item">
+ <i class="fas fa-cog"></i>
+ Settings
+ </a>
+ <a href="{% url 'account:logout' %}?next={{ request.path }}" class="text-dark dropdown-item">
+ <i class="fas fa-sign-out-alt"></i>
+ Logout
+ </a>
+ {% else %}
+ <a href="{% url 'account:login' %}" class="text-dark dropdown-item">
+ <i class="fas fa-sign-in-alt"></i>
+ Login with Jira
+ </a>
+ {% endif %}
+ </div> <!-- End dropdown-menu -->
+ </li> <!-- End dropdown -->
+ </ul>
+ </div> <!-- End top right account menu -->
+ </div>
+ </nav>
+ <!-- /.navbar-top-links -->
+
+ <!-- Page Content -->
+ <div class="container-fluid d-lg-flex flex-lg-grow-1 px-0">
+ <div class="row h-100 w-100 mx-0">
+ <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">
+ <div class="collapse navbar-collapse" id="sidebar">
+ <div class="list-group list-group-flush w-100 bg-light">
+ <a href="/" class="list-group-item list-group-item-action bg-light">
+ Home
+ </a>
+ {% csrf_token %}
+ <a class="list-group-item list-group-item-action bg-light" 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">
+ Express Booking
+ </a>
+ <a href="#" onclick="cwf(0)" class="list-group-item list-group-item-action list-group-item-secondary">
+ Book a Pod
+ </a>
+ <a href="#" onclick="cwf(1)" class="list-group-item list-group-item-action list-group-item-secondary">
+ Design a Pod
+ </a>
+ <a href="#" onclick="cwf(2)" class="list-group-item list-group-item-action list-group-item-secondary">
+ Configure a Pod
+ </a>
+ <a href="#" onclick="cwf(3)" class="list-group-item list-group-item-action list-group-item-secondary">
+ Create a Snapshot
+ </a>
+ <a href="#" onclick="cwf(4)" class="list-group-item list-group-item-action list-group-item-secondary">
+ Configure OPNFV
+ </a>
+ </div>
+ <a href="{% url 'resource:hosts' %}" class="list-group-item list-group-item-action bg-light">
+ Hosts
</a>
+ {% if user.is_authenticated %}
+ <a href="{% url 'account:users' %}" class="list-group-item list-group-item-action bg-light">
+ User List
+ </a>
{% endif %}
- </li>
- <li>
- <a href="{% url 'booking:list' %}"><i
- class="fa fa-fw"></i>Booking List
+ <a href="{% url 'booking:list' %}" class="list-group-item list-group-item-action bg-light">
+ Booking List
</a>
- </li>
- <li>
- <a href="{% url 'api-root' %}"><i
- class="fa fa-fw"></i>API
+ <a href="{% url 'booking:stats' %}" class="list-group-item list-group-item-action bg-light">
+ Booking Statistics
</a>
- </li>
- </ul>
- </div>
- <!-- /.sidebar-collapse -->
+ <!-- <a href="{% url 'account:my-account' %}" class="list-group-item list-group-item-action bg-light">
+ Account
+ </a> -->
+ <a class="list-group-item list-group-item-action bg-light" 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">
+ My Resources
+ </a>
+ <a href="{% url 'account:my-bookings' %}" class="list-group-item list-group-item-action list-group-item-secondary">
+ My Bookings
+ </a>
+ <a href="{% url 'account:my-configurations' %}" class="list-group-item list-group-item-action list-group-item-secondary">
+ My Configurations
+ </a>
+ <a href="{% url 'account:my-images' %}" class="list-group-item list-group-item-action list-group-item-secondary">
+ My Snapshots
+ </a>
+ </div>
+ <a href="{% url 'dashboard:all_labs' %}" class="list-group-item list-group-item-action bg-light">
+ Lab Info
+ </a>
+ <a href="{% url 'notifier:messages' %}" class="list-group-item list-group-item-action bg-light">
+ Inbox
+ </a>
+ </div>
+ </div>
+ </nav>
+ <!--/.well -->
</div>
- <!-- /.navbar-static-side -->
- </nav>
+ <!--/span-->
- <!-- Page Content -->
- <div id="page-wrapper">
- <div class="row">
- <div class="col-lg-12">
- <h1 class="page-header">{{ title }}</h1>
+ <div class="col flex-grow-1 d-flex flex-column">
+ {% if title %}
+ <div class="row flex-shrink-1">
+ <div class="col-lg-12">
+ <h1 class="page-header">{{ title }}</h1>
+ </div>
+ <!-- /.col-lg-12 -->
</div>
- <!-- /.col-lg-12 -->
+ {% endif %}
+ <div id="bsm">{% bootstrap_messages %}</div>
+ <!-- Content block placed here -->
+ {% block content %}
+ {% endblock content %}
</div>
+ <!--/span-->
- {% bootstrap_messages %}
-
- {% block content %}
- {% endblock content %}
</div>
- <!-- /#page-wrapper -->
+ <!--/row-->
</div>
- <!-- /#wrapper -->
+ <!-- /#page-wrapper -->
+</div>
+<!-- /#wrapper -->
{% endblock basecontent %}
diff --git a/dashboard/src/templates/booking/booking_calendar.html b/dashboard/src/templates/booking/booking_calendar.html
index 81efbe5..ddcb45d 100644
--- a/dashboard/src/templates/booking/booking_calendar.html
+++ b/dashboard/src/templates/booking/booking_calendar.html
@@ -1,9 +1,11 @@
{% extends "base.html" %}
{% load staticfiles %}
-{% load bootstrap3 %}
+{% 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" %}"
@@ -64,13 +66,27 @@
{% 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 %}
@@ -82,6 +98,15 @@
{% 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' %}">
@@ -104,7 +129,7 @@
<div class="modal-body" id="booking_detail_content">
</div>
<div class="modal-footer">
- <button type="button" class="btn btn-default" data-dismiss="modal">Close
+ <button type="button" class="btn btn-primary" data-dismiss="modal">Close
</button>
</div>
</div>
@@ -119,8 +144,61 @@
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"
diff --git a/dashboard/src/templates/booking/booking_delete.html b/dashboard/src/templates/booking/booking_delete.html
index 76a5634..b89eb15 100644
--- a/dashboard/src/templates/booking/booking_delete.html
+++ b/dashboard/src/templates/booking/booking_delete.html
@@ -1,5 +1,5 @@
{% load jira_filters %}
-{% load bootstrap3 %}
+{% load bootstrap4 %}
<p>
Really delete Booking from {{ booking.start}} to {{ booking.end }}?
diff --git a/dashboard/src/templates/booking/booking_detail.html b/dashboard/src/templates/booking/booking_detail.html
index c88cd84..918f5af 100644
--- a/dashboard/src/templates/booking/booking_detail.html
+++ b/dashboard/src/templates/booking/booking_detail.html
@@ -1,70 +1,365 @@
-{% load jira_filters %}
+{% extends "base.html" %}
+{% load staticfiles %}
+{% load bootstrap4 %}
-<script type="text/javascript">
- function deleteClick(id) {
- var booking_delete_url = booking_delete_prefix + id
- $.ajax({
- url: booking_delete_url,
- type: 'get',
- success: function (data) {
- $('#booking_delete_content').html(data);
- },
- failure: function (data) {
- alert('Error finding that booking');
- }
- });
- $('#booking_delete_modal').modal('show');
- }
-</script>
+{% block extrahead %}
+ {{block.super}}
+<script src="https://cdn.rawgit.com/google/code-prettify/master/loader/run_prettify.js?lang=yaml"></script>
+{% endblock %}
+
+{% block content %}
+
+<style>
+#modal_warning {
+ transition: max-height 0.5s ease-out;
+ overflow: hidden;
+}
+
+</style>
+
+<div class="container-fluid">
+ <div class="row">
+ <div class="col-12 col-lg-5">
+ <div class="card mb-4">
+ <div class="card-header d-flex">
+ <h4 style="display: inline;">Overview</h4>
+ <button data-toggle="collapse" data-target="#panel_overview" class="btn btn-outline-secondary ml-auto">Expand</button>
+ </div>
+ <div class="card-body collapse show" id="panel_overview">
+ <table class="table">
+ <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>Pod Configuration</td>
+ <td>{{ booking.config_bundle }}</td>
+ </tr>
+ <tr>
+ <td>Lab Deployed At</td>
+ <td>{{ booking.lab }}</td>
+ </tr>
+ </table>
+ </div>
+ </div>
+ <div class="row">
+ <div class="col-lg-12">
+ <div class="card">
+ <div class="card-header d-flex">
+ <h4 style="display: inline;">Pod</h4>
+ <button data-toggle="collapse" data-target="#pod_panel" class="btn btn-outline-secondary ml-auto">Expand</button>
+ </div>
+ <div class="card-body collapse show" id="pod_panel">
+ <table class="table">
+ {% for host in booking.resource.hosts.all %}
+ <tr>
+ <td><h4>{{host.template.resource.name}}</h4></td>
+ <td>
+ <table class="table">
+ <tr>
+ <td>Hostname:</td>
+ <td>{{host.template.resource.name}}</td>
+ </tr>
+ <tr>
+ <td>Profile:</td>
+ <td>{{host.name}}</td>
+ </tr>
+ <tr>
+ <td>Role:</td>
+ <td>{{host.config.opnfvRole}}</td>
+ </tr>
+ <tr>
+ <td>Image:</td>
+ <td id="host_image_{{host.id}}">
+ {{host.config.image}}
+ <button
+ style="margin-left:10px;"
+ class="btn btn-primary"
+ 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">
+ <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">
+ <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>
+ <style>
+ .borderless td {
+ border: none !important;
+ }
+ </style>
+ <table class="table">
+ {% for intprof in host.profile.interfaceprofile.all %}
+ <tr>
+ <td>
+ <table class="table borderless">
+ <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>
+ </td>
+ {% endfor %}
+ </tr>
+ </table>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ <div class="col">
+ <div class="card mb-4">
+ <div class="card-header d-flex">
+ <h4 style="display: inline;">Deployment Progress</h4>
+ <p style="display: inline; margin-left: 10px;"> These are the different tasks that have to be completed before your deployment is ready</p>
+ <button data-toggle="collapse" data-target="#panel_tasks" class="btn btn-outline-secondary ml-auto">Expand</button>
+ </div>
+ <div class="card-body collapse show" id="panel_tasks">
+ <table class="table">
+ <style>
+ .progress {
+ display: inline-block;
+ border: 3px solid #f3f3f3;
+ border-radius: 50%;
+ border-top: 3px solid #12aebb;
+ width: 20px;
+ height: 20px;
+ -webkit-animation: spin 2s linear infinite; /* Safari */
+ animation: spin 2s linear infinite;
+ }
+
+ @keyframes spin {
+ 0% {transform: rotate(0deg);}
+ 100% {transform: rotate(360deg);}
+ }
+
+ .new {
+ display: inline-block;
+ width: 20px;
+ height: 20px;
+ background: #f3f3f3;
+ border-radius: 50%;
+ animation: fadeInOut 1s infinite alternate;
-<p>
- <b>Resource: </b>
- <a href="{{ booking.resource.url }}">
- {{ booking.resource.name }}
- </a>
-</p>
-<p>
- <b>User: </b> {{ booking.user.username }}
-</p>
-<p>
- <b>Start: </b> {{ booking.start }}
-</p>
-<p>
- <b>End: </b> {{ booking.end }}
-</p>
-<p>
- <b>Purpose: </b> {{ booking.purpose }}
-</p>
-<p>
- <b>Operating System: </b> {{ booking.opsys }}
-</p>
-<p>
- <b>Installer: </b> {{ booking.installer }}
-</p>
-<p>
- <b>Scenario: </b> {{ booking.scenario }}
-</p>
-{% if user.is_authenticated %}
-{% if user.get_username == booking.user.username %}
-<p>
- <a href="{% url 'booking:edit' booking_id=booking.id resource_id=booking.resource.id %}" class="btn btn btn-success">
- Edit Booking
- </a>
- <a href="javascript:deleteClick({{ booking.id }})" class="btn btn btn-danger">
- Delete Booking
- </a>
- <div id="booking_delete_modal" class="modal fade" role="dialog">
- <div class="modal-dialog">
- <div class="modal-content">
- <div class="modal-header">
- <button type="button" class="close" data-dismiss="modal">&times;</button>
- <h4 class="modal-title">Delete Booking</h4>
+ }
+ @keyframes fadeInOut {
+ from { opacity: 0;}
+ }
+ .done {
+ display: inline-block;
+ width: 20px;
+ height: 20px;
+ background: #40B976;
+ border-radius: 50%;
+ }
+ </style>
+ <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="new"></div>
+ {% elif task.status < 200 %}
+ <div class="progress"></div>
+ {% else %}
+ <div class="done"></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>{{ task.message }}</pre>
+ {% endif %}
+ {% else %}
+ No response provided (yet)
+ {% endif %}
+ </td>
+ <td>
+ {{ task.type_str }}
+ </td>
+ </tr>
+ {% endfor %}
+ </table>
</div>
- <div class="modal-body" id="booking_delete_content">
+ </div>
+ <div class="row">
+ <div class="col">
+ <div class="card">
+ <div class="card-header d-flex">
+ <h4 style="display: inline;">PDF</h4>
+ <button data-toggle="collapse" data-target="#pdf_panel" class="btn btn-outline-secondary ml-auto">Expand</button>
+ </div>
+ <div class="card-body collapse show" id="pdf_panel" style="padding: 0px;">
+ <pre class="prettyprint lang-yaml" style="margin: 0px; padding: 15px; border: none;">
+ {{pdf}}
+ </pre>
+ </div>
+ </div>
</div>
</div>
</div>
</div>
-</p>
-{% endif %}
-{% endif %} \ No newline at end of file
+</div>
+
+
+<div class="modal fade" id="imageModal" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true">
+ <div class="modal-dialog" style="width: 450px;" role="document">
+ <div class="modal-content">
+ <div class="modal-header">
+ <h4 class="modal-title" id="exampleModalLabel" style="display: inline; float: left;">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" style="width: 80%; margin-left: 10%" id="image_select" name="image_id">
+ </select>
+ <input id="host_id_input" type="hidden" name="host_id">
+ </input>
+ </form>
+ </div>
+ <div class="modal-footer">
+ <button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
+ <button type="button" class="btn btn-primary" onclick="document.getElementById('modal_warning').style['max-height'] = '500px';">Reset Host</button>
+ </div>
+ <div id="modal_warning" class="modal-footer" style="max-height:0px;" >
+ <div style="text-align:center; margin: 5px">
+ <h3>Are You Sure?</h3>
+ <p>This will wipe the disk and reimage the host</p>
+ <button class="btn" onclick="document.getElementById('modal_warning').style['max-height'] = '0px';">Nevermind</button>
+ <button class="btn btn-danger" data-dismiss="modal" onclick="submit_image_form();">I'm Sure</button>
+ </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").style['max-height'] = '0px';
+ }
+
+ 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/dashboard/src/templates/booking/booking_grid_item.html b/dashboard/src/templates/booking/booking_grid_item.html
new file mode 100644
index 0000000..3c72fd2
--- /dev/null
+++ b/dashboard/src/templates/booking/booking_grid_item.html
@@ -0,0 +1,11 @@
+<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/dashboard/src/templates/booking/booking_list.html b/dashboard/src/templates/booking/booking_list.html
index ccdc46d..591ecc9 100644
--- a/dashboard/src/templates/booking/booking_list.html
+++ b/dashboard/src/templates/booking/booking_list.html
@@ -1,48 +1,44 @@
{% extends "base.html" %}
{% load staticfiles %}
+{% load bootstrap4 %}
{% block extrahead %}
+ {{ block.super }}
<!-- DataTables CSS -->
- <link href="{% static "bower_components/datatables-plugins/integration/bootstrap/3/dataTables.bootstrap.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-responsive/css/dataTables.responsive.css" %}"
+ <link href="{% static "bower_components/datatables.net-responsive-bs4/css/responsive.bootstrap4.min.css" %}"
rel="stylesheet">
{% endblock extrahead %}
{% block content %}
<div class="row">
- <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>
- <!-- /.table-responsive -->
- <!-- /.panel-body -->
- <!-- /.panel -->
+ <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>
- <!-- /.col-lg-12 -->
</div>
{% endblock content %}
{% block extrajs %}
<!-- DataTables JavaScript -->
- <link href="{% static "bower_components/datatables-plugins/integration/bootstrap/3/dataTables.bootstrap.css" %}"
- rel="stylesheet">
-
-
- <script src={% static "bower_components/datatables/media/js/jquery.dataTables.min.js" %}></script>
- <script src={% static "bower_components/datatables-plugins/integration/bootstrap/3/dataTables.bootstrap.min.js" %}></script>
+ <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>
<script type="text/javascript">
$(document).ready(function () {
$('#table').DataTable({
- scrollX: true,
- });
+ scrollX: true,
+ });
});
</script>
{% endblock extrajs %}
diff --git a/dashboard/src/templates/booking/booking_table.html b/dashboard/src/templates/booking/booking_table.html
index af2248c..32a0146 100644
--- a/dashboard/src/templates/booking/booking_table.html
+++ b/dashboard/src/templates/booking/booking_table.html
@@ -3,38 +3,34 @@
<thead>
<tr>
- <th>User</th>
+ <th>Owner</th>
<th>Purpose</th>
+ <th>Project</th>
<th>Start</th>
<th>End</th>
<th>Operating System</th>
- <th>Installer</th>
- <th>Scenario</th>
</tr>
</thead>
<tbody>
{% for booking in bookings %}
<tr>
<td>
- {{ booking.user.username }}
+ {{ booking.owner.username }}
</td>
<td>
{{ booking.purpose }}
</td>
<td>
- {{ booking.start }}
- </td>
- <td>
- {{ booking.end }}
+ {{ booking.project }}
</td>
<td>
- {{ booking.opsys }}
+ {{ booking.start }}
</td>
<td>
- {{ booking.installer }}
+ {{ booking.end }}
</td>
<td>
- {{ booking.scenario }}
+ {{ booking.resource.get_head_node.config.image.os.name }}
</td>
</tr>
{% endfor %}
diff --git a/dashboard/src/templates/booking/quick_deploy.html b/dashboard/src/templates/booking/quick_deploy.html
new file mode 100644
index 0000000..07f3d89
--- /dev/null
+++ b/dashboard/src/templates/booking/quick_deploy.html
@@ -0,0 +1,187 @@
+{% extends "base.html" %}
+{% load staticfiles %}
+{% load bootstrap4 %}
+{% block content %}
+<style>
+ .grid_container {
+ display: grid;
+ grid-template-columns: repeat(12, 1fr);
+ padding: 30px;
+ }
+ .grid_element {
+ border-radius: 5px;
+ border: 1px solid #ccc;
+ margin: 10px;
+ padding: 7px;
+ }
+ .grid_element_wide {
+ grid-column-start: span 12;
+ }
+ .grid_element_half {
+ grid-column-start: span 6;
+ }
+ .grid_element_1third {
+ grid-column-start: span 4;
+ }
+ .grid_element_2third {
+ grid-column-start: span 8;
+ }
+ .collaborator_pane {
+ display: flex;
+ flex-direction: column;
+ }
+ #id_length {
+ -moz-appearance: none;
+ border: none;
+ box-shadow: none;
+ }
+
+ input[type=range]::-moz-range-track {
+ background: #cccccc;
+ }
+
+ .grid_element {
+ overflow: hidden;
+ }
+</style>
+{% bootstrap_form_errors form type='non_fields' %}
+<form id="quick_booking_form" action="/booking/quick/" method="POST" class="form">
+{% csrf_token %}
+<div class="grid_container">
+<div class="grid_element host_select_pane grid_element_wide">
+<p>Please select a host type you wish to book. Only available types are shown.</p>
+{% bootstrap_field form.filter_field show_label=False %}
+</div>
+<div class="grid_element booking_info_pane grid_element_1third">
+ {% bootstrap_field form.purpose %}
+ {% bootstrap_field form.project %}
+ {% bootstrap_field form.length %}
+ <p style="display:inline;">Days: </p><output id="daysout" style="display:inline;">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 class="grid_element collaborator_pane grid_element_1third">
+ <label>Collaborators</label>
+ {{ form.users }}
+</div>
+<div class="grid_element_1third">
+ <div class="configuration_pane grid_element">
+ {% bootstrap_field form.hostname %}
+ {% bootstrap_field form.image %}
+ </div>
+ <div class="configuration_pane grid_element">
+ <strong>OPNFV: (Optional)</strong>
+ {% bootstrap_field form.installer %}
+ {% bootstrap_field form.scenario %}
+ </div>
+</div>
+</div>
+<script type="text/javascript">
+
+ function submit_form()
+ {
+ //formats data for form submission
+ multi_filter_widget.finish();
+ }
+
+ 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;
+ }
+
+ //cross browser hide children
+ $('#id_image').children().hide();
+ for( var i = 0; i < drop.childNodes.length; i++ )
+ {
+ drop.childNodes[i].disabled = true; // closest we can get on safari to hiding it outright
+ }
+ }
+
+ function get_selected_value(key){
+ for( var attr in multi_filter_widget.result[key] ){
+ if(!(attr in {}) )
+ return attr;
+ }
+ return null;
+ }
+
+ var sup_image_dict = {{ image_filter|safe }};
+ var sup_installer_dict = {{ installer_filter|safe }};
+ var sup_scenario_dict = {{ scenario_filter|safe }};
+
+ function imageHider() {
+ var drop = document.getElementById("id_image");
+
+ hide_dropdown("id_image");
+
+ var lab_pk = get_selected_value("lab");
+ var host_pk = get_selected_value("host");
+
+ for ( var i=0; i < drop.childNodes.length; i++ )
+ {
+ var image_object = sup_image_dict[drop.childNodes[i].value];
+ if( image_object ) //weed out empty option
+ {
+ if( image_object.host_profile == host_pk && image_object.lab == lab_pk )
+ {
+ drop.childNodes[i].style.display = "inherit";
+ drop.childNodes[i].disabled = false;
+ }
+ }
+ }
+ }
+
+ imageHider();
+ $('#id_installer').children().hide();
+ $('#id_scenario').children().hide();
+
+
+ Array.from(document.getElementsByClassName("grid-item-select-btn")).forEach(function(element) {
+ element.addEventListener('click', imageHider);
+ });
+
+ function installerHider() {
+ dropFilter("id_installer", sup_installer_dict, "id_image");
+ scenarioHider();
+ }
+ document.getElementById('id_image').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) {
+ 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>
+ <button id="quick_booking_confirm" onclick="submit_form();" class="btn btn-success">Confirm</button>
+</form>
+{% endblock %}
diff --git a/dashboard/src/templates/booking/stats.html b/dashboard/src/templates/booking/stats.html
new file mode 100644
index 0000000..8bc68cd
--- /dev/null
+++ b/dashboard/src/templates/booking/stats.html
@@ -0,0 +1,73 @@
+{% extends "base.html" %}
+{% load staticfiles %}
+
+{% block extrahead %}
+{{ block.super }}
+<script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
+<script>
+function drawGraph(data, graph_id, graph_title){
+ var container = document.getElementById(graph_id);
+ var plot_data = { x: data[0],
+ y: data[1],
+ fill: "tonexty",
+ type: "scatter",
+ mode: "none",
+ };
+ var layout = {
+ title: graph_title
+ };
+ Plotly.newPlot(container, [plot_data], layout);
+}
+function getData(){
+ var req = new XMLHttpRequest();
+ var url = "/booking/stats/json";
+ var day_input = document.getElementById("number_days");
+ var days = day_input.value;
+ //var days = document.getElementById("number_days").value;
+ if(days){
+ url = url + "?days=" + days;
+ }
+ req.onreadystatechange = function(){
+ if( req.readyState == XMLHttpRequest.DONE) {
+ var data = JSON.parse(req.responseText);
+ drawGraph(data["booking"], "booking_graph_container", "Active Bookings");
+ drawGraph(data["user"], "user_graph_container", "Active Users");
+ }
+ }
+ req.open("GET", url, true);
+ req.send();
+}
+</script>
+{% endblock %}
+
+{% block content %}
+ <div class="container-fluid">
+ <div class="row">
+ <div class="col">
+ <p>Number of days to plot: </p>
+ <div class="form-group">
+ <input id="number_days" type="number" class="form-control" min="1" step="1" style="display:inline;width:200px"/>
+ <button class="btn btn-primary" onclick="getData();" style="display:inline;">Submit</button>
+ </div>
+ </div>
+ </div>
+ <div class="row">
+ <div class="col-12 col-md-10">
+ <!-- These graphs do NOT get redrawn when the browser size is changed -->
+ <div id="all_graph_container border" class="mw-100">
+ <div id="booking_graph_wrapper">
+ <div id="booking_graph_container"/>
+ </div>
+ <div id="user_graph_wrapper">
+ <div id="user_graph_container"/>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ <script>
+var data = {{data|safe}};
+drawGraph(data["booking"], "booking_graph_container", "Active Bookings");
+drawGraph(data["user"], "user_graph_container", "Active Users");
+ </script>
+{% endblock content %}
diff --git a/dashboard/src/templates/booking/steps/booking_confirm.html b/dashboard/src/templates/booking/steps/booking_confirm.html
new file mode 100644
index 0000000..40c30a9
--- /dev/null
+++ b/dashboard/src/templates/booking/steps/booking_confirm.html
@@ -0,0 +1,25 @@
+{% extends "workflow/viewport-element.html" %}
+{% load staticfiles %}
+
+{% load bootstrap4 %}
+
+{% block content %}
+
+<style>
+ #form_div {
+ width: 100%;
+ padding: 5%;
+ text-align: center;
+ }
+</style>
+
+<div id="form_div">
+<p>confirm booking</p>
+<p>{{info|default:"booking info"}}</p>
+<form action="/wf/workflow/" method="post">
+ {% csrf_token %}
+ {{form|default:"<p> No Form Loaded</p>"}}
+ <input type="submit" value="Submit" style="display: none;"/>
+</form>
+</div>
+{% endblock content %} \ No newline at end of file
diff --git a/dashboard/src/templates/booking/steps/booking_meta.html b/dashboard/src/templates/booking/steps/booking_meta.html
new file mode 100644
index 0000000..710d4ee
--- /dev/null
+++ b/dashboard/src/templates/booking/steps/booking_meta.html
@@ -0,0 +1,87 @@
+{% extends "workflow/viewport-element.html" %}
+{% load staticfiles %}
+
+{% load bootstrap4 %}
+
+{% block content %}
+
+<style>
+ .bkmeta_panel {
+ padding: 5%;
+ }
+
+ .bkcontrib_panel {
+ display: flex;
+ flex-direction: column;
+ }
+
+ .bkcontrib_panel > .form-group {
+ flex: 1;
+ display: flex;
+ flex-direction: column;
+ }
+
+ .panel{
+ padding: 5%;
+ /*border: solid 1px black;*/
+ }
+
+ .panel_wrap {
+ width: 100%;
+ display: grid;
+ grid-template-columns: 45% 10% 45%;
+ border: none;
+ }
+
+ #id_length {
+ -moz-appearance: none;
+ border: none;
+ box-shadow: none;
+ }
+ input[type=range]::-moz-range-track {
+ background: #cccccc;
+ }
+</style>
+
+{% bootstrap_form_errors form type='non_fields' %}
+<form id="booking_meta_form" action="/wf/workflow/" method="POST" class="form">
+{% csrf_token %}
+<div id="form_div">
+ <div class="panel_wrap">
+ <div class="panel bkmeta_panel">
+ {% bootstrap_field form.purpose %}
+ {% bootstrap_field form.project %}
+ {% bootstrap_field form.length %}
+ <p style="display:inline;">Days: </p><output id="daysout" style="display:inline;">0</output>
+ <script>
+ document.getElementById("id_length").setAttribute("oninput", "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="panel panel_center">
+ </div>
+ <div class="panel bkcontrib_panel">
+ <p>You may add collaborators on your booking to share resources with coworkers.</p>
+ {% bootstrap_field form.users label="Collaborators" %}
+ </div>
+
+ {% buttons %}
+ <button type="submit" style="display: none;" class="btn btn-success">Confirm</button>
+ {% endbuttons %}
+ </div>
+</div>
+</form>
+{% endblock content %}
+
+{% block onleave %}
+var ajaxForm = $("#booking_meta_form");
+var formData = ajaxForm.serialize();
+req = new XMLHttpRequest();
+req.open("POST", "/wf/workflow/", false);
+req.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
+req.onerror = function() { alert("problem submitting form"); }
+req.send(formData);
+{% endblock %}
diff --git a/dashboard/src/templates/booking/steps/resource_select.html b/dashboard/src/templates/booking/steps/resource_select.html
new file mode 100644
index 0000000..382316f
--- /dev/null
+++ b/dashboard/src/templates/booking/steps/resource_select.html
@@ -0,0 +1,73 @@
+{% extends "workflow/viewport-element.html" %}
+{% load staticfiles %}
+
+{% load bootstrap4 %}
+
+{% block content %}
+
+<style>
+ #resource_form_div {
+ width: 100%;
+ padding: 5%;
+ }
+
+ .panel {
+ /*border: solid 1px black;*/
+ border: none;
+ }
+ .select_panels {
+ width: 100%;
+ display: grid;
+ grid-template-columns: 45% 10% 45%;
+
+ }
+
+ .panel_center {
+ text-align: center;
+ border: none;
+
+
+ }
+ .panel_center p{
+ font-size: 20pt;
+ }
+</style>
+
+<div id="resource_form_div">
+ <div class="select_panels">
+ <div class="panel_chooser panel">
+ <form id="resource_select_form" method="post" action="" class="form" id="resourceselectorform">
+ {% csrf_token %}
+ {{ form|default:"<p>no form loaded</p>" }}
+ {% buttons %}
+
+ {% endbuttons %}
+ </form>
+ </div>
+ <div class="panel_center panel"><p>OR</p></div>
+ <div class="panel_add panel">
+ <button class="btn {% if disabled %} disabled {% endif %}"
+ style="width: 100%; height: 100%;"
+ {% if not disabled %}onclick="parent.add_wf(1)"
+ {% endif %}>Create Resource</button>
+ </div>
+
+</div>
+</div>
+<script>
+ {% if disabled %}
+ disable();
+ {% endif %}
+</script>
+
+{% endblock content %}
+{% block onleave %}
+var form = $("#resource_select_form");
+var formData = form.serialize();
+var req = new XMLHttpRequest();
+req.open("POST", "/wf/workflow/", false);
+req.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
+req.onerror = function() { alert("problem with form submission"); }
+req.send(formData);
+{% endblock %}
+
diff --git a/dashboard/src/templates/booking/steps/swconfig_select.html b/dashboard/src/templates/booking/steps/swconfig_select.html
new file mode 100644
index 0000000..60a0df7
--- /dev/null
+++ b/dashboard/src/templates/booking/steps/swconfig_select.html
@@ -0,0 +1,73 @@
+{% extends "workflow/viewport-element.html" %}
+{% load staticfiles %}
+
+{% load bootstrap4 %}
+
+{% block content %}
+
+<style>
+ #sw_form_div {
+ width: 100%;
+ padding: 5%;
+ }
+
+ .panel {
+ /*border: solid 1px black;*/
+ border: none;
+ }
+ .select_panels {
+ width: 100%;
+ display: grid;
+ grid-template-columns: 45% 10% 45%;
+
+ }
+
+ .panel_center {
+ text-align: center;
+ border: none;
+
+
+ }
+ .panel_center p{
+ font-size: 20pt;
+ }
+</style>
+
+<div id="sw_form_div">
+ <div class="select_panels">
+ <div class="panel_chooser panel">
+ <form id="software_select_form" method="post" action="" class="form" id="swselectorform">
+ {% csrf_token %}
+ {{ form|default:"<p>no form loaded</p>" }}
+ {% buttons %}
+
+ {% endbuttons %}
+ </form>
+ </div>
+ <div class="panel_center panel"><p>OR</p></div>
+ <div class="panel_add panel">
+ <button class="btn {% if disabled %} disabled {% endif %}"
+ style="width: 100%; height: 100%;"
+ {% if not disabled %}onclick="parent.add_wf(2)"
+ {% endif %}>Create Config</button>
+ </div>
+ <script>
+ {% if disabled %}
+ disable();
+ {% endif %}
+ </script>
+
+</div>
+</div>
+
+{% endblock content %}
+
+{% block onleave %}
+var form = $("#software_select_form");
+var formData = form.serialize();
+var req = new XMLHttpRequest();
+req.open("POST", "/wf/workflow/", false);
+req.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
+req.onerror = function() { alert("problem with form submission"); }
+req.send(formData);
+{% endblock %}
diff --git a/dashboard/src/templates/config_bundle/steps/assign_host_roles.html b/dashboard/src/templates/config_bundle/steps/assign_host_roles.html
new file mode 100644
index 0000000..b87a17f
--- /dev/null
+++ b/dashboard/src/templates/config_bundle/steps/assign_host_roles.html
@@ -0,0 +1,22 @@
+{% 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/dashboard/src/templates/config_bundle/steps/assign_network_roles.html b/dashboard/src/templates/config_bundle/steps/assign_network_roles.html
new file mode 100644
index 0000000..aa1df44
--- /dev/null
+++ b/dashboard/src/templates/config_bundle/steps/assign_network_roles.html
@@ -0,0 +1,22 @@
+{% 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/dashboard/src/templates/config_bundle/steps/config_software.html b/dashboard/src/templates/config_bundle/steps/config_software.html
new file mode 100644
index 0000000..68417bc
--- /dev/null
+++ b/dashboard/src/templates/config_bundle/steps/config_software.html
@@ -0,0 +1,28 @@
+{% extends "workflow/viewport-element.html" %}
+{% load staticfiles %}
+
+{% load bootstrap4 %}
+
+{% block content %}
+
+<form action="/wf/workflow/" method="POST" id="software_config_form" class="form">
+ {% csrf_token %}
+ <p>Give it a name:</p>
+ {% bootstrap_field form.name %}
+
+ <p>And a description:</p>
+ {% bootstrap_field form.description %}
+</form>
+
+
+{% endblock content %}
+
+{% block onleave %}
+var ajaxForm = $("#software_config_form");
+var formData = ajaxForm.serialize();
+req = new XMLHttpRequest();
+req.open("POST", "/wf/workflow/", false);
+req.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
+req.onerror = function() { alert("problem submitting form"); }
+req.send(formData);
+{% endblock %}
diff --git a/dashboard/src/templates/config_bundle/steps/define_software.html b/dashboard/src/templates/config_bundle/steps/define_software.html
new file mode 100644
index 0000000..87e5997
--- /dev/null
+++ b/dashboard/src/templates/config_bundle/steps/define_software.html
@@ -0,0 +1,55 @@
+{% 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>
+
+ document.getElementById("radio_{{headnode}}").checked = true;
+
+</script>
+{% endblock tablejs %}
+
+
+{% block onleave %}
+var parents = document.getElementsByClassName("table_hidden_input_parent");
+for(var i=0; i<parents.length; i++){
+ var node = parents[i];
+ var radio = node.getElementsByClassName("my_radio")[0];
+ var checkbox = radio.nextElementSibling;
+ if(radio.checked){
+ checkbox.value = "True";
+ }
+}
+
+var form = $("#table_formset");
+var formData = form.serialize();
+var req = new XMLHttpRequest();
+req.open("POST", "/wf/workflow/", false);
+req.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
+req.onerror = function() { alert("problem with form submission"); }
+req.send(formData);
+{% endblock onleave %}
diff --git a/dashboard/src/templates/config_bundle/steps/pick_installer.html b/dashboard/src/templates/config_bundle/steps/pick_installer.html
new file mode 100644
index 0000000..31a06de
--- /dev/null
+++ b/dashboard/src/templates/config_bundle/steps/pick_installer.html
@@ -0,0 +1,32 @@
+{% extends "workflow/viewport-element.html" %}
+{% load staticfiles %}
+
+{% load bootstrap4 %}
+
+{% block content %}
+
+{% if unavailable %}
+<h1>Please choose a config bundle first</h1>
+{% else %}
+
+<form id="installer_form" action="/wf/workflow/" method="POST" id="installer_config_form" 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 %}
+
+{% block onleave %}
+var form = $("#installer_form");
+var formData = form.serialize();
+var req = new XMLHttpRequest();
+req.open("POST", "/wf/workflow/", false);
+req.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
+req.onerror = function() { alert("problem with form submission"); }
+req.send(formData);
+{% endblock %}
diff --git a/dashboard/src/templates/config_bundle/steps/table_formset.html b/dashboard/src/templates/config_bundle/steps/table_formset.html
new file mode 100644
index 0000000..18edc72
--- /dev/null
+++ b/dashboard/src/templates/config_bundle/steps/table_formset.html
@@ -0,0 +1,64 @@
+{% extends "workflow/viewport-element.html" %}
+{% load staticfiles %}
+
+{% load bootstrap4 %}
+
+{% block extrahead %}
+ <!-- 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 %}
+{% if error %}
+ <h1 style="text-align:center;">{{ error }}</h1>
+{% else %}
+<div style="padding: 5%;">
+ <form method="post" action="" class="form" id="table_formset">
+ {% 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 "bower_components/datatables.net/js/jquery.dataTables.min.js" %}></script>
+ <script src={% static "bower_components/datatables.net-bs4/js/dataTables.bootstrap4.min.js" %}></script>
+
+ <script src={% static "js/dataTables-sort.js" %}></script>
+
+ {% block tablejs %}
+ {% endblock tablejs %}
+{% endblock extrajs %}
+
+
+{% block onleave %}
+var form = $("#table_formset");
+var formData = form.serialize();
+var req = new XMLHttpRequest();
+req.open("POST", "/wf/workflow/", false);
+req.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
+req.onerror = function() { alert("problem with form submission"); }
+req.send(formData);
+{% endblock %}
diff --git a/dashboard/src/templates/dashboard/ci_pods.html b/dashboard/src/templates/dashboard/ci_pods.html
deleted file mode 100644
index a20be95..0000000
--- a/dashboard/src/templates/dashboard/ci_pods.html
+++ /dev/null
@@ -1,61 +0,0 @@
-{% extends "dashboard/table.html" %}
-{% load staticfiles %}
-{% load jenkins_filters %}
-
-{% block table %}
- <thead>
- <tr>
- <th>Name</th>
- <th>Slave Name</th>
- <th>Status</th>
- <th>Installer</th>
- <th>Scenario</th>
- <th>Branch</th>
- <th>Job</th>
- </tr>
- </thead>
- <tbody>
- {% for pod in ci_pods %}
- <tr>
- <td>
- <a target='_blank' href={{ pod.url }}>{{ pod.name }}</a>
- </td>
- <td>
- <a target='_blank' href={{ pod.slave.url }}>{{ pod.slave.name }}</a>
- </td>
- <td style="background-color:{{ pod.slave.status | jenkins_status_color }}">
- {{ pod.slave.status }}
- </td>
- <td {{ pod.slave.last_job_result | jenkins_job_blink }}>
- {{ pod.slave.last_job_installer }}
- </td>
- <td {{ pod.slave.last_job_result | jenkins_job_blink }}>
- {{ pod.slave.last_job_scenario }}
- </td>
- <td {{ pod.slave.last_job_result | jenkins_job_blink }}>
- {{ pod.slave.last_job_branch }}
- </td>
- <td><a {{ pod.slave.last_job_result | jenkins_job_blink }}
- style="color:{{ pod.slave.last_job_result | jenkins_job_color }}"
- target='_blank'
- href={{ pod.slave.last_job_url }}>{{ pod.slave.last_job_name }}</a>
- </td>
- </tr>
- {% endfor %}
- </tbody>
-{% endblock table %}
-
-
-{% block tablejs %}
- <script type="text/javascript">
- $(document).ready(function () {
- $('#table').DataTable({
- scrollX: true,
- columnDefs: [
- {type: 'status', targets: 2}
- ],
- "order": [[2, "asc"]]
- });
- });
- </script>
-{% endblock tablejs %}
diff --git a/dashboard/src/templates/dashboard/dev_pods.html b/dashboard/src/templates/dashboard/dev_pods.html
deleted file mode 100644
index a6f3b2e..0000000
--- a/dashboard/src/templates/dashboard/dev_pods.html
+++ /dev/null
@@ -1,70 +0,0 @@
-{% extends "dashboard/table.html" %}
-{% load staticfiles %}
-{% load jenkins_filters %}
-
-{% block table %}
- <thead>
- <tr>
- <th>Name</th>
- <th>Slave Name</th>
- <th>Booked by</th>
- <th>Booked until</th>
- <th>Purpose</th>
- <th>Utilization</th>
- <th>Status</th>
- <th></th>
- <th></th>
- </tr>
- </thead>
- <tbody>
- {% for pod, booking, utilization in dev_pods %}
- <tr>
- <td>
- <a href={% url 'dashboard:resource' resource_id=pod.id %}>{{ pod.name }}</a>
- </td>
- <td>
- <a target='_blank' href={{ pod.slave.url }}>{{ pod.slave.name }}</a>
- </td>
- <td>
- {{ booking.user.username }}
- </td>
- <td>
- {{ booking.end }}
- </td>
- <td>
- {{ booking.purpose }}
- </td>
- <td>
- {{ utilization }}
- </td>
- <td style="background-color:{{ pod.slave.status | jenkins_status_color }}">
- {{ pod.slave.status }}
- </td>
- <td>
- <a href="{% url 'booking:create' resource_id=pod.id %}" class="btn btn-primary">
- Book
- </a>
- </td>
- <td>
- <a href="{% url 'dashboard:resource' resource_id=pod.id %}" class="btn btn-primary">
- Info
- </a>
- </td>
- </tr>
- {% endfor %}
- </tbody>
-{% endblock table %}
-
-{% block tablejs %}
- <script type="text/javascript">
- $(document).ready(function () {
- $('#table').DataTable({
- scrollX: true,
- columnDefs: [
- {type: 'status', targets: 6}
- ],
- "order": [[6, "asc"]]
- });
- });
- </script>
-{% endblock tablejs %}
diff --git a/dashboard/src/templates/dashboard/genericselect.html b/dashboard/src/templates/dashboard/genericselect.html
new file mode 100644
index 0000000..441d8dc
--- /dev/null
+++ b/dashboard/src/templates/dashboard/genericselect.html
@@ -0,0 +1,104 @@
+{% extends "workflow/viewport-element.html" %}
+{% load staticfiles %}
+
+{% load bootstrap4 %}
+
+{% block content %}
+
+<style>
+ #page-wrapper {
+ display: flex;
+ flex-direction: column;
+ }
+
+ #{{select_type}}_form_div div {
+ }
+
+ #{{select_type}}_form_div > * {
+ margin-left: 10px;
+ margin-right: 10px;
+ margin-bottom: 20px;
+ }
+
+ #{{select_type}}_form_div div * {
+ }
+
+ #{{select_type}}_form_div {
+ flex: 1;
+ margin: 30px;
+ display: flex;
+ flex-direction: column;
+ }
+
+ #select_section {
+ flex: 1;
+ display: flex;
+ flex-direction: column;
+ }
+
+ #{{select_type}}_select_form {
+ flex: 1;
+ display: flex;
+ flex-direction: column;
+ }
+
+ .autocomplete {
+ flex: 1;
+ }
+
+ #create_section {
+ }
+
+ #select_header_section {
+ }
+
+ h3 {
+ margin-top: 0;
+ margin-bottom: 0;
+ vertical-align: middle;
+ }
+
+ .divider {
+ border-top: 1px solid #ccc;
+ }
+
+
+</style>
+
+<div id="{{select_type}}_form_div">
+ <h3 id="create_section">Create a Resource
+ <button class="btn btn-primary {% if disabled %} disabled {% endif %}"
+ {% if not disabled %}onclick="parent.add_wf({{addable_type_num}})"
+ {% endif %}>Here
+ </button>
+ </h3>
+ <div class="divider"></div>
+ <h3 id="select_header_section">Or select from the list below:</h3>
+ <div id="select_section">
+ <form id="{{select_type}}_select_form" method="post" action="" class="form" id="{{select_type}}selectorform">
+ {% csrf_token %}
+ {{ form|default:"<p>no form loaded</p>" }}
+ {% buttons %}
+
+ {% endbuttons %}
+ </form>
+ </div>
+</div>
+
+<script>
+ {% if disabled %}
+ disable();
+ {% endif %}
+</script>
+
+{% endblock content %}
+{% block onleave %}
+var form = $("#{{select_type}}_select_form");
+var formData = form.serialize();
+var req = new XMLHttpRequest();
+req.open("POST", "/wf/workflow/", false);
+req.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
+req.onerror = function() { alert("problem with form submission"); }
+req.send(formData);
+{% endblock %}
+
diff --git a/dashboard/src/templates/dashboard/grid.html b/dashboard/src/templates/dashboard/grid.html
new file mode 100644
index 0000000..ca47f44
--- /dev/null
+++ b/dashboard/src/templates/dashboard/grid.html
@@ -0,0 +1,10 @@
+{% extends "base.html" %}
+{% load staticfiles %}
+{% if script %} <script>{{ script }}</script> {% endif %}
+{% block content %}
+ {% for item in grid_items %}
+ <div class="grid-item" style="border:2px; border-style:solid; border-color:black; margin:3px">
+ {{ item }}
+ </div>
+ {% endfor %}
+{% endblock content %}
diff --git a/dashboard/src/templates/dashboard/host_profile_detail.html b/dashboard/src/templates/dashboard/host_profile_detail.html
new file mode 100644
index 0000000..abc7648
--- /dev/null
+++ b/dashboard/src/templates/dashboard/host_profile_detail.html
@@ -0,0 +1,71 @@
+{% 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/dashboard/src/templates/dashboard/idf.yaml b/dashboard/src/templates/dashboard/idf.yaml
new file mode 100644
index 0000000..9e0cc26
--- /dev/null
+++ b/dashboard/src/templates/dashboard/idf.yaml
@@ -0,0 +1,46 @@
+---
+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/dashboard/src/templates/dashboard/jenkins_slaves.html b/dashboard/src/templates/dashboard/jenkins_slaves.html
deleted file mode 100644
index fa361b1..0000000
--- a/dashboard/src/templates/dashboard/jenkins_slaves.html
+++ /dev/null
@@ -1,46 +0,0 @@
-{% extends "dashboard/table.html" %}
-{% load staticfiles %}
-
-{% load jenkins_filters %}
-
-{% block table %}
- <thead>
- <tr>
- <th>Slave name</th>
- <th>Status</th>
- <th>Job</th>
- </tr>
- </thead>
- <tbody>
- {% for slave in slaves %}
- <tr>
- <td><a target='_blank'
- href={{ slave.url }}>{{ slave.name }}</a>
- </td>
- <td style="background-color:{{ slave.status | jenkins_status_color }}">
- {{ slave.status }}
- </td>
- <td><a {{ slave.last_job_result | jenkins_job_blink }}
- style="color:{{ slave.last_job_result | jenkins_job_color }}"
- target="_blank" href={{ slave.last_job_url }}>
- {{ slave.last_job_name }}</a>
- </td>
- </tr>
- {% endfor %}
- </tbody>
-{% endblock table %}
-
-
-{% block tablejs %}
- <script type="text/javascript">
- $(document).ready(function () {
- $('#table').DataTable({
- scrollX: true,
- columnDefs: [
- {type: 'status', targets: 1}
- ],
- "order": [[1, "asc"]]
- });
- });
- </script>
-{% endblock tablejs %}
diff --git a/dashboard/src/templates/dashboard/lab_detail.html b/dashboard/src/templates/dashboard/lab_detail.html
new file mode 100644
index 0000000..336b32e
--- /dev/null
+++ b/dashboard/src/templates/dashboard/lab_detail.html
@@ -0,0 +1,154 @@
+{% 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 my-2">
+ <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 id="panel_overview" class="card-body collapse show">
+ <table class="table">
+ <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 style="border-radius: 50%; background:#40B976; height: 20px; width: 20px; display: inline-block;"></div>
+ Up</td>
+ {% elif lab.status < 200 %}
+ <td>
+ <div style="border-radius: 50%; background:#eeee00; height: 20px; width: 20px; display: inline-block;"></div>
+ Temporarily Offline</td>
+ {% else %}
+ <td>
+ <div style="border-radius: 50%; background:#e50000; height: 20px; width: 20px; display: inline-block;"></div>
+ Offline Indefinitely</td>
+ {% endif %}
+ </tr>
+ </table>
+ </div>
+ </div>
+ <div class="card my-2">
+ <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" style="line-height: 1;" >Expand</button>
+ </div>
+ <div id="profile_panel" class="card-body collapse show">
+ <table class="table">
+ {% 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 class="card my-2">
+ <div class="card-header d-flex">
+ <h4 style="display: inline;">Networking Capabilities</h4>
+ <button data-toggle="collapse" data-target="#network_panel" class="btn btn-outline-secondary ml-auto" style="line-height: 1;" >Expand</button>
+ </div>
+
+ <div class="card-body collapse show" id="network_panel">
+ <table class="table">
+ <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}}</td>
+ </tr>
+ </table>
+ </div>
+ </div>
+ <div class="card my-2">
+ <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="card-body collapse show" id="image_panel">
+ <table class="table">
+ <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 class="col-lg-8">
+ <div class="card my-2">
+ <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="card-body collapse show" id="lab_hosts_panel">
+ <table class="table">
+ <tr>
+ <th>Name</th>
+ <th>Profile</th>
+ <th>Booked</th>
+ <th>Working</th>
+ <th>Vendor</th>
+ </tr>
+ {% for host in lab.host_set.all %}
+ <tr>
+ <td>{{host.labid}}</td>
+ <td>{{host.profile}}</td>
+ <td>{{host.booked}}</td>
+ {% if host.working %}
+ <td style="background-color: #40B976;">{{host.working}}</td>
+ {% else %}
+ <td>{{host.working}}</td>
+ {% endif %}
+ <td>{{host.vendor}}</td>
+ </tr>
+ {% endfor %}
+ </table>
+ </div>
+ </div>
+ </div>
+
+</div>
+
+
+{% endblock content %}
+
diff --git a/dashboard/src/templates/dashboard/lab_list.html b/dashboard/src/templates/dashboard/lab_list.html
new file mode 100644
index 0000000..9cde80c
--- /dev/null
+++ b/dashboard/src/templates/dashboard/lab_list.html
@@ -0,0 +1,28 @@
+{% extends "base.html" %}
+{% block content %}
+<h2>Labs</h2>
+<div class="card_container">
+ {% for lab in labs %}
+ <div class="card">
+ <div class="card-header">
+ <h3 class="mt-2">{{lab.name}}</h3>
+ </div>
+ <div class="p-4">
+ <ul class="list-group">
+ <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>
+ <a class="btn btn-primary mt-4 w-100" href="/lab/{{lab.name}}/">Details</a>
+ </div>
+ </div>
+ {% endfor %}
+</div>
+{% endblock %} \ No newline at end of file
diff --git a/dashboard/src/templates/dashboard/landing.html b/dashboard/src/templates/dashboard/landing.html
new file mode 100644
index 0000000..e6a235f
--- /dev/null
+++ b/dashboard/src/templates/dashboard/landing.html
@@ -0,0 +1,172 @@
+{% extends "base.html" %}
+{% load staticfiles %}
+
+{% block content %}
+<div class="" style="text-align: center;">
+ {% if not request.user.is_anonymous %}
+ {% if not request.user.userprofile.ssh_public_key %}
+ <div class="alert alert-danger" role="alert">
+ Warning: you need to upload an ssh key under <a href="/accounts/settings">account settings</a> if you wish to
+ log into the servers you book
+ </div>
+ {% endif %}
+ {% else %}
+ {% endif %}
+</div>
+{% csrf_token %}
+
+<style>
+ .wf_create {
+ display: inline-block;
+ text-align: center;
+ }
+
+ .wf_create_div {
+ text-align: center;
+ }
+
+ .hidden_form {
+ display: none;
+ }
+
+ .panel {
+ border: none;
+ }
+
+ .panels {
+ display: grid;
+ grid-template-columns: 33% 34% 33%;
+ }
+
+ .landing_container {
+ display: grid;
+ grid-template-columns: 1fr 30px 1fr;
+ }
+
+ .grid_panel {
+ padding: 30px;
+ }
+
+ .btn-primary {
+ margin: 10px;
+ }
+
+ h2 {
+ border-bottom: 1px solid #cccccc;
+ }
+
+ h1 {}
+</style>
+<div class="container-fluid">
+ <div class="row">
+ <!-- About us -->
+ <div class="col-12 col-lg-6 mb-4">
+ <h2>About Us:</h2>
+ <p>The Lab as a Service (LaaS) project aims to help in the development and testing of LFN projects such as
+ OPNFV
+ 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.opnfv.org/display/INF/Lab+as+a+Service+2.0">LaaS Wiki</a></p>
+ </div>
+ <!-- Get started -->
+ <div class="col-12 col-lg-6 mb-4">
+ <h2>Get Started:</h2>
+ {% if request.user.is_anonymous %}
+ <h4 style="text-align:center;">To get started, please log in with your <a href="/accounts/login">Linux
+ Foundation Jira account</a></h4>
+ {% else %}
+ <p>To get started, book a server below:</p>
+ <a class="wf_create btn btn-primary"
+ style="display: flex; flex-direction: column; justify-content: center; margin: 20px; height: 100pt; vertical-align: middle; text-align: center; color: #FFF;"
+ href="/booking/quick/">
+ <p style="font-size: xx-large">Book a Server</p>
+ </a>
+ <p>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='container'>
+ <div class="row">
+ <div class="col-12 col-xl-4">
+ <button class="wf_create btn btn-primary w-100" onclick="cwf(0)">Book a Pod</button>
+ </div>
+ <div class="col-12 col-xl-4">
+ <button class="wf_create btn btn-primary w-100" onclick="cwf(1)">Design a Pod</button>
+ </div>
+ <div class="col-12 col-xl-4">
+ <button class="wf_create btn btn-primary w-100" onclick="cwf(2)">Configure a Pod</button>
+ </div>
+ </div>
+ {% endif %}
+ </div>
+ </div>
+ <!-- Returning users -->
+ {% if not request.user.is_anonymous %}
+ <div class="col-12 col-lg-6 offset-lg-6 mb-4 mt-lg-4">
+ <h2 class="ht-4">Returning Users:</h2>
+ <p>If you're a returning user, some of the following options may be of interest:</p>
+ <div class="container">
+ <div class="row">
+ <div class="col-12 col-xl-4">
+ <button class="wf_create btn btn-primary w-100" onclick="cwf(3)">Snapshot a Host</button>
+ </div>
+ <div class="col-12 col-xl-4">
+ <a class="wf_create btn btn-primary w-100" href="{% url 'account:my-bookings' %}">My
+ Bookings</a>
+ </div>
+ {% if manager == True %}
+ <div class="col-12 col-xl-4">
+ <button class="wf_continue btn btn-primary w-100" onclick="continue_wf()">Resume
+ Workflow</button>
+ </div>
+ {% endif %}
+ </div>
+ </div>
+ </div>
+ {% endif %}
+ </div>
+</div>
+
+<script type="text/javascript">
+ function cwf(type) {
+ $.ajax({
+ type: "POST",
+ url: "/",
+ data: {
+ "create": type
+ },
+ beforeSend: function (request) {
+ request.setRequestHeader("X-CSRFToken",
+ $('input[name="csrfmiddlewaretoken"]').val()
+ );
+ }
+
+ }).done(function (data) {
+ window.location.replace("/wf/");
+ }).fail(function (jqxHR, textstatus) {
+ alert("Something went wrong...");
+ });
+ }
+
+ function continue_wf() {
+ window.location.replace("/wf/");
+ }
+</script>
+
+<div class="hidden_form" id="form_div">
+ <form method="post" action="" class="form" id="wf_selection_form">
+ {% csrf_token %}
+
+ <input type="hidden" id="landing_action">
+
+ <button type="submit" class="btn btn btn-success">
+ Confirm Edit
+ </button>
+ </form>
+</div>
+
+{% block vport_comm %}
+{% endblock %}
+{% endblock content %} \ No newline at end of file
diff --git a/dashboard/src/templates/dashboard/login.html b/dashboard/src/templates/dashboard/login.html
new file mode 100644
index 0000000..d3aa4ad
--- /dev/null
+++ b/dashboard/src/templates/dashboard/login.html
@@ -0,0 +1,8 @@
+{% extends "base.html" %}
+
+{% block content %}
+<h3>You Must Login To Do That</h3>
+
+<a href="/accounts/login">Login Here</a>
+
+{% endblock %}
diff --git a/dashboard/src/templates/dashboard/multiple_select_filter_widget.html b/dashboard/src/templates/dashboard/multiple_select_filter_widget.html
new file mode 100644
index 0000000..4302543
--- /dev/null
+++ b/dashboard/src/templates/dashboard/multiple_select_filter_widget.html
@@ -0,0 +1,149 @@
+<script src="/static/js/dashboard.js">
+</script>
+
+<style>
+.object_class_wrapper {
+ display: grid;
+ grid-template-columns: 1fr 1fr 1fr;
+ border: 0px;
+}
+
+.class_grid_wrapper {
+ border: 0px;
+ text-align: center;
+ border-right: 1px;
+ border-style: solid;
+ border-color: grey;
+}
+
+.class_grid_wrapper:last-child {
+ border-right: none;
+}
+
+.grid_wrapper {
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+}
+
+.grid-item {
+ cursor: pointer;
+ border: 1px solid #cccccc;
+ border-radius: 5px;
+ margin: 20px;
+ height: 200px;
+ padding: 7px;
+ transition: border-color ease-in-out .1s,box-shadow ease-in-out .1s;
+ box-shadow: 0 1px 1px rgba(0,0,0,.075);
+
+ display: flex;
+ flex-direction: column;
+}
+
+.grid-item > .btn:active, .grid-item > .btn:focus {
+ outline: none; !important;
+ box-shadow: none;
+}
+
+.grid-item-description {
+ flex: 1;
+}
+
+.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;
+}
+
+.grid-item:hover:not(.selected_node):not(.disabled_node) {
+ box-shadow: 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(100, 100, 100, 0.3);
+ transition: border-color ease-in-out .1s,box-shadow ease-in-out .1s;
+}
+
+.disabled_node {
+ cursor: not-allowed;
+ background-color: #EFEFEF;
+}
+
+.disabled_node:hover {}
+
+.cleared_node {
+ background-color: #FFFFFF;
+}
+
+.grid-item-header {
+ font-weight: bold;
+ font-size: 20px;
+ margin-top: 10px;
+}
+
+.dropdown_item {
+ border: 1px;
+ border-style: solid;
+ border-color: lightgray;
+ border-radius: 5px;
+ margin: 20px;
+ padding: 2px;
+ grid-column: 1;
+ display: grid;
+ grid-template-columns: 1fr 3fr 1fr;
+ justify-items: center;
+}
+
+.dropdown_item > button {
+ margin: 2px;
+ justify-self: end;
+}
+
+.dropdown_item > h5 {
+ margin: auto;
+}
+
+.dropdown_item > input {
+ padding: 7px;
+ margin: 2px;
+ width: 90%;
+}
+
+#dropdown_wrapper {
+ display: grid;
+ grid-template-columns: 4fr 5fr;
+}
+</style>
+
+<input name="filter_field" id="filter_field" type="hidden"/>
+<div id="grid_wrapper" class="grid_wrapper">
+{% for object_class, object_list in display_objects %}
+ <div class="class_grid_wrapper">
+ <div style="display:inline-block;margin:auto">
+ <h4>{{object_class}}</h4>
+ </div>
+ <div id="{{object_class}}" class="object_class_wrapper">
+ {% for obj in object_list %}
+ <div id="{{ obj.id|default:'not_provided' }}" class="grid-item" onclick="multi_filter_widget.processClick(
+ '{{obj.id}}');">
+ <p class="grid-item-header">{{obj.name}}</p>
+ <p class="grid-item-description">{{obj.description}}</p>
+ <button type="button" class="btn btn-success grid-item-select-btn">
+ {% if obj.multiple %}Add{% else %}Select{% endif %}
+ </button>
+ </div>
+ {% endfor %}
+ </div>
+ </div>
+{% endfor %}
+</div>
+
+<div id="dropdown_wrapper">
+</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 variable
+ multi_filter_widget = new MultipleSelectFilterWidget(graph_neighbors, filter_items, initial_value);
+}
+
+multipleSelectFilterWidgetEntry();
+</script>
diff --git a/dashboard/src/templates/dashboard/pdf.yaml b/dashboard/src/templates/dashboard/pdf.yaml
new file mode 100644
index 0000000..c893919
--- /dev/null
+++ b/dashboard/src/templates/dashboard/pdf.yaml
@@ -0,0 +1,92 @@
+---
+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/dashboard/src/templates/dashboard/resource.html b/dashboard/src/templates/dashboard/resource.html
index c9e5735..f36ee7b 100644
--- a/dashboard/src/templates/dashboard/resource.html
+++ b/dashboard/src/templates/dashboard/resource.html
@@ -2,15 +2,16 @@
{% 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-plugins/integration/bootstrap/3/dataTables.bootstrap.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-responsive/css/dataTables.responsive.css" %}"
+ <link href="{% static "bower_components/datatables.net-responsive-bs4/css/responsive.bootstrap4.min.css" %}"
rel="stylesheet">
{% endblock extrahead %}
@@ -22,11 +23,11 @@
{% block extrajs %}
<!-- DataTables JavaScript -->
- <link href="{% static "bower_components/datatables-plugins/integration/bootstrap/3/dataTables.bootstrap.css" %}"
+ <link href="{% static "bower_components/datatables/media/css/dataTables.bootstrap.css" %}"
rel="stylesheet">
- <script src={% static "bower_components/datatables/media/js/jquery.dataTables.min.js" %}></script>
- <script src={% static "bower_components/datatables-plugins/integration/bootstrap/3/dataTables.bootstrap.min.js" %}></script>
+ <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>
@@ -50,8 +51,6 @@
var utilization_url = "{% url 'dashboard:booking_utilization' resource_id=resource.id weeks=4 %}";
loadChartData(chart_id, utilization_url);
- var chart_id = "{{ resource.id }}_jenkins_utilization";
- var utilization_url = "{% url 'dashboard:jenkins_utilization' resource_id=resource.id weeks=1 %}";
loadChartData(chart_id, utilization_url);
});
</script>
diff --git a/dashboard/src/templates/dashboard/resource_all.html b/dashboard/src/templates/dashboard/resource_all.html
index a770d4e..fb8cc7e 100644
--- a/dashboard/src/templates/dashboard/resource_all.html
+++ b/dashboard/src/templates/dashboard/resource_all.html
@@ -2,15 +2,16 @@
{% 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-plugins/integration/bootstrap/3/dataTables.bootstrap.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-responsive/css/dataTables.responsive.css" %}"
+ <link href="{% static "bower_components/datatables.net-responsive-bs4/css/responsive.bootstrap4.min.css" %}"
rel="stylesheet">
{% endblock extrahead %}
@@ -35,11 +36,11 @@
{% block extrajs %}
<!-- DataTables JavaScript -->
- <link href="{% static "bower_components/datatables-plugins/integration/bootstrap/3/dataTables.bootstrap.css" %}"
+ <link href="{% static "bower_components/datatables/media/css/dataTables.bootstrap.css" %}"
rel="stylesheet">
- <script src={% static "bower_components/datatables/media/js/jquery.dataTables.min.js" %}></script>
- <script src={% static "bower_components/datatables-plugins/integration/bootstrap/3/dataTables.bootstrap.min.js" %}></script>
+ <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>
@@ -63,10 +64,6 @@
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);
-
- var chart_id = "{{ resource.id }}_jenkins_utilization";
- var utilization_url = "{% url 'dashboard:jenkins_utilization' resource_id=resource.id weeks=1 %}";
- loadChartData(chart_id, utilization_url);
{% endfor %}
});
</script>
diff --git a/dashboard/src/templates/dashboard/resource_detail.html b/dashboard/src/templates/dashboard/resource_detail.html
index 740dd25..0a443d9 100644
--- a/dashboard/src/templates/dashboard/resource_detail.html
+++ b/dashboard/src/templates/dashboard/resource_detail.html
@@ -1,32 +1,4 @@
-{% load jenkins_filters %}
-
<div class="row">
- <div class="col-lg-3">
- <div class="panel panel-default">
- <div class="panel-heading">
- Jenkins Utilization
- <div class="pull-right">
- <div class="form-group">
- <select onchange="loadChartData('{{ resource.id }}_jenkins_utilization', this.value);">
- <option value="{% url 'dashboard:jenkins_utilization' resource_id=resource.id weeks=1 %}">
- Last Week
- </option>
- <option value="{% url 'dashboard:jenkins_utilization' resource_id=resource.id weeks=4 %}">
- Last Month
- </option>
- </select>
- </div>
- </div>
- </div>
- <div class="panel-body">
- <div class="flot-chart">
- <div class="flot-chart-content"
- id="{{ resource.id }}_jenkins_utilization"></div>
- </div>
- </div>
- </div>
- </div>
-
<div class="col-lg-9">
<div class="panel panel-default">
<div class="panel-heading">
@@ -129,42 +101,16 @@
{{ resource.owner.email }}
</p>
<p>
- <a href="{% url 'booking:create' resource_id=resource.id %}" class="btn
- btn-primary">
+ <a href="{% url 'booking:create' resource_id=resource.id %}" class="btn btn-primary">
Booking
</a>
- <a href="{{ resource.url }}" class="btn
- btn-primary">
+ <a href="{{ resource.url }}" class="btn btn-primary">
OPNFV Wiki
</a>
</p>
</div>
</div>
</div>
- <div class="col-lg-3">
- <div class="panel panel-default">
- <div class="panel-heading">
- Jenkins Status
- </div>
- <div class="panel-body">
- <p>
- <b>Slave Name: </b>
- <a target='_blank'
- href={{ resource.slave.url }}>{{ resource.slave.name }}</a>
- </p>
- <p>
- <b>Status: </b>
- {{ resource.slave.status }}
- </p>
- <p>
- <b>Last Job: </b>
- <a href="{{ resource.slave.last_job_url }}">
- {{ resource.slave.last_job_name }}
- </a>
- </p>
- </div>
- </div>
- </div>
<div class="col-lg-6">
<div class="panel panel-default">
<div class="panel-heading">
diff --git a/dashboard/src/templates/dashboard/searchable_select_multiple.html b/dashboard/src/templates/dashboard/searchable_select_multiple.html
new file mode 100644
index 0000000..8bcf890
--- /dev/null
+++ b/dashboard/src/templates/dashboard/searchable_select_multiple.html
@@ -0,0 +1,209 @@
+<script src="https://code.jquery.com/jquery-2.2.4.min.js"></script>
+<script src="/static/js/dashboard.js"></script>
+
+<div id="search_select_outer" class="autocomplete">
+ <div id="warning_pane" style="background: #FFFFFF; color: #CC0000;">
+ {% if incompatible == "true" %}
+ <h3>Warning: Incompatible Configuration</h3>
+ <p>Please make a different selection, as the current config conflicts with the selected pod</p>
+ {% endif %}
+ </div>
+ <div id="added_counter">
+ <p id="added_number">0</p>
+ <p id="addable_limit">/ {% if selectable_limit > -1 %} {{ selectable_limit }} {% else %} &infin; {% endif %}added</p>
+ </div>
+
+ <div id="added_list">
+
+ </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>
+
+ <input type="hidden" id="selector" name="{{ name }}" class="form-control" style="display: none;"
+ {% if disabled %} disabled {% endif %}
+ >
+ </input>
+
+ <div id="scroll_restrictor">
+ <ul id="drop_results"></ul>
+ </div>
+ <style>
+ #scroll_restrictor {
+ flex: 1;
+ position: relative;
+ overflow-y: auto;
+ padding-bottom: 10px;
+ }
+
+ #added_list {
+ margin-bottom: 5px;
+ }
+
+ .autocomplete {
+ display: flex;
+ flex: 1;
+ flex-direction: column;
+ }
+ #user_field {
+ font-size: 14pt;
+ padding: 5px;
+ height: 40px;
+ border: 1px solid #ccc;
+ border-radius: 5px;
+
+ }
+
+ #drop_results{
+ list-style-type: none;
+ padding: 0;
+ margin: 0;
+ min-height: 0;
+ border: solid 1px #ddd;
+ border-top: none;
+ border-bottom: none;
+ visibility: inherit;
+ flex: 1;
+
+ position: absolute;
+ width: 100%;
+
+ }
+
+ #drop_results li a{
+ font-size: 14pt;
+ background-color: #f6f6f6;
+ padding: 7px;
+ text-decoration: none;
+ display: block;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ }
+
+ #drop_results li a {
+ border-bottom: 1px solid #ddd;
+ }
+
+ .list_entry {
+ border: 1px solid #ccc;
+ border-radius: 5px;
+ margin-top: 5px;
+ vertical-align: middle;
+ line-height: 40px;
+ height: 40px;
+ padding-left: 12px;
+ width: 100%;
+ display: flex;
+ }
+
+ #drop_results li a:hover{
+ background-color: #ffffff;
+ }
+
+ .added_entry_text {
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ display: inline;
+ width: 100%;
+ }
+
+ .btn-remove {
+ float: right;
+ height: 30px;
+ margin: 4px;
+ padding: 1px;
+ max-width: 20%;
+ width: 15%;
+ min-width: 70px;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ }
+
+ .entry_tooltip {
+ display: none;
+ }
+
+ #drop_results li a:hover .entry_tooltip {
+ position: absolute;
+ background: #444;
+ color: #ddd;
+ text-align: center;
+ font-size: 12pt;
+ border-radius: 3px;
+
+ }
+
+ #drop_results {
+ max-width: 100%;
+ display: inline-block;
+ list-style-type: none;
+ overflow: hidden;
+ white-space: nowrap;
+ text-overflow: ellipsis;
+ }
+
+ #drop_results li {
+ overflow: hidden;
+ text-overflow: ellipsis;
+ }
+
+ #added_counter {
+ text-align: center;
+ }
+
+ #added_number, #addable_limit {
+ display: inline;
+ }
+ </style>
+</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/dashboard/src/templates/dashboard/table.html b/dashboard/src/templates/dashboard/table.html
index d59f0e3..0a37ded 100644
--- a/dashboard/src/templates/dashboard/table.html
+++ b/dashboard/src/templates/dashboard/table.html
@@ -2,12 +2,14 @@
{% load staticfiles %}
{% block extrahead %}
+ {{ block.super }}
<!-- DataTables CSS -->
- <link href="{% static "bower_components/datatables-plugins/integration/bootstrap/3/dataTables.bootstrap.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-responsive/css/dataTables.responsive.css" %}" rel="stylesheet">
+ <link href="{% static "bower_components/datatables.net-responsive-bs4/css/responsive.bootstrap4.min.css" %}"
+ rel="stylesheet">
{% endblock extrahead %}
{% block content %}
@@ -33,8 +35,8 @@
{% block extrajs %}
<!-- DataTables JavaScript -->
- <script src={% static "bower_components/datatables/media/js/jquery.dataTables.min.js" %}></script>
- <script src={% static "bower_components/datatables-plugins/integration/bootstrap/3/dataTables.bootstrap.min.js" %}></script>
+ <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>
<script src={% static "js/dataTables-sort.js" %}></script>
diff --git a/dashboard/src/templates/layout.html b/dashboard/src/templates/layout.html
index 9578e15..d37d4f5 100644
--- a/dashboard/src/templates/layout.html
+++ b/dashboard/src/templates/layout.html
@@ -19,17 +19,14 @@
<!-- MetisMenu CSS -->
<link href="{% static "bower_components/metisMenu/dist/metisMenu.min.css" %}" rel="stylesheet">
- <!-- Custom CSS -->
- <link href="{% static "bower_components/startbootstrap-sb-admin-2-blackrockdigital/dist/css/sb-admin-2.min.css" %}"
- rel="stylesheet">
- <link href="{% static "css/theme.css" %}" rel="stylesheet">
-
<!-- Custom Fonts -->
- <link href="{% static "bower_components/font-awesome/css/font-awesome.min.css" %}"
+ <link href="{% static "bower_components/Font-Awesome/css/all.min.css" %}"
rel="stylesheet" type="text/css">
<!-- Favicon -->
<link rel="shortcut icon" href="{% static 'favicon.ico' %}">
+ <script src="https://code.jquery.com/jquery-2.2.4.min.js"
+ integrity="sha256-BbhdlvQf/xTY9gja0Dq3HiwQF8LaCRTXxZKRutelT44=" crossorigin="anonymous"></script>
{% block extrahead %}
{% endblock extrahead %}
@@ -50,23 +47,15 @@
{% block basecontent %}
{% endblock basecontent %}
-
-<script src="https://code.jquery.com/jquery-2.2.4.min.js"
- integrity="sha256-BbhdlvQf/xTY9gja0Dq3HiwQF8LaCRTXxZKRutelT44=" crossorigin="anonymous"></script>
{#<!-- jQuery -->#}
{#<script src="{% static "bower_components/jquery/dist/jquery.min.js" %}"></script>#}
{#<script src="{% static "bower_components/jquery-migrate/jquery-migrate.min.js" %}"></script>#}
-
+<!-- Popper.js -->
+<script src="{% static "bower_components/popper.js/dist/umd/popper.min.js" %}"></script>
{#<script src="https://code.jquery.com/jquery-2.2.0.min.js"></script>#}
<!-- Bootstrap Core JavaScript -->
<script src="{% static "bower_components/bootstrap/dist/js/bootstrap.min.js" %}"></script>
-<!-- Metis Menu Plugin JavaScript -->
-<script src="{% static "bower_components/metisMenu/dist/metisMenu.min.js" %}"></script>
-
-<!-- Custom Theme JavaScript -->
-<script src="{% static "bower_components/startbootstrap-sb-admin-2-blackrockdigital/dist/js/sb-admin-2.min.js" %}"></script>
-
{% block extrajs %}
{% endblock extrajs %}
</body>
diff --git a/dashboard/src/templates/notifier/email_ended.txt b/dashboard/src/templates/notifier/email_ended.txt
new file mode 100644
index 0000000..7467a0e
--- /dev/null
+++ b/dashboard/src/templates/notifier/email_ended.txt
@@ -0,0 +1,21 @@
+{{user_name|default:"Developer"}},
+
+The booking you requested of the OPNFV Lab as a Service has ended.
+
+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 OPNFV platform!
+
+ - The Lab-as-a-Service team
diff --git a/dashboard/src/templates/notifier/email_fulfilled.txt b/dashboard/src/templates/notifier/email_fulfilled.txt
new file mode 100644
index 0000000..65593db
--- /dev/null
+++ b/dashboard/src/templates/notifier/email_fulfilled.txt
@@ -0,0 +1,17 @@
+{{user_name|default:"Developer"}},
+
+The booking you requested of the OPNFV Lab as a Service has finished deploying and is ready for you to use.
+
+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 OPNFV platform!
+
+ - The Lab-as-a-Service team
diff --git a/dashboard/src/templates/notifier/end_booking.html b/dashboard/src/templates/notifier/end_booking.html
new file mode 100644
index 0000000..a2981c1
--- /dev/null
+++ b/dashboard/src/templates/notifier/end_booking.html
@@ -0,0 +1,36 @@
+<html>
+ <body>
+ <div id="message_content_wrapper">
+ {% if owner %}
+ <h3>Your booking has expired</h3>
+ <p>Your booking has ended and the machines have been cleaned up.</p>
+ <p>Thank you for working on OPNFV, and feel free to book more machines if you need them.</p>
+ {% else %}
+ <h3>A booking that you collaborated on has expired</h3>
+ <p>The booking owned by {{booking.owner.username}} that you worked on has ended</p>
+ <p>Thank you for contributing to OPNFV!</p>
+ {% endif %}
+ <p>Booking information:</p>
+ <ul>
+ <li>owner: {{booking.owner.username}}</li>
+ <li>id: {{booking.id}}</li>
+ <li>lab: {{booking.resource.template.lab.lab_user.username}}</li>
+ <li>resource: {{booking.resource.template.name}}</li>
+ <li>start: {{booking.start}}</li>
+ <li>end: {{booking.end}}</li>
+ <li>purpose: {{booking.purpose}}</li>
+ <li>collaborators:
+ <ul>
+ {% for user in booking.collaborators.all %}
+ <li>user.username</li>
+ {% empty %}
+ <li>No collaborators</li>
+ {% endfor %}
+ </ul>
+ </li>
+ </ul>
+
+ <p>You can find more detailed information <a href=/booking/detail/{{booking.id}}/>Here</a></p>
+ </div>
+ </body>
+</html>
diff --git a/dashboard/src/templates/notifier/inbox.html b/dashboard/src/templates/notifier/inbox.html
new file mode 100644
index 0000000..72207ed
--- /dev/null
+++ b/dashboard/src/templates/notifier/inbox.html
@@ -0,0 +1,129 @@
+{% extends "base.html" %}
+
+
+{% load staticfiles %}
+
+{% block content %}
+
+<style media="screen">
+ .inbox-panel {
+ display: grid;
+ grid-template-columns: 30% 5% 65%;
+ }
+
+ .section-panel {
+ padding: 10px;
+ }
+
+ .iframe-panel {
+ padding: 0px;
+ margin-top: 0px;
+ }
+
+ .card-container {
+ border: 1px solid #cccccc;
+ border-bottom: 0px;
+ }
+
+ .card {
+ height: 50px;
+ position: relative;
+ border-bottom: 1px solid #cccccc;
+ padding: 10px;
+ width: 100%;
+ background-color: #ffffff;
+ z-index: 5;
+ }
+
+ .selected-card {
+ background-color: #f3f3f3;
+ }
+
+ .card:hover {
+ box-shadow: 0px 0 5px 2px #cccccc;
+ z-index: 6;
+ }
+
+ .half_width {
+ width: 50%;
+ }
+
+ #page-wrapper {
+ padding: 0px;
+ }
+
+ .read_notification {
+ background-color: #efefef;
+ }
+
+ .scrollable {
+ overflow-y: auto;
+ }
+</style>
+<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">
+ <div class="list-group rounded-0 rounded-left scrollable 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 scrollable 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 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) {
+ iframe = document.getElementById("inbox-iframe");
+ iframe.src = "notification/" + msg_id;
+ }
+
+ function setactive(obj) {
+ $(".notification").removeClass("active");
+ $(obj).addClass("active");
+ }
+
+ $(document).ready(function(){
+ // For all / unread / read
+ $("#filterGroup button").click(function(){
+ let read = $(this).attr("data-read");
+ $(this).siblings().removeClass("active");
+ $(".notifications").addClass("d-none");
+ $(this).addClass("active");
+ if (read === "-1") {
+ return $(".notifications").removeClass("d-none");
+ }
+ $(`.notifications[data-read="${read}"]`).removeClass("d-none");
+ });
+ });
+</script>
+{% endblock %} \ No newline at end of file
diff --git a/dashboard/src/templates/notifier/new_booking.html b/dashboard/src/templates/notifier/new_booking.html
new file mode 100644
index 0000000..d23b12e
--- /dev/null
+++ b/dashboard/src/templates/notifier/new_booking.html
@@ -0,0 +1,34 @@
+<html>
+ <body>
+ <div id="message_content_wrapper">
+ {% if owner %}
+ <h3>You have created a new booking</h3>
+ <p>We have recieved your booking request and will start working on it right away.</p>
+ {% else %}
+ <h3>You have been added as a collaborator to a booking</h3>
+ <p>{{booking.owner.username}} has given you access to thier booking.</p>
+ {% endif %}
+ <p>Booking information:</p>
+ <ul>
+ <li>owner: {{booking.owner.username}}</li>
+ <li>id: {{booking.id}}</li>
+ <li>lab: {{booking.resource.template.lab.lab_user.username}}</li>
+ <li>resource: {{booking.resource.template.name}}</li>
+ <li>start: {{booking.start}}</li>
+ <li>end: {{booking.end}}</li>
+ <li>purpose: {{booking.purpose}}</li>
+ <li>collaborators:
+ <ul>
+ {% for user in booking.collaborators.all %}
+ <li>user.username</li>
+ {% empty %}
+ <li>No collaborators</li>
+ {% endfor %}
+ </ul>
+ </li>
+ </ul>
+
+ <p>You can find more detailed information <a href=/booking/detail/{{booking.id}}/>Here</a></p>
+ </div>
+ </body>
+</html>
diff --git a/dashboard/src/templates/notifier/notification.html b/dashboard/src/templates/notifier/notification.html
new file mode 100644
index 0000000..0eafa60
--- /dev/null
+++ b/dashboard/src/templates/notifier/notification.html
@@ -0,0 +1,83 @@
+{% 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>
+ <h3 class="msg_header">{{notification.title}}
+ <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>
+ </h3>
+</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>
+
+<style media="screen">
+ .card-container {
+ border: 1px solid #ffffff;
+ margin-top: 11px;
+ }
+ .card {
+ height: 50px;
+ margin: 0px;
+ position: relative;
+ border-bottom: 1px solid #cccccc;
+ padding: 0px;
+ width: 100%;
+ background-color: #ffffff;
+ z-index: 5;
+ }
+ .sender {
+ color: #636363;
+ }
+ .content-divider {
+ border-bottom: 1px solid #cccccc;
+ padding-bottom: 15px;
+ clear: right;
+ }
+ .inbox-btn{
+ display: inline;
+ margin: 3px;
+ }
+ .btn_group{
+ float: right;
+ }
+</style>
+{% endblock %}
diff --git a/dashboard/src/templates/resource/hostprofile_detail.html b/dashboard/src/templates/resource/hostprofile_detail.html
new file mode 100644
index 0000000..dc20600
--- /dev/null
+++ b/dashboard/src/templates/resource/hostprofile_detail.html
@@ -0,0 +1,102 @@
+{% 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 style="display: inline;">Available at</h4>
+ <button data-toggle="collapse" data-target="#avilableAt" class="btn ml-auto btn-outline-secondary">Expand</button>
+ </div>
+ <div class="card-body collapse show" id="avilableAt">
+ <ul class="list-group">
+ {% 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 style="display: inline;">RAM</h4>
+ <button data-toggle="collapse" data-target="#ramPanel" class="btn ml-auto btn-outline-secondary">Expand</button>
+ </div>
+ <div class="card-body collapse show" id="ramPanel">
+ {{hostprofile.ramprofile.first.amount}}G,
+ {{hostprofile.ramprofile.first.channels}} channels
+ </div>
+ </div>
+ <div class="card mb-4">
+ <div class="card-header d-flex">
+ <h4 style="display: inline;">CPU</h4>
+ <button data-toggle="collapse" data-target="#cpuPanel" class="btn ml-auto btn-outline-secondary">Expand</button>
+ </div>
+ <div class="card-body 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 style="display: inline;">Disk</h4>
+ <button data-toggle="collapse" data-target="#diskPanel" class="btn ml-auto btn-outline-secondary">Expand</button>
+ </div>
+ <div class="card-body 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 style="display: inline;">Interfaces</h4>
+ <button data-toggle="collapse" data-target="#interfacePanel" class="btn ml-auto btn-outline-secondary">Expand</button>
+ </div>
+ <div class="card-body 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/dashboard/src/templates/resource/hosts.html b/dashboard/src/templates/resource/hosts.html
new file mode 100644
index 0000000..69b7231
--- /dev/null
+++ b/dashboard/src/templates/resource/hosts.html
@@ -0,0 +1,42 @@
+{% 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 }}
+ </td>
+ <td>
+ {{ host.working }}
+ </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/dashboard/src/templates/resource/mxClient.min.js b/dashboard/src/templates/resource/mxClient.min.js
new file mode 100644
index 0000000..150a0ac
--- /dev/null
+++ b/dashboard/src/templates/resource/mxClient.min.js
@@ -0,0 +1,1808 @@
+var mxClient={VERSION:"3.9.6",IS_IE:0<=navigator.userAgent.indexOf("MSIE"),IS_IE6:0<=navigator.userAgent.indexOf("MSIE 6"),IS_IE11:!!navigator.userAgent.match(/Trident\/7\./),IS_EDGE:!!navigator.userAgent.match(/Edge\//),IS_QUIRKS:0<=navigator.userAgent.indexOf("MSIE")&&(null==document.documentMode||5==document.documentMode),IS_EM:"spellcheck"in document.createElement("textarea")&&8==document.documentMode,VML_PREFIX:"v",OFFICE_PREFIX:"o",IS_NS:0<=navigator.userAgent.indexOf("Mozilla/")&&0>navigator.userAgent.indexOf("MSIE")&&
+0>navigator.userAgent.indexOf("Edge/"),IS_OP:0<=navigator.userAgent.indexOf("Opera/")||0<=navigator.userAgent.indexOf("OPR/"),IS_OT:0<=navigator.userAgent.indexOf("Presto/")&&0>navigator.userAgent.indexOf("Presto/2.4.")&&0>navigator.userAgent.indexOf("Presto/2.3.")&&0>navigator.userAgent.indexOf("Presto/2.2.")&&0>navigator.userAgent.indexOf("Presto/2.1.")&&0>navigator.userAgent.indexOf("Presto/2.0.")&&0>navigator.userAgent.indexOf("Presto/1."),IS_SF:0<=navigator.userAgent.indexOf("AppleWebKit/")&&
+0>navigator.userAgent.indexOf("Chrome/")&&0>navigator.userAgent.indexOf("Edge/"),IS_IOS:navigator.userAgent.match(/(iPad|iPhone|iPod)/g)?!0:!1,IS_GC:0<=navigator.userAgent.indexOf("Chrome/")&&0>navigator.userAgent.indexOf("Edge/"),IS_CHROMEAPP:null!=window.chrome&&null!=chrome.app&&null!=chrome.app.runtime,IS_FF:0<=navigator.userAgent.indexOf("Firefox/"),IS_MT:0<=navigator.userAgent.indexOf("Firefox/")&&0>navigator.userAgent.indexOf("Firefox/1.")&&0>navigator.userAgent.indexOf("Firefox/2.")||0<=navigator.userAgent.indexOf("Iceweasel/")&&
+0>navigator.userAgent.indexOf("Iceweasel/1.")&&0>navigator.userAgent.indexOf("Iceweasel/2.")||0<=navigator.userAgent.indexOf("SeaMonkey/")&&0>navigator.userAgent.indexOf("SeaMonkey/1.")||0<=navigator.userAgent.indexOf("Iceape/")&&0>navigator.userAgent.indexOf("Iceape/1."),IS_SVG:0<=navigator.userAgent.indexOf("Firefox/")||0<=navigator.userAgent.indexOf("Iceweasel/")||0<=navigator.userAgent.indexOf("Seamonkey/")||0<=navigator.userAgent.indexOf("Iceape/")||0<=navigator.userAgent.indexOf("Galeon/")||
+0<=navigator.userAgent.indexOf("Epiphany/")||0<=navigator.userAgent.indexOf("AppleWebKit/")||0<=navigator.userAgent.indexOf("Gecko/")||0<=navigator.userAgent.indexOf("Opera/")||null!=document.documentMode&&9<=document.documentMode,NO_FO:!document.createElementNS||"[object SVGForeignObjectElement]"!=document.createElementNS("http://www.w3.org/2000/svg","foreignObject")||0<=navigator.userAgent.indexOf("Opera/"),IS_VML:"MICROSOFT INTERNET EXPLORER"==navigator.appName.toUpperCase(),IS_WIN:0<navigator.appVersion.indexOf("Win"),
+IS_MAC:0<navigator.appVersion.indexOf("Mac"),IS_TOUCH:"ontouchstart"in document.documentElement,IS_POINTER:null!=window.PointerEvent&&!(0<navigator.appVersion.indexOf("Mac")),IS_LOCAL:0>document.location.href.indexOf("http://")&&0>document.location.href.indexOf("https://"),defaultBundles:[],isBrowserSupported:function(){return mxClient.IS_VML||mxClient.IS_SVG},link:function(a,b,c){c=c||document;if(mxClient.IS_IE6)c.write('<link rel="'+a+'" href="'+b+'" charset="UTF-8" type="text/css"/>');else{var d=
+c.createElement("link");d.setAttribute("rel",a);d.setAttribute("href",b);d.setAttribute("charset","UTF-8");d.setAttribute("type","text/css");c.getElementsByTagName("head")[0].appendChild(d)}},loadResources:function(a,b){function c(){0==--d&&a()}for(var d=mxClient.defaultBundles.length,e=0;e<mxClient.defaultBundles.length;e++)mxResources.add(mxClient.defaultBundles[e],b,c)},include:function(a){document.write('<script src="'+a+'">\x3c/script>')},dispose:function(){for(var a=0;a<mxEvent.objects.length;a++)null!=
+mxEvent.objects[a].mxListenerList&&mxEvent.removeAllListeners(mxEvent.objects[a])}};"undefined"==typeof mxLoadResources&&(mxLoadResources=!0);"undefined"==typeof mxForceIncludes&&(mxForceIncludes=!1);"undefined"==typeof mxResourceExtension&&(mxResourceExtension=".txt");"undefined"==typeof mxLoadStylesheets&&(mxLoadStylesheets=!0);
+"undefined"!=typeof mxBasePath&&0<mxBasePath.length?("/"==mxBasePath.substring(mxBasePath.length-1)&&(mxBasePath=mxBasePath.substring(0,mxBasePath.length-1)),mxClient.basePath=mxBasePath):mxClient.basePath=".";"undefined"!=typeof mxImageBasePath&&0<mxImageBasePath.length?("/"==mxImageBasePath.substring(mxImageBasePath.length-1)&&(mxImageBasePath=mxImageBasePath.substring(0,mxImageBasePath.length-1)),mxClient.imageBasePath=mxImageBasePath):mxClient.imageBasePath=mxClient.basePath+"/images";
+mxClient.language="undefined"!=typeof mxLanguage&&null!=mxLanguage?mxLanguage:mxClient.IS_IE?navigator.userLanguage:navigator.language;mxClient.defaultLanguage="undefined"!=typeof mxDefaultLanguage&&null!=mxDefaultLanguage?mxDefaultLanguage:"en";mxLoadStylesheets&&mxClient.link("stylesheet",mxClient.basePath+"/css/common.css");"undefined"!=typeof mxLanguages&&null!=mxLanguages&&(mxClient.languages=mxLanguages);
+mxClient.IS_VML&&(mxClient.IS_SVG?mxClient.IS_VML=!1:(8==document.documentMode?(document.namespaces.add(mxClient.VML_PREFIX,"urn:schemas-microsoft-com:vml","#default#VML"),document.namespaces.add(mxClient.OFFICE_PREFIX,"urn:schemas-microsoft-com:office:office","#default#VML")):(document.namespaces.add(mxClient.VML_PREFIX,"urn:schemas-microsoft-com:vml"),document.namespaces.add(mxClient.OFFICE_PREFIX,"urn:schemas-microsoft-com:office:office")),mxClient.IS_QUIRKS&&30<=document.styleSheets.length?function(){var a=
+document.createElement("style");a.type="text/css";a.styleSheet.cssText=mxClient.VML_PREFIX+"\\:*{behavior:url(#default#VML)}"+mxClient.OFFICE_PREFIX+"\\:*{behavior:url(#default#VML)}";document.getElementsByTagName("head")[0].appendChild(a)}():document.createStyleSheet().cssText=mxClient.VML_PREFIX+"\\:*{behavior:url(#default#VML)}"+mxClient.OFFICE_PREFIX+"\\:*{behavior:url(#default#VML)}",mxLoadStylesheets&&mxClient.link("stylesheet",mxClient.basePath+"/css/explorer.css"),window.attachEvent("onunload",
+mxClient.dispose)));
+var mxLog={consoleName:"Console",TRACE:!1,DEBUG:!0,WARN:!0,buffer:"",init:function(){if(null==mxLog.window&&null!=document.body){var a=mxLog.consoleName+" - mxGraph "+mxClient.VERSION,b=document.createElement("table");b.setAttribute("width","100%");b.setAttribute("height","100%");var c=document.createElement("tbody"),d=document.createElement("tr"),e=document.createElement("td");e.style.verticalAlign="top";mxLog.textarea=document.createElement("textarea");mxLog.textarea.setAttribute("wrap","off");
+mxLog.textarea.setAttribute("readOnly","true");mxLog.textarea.style.height="100%";mxLog.textarea.style.resize="none";mxLog.textarea.value=mxLog.buffer;mxLog.textarea.style.width=mxClient.IS_NS&&"BackCompat"!=document.compatMode?"99%":"100%";e.appendChild(mxLog.textarea);d.appendChild(e);c.appendChild(d);d=document.createElement("tr");mxLog.td=document.createElement("td");mxLog.td.style.verticalAlign="top";mxLog.td.setAttribute("height","30px");d.appendChild(mxLog.td);c.appendChild(d);b.appendChild(c);
+mxLog.addButton("Info",function(a){mxLog.info()});mxLog.addButton("DOM",function(a){a=mxUtils.getInnerHtml(document.body);mxLog.debug(a)});mxLog.addButton("Trace",function(a){mxLog.TRACE=!mxLog.TRACE;mxLog.TRACE?mxLog.debug("Tracing enabled"):mxLog.debug("Tracing disabled")});mxLog.addButton("Copy",function(a){try{mxUtils.copy(mxLog.textarea.value)}catch(k){mxUtils.alert(k)}});mxLog.addButton("Show",function(a){try{mxUtils.popup(mxLog.textarea.value)}catch(k){mxUtils.alert(k)}});mxLog.addButton("Clear",
+function(a){mxLog.textarea.value=""});d=c=0;"number"===typeof window.innerWidth?(c=window.innerHeight,d=window.innerWidth):(c=document.documentElement.clientHeight||document.body.clientHeight,d=document.body.clientWidth);mxLog.window=new mxWindow(a,b,Math.max(0,d-320),Math.max(0,c-210),300,160);mxLog.window.setMaximizable(!0);mxLog.window.setScrollable(!1);mxLog.window.setResizable(!0);mxLog.window.setClosable(!0);mxLog.window.destroyOnClose=!1;if((mxClient.IS_NS||mxClient.IS_IE)&&!mxClient.IS_GC&&
+!mxClient.IS_SF&&"BackCompat"!=document.compatMode||11==document.documentMode){var f=mxLog.window.getElement(),a=function(a,b){mxLog.textarea.style.height=Math.max(0,f.offsetHeight-70)+"px"};mxLog.window.addListener(mxEvent.RESIZE_END,a);mxLog.window.addListener(mxEvent.MAXIMIZE,a);mxLog.window.addListener(mxEvent.NORMALIZE,a);mxLog.textarea.style.height="92px"}}},info:function(){mxLog.writeln(mxUtils.toString(navigator))},addButton:function(a,b){var c=document.createElement("button");mxUtils.write(c,
+a);mxEvent.addListener(c,"click",b);mxLog.td.appendChild(c)},isVisible:function(){return null!=mxLog.window?mxLog.window.isVisible():!1},show:function(){mxLog.setVisible(!0)},setVisible:function(a){null==mxLog.window&&mxLog.init();null!=mxLog.window&&mxLog.window.setVisible(a)},enter:function(a){if(mxLog.TRACE)return mxLog.writeln("Entering "+a),(new Date).getTime()},leave:function(a,b){if(mxLog.TRACE){var c=0!=b?" ("+((new Date).getTime()-b)+" ms)":"";mxLog.writeln("Leaving "+a+c)}},debug:function(){mxLog.DEBUG&&
+mxLog.writeln.apply(this,arguments)},warn:function(){mxLog.WARN&&mxLog.writeln.apply(this,arguments)},write:function(){for(var a="",b=0;b<arguments.length;b++)a+=arguments[b],b<arguments.length-1&&(a+=" ");null!=mxLog.textarea?(mxLog.textarea.value+=a,0<=navigator.userAgent.indexOf("Presto/2.5")&&(mxLog.textarea.style.visibility="hidden",mxLog.textarea.style.visibility="visible"),mxLog.textarea.scrollTop=mxLog.textarea.scrollHeight):mxLog.buffer+=a},writeln:function(){for(var a="",b=0;b<arguments.length;b++)a+=
+arguments[b],b<arguments.length-1&&(a+=" ");mxLog.write(a+"\n")}},mxObjectIdentity={FIELD_NAME:"mxObjectId",counter:0,get:function(a){if(null!=a){if(null==a[mxObjectIdentity.FIELD_NAME])if("object"===typeof a){var b=mxUtils.getFunctionName(a.constructor);a[mxObjectIdentity.FIELD_NAME]=b+"#"+mxObjectIdentity.counter++}else"function"===typeof a&&(a[mxObjectIdentity.FIELD_NAME]="Function#"+mxObjectIdentity.counter++);return a[mxObjectIdentity.FIELD_NAME]}return null},clear:function(a){"object"!==typeof a&&
+"function"!==typeof a||delete a[mxObjectIdentity.FIELD_NAME]}};function mxDictionary(){this.clear()}mxDictionary.prototype.map=null;mxDictionary.prototype.clear=function(){this.map={}};mxDictionary.prototype.get=function(a){a=mxObjectIdentity.get(a);return this.map[a]};mxDictionary.prototype.put=function(a,b){var c=mxObjectIdentity.get(a),d=this.map[c];this.map[c]=b;return d};mxDictionary.prototype.remove=function(a){a=mxObjectIdentity.get(a);var b=this.map[a];delete this.map[a];return b};
+mxDictionary.prototype.getKeys=function(){var a=[],b;for(b in this.map)a.push(b);return a};mxDictionary.prototype.getValues=function(){var a=[],b;for(b in this.map)a.push(this.map[b]);return a};mxDictionary.prototype.visit=function(a){for(var b in this.map)a(b,this.map[b])};
+var mxResources={resources:{},extension:mxResourceExtension,resourcesEncoded:!1,loadDefaultBundle:!0,loadSpecialBundle:!0,isLanguageSupported:function(a){return null!=mxClient.languages?0<=mxUtils.indexOf(mxClient.languages,a):!0},getDefaultBundle:function(a,b){return mxResources.loadDefaultBundle||!mxResources.isLanguageSupported(b)?a+mxResources.extension:null},getSpecialBundle:function(a,b){if(null==mxClient.languages||!this.isLanguageSupported(b)){var c=b.indexOf("-");0<c&&(b=b.substring(0,c))}return mxResources.loadSpecialBundle&&
+mxResources.isLanguageSupported(b)&&b!=mxClient.defaultLanguage?a+"_"+b+mxResources.extension:null},add:function(a,b,c){b=null!=b?b:null!=mxClient.language?mxClient.language.toLowerCase():mxConstants.NONE;if(b!=mxConstants.NONE){var d=mxResources.getDefaultBundle(a,b),e=mxResources.getSpecialBundle(a,b),f=function(){if(null!=e)if(c)mxUtils.get(e,function(a){mxResources.parse(a.getText());c()},function(){c()});else try{var a=mxUtils.load(e);a.isReady()&&mxResources.parse(a.getText())}catch(l){}else null!=
+c&&c()};if(null!=d)if(c)mxUtils.get(d,function(a){mxResources.parse(a.getText());f()},function(){f()});else try{var g=mxUtils.load(d);g.isReady()&&mxResources.parse(g.getText());f()}catch(k){}else f()}},parse:function(a){if(null!=a){a=a.split("\n");for(var b=0;b<a.length;b++)if("#"!=a[b].charAt(0)){var c=a[b].indexOf("=");if(0<c){var d=a[b].substring(0,c),e=a[b].length;13==a[b].charCodeAt(e-1)&&e--;c=a[b].substring(c+1,e);this.resourcesEncoded?(c=c.replace(/\\(?=u[a-fA-F\d]{4})/g,"%"),mxResources.resources[d]=
+unescape(c)):mxResources.resources[d]=c}}}},get:function(a,b,c){a=mxResources.resources[a];null==a&&(a=c);null!=a&&null!=b&&(a=mxResources.replacePlaceholders(a,b));return a},replacePlaceholders:function(a,b){for(var c=[],d=null,e=0;e<a.length;e++){var f=a.charAt(e);"{"==f?d="":null!=d&&"}"==f?(d=parseInt(d)-1,0<=d&&d<b.length&&c.push(b[d]),d=null):null!=d?d+=f:c.push(f)}return c.join("")},loadResources:function(a){mxResources.add(mxClient.basePath+"/resources/editor",null,function(){mxResources.add(mxClient.basePath+
+"/resources/graph",null,a)})}};function mxPoint(a,b){this.x=null!=a?a:0;this.y=null!=b?b:0}mxPoint.prototype.x=null;mxPoint.prototype.y=null;mxPoint.prototype.equals=function(a){return null!=a&&a.x==this.x&&a.y==this.y};mxPoint.prototype.clone=function(){return mxUtils.clone(this)};function mxRectangle(a,b,c,d){mxPoint.call(this,a,b);this.width=null!=c?c:0;this.height=null!=d?d:0}mxRectangle.prototype=new mxPoint;mxRectangle.prototype.constructor=mxRectangle;mxRectangle.prototype.width=null;
+mxRectangle.prototype.height=null;mxRectangle.prototype.setRect=function(a,b,c,d){this.x=a;this.y=b;this.width=c;this.height=d};mxRectangle.prototype.getCenterX=function(){return this.x+this.width/2};mxRectangle.prototype.getCenterY=function(){return this.y+this.height/2};
+mxRectangle.prototype.add=function(a){if(null!=a){var b=Math.min(this.x,a.x),c=Math.min(this.y,a.y),d=Math.max(this.x+this.width,a.x+a.width);a=Math.max(this.y+this.height,a.y+a.height);this.x=b;this.y=c;this.width=d-b;this.height=a-c}};mxRectangle.prototype.intersect=function(a){if(null!=a){var b=this.x+this.width,c=a.x+a.width,d=this.y+this.height,e=a.y+a.height;this.x=Math.max(this.x,a.x);this.y=Math.max(this.y,a.y);this.width=Math.min(b,c)-this.x;this.height=Math.min(d,e)-this.y}};
+mxRectangle.prototype.grow=function(a){this.x-=a;this.y-=a;this.width+=2*a;this.height+=2*a};mxRectangle.prototype.getPoint=function(){return new mxPoint(this.x,this.y)};mxRectangle.prototype.rotate90=function(){var a=(this.width-this.height)/2;this.x+=a;this.y-=a;a=this.width;this.width=this.height;this.height=a};mxRectangle.prototype.equals=function(a){return null!=a&&a.x==this.x&&a.y==this.y&&a.width==this.width&&a.height==this.height};
+mxRectangle.fromRectangle=function(a){return new mxRectangle(a.x,a.y,a.width,a.height)};
+var mxEffects={animateChanges:function(a,b,c){var d=0,e=function(){for(var g=!1,k=0;k<b.length;k++){var l=b[k];if(l instanceof mxGeometryChange||l instanceof mxTerminalChange||l instanceof mxValueChange||l instanceof mxChildChange||l instanceof mxStyleChange){var m=a.getView().getState(l.cell||l.child,!1);if(null!=m)if(g=!0,l.constructor!=mxGeometryChange||a.model.isEdge(l.cell))mxUtils.setOpacity(m.shape.node,100*d/10);else{var n=a.getView().scale,p=(l.geometry.x-l.previous.x)*n,q=(l.geometry.y-
+l.previous.y)*n,r=(l.geometry.width-l.previous.width)*n,n=(l.geometry.height-l.previous.height)*n;0==d?(m.x-=p,m.y-=q,m.width-=r,m.height-=n):(m.x+=p/10,m.y+=q/10,m.width+=r/10,m.height+=n/10);a.cellRenderer.redraw(m);mxEffects.cascadeOpacity(a,l.cell,100*d/10)}}}10>d&&g?(d++,window.setTimeout(e,f)):null!=c&&c()},f=30;e()},cascadeOpacity:function(a,b,c){for(var d=a.model.getChildCount(b),e=0;e<d;e++){var f=a.model.getChildAt(b,e),g=a.getView().getState(f);null!=g&&(mxUtils.setOpacity(g.shape.node,
+c),mxEffects.cascadeOpacity(a,f,c))}b=a.model.getEdges(b);if(null!=b)for(e=0;e<b.length;e++)d=a.getView().getState(b[e]),null!=d&&mxUtils.setOpacity(d.shape.node,c)},fadeOut:function(a,b,c,d,e,f){d=d||40;e=e||30;var g=b||100;mxUtils.setOpacity(a,g);if(f||null==f){var k=function(){g=Math.max(g-d,0);mxUtils.setOpacity(a,g);0<g?window.setTimeout(k,e):(a.style.visibility="hidden",c&&a.parentNode&&a.parentNode.removeChild(a))};window.setTimeout(k,e)}else a.style.visibility="hidden",c&&a.parentNode&&a.parentNode.removeChild(a)}},
+mxUtils={errorResource:"none"!=mxClient.language?"error":"",closeResource:"none"!=mxClient.language?"close":"",errorImage:mxClient.imageBasePath+"/error.gif",removeCursors:function(a){null!=a.style&&(a.style.cursor="");a=a.childNodes;if(null!=a)for(var b=a.length,c=0;c<b;c+=1)mxUtils.removeCursors(a[c])},getCurrentStyle:function(){return mxClient.IS_IE&&(null==document.documentMode||9>document.documentMode)?function(a){return null!=a?a.currentStyle:null}:function(a){return null!=a?window.getComputedStyle(a,
+""):null}}(),parseCssNumber:function(a){"thin"==a?a="2":"medium"==a?a="4":"thick"==a&&(a="6");a=parseFloat(a);isNaN(a)&&(a=0);return a},setPrefixedStyle:function(){var a=null;mxClient.IS_OT?a="O":mxClient.IS_SF||mxClient.IS_GC?a="Webkit":mxClient.IS_MT?a="Moz":mxClient.IS_IE&&9<=document.documentMode&&10>document.documentMode&&(a="ms");return function(b,c,d){b[c]=d;null!=a&&0<c.length&&(c=a+c.substring(0,1).toUpperCase()+c.substring(1),b[c]=d)}}(),hasScrollbars:function(a){a=mxUtils.getCurrentStyle(a);
+return null!=a&&("scroll"==a.overflow||"auto"==a.overflow)},bind:function(a,b){return function(){return b.apply(a,arguments)}},eval:function(a){var b=null;if(0<=a.indexOf("function"))try{eval("var _mxJavaScriptExpression="+a),b=_mxJavaScriptExpression,_mxJavaScriptExpression=null}catch(c){mxLog.warn(c.message+" while evaluating "+a)}else try{b=eval(a)}catch(c){mxLog.warn(c.message+" while evaluating "+a)}return b},findNode:function(a,b,c){if(a.nodeType==mxConstants.NODETYPE_ELEMENT){var d=a.getAttribute(b);
+if(null!=d&&d==c)return a}for(a=a.firstChild;null!=a;){d=mxUtils.findNode(a,b,c);if(null!=d)return d;a=a.nextSibling}return null},getFunctionName:function(a){var b=null;null!=a&&(null!=a.name?b=a.name:(b=mxUtils.trim(a.toString()),/^function\s/.test(b)&&(b=mxUtils.ltrim(b.substring(9)),a=b.indexOf("("),0<a&&(b=b.substring(0,a)))));return b},indexOf:function(a,b){if(null!=a&&null!=b)for(var c=0;c<a.length;c++)if(a[c]==b)return c;return-1},forEach:function(a,b){if(null!=a&&null!=b)for(var c=0;c<a.length;c++)b(a[c]);
+return a},remove:function(a,b){var c=null;if("object"==typeof b)for(var d=mxUtils.indexOf(b,a);0<=d;)b.splice(d,1),c=a,d=mxUtils.indexOf(b,a);for(var e in b)b[e]==a&&(delete b[e],c=a);return c},isNode:function(a,b,c,d){return null==a||isNaN(a.nodeType)||null!=b&&a.nodeName.toLowerCase()!=b.toLowerCase()?!1:null==c||a.getAttribute(c)==d},isAncestorNode:function(a,b){for(var c=b;null!=c;){if(c==a)return!0;c=c.parentNode}return!1},getChildNodes:function(a,b){b=b||mxConstants.NODETYPE_ELEMENT;for(var c=
+[],d=a.firstChild;null!=d;)d.nodeType==b&&c.push(d),d=d.nextSibling;return c},importNode:function(a,b,c){if(mxClient.IS_IE&&(null==document.documentMode||10>document.documentMode))switch(b.nodeType){case 1:var d=a.createElement(b.nodeName);if(b.attributes&&0<b.attributes.length){for(var e=0;e<b.attributes.length;e++)d.setAttribute(b.attributes[e].nodeName,b.getAttribute(b.attributes[e].nodeName));if(c&&b.childNodes&&0<b.childNodes.length)for(e=0;e<b.childNodes.length;e++)d.appendChild(mxUtils.importNode(a,
+b.childNodes[e],c))}return d;case 3:case 4:case 8:return a.createTextNode(b.value)}else return a.importNode(b,c)},createXmlDocument:function(){var a=null;document.implementation&&document.implementation.createDocument?a=document.implementation.createDocument("","",null):window.ActiveXObject&&(a=new ActiveXObject("Microsoft.XMLDOM"));return a},parseXml:function(){return window.DOMParser?function(a){return(new DOMParser).parseFromString(a,"text/xml")}:function(a){var b=mxUtils.createXmlDocument();b.async=
+!1;b.validateOnParse=!1;b.resolveExternals=!1;b.loadXML(a);return b}}(),clearSelection:function(){return document.selection?function(){document.selection.empty()}:window.getSelection?function(){window.getSelection().empty?window.getSelection().empty():window.getSelection().removeAllRanges&&window.getSelection().removeAllRanges()}:function(){}}(),getPrettyXml:function(a,b,c){var d=[];if(null!=a)if(b=b||" ",c=c||"",a.nodeType==mxConstants.NODETYPE_TEXT)a=mxUtils.trim(mxUtils.getTextContent(a)),0<a.length&&
+d.push(c+mxUtils.htmlEntities(a)+"\n");else{d.push(c+"<"+a.nodeName);var e=a.attributes;if(null!=e)for(var f=0;f<e.length;f++){var g=mxUtils.htmlEntities(e[f].value);d.push(" "+e[f].nodeName+'="'+g+'"')}e=a.firstChild;if(null!=e){for(d.push(">\n");null!=e;)d.push(mxUtils.getPrettyXml(e,b,c+b)),e=e.nextSibling;d.push(c+"</"+a.nodeName+">\n")}else d.push("/>\n")}return d.join("")},removeWhitespace:function(a,b){for(var c=b?a.previousSibling:a.nextSibling;null!=c&&c.nodeType==mxConstants.NODETYPE_TEXT;){var d=
+b?c.previousSibling:c.nextSibling,e=mxUtils.getTextContent(c);0==mxUtils.trim(e).length&&c.parentNode.removeChild(c);c=d}},htmlEntities:function(a,b){a=String(a||"");a=a.replace(/&/g,"&amp;");a=a.replace(/"/g,"&quot;");a=a.replace(/\'/g,"&#39;");a=a.replace(/</g,"&lt;");a=a.replace(/>/g,"&gt;");if(null==b||b)a=a.replace(/\n/g,"&#xa;");return a},isVml:function(a){return null!=a&&"urn:schemas-microsoft-com:vml"==a.tagUrn},getXml:function(a,b){var c="";null!=window.XMLSerializer?c=(new XMLSerializer).serializeToString(a):
+null!=a.xml&&(c=a.xml.replace(/\r\n\t[\t]*/g,"").replace(/>\r\n/g,">").replace(/\r\n/g,"\n"));return c=c.replace(/\n/g,b||"&#xa;")},extractTextWithWhitespace:function(a){function b(a){if(1!=a.length||"BR"!=a[0].nodeName&&"\n"!=a[0].innerHTML)for(var e=0;e<a.length;e++){var g=a[e];"BR"==g.nodeName||"\n"==g.innerHTML||(1==a.length||0==e)&&"DIV"==g.nodeName&&"<br>"==g.innerHTML.toLowerCase()?d.push("\n"):(3===g.nodeType||4===g.nodeType?0<g.nodeValue.length&&d.push(g.nodeValue):8!==g.nodeType&&0<g.childNodes.length&&
+b(g.childNodes),e<a.length-1&&0<=mxUtils.indexOf(c,a[e+1].nodeName)&&d.push("\n"))}}var c="BLOCKQUOTE DIV H1 H2 H3 H4 H5 H6 OL P PRE TABLE UL".split(" "),d=[];b(a);return d.join("")},replaceTrailingNewlines:function(a,b){for(var c="";0<a.length&&"\n"==a.charAt(a.length-1);)a=a.substring(0,a.length-1),c+=b;return a+c},getTextContent:function(a){return mxClient.IS_IE&&void 0!==a.innerText?a.innerText:null!=a?a[void 0===a.textContent?"text":"textContent"]:""},setTextContent:function(a,b){void 0!==a.innerText?
+a.innerText=b:a[void 0===a.textContent?"text":"textContent"]=b},getInnerHtml:function(){return mxClient.IS_IE?function(a){return null!=a?a.innerHTML:""}:function(a){return null!=a?(new XMLSerializer).serializeToString(a):""}}(),getOuterHtml:function(){return mxClient.IS_IE?function(a){if(null!=a){if(null!=a.outerHTML)return a.outerHTML;var b=[];b.push("<"+a.nodeName);var c=a.attributes;if(null!=c)for(var d=0;d<c.length;d++){var e=c[d].value;null!=e&&0<e.length&&(b.push(" "),b.push(c[d].nodeName),
+b.push('="'),b.push(e),b.push('"'))}0==a.innerHTML.length?b.push("/>"):(b.push(">"),b.push(a.innerHTML),b.push("</"+a.nodeName+">"));return b.join("")}return""}:function(a){return null!=a?(new XMLSerializer).serializeToString(a):""}}(),write:function(a,b){var c=a.ownerDocument.createTextNode(b);null!=a&&a.appendChild(c);return c},writeln:function(a,b){var c=a.ownerDocument.createTextNode(b);null!=a&&(a.appendChild(c),a.appendChild(document.createElement("br")));return c},br:function(a,b){b=b||1;for(var c=
+null,d=0;d<b;d++)null!=a&&(c=a.ownerDocument.createElement("br"),a.appendChild(c));return c},button:function(a,b,c){c=null!=c?c:document;c=c.createElement("button");mxUtils.write(c,a);mxEvent.addListener(c,"click",function(a){b(a)});return c},para:function(a,b){var c=document.createElement("p");mxUtils.write(c,b);null!=a&&a.appendChild(c);return c},addTransparentBackgroundFilter:function(a){a.style.filter+="progid:DXImageTransform.Microsoft.AlphaImageLoader(src='"+mxClient.imageBasePath+"/transparent.gif', sizingMethod='scale')"},
+linkAction:function(a,b,c,d,e){return mxUtils.link(a,b,function(){c.execute(d)},e)},linkInvoke:function(a,b,c,d,e,f){return mxUtils.link(a,b,function(){c[d](e)},f)},link:function(a,b,c,d){var e=document.createElement("span");e.style.color="blue";e.style.textDecoration="underline";e.style.cursor="pointer";null!=d&&(e.style.paddingLeft=d+"px");mxEvent.addListener(e,"click",c);mxUtils.write(e,b);null!=a&&a.appendChild(e);return e},fit:function(a){var b=parseInt(a.offsetLeft),c=parseInt(a.offsetWidth),
+d=mxUtils.getDocumentScrollOrigin(a.ownerDocument),e=d.x,d=d.y,f=document.body,g=document.documentElement,k=e+(f.clientWidth||g.clientWidth);b+c>k&&(a.style.left=Math.max(e,k-c)+"px");b=parseInt(a.offsetTop);c=parseInt(a.offsetHeight);f=d+Math.max(f.clientHeight||0,g.clientHeight);b+c>f&&(a.style.top=Math.max(d,f-c)+"px")},load:function(a){a=new mxXmlRequest(a,null,"GET",!1);a.send();return a},get:function(a,b,c,d,e,f){a=new mxXmlRequest(a,null,"GET");null!=d&&a.setBinary(d);a.send(b,c,e,f);return a},
+getAll:function(a,b,c){for(var d=a.length,e=[],f=0,g=function(){0==f&&null!=c&&c();f++},k=0;k<a.length;k++)(function(a,c){mxUtils.get(a,function(a){var f=a.getStatus();200>f||299<f?g():(e[c]=a,d--,0==d&&b(e))},g)})(a[k],k);0==d&&b(e)},post:function(a,b,c,d){return(new mxXmlRequest(a,b)).send(c,d)},submit:function(a,b,c,d){return(new mxXmlRequest(a,b)).simulate(c,d)},loadInto:function(a,b,c){mxClient.IS_IE?b.onreadystatechange=function(){4==b.readyState&&c()}:b.addEventListener("load",c,!1);b.load(a)},
+getValue:function(a,b,c){a=null!=a?a[b]:null;null==a&&(a=c);return a},getNumber:function(a,b,c){a=null!=a?a[b]:null;null==a&&(a=c||0);return Number(a)},getColor:function(a,b,c){a=null!=a?a[b]:null;null==a?a=c:a==mxConstants.NONE&&(a=null);return a},clone:function(a,b,c){c=null!=c?c:!1;var d=null;if(null!=a&&"function"==typeof a.constructor){var d=new a.constructor,e;for(e in a)e!=mxObjectIdentity.FIELD_NAME&&(null==b||0>mxUtils.indexOf(b,e))&&(d[e]=c||"object"!=typeof a[e]?a[e]:mxUtils.clone(a[e]))}return d},
+equalPoints:function(a,b){if(null==a&&null!=b||null!=a&&null==b||null!=a&&null!=b&&a.length!=b.length)return!1;if(null!=a&&null!=b)for(var c=0;c<a.length;c++)if(a[c]==b[c]||null!=a[c]&&!a[c].equals(b[c]))return!1;return!0},equalEntries:function(a,b){if(null==a&&null!=b||null!=a&&null==b||null!=a&&null!=b&&a.length!=b.length)return!1;if(null!=a&&null!=b){var c=0,d;for(d in b)c++;for(d in a)if(c--,!(mxUtils.isNaN(a[d])&&mxUtils.isNaN(b[d])||a[d]==b[d]))return!1}return 0==c},removeDuplicates:function(a){for(var b=
+new mxDictionary,c=[],d=0;d<a.length;d++)b.get(a[d])||(c.push(a[d]),b.put(a[d],!0));return c},isNaN:function(a){return"number"==typeof a&&isNaN(a)},extend:function(a,b){var c=function(){};c.prototype=b.prototype;a.prototype=new c;a.prototype.constructor=a},toString:function(a){var b="",c;for(c in a)try{if(null==a[c])b+=c+" = [null]\n";else if("function"==typeof a[c])b+=c+" => [Function]\n";else if("object"==typeof a[c])var d=mxUtils.getFunctionName(a[c].constructor),b=b+(c+" => ["+d+"]\n");else b+=
+c+" = "+a[c]+"\n"}catch(e){b+=c+"="+e.message}return b},toRadians:function(a){return Math.PI*a/180},toDegree:function(a){return 180*a/Math.PI},arcToCurves:function(a,b,c,d,e,f,g,k,l){k-=a;l-=b;if(0===c||0===d)return q;c=Math.abs(c);d=Math.abs(d);var m=-k/2,n=-l/2,p=Math.cos(e*Math.PI/180),q=Math.sin(e*Math.PI/180);e=p*m+q*n;var m=-1*q*m+p*n,n=e*e,r=m*m,t=c*c,u=d*d,x=n/t+r/u;1<x?(c*=Math.sqrt(x),d*=Math.sqrt(x),f=0):(x=1,f===g&&(x=-1),f=x*Math.sqrt((t*u-t*r-u*n)/(t*r+u*n)));n=f*c*m/d;r=-1*f*d*e/c;
+k=p*n-q*r+k/2;l=q*n+p*r+l/2;t=Math.atan2((m-r)/d,(e-n)/c)-Math.atan2(0,1);f=0<=t?t:2*Math.PI+t;t=Math.atan2((-m-r)/d,(-e-n)/c)-Math.atan2((m-r)/d,(e-n)/c);e=0<=t?t:2*Math.PI+t;0==g&&0<e?e-=2*Math.PI:0!=g&&0>e&&(e+=2*Math.PI);g=2*e/Math.PI;g=Math.ceil(0>g?-1*g:g);e/=g;m=8/3*Math.sin(e/4)*Math.sin(e/4)/Math.sin(e/2);n=p*c;p*=d;c*=q;d*=q;for(var y=Math.cos(f),A=Math.sin(f),r=-m*(n*A+d*y),t=-m*(c*A-p*y),q=[],z=0;z<g;++z){f+=e;var y=Math.cos(f),A=Math.sin(f),u=n*y-d*A+k,x=c*y+p*A+l,v=-m*(n*A+d*y),y=-m*
+(c*A-p*y),A=6*z;q[A]=Number(r+a);q[A+1]=Number(t+b);q[A+2]=Number(u-v+a);q[A+3]=Number(x-y+b);q[A+4]=Number(u+a);q[A+5]=Number(x+b);r=u+v;t=x+y}return q},getBoundingBox:function(a,b,c){var d=null;if(null!=a&&null!=b&&0!=b){b=mxUtils.toRadians(b);var d=Math.cos(b),e=Math.sin(b);c=null!=c?c:new mxPoint(a.x+a.width/2,a.y+a.height/2);var f=new mxPoint(a.x,a.y);b=new mxPoint(a.x+a.width,a.y);var g=new mxPoint(b.x,a.y+a.height);a=new mxPoint(a.x,g.y);f=mxUtils.getRotatedPoint(f,d,e,c);b=mxUtils.getRotatedPoint(b,
+d,e,c);g=mxUtils.getRotatedPoint(g,d,e,c);a=mxUtils.getRotatedPoint(a,d,e,c);d=new mxRectangle(f.x,f.y,0,0);d.add(new mxRectangle(b.x,b.y,0,0));d.add(new mxRectangle(g.x,g.y,0,0));d.add(new mxRectangle(a.x,a.y,0,0))}return d},getRotatedPoint:function(a,b,c,d){d=null!=d?d:new mxPoint;var e=a.x-d.x;a=a.y-d.y;return new mxPoint(e*b-a*c+d.x,a*b+e*c+d.y)},getPortConstraints:function(a,b,c,d){b=mxUtils.getValue(a.style,mxConstants.STYLE_PORT_CONSTRAINT,mxUtils.getValue(b.style,c?mxConstants.STYLE_SOURCE_PORT_CONSTRAINT:
+mxConstants.STYLE_TARGET_PORT_CONSTRAINT,null));if(null==b)return d;d=b.toString();b=mxConstants.DIRECTION_MASK_NONE;c=0;1==mxUtils.getValue(a.style,mxConstants.STYLE_PORT_CONSTRAINT_ROTATION,0)&&(c=mxUtils.getValue(a.style,mxConstants.STYLE_ROTATION,0));a=0;45<c?(a=1,135<=c&&(a=2)):-45>c&&(a=3,-135>=c&&(a=2));if(0<=d.indexOf(mxConstants.DIRECTION_NORTH))switch(a){case 0:b|=mxConstants.DIRECTION_MASK_NORTH;break;case 1:b|=mxConstants.DIRECTION_MASK_EAST;break;case 2:b|=mxConstants.DIRECTION_MASK_SOUTH;
+break;case 3:b|=mxConstants.DIRECTION_MASK_WEST}if(0<=d.indexOf(mxConstants.DIRECTION_WEST))switch(a){case 0:b|=mxConstants.DIRECTION_MASK_WEST;break;case 1:b|=mxConstants.DIRECTION_MASK_NORTH;break;case 2:b|=mxConstants.DIRECTION_MASK_EAST;break;case 3:b|=mxConstants.DIRECTION_MASK_SOUTH}if(0<=d.indexOf(mxConstants.DIRECTION_SOUTH))switch(a){case 0:b|=mxConstants.DIRECTION_MASK_SOUTH;break;case 1:b|=mxConstants.DIRECTION_MASK_WEST;break;case 2:b|=mxConstants.DIRECTION_MASK_NORTH;break;case 3:b|=
+mxConstants.DIRECTION_MASK_EAST}if(0<=d.indexOf(mxConstants.DIRECTION_EAST))switch(a){case 0:b|=mxConstants.DIRECTION_MASK_EAST;break;case 1:b|=mxConstants.DIRECTION_MASK_SOUTH;break;case 2:b|=mxConstants.DIRECTION_MASK_WEST;break;case 3:b|=mxConstants.DIRECTION_MASK_NORTH}return b},reversePortConstraints:function(a){var b;b=(a&mxConstants.DIRECTION_MASK_WEST)<<3;b|=(a&mxConstants.DIRECTION_MASK_NORTH)<<1;b|=(a&mxConstants.DIRECTION_MASK_SOUTH)>>1;return b|=(a&mxConstants.DIRECTION_MASK_EAST)>>3},
+findNearestSegment:function(a,b,c){var d=-1;if(0<a.absolutePoints.length)for(var e=a.absolutePoints[0],f=null,g=1;g<a.absolutePoints.length;g++){var k=a.absolutePoints[g],e=mxUtils.ptSegDistSq(e.x,e.y,k.x,k.y,b,c);if(null==f||e<f)f=e,d=g-1;e=k}return d},getDirectedBounds:function(a,b,c,d,e){var f=mxUtils.getValue(c,mxConstants.STYLE_DIRECTION,mxConstants.DIRECTION_EAST);d=null!=d?d:mxUtils.getValue(c,mxConstants.STYLE_FLIPH,!1);e=null!=e?e:mxUtils.getValue(c,mxConstants.STYLE_FLIPV,!1);b.x=Math.round(Math.max(0,
+Math.min(a.width,b.x)));b.y=Math.round(Math.max(0,Math.min(a.height,b.y)));b.width=Math.round(Math.max(0,Math.min(a.width,b.width)));b.height=Math.round(Math.max(0,Math.min(a.height,b.height)));if(e&&(f==mxConstants.DIRECTION_SOUTH||f==mxConstants.DIRECTION_NORTH)||d&&(f==mxConstants.DIRECTION_EAST||f==mxConstants.DIRECTION_WEST))c=b.x,b.x=b.width,b.width=c;if(d&&(f==mxConstants.DIRECTION_SOUTH||f==mxConstants.DIRECTION_NORTH)||e&&(f==mxConstants.DIRECTION_EAST||f==mxConstants.DIRECTION_WEST))c=b.y,
+b.y=b.height,b.height=c;d=mxRectangle.fromRectangle(b);f==mxConstants.DIRECTION_SOUTH?(d.y=b.x,d.x=b.height,d.width=b.y,d.height=b.width):f==mxConstants.DIRECTION_WEST?(d.y=b.height,d.x=b.width,d.width=b.x,d.height=b.y):f==mxConstants.DIRECTION_NORTH&&(d.y=b.width,d.x=b.y,d.width=b.height,d.height=b.x);return new mxRectangle(a.x+d.x,a.y+d.y,a.width-d.width-d.x,a.height-d.height-d.y)},getPerimeterPoint:function(a,b,c){for(var d=null,e=0;e<a.length-1;e++){var f=mxUtils.intersection(a[e].x,a[e].y,a[e+
+1].x,a[e+1].y,b.x,b.y,c.x,c.y);if(null!=f){var g=c.x-f.x,k=c.y-f.y,f={p:f,distSq:k*k+g*g};null!=f&&(null==d||d.distSq>f.distSq)&&(d=f)}}return null!=d?d.p:null},rectangleIntersectsSegment:function(a,b,c){var d=a.y,e=a.x,f=d+a.height,g=e+a.width;a=b.x;var k=c.x;b.x>c.x&&(a=c.x,k=b.x);k>g&&(k=g);a<e&&(a=e);if(a>k)return!1;var e=b.y,g=c.y,l=c.x-b.x;1E-7<Math.abs(l)&&(c=(c.y-b.y)/l,b=b.y-c*b.x,e=c*a+b,g=c*k+b);e>g&&(b=g,g=e,e=b);g>f&&(g=f);e<d&&(e=d);return e>g?!1:!0},contains:function(a,b,c){return a.x<=
+b&&a.x+a.width>=b&&a.y<=c&&a.y+a.height>=c},intersects:function(a,b){var c=a.width,d=a.height,e=b.width,f=b.height;if(0>=e||0>=f||0>=c||0>=d)return!1;var g=a.x,k=a.y,l=b.x,m=b.y,e=e+l,f=f+m,c=c+g,d=d+k;return(e<l||e>g)&&(f<m||f>k)&&(c<g||c>l)&&(d<k||d>m)},intersectsHotspot:function(a,b,c,d,e,f){d=null!=d?d:1;e=null!=e?e:0;f=null!=f?f:0;if(0<d){var g=a.getCenterX(),k=a.getCenterY(),l=a.width,m=a.height,n=mxUtils.getValue(a.style,mxConstants.STYLE_STARTSIZE)*a.view.scale;0<n&&(mxUtils.getValue(a.style,
+mxConstants.STYLE_HORIZONTAL,!0)?(k=a.y+n/2,m=n):(g=a.x+n/2,l=n));l=Math.max(e,l*d);m=Math.max(e,m*d);0<f&&(l=Math.min(l,f),m=Math.min(m,f));d=new mxRectangle(g-l/2,k-m/2,l,m);g=mxUtils.toRadians(mxUtils.getValue(a.style,mxConstants.STYLE_ROTATION)||0);0!=g&&(e=Math.cos(-g),f=Math.sin(-g),g=new mxPoint(a.getCenterX(),a.getCenterY()),a=mxUtils.getRotatedPoint(new mxPoint(b,c),e,f,g),b=a.x,c=a.y);return mxUtils.contains(d,b,c)}return!0},getOffset:function(a,b){for(var c=0,d=0,e=!1,f=a,g=document.body,
+k=document.documentElement;null!=f&&f!=g&&f!=k&&!e;){var l=mxUtils.getCurrentStyle(f);null!=l&&(e=e||"fixed"==l.position);f=f.parentNode}b||e||(e=mxUtils.getDocumentScrollOrigin(a.ownerDocument),c+=e.x,d+=e.y);e=a.getBoundingClientRect();null!=e&&(c+=e.left,d+=e.top);return new mxPoint(c,d)},getDocumentScrollOrigin:function(a){if(mxClient.IS_QUIRKS)return new mxPoint(a.body.scrollLeft,a.body.scrollTop);a=a.defaultView||a.parentWindow;return new mxPoint(null!=a&&void 0!==window.pageXOffset?window.pageXOffset:
+(document.documentElement||document.body.parentNode||document.body).scrollLeft,null!=a&&void 0!==window.pageYOffset?window.pageYOffset:(document.documentElement||document.body.parentNode||document.body).scrollTop)},getScrollOrigin:function(a,b,c){b=null!=b?b:!1;c=null!=c?c:!0;for(var d=null!=a?a.ownerDocument:document,e=d.body,f=d.documentElement,g=new mxPoint,k=!1;null!=a&&a!=e&&a!=f;){isNaN(a.scrollLeft)||isNaN(a.scrollTop)||(g.x+=a.scrollLeft,g.y+=a.scrollTop);var l=mxUtils.getCurrentStyle(a);
+null!=l&&(k=k||"fixed"==l.position);a=b?a.parentNode:null}!k&&c&&(a=mxUtils.getDocumentScrollOrigin(d),g.x+=a.x,g.y+=a.y);return g},convertPoint:function(a,b,c){var d=mxUtils.getScrollOrigin(a,!1);a=mxUtils.getOffset(a);a.x-=d.x;a.y-=d.y;return new mxPoint(b-a.x,c-a.y)},ltrim:function(a,b){return null!=a?a.replace(new RegExp("^["+(b||"\\s")+"]+","g"),""):null},rtrim:function(a,b){return null!=a?a.replace(new RegExp("["+(b||"\\s")+"]+$","g"),""):null},trim:function(a,b){return mxUtils.ltrim(mxUtils.rtrim(a,
+b),b)},isNumeric:function(a){return!isNaN(parseFloat(a))&&isFinite(a)&&("string"!=typeof a||0>a.toLowerCase().indexOf("0x"))},isInteger:function(a){return String(parseInt(a))===String(a)},mod:function(a,b){return(a%b+b)%b},intersection:function(a,b,c,d,e,f,g,k){var l=(k-f)*(c-a)-(g-e)*(d-b);g=((g-e)*(b-f)-(k-f)*(a-e))/l;e=((c-a)*(b-f)-(d-b)*(a-e))/l;return 0<=g&&1>=g&&0<=e&&1>=e?new mxPoint(a+g*(c-a),b+g*(d-b)):null},ptSegDistSq:function(a,b,c,d,e,f){c-=a;d-=b;e-=a;f-=b;0>=e*c+f*d?c=0:(e=c-e,f=d-
+f,a=e*c+f*d,c=0>=a?0:a*a/(c*c+d*d));e=e*e+f*f-c;0>e&&(e=0);return e},ptLineDist:function(a,b,c,d,e,f){return Math.abs((d-b)*e-(c-a)*f+c*b-d*a)/Math.sqrt((d-b)*(d-b)+(c-a)*(c-a))},relativeCcw:function(a,b,c,d,e,f){c-=a;d-=b;e-=a;f-=b;a=e*d-f*c;0==a&&(a=e*c+f*d,0<a&&(a=(e-c)*c+(f-d)*d,0>a&&(a=0)));return 0>a?-1:0<a?1:0},animateChanges:function(a,b){mxEffects.animateChanges.apply(this,arguments)},cascadeOpacity:function(a,b,c){mxEffects.cascadeOpacity.apply(this,arguments)},fadeOut:function(a,b,c,d,
+e,f){mxEffects.fadeOut.apply(this,arguments)},setOpacity:function(a,b){mxUtils.isVml(a)?a.style.filter=100<=b?"":"alpha(opacity="+b/5+")":mxClient.IS_IE&&("undefined"===typeof document.documentMode||9>document.documentMode)?a.style.filter=100<=b?"":"alpha(opacity="+b+")":a.style.opacity=b/100},createImage:function(a){var b;mxClient.IS_IE6&&"CSS1Compat"!=document.compatMode?(b=document.createElement(mxClient.VML_PREFIX+":image"),b.setAttribute("src",a),b.style.borderStyle="none"):(b=document.createElement("img"),
+b.setAttribute("src",a),b.setAttribute("border","0"));return b},sortCells:function(a,b){b=null!=b?b:!0;var c=new mxDictionary;a.sort(function(a,e){var d=c.get(a);null==d&&(d=mxCellPath.create(a).split(mxCellPath.PATH_SEPARATOR),c.put(a,d));var g=c.get(e);null==g&&(g=mxCellPath.create(e).split(mxCellPath.PATH_SEPARATOR),c.put(e,g));d=mxCellPath.compare(d,g);return 0==d?0:0<d==b?1:-1});return a},getStylename:function(a){return null!=a&&(a=a.split(";")[0],0>a.indexOf("="))?a:""},getStylenames:function(a){var b=
+[];if(null!=a){a=a.split(";");for(var c=0;c<a.length;c++)0>a[c].indexOf("=")&&b.push(a[c])}return b},indexOfStylename:function(a,b){if(null!=a&&null!=b)for(var c=a.split(";"),d=0,e=0;e<c.length;e++){if(c[e]==b)return d;d+=c[e].length+1}return-1},addStylename:function(a,b){0>mxUtils.indexOfStylename(a,b)&&(null==a?a="":0<a.length&&";"!=a.charAt(a.length-1)&&(a+=";"),a+=b);return a},removeStylename:function(a,b){var c=[];if(null!=a)for(var d=a.split(";"),e=0;e<d.length;e++)d[e]!=b&&c.push(d[e]);return c.join(";")},
+removeAllStylenames:function(a){var b=[];if(null!=a){a=a.split(";");for(var c=0;c<a.length;c++)0<=a[c].indexOf("=")&&b.push(a[c])}return b.join(";")},setCellStyles:function(a,b,c,d){if(null!=b&&0<b.length){a.beginUpdate();try{for(var e=0;e<b.length;e++)if(null!=b[e]){var f=mxUtils.setStyle(a.getStyle(b[e]),c,d);a.setStyle(b[e],f)}}finally{a.endUpdate()}}},setStyle:function(a,b,c){var d=null!=c&&("undefined"==typeof c.length||0<c.length);if(null==a||0==a.length)d&&(a=b+"="+c+";");else if(a.substring(0,
+b.length+1)==b+"="){var e=a.indexOf(";");a=d?b+"="+c+(0>e?";":a.substring(e)):0>e||e==a.length-1?"":a.substring(e+1)}else{var f=a.indexOf(";"+b+"=");0>f?d&&(d=";"==a.charAt(a.length-1)?"":";",a=a+d+b+"="+c+";"):(e=a.indexOf(";",f+1),a=d?a.substring(0,f+1)+b+"="+c+(0>e?";":a.substring(e)):a.substring(0,f)+(0>e?";":a.substring(e)))}return a},setCellStyleFlags:function(a,b,c,d,e){if(null!=b&&0<b.length){a.beginUpdate();try{for(var f=0;f<b.length;f++)if(null!=b[f]){var g=mxUtils.setStyleFlag(a.getStyle(b[f]),
+c,d,e);a.setStyle(b[f],g)}}finally{a.endUpdate()}}},setStyleFlag:function(a,b,c,d){if(null==a||0==a.length)a=d||null==d?b+"="+c:b+"=0";else{var e=a.indexOf(b+"=");if(0>e)e=";"==a.charAt(a.length-1)?"":";",a=d||null==d?a+e+b+"="+c:a+e+b+"=0";else{var f=a.indexOf(";",e),g;g=0>f?a.substring(e+b.length+1):a.substring(e+b.length+1,f);g=null==d?parseInt(g)^c:d?parseInt(g)|c:parseInt(g)&~c;a=a.substring(0,e)+b+"="+g+(0<=f?a.substring(f):"")}}return a},getAlignmentAsPoint:function(a,b){var c=0,d=0;a==mxConstants.ALIGN_CENTER?
+c=-.5:a==mxConstants.ALIGN_RIGHT&&(c=-1);b==mxConstants.ALIGN_MIDDLE?d=-.5:b==mxConstants.ALIGN_BOTTOM&&(d=-1);return new mxPoint(c,d)},getSizeForString:function(a,b,c,d){b=null!=b?b:mxConstants.DEFAULT_FONTSIZE;c=null!=c?c:mxConstants.DEFAULT_FONTFAMILY;var e=document.createElement("div");e.style.fontFamily=c;e.style.fontSize=Math.round(b)+"px";e.style.lineHeight=Math.round(b*mxConstants.LINE_HEIGHT)+"px";e.style.position="absolute";e.style.visibility="hidden";e.style.display=mxClient.IS_QUIRKS?
+"inline":"inline-block";e.style.zoom="1";null!=d?(e.style.width=d+"px",e.style.whiteSpace="normal"):e.style.whiteSpace="nowrap";e.innerHTML=a;document.body.appendChild(e);a=new mxRectangle(0,0,e.offsetWidth,e.offsetHeight);document.body.removeChild(e);return a},getViewXml:function(a,b,c,d,e){d=null!=d?d:0;e=null!=e?e:0;b=null!=b?b:1;null==c&&(c=[a.getModel().getRoot()]);var f=a.getView(),g=null,k=f.isEventsEnabled();f.setEventsEnabled(!1);var l=f.drawPane,m=f.overlayPane;a.dialect==mxConstants.DIALECT_SVG?
+(f.drawPane=document.createElementNS(mxConstants.NS_SVG,"g"),f.canvas.appendChild(f.drawPane),f.overlayPane=document.createElementNS(mxConstants.NS_SVG,"g")):(f.drawPane=f.drawPane.cloneNode(!1),f.canvas.appendChild(f.drawPane),f.overlayPane=f.overlayPane.cloneNode(!1));f.canvas.appendChild(f.overlayPane);var n=f.getTranslate();f.translate=new mxPoint(d,e);b=new mxTemporaryCellStates(a.getView(),b,c);try{g=(new mxCodec).encode(a.getView())}finally{b.destroy(),f.translate=n,f.canvas.removeChild(f.drawPane),
+f.canvas.removeChild(f.overlayPane),f.drawPane=l,f.overlayPane=m,f.setEventsEnabled(k)}return g},getScaleForPageCount:function(a,b,c,d){if(1>a)return 1;c=null!=c?c:mxConstants.PAGE_FORMAT_A4_PORTRAIT;d=null!=d?d:0;var e=c.width-2*d;c=c.height-2*d;d=b.getGraphBounds().clone();b=b.getView().getScale();d.width/=b;d.height/=b;b=d.width;var f=Math.sqrt(a);d=Math.sqrt(b/d.height/(e/c));c=f*d;d=f/d;if(1>c&&d>a){var g=d/a;d=a;c/=g}1>d&&c>a&&(g=c/a,c=a,d/=g);g=Math.ceil(c)*Math.ceil(d);for(f=0;g>a;){var g=
+Math.floor(c)/c,k=Math.floor(d)/d;1==g&&(g=Math.floor(c-1)/c);1==k&&(k=Math.floor(d-1)/d);g=g>k?g:k;c*=g;d*=g;g=Math.ceil(c)*Math.ceil(d);f++;if(10<f)break}return e*c/b*.99999},show:function(a,b,c,d,e,f){c=null!=c?c:0;d=null!=d?d:0;null==b?b=window.open().document:b.open();9==document.documentMode&&b.writeln('\x3c!--[if IE]><meta http-equiv="X-UA-Compatible" content="IE=9"><![endif]--\x3e');var g=a.getGraphBounds(),k=Math.ceil(c-g.x),l=Math.ceil(d-g.y);null==e&&(e=Math.ceil(g.width+c)+Math.ceil(Math.ceil(g.x)-
+g.x));null==f&&(f=Math.ceil(g.height+d)+Math.ceil(Math.ceil(g.y)-g.y));if(mxClient.IS_IE||11==document.documentMode){d="<html><head>";g=document.getElementsByTagName("base");for(c=0;c<g.length;c++)d+=g[c].outerHTML;d+="<style>";for(c=0;c<document.styleSheets.length;c++)try{d+=document.styleSheets[c].cssText}catch(m){}d=d+'</style></head><body style="margin:0px;">'+('<div style="position:absolute;overflow:hidden;width:'+e+"px;height:"+f+'px;"><div style="position:relative;left:'+k+"px;top:"+l+'px;">')+
+a.container.innerHTML;b.writeln(d+"</div></div></body><html>");b.close()}else{b.writeln("<html><head>");g=document.getElementsByTagName("base");for(c=0;c<g.length;c++)b.writeln(mxUtils.getOuterHtml(g[c]));d=document.getElementsByTagName("link");for(c=0;c<d.length;c++)b.writeln(mxUtils.getOuterHtml(d[c]));d=document.getElementsByTagName("style");for(c=0;c<d.length;c++)b.writeln(mxUtils.getOuterHtml(d[c]));b.writeln('</head><body style="margin:0px;"></body></html>');b.close();c=b.createElement("div");
+c.position="absolute";c.overflow="hidden";c.style.width=e+"px";c.style.height=f+"px";e=b.createElement("div");e.style.position="absolute";e.style.left=k+"px";e.style.top=l+"px";f=a.container.firstChild;for(d=null;null!=f;)g=f.cloneNode(!0),f==a.view.drawPane.ownerSVGElement?(c.appendChild(g),d=g):e.appendChild(g),f=f.nextSibling;b.body.appendChild(c);null!=e.firstChild&&b.body.appendChild(e);null!=d&&(d.style.minWidth="",d.style.minHeight="",d.firstChild.setAttribute("transform","translate("+k+","+
+l+")"))}mxUtils.removeCursors(b.body);return b},printScreen:function(a){var b=window.open();a.getGraphBounds();mxUtils.show(a,b.document);a=function(){b.focus();b.print();b.close()};mxClient.IS_GC?b.setTimeout(a,500):a()},popup:function(a,b){if(b){var c=document.createElement("div");c.style.overflow="scroll";c.style.width="636px";c.style.height="460px";var d=document.createElement("pre");d.innerHTML=mxUtils.htmlEntities(a,!1).replace(/\n/g,"<br>").replace(/ /g,"&nbsp;");c.appendChild(d);c=new mxWindow("Popup Window",
+c,document.body.clientWidth/2-320,Math.max(document.body.clientHeight||0,document.documentElement.clientHeight)/2-240,640,480,!1,!0);c.setClosable(!0);c.setVisible(!0)}else mxClient.IS_NS?(c=window.open(),c.document.writeln("<pre>"+mxUtils.htmlEntities(a)+"</pre"),c.document.close()):(c=window.open(),d=c.document.createElement("pre"),d.innerHTML=mxUtils.htmlEntities(a,!1).replace(/\n/g,"<br>").replace(/ /g,"&nbsp;"),c.document.body.appendChild(d))},alert:function(a){alert(a)},prompt:function(a,b){return prompt(a,
+null!=b?b:"")},confirm:function(a){return confirm(a)},error:function(a,b,c,d){var e=document.createElement("div");e.style.padding="20px";var f=document.createElement("img");f.setAttribute("src",d||mxUtils.errorImage);f.setAttribute("valign","bottom");f.style.verticalAlign="middle";e.appendChild(f);e.appendChild(document.createTextNode(" "));e.appendChild(document.createTextNode(" "));e.appendChild(document.createTextNode(" "));mxUtils.write(e,a);a=document.body.clientWidth;d=document.body.clientHeight||
+document.documentElement.clientHeight;var g=new mxWindow(mxResources.get(mxUtils.errorResource)||mxUtils.errorResource,e,(a-b)/2,d/4,b,null,!1,!0);c&&(mxUtils.br(e),b=document.createElement("p"),c=document.createElement("button"),mxClient.IS_IE?c.style.cssText="float:right":c.setAttribute("style","float:right"),mxEvent.addListener(c,"click",function(a){g.destroy()}),mxUtils.write(c,mxResources.get(mxUtils.closeResource)||mxUtils.closeResource),b.appendChild(c),e.appendChild(b),mxUtils.br(e),g.setClosable(!0));
+g.setVisible(!0);return g},makeDraggable:function(a,b,c,d,e,f,g,k,l,m){a=new mxDragSource(a,c);a.dragOffset=new mxPoint(null!=e?e:0,null!=f?f:mxConstants.TOOLTIP_VERTICAL_OFFSET);a.autoscroll=g;a.setGuidesEnabled(!1);null!=l&&(a.highlightDropTargets=l);null!=m&&(a.getDropTarget=m);a.getGraphForEvent=function(a){return"function"==typeof b?b(a):b};null!=d&&(a.createDragElement=function(){return d.cloneNode(!0)},k&&(a.createPreviewElement=function(a){var b=d.cloneNode(!0),c=parseInt(b.style.width),e=
+parseInt(b.style.height);b.style.width=Math.round(c*a.view.scale)+"px";b.style.height=Math.round(e*a.view.scale)+"px";return b}));return a}},mxConstants={DEFAULT_HOTSPOT:.3,MIN_HOTSPOT_SIZE:8,MAX_HOTSPOT_SIZE:0,RENDERING_HINT_EXACT:"exact",RENDERING_HINT_FASTER:"faster",RENDERING_HINT_FASTEST:"fastest",DIALECT_SVG:"svg",DIALECT_VML:"vml",DIALECT_MIXEDHTML:"mixedHtml",DIALECT_PREFERHTML:"preferHtml",DIALECT_STRICTHTML:"strictHtml",NS_SVG:"http://www.w3.org/2000/svg",NS_XHTML:"http://www.w3.org/1999/xhtml",
+NS_XLINK:"http://www.w3.org/1999/xlink",SHADOWCOLOR:"gray",VML_SHADOWCOLOR:"gray",SHADOW_OFFSET_X:2,SHADOW_OFFSET_Y:3,SHADOW_OPACITY:1,NODETYPE_ELEMENT:1,NODETYPE_ATTRIBUTE:2,NODETYPE_TEXT:3,NODETYPE_CDATA:4,NODETYPE_ENTITY_REFERENCE:5,NODETYPE_ENTITY:6,NODETYPE_PROCESSING_INSTRUCTION:7,NODETYPE_COMMENT:8,NODETYPE_DOCUMENT:9,NODETYPE_DOCUMENTTYPE:10,NODETYPE_DOCUMENT_FRAGMENT:11,NODETYPE_NOTATION:12,TOOLTIP_VERTICAL_OFFSET:16,DEFAULT_VALID_COLOR:"#00FF00",DEFAULT_INVALID_COLOR:"#FF0000",OUTLINE_HIGHLIGHT_COLOR:"#00FF00",
+OUTLINE_HIGHLIGHT_STROKEWIDTH:5,HIGHLIGHT_STROKEWIDTH:3,HIGHLIGHT_SIZE:2,HIGHLIGHT_OPACITY:100,CURSOR_MOVABLE_VERTEX:"move",CURSOR_MOVABLE_EDGE:"move",CURSOR_LABEL_HANDLE:"default",CURSOR_TERMINAL_HANDLE:"pointer",CURSOR_BEND_HANDLE:"crosshair",CURSOR_VIRTUAL_BEND_HANDLE:"crosshair",CURSOR_CONNECT:"pointer",HIGHLIGHT_COLOR:"#00FF00",CONNECT_TARGET_COLOR:"#0000FF",INVALID_CONNECT_TARGET_COLOR:"#FF0000",DROP_TARGET_COLOR:"#0000FF",VALID_COLOR:"#00FF00",INVALID_COLOR:"#FF0000",EDGE_SELECTION_COLOR:"#00FF00",
+VERTEX_SELECTION_COLOR:"#00FF00",VERTEX_SELECTION_STROKEWIDTH:1,EDGE_SELECTION_STROKEWIDTH:1,VERTEX_SELECTION_DASHED:!0,EDGE_SELECTION_DASHED:!0,GUIDE_COLOR:"#FF0000",GUIDE_STROKEWIDTH:1,OUTLINE_COLOR:"#0099FF",OUTLINE_STROKEWIDTH:mxClient.IS_IE?2:3,HANDLE_SIZE:6,LABEL_HANDLE_SIZE:4,HANDLE_FILLCOLOR:"#00FF00",HANDLE_STROKECOLOR:"black",LABEL_HANDLE_FILLCOLOR:"yellow",CONNECT_HANDLE_FILLCOLOR:"#0000FF",LOCKED_HANDLE_FILLCOLOR:"#FF0000",OUTLINE_HANDLE_FILLCOLOR:"#00FFFF",OUTLINE_HANDLE_STROKECOLOR:"#0033FF",
+DEFAULT_FONTFAMILY:"Arial,Helvetica",DEFAULT_FONTSIZE:11,DEFAULT_TEXT_DIRECTION:"",LINE_HEIGHT:1.2,WORD_WRAP:"normal",ABSOLUTE_LINE_HEIGHT:!1,DEFAULT_FONTSTYLE:0,DEFAULT_STARTSIZE:40,DEFAULT_MARKERSIZE:6,DEFAULT_IMAGESIZE:24,ENTITY_SEGMENT:30,RECTANGLE_ROUNDING_FACTOR:.15,LINE_ARCSIZE:20,ARROW_SPACING:0,ARROW_WIDTH:30,ARROW_SIZE:30,PAGE_FORMAT_A4_PORTRAIT:new mxRectangle(0,0,827,1169),PAGE_FORMAT_A4_LANDSCAPE:new mxRectangle(0,0,1169,827),PAGE_FORMAT_LETTER_PORTRAIT:new mxRectangle(0,0,850,1100),
+PAGE_FORMAT_LETTER_LANDSCAPE:new mxRectangle(0,0,1100,850),NONE:"none",STYLE_PERIMETER:"perimeter",STYLE_SOURCE_PORT:"sourcePort",STYLE_TARGET_PORT:"targetPort",STYLE_PORT_CONSTRAINT:"portConstraint",STYLE_PORT_CONSTRAINT_ROTATION:"portConstraintRotation",STYLE_SOURCE_PORT_CONSTRAINT:"sourcePortConstraint",STYLE_TARGET_PORT_CONSTRAINT:"targetPortConstraint",STYLE_OPACITY:"opacity",STYLE_FILL_OPACITY:"fillOpacity",STYLE_STROKE_OPACITY:"strokeOpacity",STYLE_TEXT_OPACITY:"textOpacity",STYLE_TEXT_DIRECTION:"textDirection",
+STYLE_OVERFLOW:"overflow",STYLE_ORTHOGONAL:"orthogonal",STYLE_EXIT_X:"exitX",STYLE_EXIT_Y:"exitY",STYLE_EXIT_PERIMETER:"exitPerimeter",STYLE_ENTRY_X:"entryX",STYLE_ENTRY_Y:"entryY",STYLE_ENTRY_PERIMETER:"entryPerimeter",STYLE_WHITE_SPACE:"whiteSpace",STYLE_ROTATION:"rotation",STYLE_FILLCOLOR:"fillColor",STYLE_POINTER_EVENTS:"pointerEvents",STYLE_SWIMLANE_FILLCOLOR:"swimlaneFillColor",STYLE_MARGIN:"margin",STYLE_GRADIENTCOLOR:"gradientColor",STYLE_GRADIENT_DIRECTION:"gradientDirection",STYLE_STROKECOLOR:"strokeColor",
+STYLE_SEPARATORCOLOR:"separatorColor",STYLE_STROKEWIDTH:"strokeWidth",STYLE_ALIGN:"align",STYLE_VERTICAL_ALIGN:"verticalAlign",STYLE_LABEL_WIDTH:"labelWidth",STYLE_LABEL_POSITION:"labelPosition",STYLE_VERTICAL_LABEL_POSITION:"verticalLabelPosition",STYLE_IMAGE_ASPECT:"imageAspect",STYLE_IMAGE_ALIGN:"imageAlign",STYLE_IMAGE_VERTICAL_ALIGN:"imageVerticalAlign",STYLE_GLASS:"glass",STYLE_IMAGE:"image",STYLE_IMAGE_WIDTH:"imageWidth",STYLE_IMAGE_HEIGHT:"imageHeight",STYLE_IMAGE_BACKGROUND:"imageBackground",
+STYLE_IMAGE_BORDER:"imageBorder",STYLE_FLIPH:"flipH",STYLE_FLIPV:"flipV",STYLE_NOLABEL:"noLabel",STYLE_NOEDGESTYLE:"noEdgeStyle",STYLE_LABEL_BACKGROUNDCOLOR:"labelBackgroundColor",STYLE_LABEL_BORDERCOLOR:"labelBorderColor",STYLE_LABEL_PADDING:"labelPadding",STYLE_INDICATOR_SHAPE:"indicatorShape",STYLE_INDICATOR_IMAGE:"indicatorImage",STYLE_INDICATOR_COLOR:"indicatorColor",STYLE_INDICATOR_STROKECOLOR:"indicatorStrokeColor",STYLE_INDICATOR_GRADIENTCOLOR:"indicatorGradientColor",STYLE_INDICATOR_SPACING:"indicatorSpacing",
+STYLE_INDICATOR_WIDTH:"indicatorWidth",STYLE_INDICATOR_HEIGHT:"indicatorHeight",STYLE_INDICATOR_DIRECTION:"indicatorDirection",STYLE_SHADOW:"shadow",STYLE_SEGMENT:"segment",STYLE_ENDARROW:"endArrow",STYLE_STARTARROW:"startArrow",STYLE_ENDSIZE:"endSize",STYLE_STARTSIZE:"startSize",STYLE_SWIMLANE_LINE:"swimlaneLine",STYLE_ENDFILL:"endFill",STYLE_STARTFILL:"startFill",STYLE_DASHED:"dashed",STYLE_DASH_PATTERN:"dashPattern",STYLE_FIX_DASH:"fixDash",STYLE_ROUNDED:"rounded",STYLE_CURVED:"curved",STYLE_ARCSIZE:"arcSize",
+STYLE_ABSOLUTE_ARCSIZE:"absoluteArcSize",STYLE_SOURCE_PERIMETER_SPACING:"sourcePerimeterSpacing",STYLE_TARGET_PERIMETER_SPACING:"targetPerimeterSpacing",STYLE_PERIMETER_SPACING:"perimeterSpacing",STYLE_SPACING:"spacing",STYLE_SPACING_TOP:"spacingTop",STYLE_SPACING_LEFT:"spacingLeft",STYLE_SPACING_BOTTOM:"spacingBottom",STYLE_SPACING_RIGHT:"spacingRight",STYLE_HORIZONTAL:"horizontal",STYLE_DIRECTION:"direction",STYLE_ELBOW:"elbow",STYLE_FONTCOLOR:"fontColor",STYLE_FONTFAMILY:"fontFamily",STYLE_FONTSIZE:"fontSize",
+STYLE_FONTSTYLE:"fontStyle",STYLE_ASPECT:"aspect",STYLE_AUTOSIZE:"autosize",STYLE_FOLDABLE:"foldable",STYLE_EDITABLE:"editable",STYLE_BENDABLE:"bendable",STYLE_MOVABLE:"movable",STYLE_RESIZABLE:"resizable",STYLE_RESIZE_WIDTH:"resizeWidth",STYLE_RESIZE_HEIGHT:"resizeHeight",STYLE_ROTATABLE:"rotatable",STYLE_CLONEABLE:"cloneable",STYLE_DELETABLE:"deletable",STYLE_SHAPE:"shape",STYLE_EDGE:"edgeStyle",STYLE_JETTY_SIZE:"jettySize",STYLE_SOURCE_JETTY_SIZE:"sourceJettySize",STYLE_TARGET_JETTY_SIZE:"targetJettySize",
+STYLE_LOOP:"loopStyle",STYLE_ORTHOGONAL_LOOP:"orthogonalLoop",STYLE_ROUTING_CENTER_X:"routingCenterX",STYLE_ROUTING_CENTER_Y:"routingCenterY",FONT_BOLD:1,FONT_ITALIC:2,FONT_UNDERLINE:4,SHAPE_RECTANGLE:"rectangle",SHAPE_ELLIPSE:"ellipse",SHAPE_DOUBLE_ELLIPSE:"doubleEllipse",SHAPE_RHOMBUS:"rhombus",SHAPE_LINE:"line",SHAPE_IMAGE:"image",SHAPE_ARROW:"arrow",SHAPE_ARROW_CONNECTOR:"arrowConnector",SHAPE_LABEL:"label",SHAPE_CYLINDER:"cylinder",SHAPE_SWIMLANE:"swimlane",SHAPE_CONNECTOR:"connector",SHAPE_ACTOR:"actor",
+SHAPE_CLOUD:"cloud",SHAPE_TRIANGLE:"triangle",SHAPE_HEXAGON:"hexagon",ARROW_CLASSIC:"classic",ARROW_CLASSIC_THIN:"classicThin",ARROW_BLOCK:"block",ARROW_BLOCK_THIN:"blockThin",ARROW_OPEN:"open",ARROW_OPEN_THIN:"openThin",ARROW_OVAL:"oval",ARROW_DIAMOND:"diamond",ARROW_DIAMOND_THIN:"diamondThin",ALIGN_LEFT:"left",ALIGN_CENTER:"center",ALIGN_RIGHT:"right",ALIGN_TOP:"top",ALIGN_MIDDLE:"middle",ALIGN_BOTTOM:"bottom",DIRECTION_NORTH:"north",DIRECTION_SOUTH:"south",DIRECTION_EAST:"east",DIRECTION_WEST:"west",
+TEXT_DIRECTION_DEFAULT:"",TEXT_DIRECTION_AUTO:"auto",TEXT_DIRECTION_LTR:"ltr",TEXT_DIRECTION_RTL:"rtl",DIRECTION_MASK_NONE:0,DIRECTION_MASK_WEST:1,DIRECTION_MASK_NORTH:2,DIRECTION_MASK_SOUTH:4,DIRECTION_MASK_EAST:8,DIRECTION_MASK_ALL:15,ELBOW_VERTICAL:"vertical",ELBOW_HORIZONTAL:"horizontal",EDGESTYLE_ELBOW:"elbowEdgeStyle",EDGESTYLE_ENTITY_RELATION:"entityRelationEdgeStyle",EDGESTYLE_LOOP:"loopEdgeStyle",EDGESTYLE_SIDETOSIDE:"sideToSideEdgeStyle",EDGESTYLE_TOPTOBOTTOM:"topToBottomEdgeStyle",EDGESTYLE_ORTHOGONAL:"orthogonalEdgeStyle",
+EDGESTYLE_SEGMENT:"segmentEdgeStyle",PERIMETER_ELLIPSE:"ellipsePerimeter",PERIMETER_RECTANGLE:"rectanglePerimeter",PERIMETER_RHOMBUS:"rhombusPerimeter",PERIMETER_HEXAGON:"hexagonPerimeter",PERIMETER_TRIANGLE:"trianglePerimeter"};function mxEventObject(a){this.name=a;this.properties=[];for(var b=1;b<arguments.length;b+=2)null!=arguments[b+1]&&(this.properties[arguments[b]]=arguments[b+1])}mxEventObject.prototype.name=null;mxEventObject.prototype.properties=null;mxEventObject.prototype.consumed=!1;
+mxEventObject.prototype.getName=function(){return this.name};mxEventObject.prototype.getProperties=function(){return this.properties};mxEventObject.prototype.getProperty=function(a){return this.properties[a]};mxEventObject.prototype.isConsumed=function(){return this.consumed};mxEventObject.prototype.consume=function(){this.consumed=!0};function mxMouseEvent(a,b){this.evt=a;this.sourceState=this.state=b}mxMouseEvent.prototype.consumed=!1;mxMouseEvent.prototype.evt=null;
+mxMouseEvent.prototype.graphX=null;mxMouseEvent.prototype.graphY=null;mxMouseEvent.prototype.state=null;mxMouseEvent.prototype.sourceState=null;mxMouseEvent.prototype.getEvent=function(){return this.evt};mxMouseEvent.prototype.getSource=function(){return mxEvent.getSource(this.evt)};mxMouseEvent.prototype.isSource=function(a){return null!=a?mxUtils.isAncestorNode(a.node,this.getSource()):!1};mxMouseEvent.prototype.getX=function(){return mxEvent.getClientX(this.getEvent())};
+mxMouseEvent.prototype.getY=function(){return mxEvent.getClientY(this.getEvent())};mxMouseEvent.prototype.getGraphX=function(){return this.graphX};mxMouseEvent.prototype.getGraphY=function(){return this.graphY};mxMouseEvent.prototype.getState=function(){return this.state};mxMouseEvent.prototype.getCell=function(){var a=this.getState();return null!=a?a.cell:null};mxMouseEvent.prototype.isPopupTrigger=function(){return mxEvent.isPopupTrigger(this.getEvent())};mxMouseEvent.prototype.isConsumed=function(){return this.consumed};
+mxMouseEvent.prototype.consume=function(a){(null!=a?a:1)&&this.evt.preventDefault&&this.evt.preventDefault();mxClient.IS_IE&&(this.evt.returnValue=!0);this.consumed=!0};function mxEventSource(a){this.setEventSource(a)}mxEventSource.prototype.eventListeners=null;mxEventSource.prototype.eventsEnabled=!0;mxEventSource.prototype.eventSource=null;mxEventSource.prototype.isEventsEnabled=function(){return this.eventsEnabled};mxEventSource.prototype.setEventsEnabled=function(a){this.eventsEnabled=a};
+mxEventSource.prototype.getEventSource=function(){return this.eventSource};mxEventSource.prototype.setEventSource=function(a){this.eventSource=a};mxEventSource.prototype.addListener=function(a,b){null==this.eventListeners&&(this.eventListeners=[]);this.eventListeners.push(a);this.eventListeners.push(b)};mxEventSource.prototype.removeListener=function(a){if(null!=this.eventListeners)for(var b=0;b<this.eventListeners.length;)this.eventListeners[b+1]==a?this.eventListeners.splice(b,2):b+=2};
+mxEventSource.prototype.fireEvent=function(a,b){if(null!=this.eventListeners&&this.isEventsEnabled()){null==a&&(a=new mxEventObject);null==b&&(b=this.getEventSource());null==b&&(b=this);for(var c=[b,a],d=0;d<this.eventListeners.length;d+=2){var e=this.eventListeners[d];null!=e&&e!=a.getName()||this.eventListeners[d+1].apply(this,c)}}};
+var mxEvent={objects:[],addListener:function(){var a=function(a,c,d){null==a.mxListenerList&&(a.mxListenerList=[],mxEvent.objects.push(a));a.mxListenerList.push({name:c,f:d})};return window.addEventListener?function(b,c,d){b.addEventListener(c,d,!1);a(b,c,d)}:function(b,c,d){b.attachEvent("on"+c,d);a(b,c,d)}}(),removeListener:function(){var a=function(a,c,d){if(null!=a.mxListenerList){c=a.mxListenerList.length;for(var b=0;b<c;b++)if(a.mxListenerList[b].f==d){a.mxListenerList.splice(b,1);break}0==
+a.mxListenerList.length&&(a.mxListenerList=null,a=mxUtils.indexOf(mxEvent.objects,a),0<=a&&mxEvent.objects.splice(a,1))}};return window.removeEventListener?function(b,c,d){b.removeEventListener(c,d,!1);a(b,c,d)}:function(b,c,d){b.detachEvent("on"+c,d);a(b,c,d)}}(),removeAllListeners:function(a){var b=a.mxListenerList;if(null!=b)for(;0<b.length;){var c=b[0];mxEvent.removeListener(a,c.name,c.f)}},addGestureListeners:function(a,b,c,d){null!=b&&mxEvent.addListener(a,mxClient.IS_POINTER?"pointerdown":
+"mousedown",b);null!=c&&mxEvent.addListener(a,mxClient.IS_POINTER?"pointermove":"mousemove",c);null!=d&&mxEvent.addListener(a,mxClient.IS_POINTER?"pointerup":"mouseup",d);!mxClient.IS_POINTER&&mxClient.IS_TOUCH&&(null!=b&&mxEvent.addListener(a,"touchstart",b),null!=c&&mxEvent.addListener(a,"touchmove",c),null!=d&&mxEvent.addListener(a,"touchend",d))},removeGestureListeners:function(a,b,c,d){null!=b&&mxEvent.removeListener(a,mxClient.IS_POINTER?"pointerdown":"mousedown",b);null!=c&&mxEvent.removeListener(a,
+mxClient.IS_POINTER?"pointermove":"mousemove",c);null!=d&&mxEvent.removeListener(a,mxClient.IS_POINTER?"pointerup":"mouseup",d);!mxClient.IS_POINTER&&mxClient.IS_TOUCH&&(null!=b&&mxEvent.removeListener(a,"touchstart",b),null!=c&&mxEvent.removeListener(a,"touchmove",c),null!=d&&mxEvent.removeListener(a,"touchend",d))},redirectMouseEvents:function(a,b,c,d,e,f,g){var k=function(a){return"function"==typeof c?c(a):c};mxEvent.addGestureListeners(a,function(a){null!=d?d(a):mxEvent.isConsumed(a)||b.fireMouseEvent(mxEvent.MOUSE_DOWN,
+new mxMouseEvent(a,k(a)))},function(a){null!=e?e(a):mxEvent.isConsumed(a)||b.fireMouseEvent(mxEvent.MOUSE_MOVE,new mxMouseEvent(a,k(a)))},function(a){null!=f?f(a):mxEvent.isConsumed(a)||b.fireMouseEvent(mxEvent.MOUSE_UP,new mxMouseEvent(a,k(a)))});mxEvent.addListener(a,"dblclick",function(a){if(null!=g)g(a);else if(!mxEvent.isConsumed(a)){var c=k(a);b.dblClick(a,null!=c?c.cell:null)}})},release:function(a){if(null!=a&&(mxEvent.removeAllListeners(a),a=a.childNodes,null!=a))for(var b=a.length,c=0;c<
+b;c+=1)mxEvent.release(a[c])},addMouseWheelListener:function(a){if(null!=a){var b=function(b){null==b&&(b=window.event);var c;c=mxClient.IS_FF?-b.detail/2:b.wheelDelta/120;0!=c&&a(b,0<c)};mxClient.IS_NS&&null==document.documentMode?mxEvent.addListener(window,mxClient.IS_SF||mxClient.IS_GC?"mousewheel":"DOMMouseScroll",b):mxEvent.addListener(document,"mousewheel",b)}},disableContextMenu:function(a){mxEvent.addListener(a,"contextmenu",function(a){a.preventDefault&&a.preventDefault();return!1})},getSource:function(a){return null!=
+a.srcElement?a.srcElement:a.target},isConsumed:function(a){return null!=a.isConsumed&&a.isConsumed},isTouchEvent:function(a){return null!=a.pointerType?"touch"==a.pointerType||a.pointerType===a.MSPOINTER_TYPE_TOUCH:null!=a.mozInputSource?5==a.mozInputSource:0==a.type.indexOf("touch")},isPenEvent:function(a){return null!=a.pointerType?"pen"==a.pointerType||a.pointerType===a.MSPOINTER_TYPE_PEN:null!=a.mozInputSource?2==a.mozInputSource:0==a.type.indexOf("pen")},isMultiTouchEvent:function(a){return null!=
+a.type&&0==a.type.indexOf("touch")&&null!=a.touches&&1<a.touches.length},isMouseEvent:function(a){return null!=a.pointerType?"mouse"==a.pointerType||a.pointerType===a.MSPOINTER_TYPE_MOUSE:null!=a.mozInputSource?1==a.mozInputSource:0==a.type.indexOf("mouse")},isLeftMouseButton:function(a){return"buttons"in a&&("mousedown"==a.type||"mousemove"==a.type)?1==a.buttons:"which"in a?1===a.which:1===a.button},isMiddleMouseButton:function(a){return"which"in a?2===a.which:4===a.button},isRightMouseButton:function(a){return"which"in
+a?3===a.which:2===a.button},isPopupTrigger:function(a){return mxEvent.isRightMouseButton(a)||mxClient.IS_MAC&&mxEvent.isControlDown(a)&&!mxEvent.isShiftDown(a)&&!mxEvent.isMetaDown(a)&&!mxEvent.isAltDown(a)},isShiftDown:function(a){return null!=a?a.shiftKey:!1},isAltDown:function(a){return null!=a?a.altKey:!1},isControlDown:function(a){return null!=a?a.ctrlKey:!1},isMetaDown:function(a){return null!=a?a.metaKey:!1},getMainEvent:function(a){"touchstart"!=a.type&&"touchmove"!=a.type||null==a.touches||
+null==a.touches[0]?"touchend"==a.type&&null!=a.changedTouches&&null!=a.changedTouches[0]&&(a=a.changedTouches[0]):a=a.touches[0];return a},getClientX:function(a){return mxEvent.getMainEvent(a).clientX},getClientY:function(a){return mxEvent.getMainEvent(a).clientY},consume:function(a,b,c){c=null!=c?c:!0;if(null!=b?b:1)a.preventDefault?(c&&a.stopPropagation(),a.preventDefault()):c&&(a.cancelBubble=!0);a.isConsumed=!0;a.preventDefault||(a.returnValue=!1)},LABEL_HANDLE:-1,ROTATION_HANDLE:-2,CUSTOM_HANDLE:-100,
+VIRTUAL_HANDLE:-1E5,MOUSE_DOWN:"mouseDown",MOUSE_MOVE:"mouseMove",MOUSE_UP:"mouseUp",ACTIVATE:"activate",RESIZE_START:"resizeStart",RESIZE:"resize",RESIZE_END:"resizeEnd",MOVE_START:"moveStart",MOVE:"move",MOVE_END:"moveEnd",PAN_START:"panStart",PAN:"pan",PAN_END:"panEnd",MINIMIZE:"minimize",NORMALIZE:"normalize",MAXIMIZE:"maximize",HIDE:"hide",SHOW:"show",CLOSE:"close",DESTROY:"destroy",REFRESH:"refresh",SIZE:"size",SELECT:"select",FIRED:"fired",FIRE_MOUSE_EVENT:"fireMouseEvent",GESTURE:"gesture",
+TAP_AND_HOLD:"tapAndHold",GET:"get",RECEIVE:"receive",CONNECT:"connect",DISCONNECT:"disconnect",SUSPEND:"suspend",RESUME:"resume",MARK:"mark",ROOT:"root",POST:"post",OPEN:"open",SAVE:"save",BEFORE_ADD_VERTEX:"beforeAddVertex",ADD_VERTEX:"addVertex",AFTER_ADD_VERTEX:"afterAddVertex",DONE:"done",EXECUTE:"execute",EXECUTED:"executed",BEGIN_UPDATE:"beginUpdate",START_EDIT:"startEdit",END_UPDATE:"endUpdate",END_EDIT:"endEdit",BEFORE_UNDO:"beforeUndo",UNDO:"undo",REDO:"redo",CHANGE:"change",NOTIFY:"notify",
+LAYOUT_CELLS:"layoutCells",CLICK:"click",SCALE:"scale",TRANSLATE:"translate",SCALE_AND_TRANSLATE:"scaleAndTranslate",UP:"up",DOWN:"down",ADD:"add",REMOVE:"remove",CLEAR:"clear",ADD_CELLS:"addCells",CELLS_ADDED:"cellsAdded",MOVE_CELLS:"moveCells",CELLS_MOVED:"cellsMoved",RESIZE_CELLS:"resizeCells",CELLS_RESIZED:"cellsResized",TOGGLE_CELLS:"toggleCells",CELLS_TOGGLED:"cellsToggled",ORDER_CELLS:"orderCells",CELLS_ORDERED:"cellsOrdered",REMOVE_CELLS:"removeCells",CELLS_REMOVED:"cellsRemoved",GROUP_CELLS:"groupCells",
+UNGROUP_CELLS:"ungroupCells",REMOVE_CELLS_FROM_PARENT:"removeCellsFromParent",FOLD_CELLS:"foldCells",CELLS_FOLDED:"cellsFolded",ALIGN_CELLS:"alignCells",LABEL_CHANGED:"labelChanged",CONNECT_CELL:"connectCell",CELL_CONNECTED:"cellConnected",SPLIT_EDGE:"splitEdge",FLIP_EDGE:"flipEdge",START_EDITING:"startEditing",EDITING_STARTED:"editingStarted",EDITING_STOPPED:"editingStopped",ADD_OVERLAY:"addOverlay",REMOVE_OVERLAY:"removeOverlay",UPDATE_CELL_SIZE:"updateCellSize",ESCAPE:"escape",DOUBLE_CLICK:"doubleClick",
+START:"start",RESET:"reset"};function mxXmlRequest(a,b,c,d,e,f){this.url=a;this.params=b;this.method=c||"POST";this.async=null!=d?d:!0;this.username=e;this.password=f}mxXmlRequest.prototype.url=null;mxXmlRequest.prototype.params=null;mxXmlRequest.prototype.method=null;mxXmlRequest.prototype.async=null;mxXmlRequest.prototype.binary=!1;mxXmlRequest.prototype.withCredentials=!1;mxXmlRequest.prototype.username=null;mxXmlRequest.prototype.password=null;mxXmlRequest.prototype.request=null;
+mxXmlRequest.prototype.decodeSimulateValues=!1;mxXmlRequest.prototype.isBinary=function(){return this.binary};mxXmlRequest.prototype.setBinary=function(a){this.binary=a};mxXmlRequest.prototype.getText=function(){return this.request.responseText};mxXmlRequest.prototype.isReady=function(){return 4==this.request.readyState};mxXmlRequest.prototype.getDocumentElement=function(){var a=this.getXml();return null!=a?a.documentElement:null};
+mxXmlRequest.prototype.getXml=function(){var a=this.request.responseXML;if(9<=document.documentMode||null==a||null==a.documentElement)a=mxUtils.parseXml(this.request.responseText);return a};mxXmlRequest.prototype.getText=function(){return this.request.responseText};mxXmlRequest.prototype.getStatus=function(){return this.request.status};
+mxXmlRequest.prototype.create=function(){if(window.XMLHttpRequest)return function(){var a=new XMLHttpRequest;this.isBinary()&&a.overrideMimeType&&a.overrideMimeType("text/plain; charset=x-user-defined");return a};if("undefined"!=typeof ActiveXObject)return function(){return new ActiveXObject("Microsoft.XMLHTTP")}}();
+mxXmlRequest.prototype.send=function(a,b,c,d){this.request=this.create();null!=this.request&&(null!=a&&(this.request.onreadystatechange=mxUtils.bind(this,function(){this.isReady()&&(a(this),this.onreadystatechaange=null)})),this.request.open(this.method,this.url,this.async,this.username,this.password),this.setRequestHeaders(this.request,this.params),window.XMLHttpRequest&&this.withCredentials&&(this.request.withCredentials="true"),!mxClient.IS_QUIRKS&&(null==document.documentMode||9<document.documentMode)&&
+window.XMLHttpRequest&&null!=c&&null!=d&&(this.request.timeout=c,this.request.ontimeout=d),this.request.send(this.params))};mxXmlRequest.prototype.setRequestHeaders=function(a,b){null!=b&&a.setRequestHeader("Content-Type","application/x-www-form-urlencoded")};
+mxXmlRequest.prototype.simulate=function(a,b){a=a||document;var c=null;a==document&&(c=window.onbeforeunload,window.onbeforeunload=null);var d=a.createElement("form");d.setAttribute("method",this.method);d.setAttribute("action",this.url);null!=b&&d.setAttribute("target",b);d.style.display="none";d.style.visibility="hidden";for(var e=0<this.params.indexOf("&")?this.params.split("&"):this.params.split(),f=0;f<e.length;f++){var g=e[f].indexOf("=");if(0<g){var k=e[f].substring(0,g),g=e[f].substring(g+
+1);this.decodeSimulateValues&&(g=decodeURIComponent(g));var l=a.createElement("textarea");l.setAttribute("wrap","off");l.setAttribute("name",k);mxUtils.write(l,g);d.appendChild(l)}}a.body.appendChild(d);d.submit();null!=d.parentNode&&d.parentNode.removeChild(d);null!=c&&(window.onbeforeunload=c)};
+var mxClipboard={STEPSIZE:10,insertCount:1,cells:null,setCells:function(a){mxClipboard.cells=a},getCells:function(){return mxClipboard.cells},isEmpty:function(){return null==mxClipboard.getCells()},cut:function(a,b){b=mxClipboard.copy(a,b);mxClipboard.insertCount=0;mxClipboard.removeCells(a,b);return b},removeCells:function(a,b){a.removeCells(b)},copy:function(a,b){b=b||a.getSelectionCells();var c=a.getExportableCells(a.model.getTopmostCells(b));mxClipboard.insertCount=1;mxClipboard.setCells(a.cloneCells(c));
+return c},paste:function(a){var b=null;if(!mxClipboard.isEmpty()){var b=a.getImportableCells(mxClipboard.getCells()),c=mxClipboard.insertCount*mxClipboard.STEPSIZE,d=a.getDefaultParent(),b=a.importCells(b,c,c,d);mxClipboard.insertCount++;a.setSelectionCells(b)}return b}};
+function mxWindow(a,b,c,d,e,f,g,k,l,m){null!=b&&(g=null!=g?g:!0,this.content=b,this.init(c,d,e,f,m),this.installMaximizeHandler(),this.installMinimizeHandler(),this.installCloseHandler(),this.setMinimizable(g),this.setTitle(a),(null==k||k)&&this.installMoveHandler(),null!=l&&null!=l.parentNode?l.parentNode.replaceChild(this.div,l):document.body.appendChild(this.div))}mxWindow.prototype=new mxEventSource;mxWindow.prototype.constructor=mxWindow;mxWindow.prototype.closeImage=mxClient.imageBasePath+"/close.gif";
+mxWindow.prototype.minimizeImage=mxClient.imageBasePath+"/minimize.gif";mxWindow.prototype.normalizeImage=mxClient.imageBasePath+"/normalize.gif";mxWindow.prototype.maximizeImage=mxClient.imageBasePath+"/maximize.gif";mxWindow.prototype.resizeImage=mxClient.imageBasePath+"/resize.gif";mxWindow.prototype.visible=!1;mxWindow.prototype.minimumSize=new mxRectangle(0,0,50,40);mxWindow.prototype.destroyOnClose=!0;
+mxWindow.prototype.contentHeightCorrection=8==document.documentMode||7==document.documentMode?6:2;mxWindow.prototype.title=null;mxWindow.prototype.content=null;
+mxWindow.prototype.init=function(a,b,c,d,e){e=null!=e?e:"mxWindow";this.div=document.createElement("div");this.div.className=e;this.div.style.left=a+"px";this.div.style.top=b+"px";this.table=document.createElement("table");this.table.className=e;mxClient.IS_POINTER&&(this.div.style.touchAction="none");null!=c&&(mxClient.IS_QUIRKS||(this.div.style.width=c+"px"),this.table.style.width=c+"px");null!=d&&(mxClient.IS_QUIRKS||(this.div.style.height=d+"px"),this.table.style.height=d+"px");a=document.createElement("tbody");
+b=document.createElement("tr");this.title=document.createElement("td");this.title.className=e+"Title";this.buttons=document.createElement("div");this.buttons.style.position="absolute";this.buttons.style.display="inline-block";this.buttons.style.right="4px";this.buttons.style.top="5px";this.title.appendChild(this.buttons);b.appendChild(this.title);a.appendChild(b);b=document.createElement("tr");this.td=document.createElement("td");this.td.className=e+"Pane";7==document.documentMode&&(this.td.style.height=
+"100%");this.contentWrapper=document.createElement("div");this.contentWrapper.className=e+"Pane";this.contentWrapper.style.width="100%";this.contentWrapper.appendChild(this.content);if(mxClient.IS_QUIRKS||"DIV"!=this.content.nodeName.toUpperCase())this.contentWrapper.style.height="100%";this.td.appendChild(this.contentWrapper);b.appendChild(this.td);a.appendChild(b);this.table.appendChild(a);this.div.appendChild(this.table);e=mxUtils.bind(this,function(a){this.activate()});mxEvent.addGestureListeners(this.title,
+e);mxEvent.addGestureListeners(this.table,e);this.hide()};mxWindow.prototype.setTitle=function(a){for(var b=this.title.firstChild;null!=b;){var c=b.nextSibling;b.nodeType==mxConstants.NODETYPE_TEXT&&b.parentNode.removeChild(b);b=c}mxUtils.write(this.title,a||"");this.title.appendChild(this.buttons)};mxWindow.prototype.setScrollable=function(a){0>navigator.userAgent.indexOf("Presto/2.5")&&(this.contentWrapper.style.overflow=a?"auto":"hidden")};
+mxWindow.prototype.activate=function(){if(mxWindow.activeWindow!=this){var a=mxUtils.getCurrentStyle(this.getElement()),a=null!=a?a.zIndex:3;if(mxWindow.activeWindow){var b=mxWindow.activeWindow.getElement();null!=b&&null!=b.style&&(b.style.zIndex=a)}b=mxWindow.activeWindow;this.getElement().style.zIndex=parseInt(a)+1;mxWindow.activeWindow=this;this.fireEvent(new mxEventObject(mxEvent.ACTIVATE,"previousWindow",b))}};mxWindow.prototype.getElement=function(){return this.div};
+mxWindow.prototype.fit=function(){mxUtils.fit(this.div)};mxWindow.prototype.isResizable=function(){return null!=this.resize?"none"!=this.resize.style.display:!1};
+mxWindow.prototype.setResizable=function(a){if(a)if(null==this.resize){this.resize=document.createElement("img");this.resize.style.position="absolute";this.resize.style.bottom="2px";this.resize.style.right="2px";this.resize.setAttribute("src",this.resizeImage);this.resize.style.cursor="nw-resize";var b=null,c=null,d=null,e=null;a=mxUtils.bind(this,function(a){this.activate();b=mxEvent.getClientX(a);c=mxEvent.getClientY(a);d=this.div.offsetWidth;e=this.div.offsetHeight;mxEvent.addGestureListeners(document,
+null,f,g);this.fireEvent(new mxEventObject(mxEvent.RESIZE_START,"event",a));mxEvent.consume(a)});var f=mxUtils.bind(this,function(a){if(null!=b&&null!=c){var f=mxEvent.getClientX(a)-b,g=mxEvent.getClientY(a)-c;this.setSize(d+f,e+g);this.fireEvent(new mxEventObject(mxEvent.RESIZE,"event",a));mxEvent.consume(a)}}),g=mxUtils.bind(this,function(a){null!=b&&null!=c&&(c=b=null,mxEvent.removeGestureListeners(document,null,f,g),this.fireEvent(new mxEventObject(mxEvent.RESIZE_END,"event",a)),mxEvent.consume(a))});
+mxEvent.addGestureListeners(this.resize,a,f,g);this.div.appendChild(this.resize)}else this.resize.style.display="inline";else null!=this.resize&&(this.resize.style.display="none")};
+mxWindow.prototype.setSize=function(a,b){a=Math.max(this.minimumSize.width,a);b=Math.max(this.minimumSize.height,b);mxClient.IS_QUIRKS||(this.div.style.width=a+"px",this.div.style.height=b+"px");this.table.style.width=a+"px";this.table.style.height=b+"px";mxClient.IS_QUIRKS||(this.contentWrapper.style.height=this.div.offsetHeight-this.title.offsetHeight-this.contentHeightCorrection+"px")};mxWindow.prototype.setMinimizable=function(a){this.minimize.style.display=a?"":"none"};
+mxWindow.prototype.getMinimumSize=function(){return new mxRectangle(0,0,0,this.title.offsetHeight)};
+mxWindow.prototype.installMinimizeHandler=function(){this.minimize=document.createElement("img");this.minimize.setAttribute("src",this.minimizeImage);this.minimize.setAttribute("title","Minimize");this.minimize.style.cursor="pointer";this.minimize.style.marginLeft="2px";this.minimize.style.display="none";this.buttons.appendChild(this.minimize);var a=!1,b=null,c=null,d=mxUtils.bind(this,function(d){this.activate();if(a)a=!1,this.minimize.setAttribute("src",this.minimizeImage),this.minimize.setAttribute("title",
+"Minimize"),this.contentWrapper.style.display="",this.maximize.style.display=b,mxClient.IS_QUIRKS||(this.div.style.height=c),this.table.style.height=c,null!=this.resize&&(this.resize.style.visibility=""),this.fireEvent(new mxEventObject(mxEvent.NORMALIZE,"event",d));else{a=!0;this.minimize.setAttribute("src",this.normalizeImage);this.minimize.setAttribute("title","Normalize");this.contentWrapper.style.display="none";b=this.maximize.style.display;this.maximize.style.display="none";c=this.table.style.height;
+var e=this.getMinimumSize();0<e.height&&(mxClient.IS_QUIRKS||(this.div.style.height=e.height+"px"),this.table.style.height=e.height+"px");0<e.width&&(mxClient.IS_QUIRKS||(this.div.style.width=e.width+"px"),this.table.style.width=e.width+"px");null!=this.resize&&(this.resize.style.visibility="hidden");this.fireEvent(new mxEventObject(mxEvent.MINIMIZE,"event",d))}mxEvent.consume(d)});mxEvent.addGestureListeners(this.minimize,d)};
+mxWindow.prototype.setMaximizable=function(a){this.maximize.style.display=a?"":"none"};
+mxWindow.prototype.installMaximizeHandler=function(){this.maximize=document.createElement("img");this.maximize.setAttribute("src",this.maximizeImage);this.maximize.setAttribute("title","Maximize");this.maximize.style.cursor="default";this.maximize.style.marginLeft="2px";this.maximize.style.cursor="pointer";this.maximize.style.display="none";this.buttons.appendChild(this.maximize);var a=!1,b=null,c=null,d=null,e=null,f=null,g=mxUtils.bind(this,function(g){this.activate();if("none"!=this.maximize.style.display){if(a)a=
+!1,this.maximize.setAttribute("src",this.maximizeImage),this.maximize.setAttribute("title","Maximize"),this.contentWrapper.style.display="",this.minimize.style.display=f,this.div.style.left=b+"px",this.div.style.top=c+"px",mxClient.IS_QUIRKS||(this.div.style.height=d,this.div.style.width=e,k=mxUtils.getCurrentStyle(this.contentWrapper),"auto"!=k.overflow&&null==this.resize)||(this.contentWrapper.style.height=this.div.offsetHeight-this.title.offsetHeight-this.contentHeightCorrection+"px"),this.table.style.height=
+d,this.table.style.width=e,null!=this.resize&&(this.resize.style.visibility=""),this.fireEvent(new mxEventObject(mxEvent.NORMALIZE,"event",g));else{a=!0;this.maximize.setAttribute("src",this.normalizeImage);this.maximize.setAttribute("title","Normalize");this.contentWrapper.style.display="";f=this.minimize.style.display;this.minimize.style.display="none";b=parseInt(this.div.style.left);c=parseInt(this.div.style.top);d=this.table.style.height;e=this.table.style.width;this.div.style.left="0px";this.div.style.top=
+"0px";k=Math.max(document.body.clientHeight||0,document.documentElement.clientHeight||0);mxClient.IS_QUIRKS||(this.div.style.width=document.body.clientWidth-2+"px",this.div.style.height=k-2+"px");this.table.style.width=document.body.clientWidth-2+"px";this.table.style.height=k-2+"px";null!=this.resize&&(this.resize.style.visibility="hidden");if(!mxClient.IS_QUIRKS){var k=mxUtils.getCurrentStyle(this.contentWrapper);if("auto"==k.overflow||null!=this.resize)this.contentWrapper.style.height=this.div.offsetHeight-
+this.title.offsetHeight-this.contentHeightCorrection+"px"}this.fireEvent(new mxEventObject(mxEvent.MAXIMIZE,"event",g))}mxEvent.consume(g)}});mxEvent.addGestureListeners(this.maximize,g);mxEvent.addListener(this.title,"dblclick",g)};
+mxWindow.prototype.installMoveHandler=function(){this.title.style.cursor="move";mxEvent.addGestureListeners(this.title,mxUtils.bind(this,function(a){var b=mxEvent.getClientX(a),c=mxEvent.getClientY(a),d=this.getX(),e=this.getY(),f=mxUtils.bind(this,function(a){var f=mxEvent.getClientX(a)-b,g=mxEvent.getClientY(a)-c;this.setLocation(d+f,e+g);this.fireEvent(new mxEventObject(mxEvent.MOVE,"event",a));mxEvent.consume(a)}),g=mxUtils.bind(this,function(a){mxEvent.removeGestureListeners(document,null,f,
+g);this.fireEvent(new mxEventObject(mxEvent.MOVE_END,"event",a));mxEvent.consume(a)});mxEvent.addGestureListeners(document,null,f,g);this.fireEvent(new mxEventObject(mxEvent.MOVE_START,"event",a));mxEvent.consume(a)}));mxClient.IS_POINTER&&(this.title.style.touchAction="none")};mxWindow.prototype.setLocation=function(a,b){this.div.style.left=a+"px";this.div.style.top=b+"px"};mxWindow.prototype.getX=function(){return parseInt(this.div.style.left)};mxWindow.prototype.getY=function(){return parseInt(this.div.style.top)};
+mxWindow.prototype.installCloseHandler=function(){this.closeImg=document.createElement("img");this.closeImg.setAttribute("src",this.closeImage);this.closeImg.setAttribute("title","Close");this.closeImg.style.marginLeft="2px";this.closeImg.style.cursor="pointer";this.closeImg.style.display="none";this.buttons.appendChild(this.closeImg);mxEvent.addGestureListeners(this.closeImg,mxUtils.bind(this,function(a){this.fireEvent(new mxEventObject(mxEvent.CLOSE,"event",a));this.destroyOnClose?this.destroy():
+this.setVisible(!1);mxEvent.consume(a)}))};mxWindow.prototype.setImage=function(a){this.image=document.createElement("img");this.image.setAttribute("src",a);this.image.setAttribute("align","left");this.image.style.marginRight="4px";this.image.style.marginLeft="0px";this.image.style.marginTop="-2px";this.title.insertBefore(this.image,this.title.firstChild)};mxWindow.prototype.setClosable=function(a){this.closeImg.style.display=a?"":"none"};
+mxWindow.prototype.isVisible=function(){return null!=this.div?"none"!=this.div.style.display:!1};mxWindow.prototype.setVisible=function(a){null!=this.div&&this.isVisible()!=a&&(a?this.show():this.hide())};
+mxWindow.prototype.show=function(){this.div.style.display="";this.activate();var a=mxUtils.getCurrentStyle(this.contentWrapper);mxClient.IS_QUIRKS||"auto"!=a.overflow&&null==this.resize||"none"==this.contentWrapper.style.display||(this.contentWrapper.style.height=this.div.offsetHeight-this.title.offsetHeight-this.contentHeightCorrection+"px");this.fireEvent(new mxEventObject(mxEvent.SHOW))};mxWindow.prototype.hide=function(){this.div.style.display="none";this.fireEvent(new mxEventObject(mxEvent.HIDE))};
+mxWindow.prototype.destroy=function(){this.fireEvent(new mxEventObject(mxEvent.DESTROY));null!=this.div&&(mxEvent.release(this.div),this.div.parentNode.removeChild(this.div),this.div=null);this.contentWrapper=this.content=this.title=null};function mxForm(a){this.table=document.createElement("table");this.table.className=a;this.body=document.createElement("tbody");this.table.appendChild(this.body)}mxForm.prototype.table=null;mxForm.prototype.body=!1;mxForm.prototype.getTable=function(){return this.table};
+mxForm.prototype.addButtons=function(a,b){var c=document.createElement("tr"),d=document.createElement("td");c.appendChild(d);var d=document.createElement("td"),e=document.createElement("button");mxUtils.write(e,mxResources.get("ok")||"OK");d.appendChild(e);mxEvent.addListener(e,"click",function(){a()});e=document.createElement("button");mxUtils.write(e,mxResources.get("cancel")||"Cancel");d.appendChild(e);mxEvent.addListener(e,"click",function(){b()});c.appendChild(d);this.body.appendChild(c)};
+mxForm.prototype.addText=function(a,b,c){var d=document.createElement("input");d.setAttribute("type",c||"text");d.value=b;return this.addField(a,d)};mxForm.prototype.addCheckbox=function(a,b){var c=document.createElement("input");c.setAttribute("type","checkbox");this.addField(a,c);b&&(c.checked=!0);return c};mxForm.prototype.addTextarea=function(a,b,c){var d=document.createElement("textarea");mxClient.IS_NS&&c--;d.setAttribute("rows",c||2);d.value=b;return this.addField(a,d)};
+mxForm.prototype.addCombo=function(a,b,c){var d=document.createElement("select");null!=c&&d.setAttribute("size",c);b&&d.setAttribute("multiple","true");return this.addField(a,d)};mxForm.prototype.addOption=function(a,b,c,d){var e=document.createElement("option");mxUtils.writeln(e,b);e.setAttribute("value",c);d&&e.setAttribute("selected",d);a.appendChild(e)};
+mxForm.prototype.addField=function(a,b){var c=document.createElement("tr"),d=document.createElement("td");mxUtils.write(d,a);c.appendChild(d);d=document.createElement("td");d.appendChild(b);c.appendChild(d);this.body.appendChild(c);return b};function mxImage(a,b,c){this.src=a;this.width=b;this.height=c}mxImage.prototype.src=null;mxImage.prototype.width=null;mxImage.prototype.height=null;
+function mxDivResizer(a,b){if("div"==a.nodeName.toLowerCase()){null==b&&(b=window);this.div=a;var c=mxUtils.getCurrentStyle(a);null!=c&&(this.resizeWidth="auto"==c.width,this.resizeHeight="auto"==c.height);mxEvent.addListener(b,"resize",mxUtils.bind(this,function(a){this.handlingResize||(this.handlingResize=!0,this.resize(),this.handlingResize=!1)}));this.resize()}}mxDivResizer.prototype.resizeWidth=!0;mxDivResizer.prototype.resizeHeight=!0;mxDivResizer.prototype.handlingResize=!1;
+mxDivResizer.prototype.resize=function(){var a=this.getDocumentWidth(),b=this.getDocumentHeight(),c=parseInt(this.div.style.left),d=parseInt(this.div.style.right),e=parseInt(this.div.style.top),f=parseInt(this.div.style.bottom);this.resizeWidth&&!isNaN(c)&&!isNaN(d)&&0<=c&&0<=d&&0<a-d-c&&(this.div.style.width=a-d-c+"px");this.resizeHeight&&!isNaN(e)&&!isNaN(f)&&0<=e&&0<=f&&0<b-e-f&&(this.div.style.height=b-e-f+"px")};mxDivResizer.prototype.getDocumentWidth=function(){return document.body.clientWidth};
+mxDivResizer.prototype.getDocumentHeight=function(){return document.body.clientHeight};function mxDragSource(a,b){this.element=a;this.dropHandler=b;mxEvent.addGestureListeners(a,mxUtils.bind(this,function(a){this.mouseDown(a)}));mxEvent.addListener(a,"dragstart",function(a){mxEvent.consume(a)});this.eventConsumer=function(a,b){var c=b.getProperty("eventName"),d=b.getProperty("event");c!=mxEvent.MOUSE_DOWN&&d.consume()}}mxDragSource.prototype.element=null;mxDragSource.prototype.dropHandler=null;
+mxDragSource.prototype.dragOffset=null;mxDragSource.prototype.dragElement=null;mxDragSource.prototype.previewElement=null;mxDragSource.prototype.enabled=!0;mxDragSource.prototype.currentGraph=null;mxDragSource.prototype.currentDropTarget=null;mxDragSource.prototype.currentPoint=null;mxDragSource.prototype.currentGuide=null;mxDragSource.prototype.currentHighlight=null;mxDragSource.prototype.autoscroll=!0;mxDragSource.prototype.guidesEnabled=!0;mxDragSource.prototype.gridEnabled=!0;
+mxDragSource.prototype.highlightDropTargets=!0;mxDragSource.prototype.dragElementZIndex=100;mxDragSource.prototype.dragElementOpacity=70;mxDragSource.prototype.checkEventSource=!0;mxDragSource.prototype.isEnabled=function(){return this.enabled};mxDragSource.prototype.setEnabled=function(a){this.enabled=a};mxDragSource.prototype.isGuidesEnabled=function(){return this.guidesEnabled};mxDragSource.prototype.setGuidesEnabled=function(a){this.guidesEnabled=a};mxDragSource.prototype.isGridEnabled=function(){return this.gridEnabled};
+mxDragSource.prototype.setGridEnabled=function(a){this.gridEnabled=a};mxDragSource.prototype.getGraphForEvent=function(a){return null};mxDragSource.prototype.getDropTarget=function(a,b,c,d){return a.getCellAt(b,c)};mxDragSource.prototype.createDragElement=function(a){return this.element.cloneNode(!0)};mxDragSource.prototype.createPreviewElement=function(a){return null};mxDragSource.prototype.isActive=function(){return null!=this.mouseMoveHandler};
+mxDragSource.prototype.reset=function(){null!=this.currentGraph&&(this.dragExit(this.currentGraph),this.currentGraph=null);this.removeDragElement();this.removeListeners();this.stopDrag()};
+mxDragSource.prototype.mouseDown=function(a){this.enabled&&!mxEvent.isConsumed(a)&&null==this.mouseMoveHandler&&(this.startDrag(a),this.mouseMoveHandler=mxUtils.bind(this,this.mouseMove),this.mouseUpHandler=mxUtils.bind(this,this.mouseUp),mxEvent.addGestureListeners(document,null,this.mouseMoveHandler,this.mouseUpHandler),mxClient.IS_TOUCH&&!mxEvent.isMouseEvent(a)&&(this.eventSource=mxEvent.getSource(a),mxEvent.addGestureListeners(this.eventSource,null,this.mouseMoveHandler,this.mouseUpHandler)))};
+mxDragSource.prototype.startDrag=function(a){this.dragElement=this.createDragElement(a);this.dragElement.style.position="absolute";this.dragElement.style.zIndex=this.dragElementZIndex;mxUtils.setOpacity(this.dragElement,this.dragElementOpacity);this.checkEventSource&&mxClient.IS_SVG&&(this.dragElement.style.pointerEvents="none")};mxDragSource.prototype.stopDrag=function(){this.removeDragElement()};
+mxDragSource.prototype.removeDragElement=function(){null!=this.dragElement&&(null!=this.dragElement.parentNode&&this.dragElement.parentNode.removeChild(this.dragElement),this.dragElement=null)};mxDragSource.prototype.getElementForEvent=function(a){return mxEvent.isTouchEvent(a)||mxEvent.isPenEvent(a)?document.elementFromPoint(mxEvent.getClientX(a),mxEvent.getClientY(a)):mxEvent.getSource(a)};
+mxDragSource.prototype.graphContainsEvent=function(a,b){var c=mxEvent.getClientX(b),d=mxEvent.getClientY(b),e=mxUtils.getOffset(a.container),f=mxUtils.getScrollOrigin(),g=this.getElementForEvent(b);if(this.checkEventSource)for(;null!=g&&g!=a.container;)g=g.parentNode;return null!=g&&c>=e.x-f.x&&d>=e.y-f.y&&c<=e.x-f.x+a.container.offsetWidth&&d<=e.y-f.y+a.container.offsetHeight};
+mxDragSource.prototype.mouseMove=function(a){var b=this.getGraphForEvent(a);null==b||this.graphContainsEvent(b,a)||(b=null);b!=this.currentGraph&&(null!=this.currentGraph&&this.dragExit(this.currentGraph,a),this.currentGraph=b,null!=this.currentGraph&&this.dragEnter(this.currentGraph,a));null!=this.currentGraph&&this.dragOver(this.currentGraph,a);if(null==this.dragElement||null!=this.previewElement&&"visible"==this.previewElement.style.visibility)null!=this.dragElement&&(this.dragElement.style.visibility=
+"hidden");else{var b=mxEvent.getClientX(a),c=mxEvent.getClientY(a);null==this.dragElement.parentNode&&document.body.appendChild(this.dragElement);this.dragElement.style.visibility="visible";null!=this.dragOffset&&(b+=this.dragOffset.x,c+=this.dragOffset.y);var d=mxUtils.getDocumentScrollOrigin(document);this.dragElement.style.left=b+d.x+"px";this.dragElement.style.top=c+d.y+"px"}mxEvent.consume(a)};
+mxDragSource.prototype.mouseUp=function(a){if(null!=this.currentGraph){if(null!=this.currentPoint&&(null==this.previewElement||"hidden"!=this.previewElement.style.visibility)){var b=this.currentGraph.view.scale,c=this.currentGraph.view.translate;this.drop(this.currentGraph,a,this.currentDropTarget,this.currentPoint.x/b-c.x,this.currentPoint.y/b-c.y)}this.dragExit(this.currentGraph);this.currentGraph=null}this.stopDrag();this.removeListeners();mxEvent.consume(a)};
+mxDragSource.prototype.removeListeners=function(){null!=this.eventSource&&(mxEvent.removeGestureListeners(this.eventSource,null,this.mouseMoveHandler,this.mouseUpHandler),this.eventSource=null);mxEvent.removeGestureListeners(document,null,this.mouseMoveHandler,this.mouseUpHandler);this.mouseUpHandler=this.mouseMoveHandler=null};
+mxDragSource.prototype.dragEnter=function(a,b){a.isMouseDown=!0;a.isMouseTrigger=mxEvent.isMouseEvent(b);this.previewElement=this.createPreviewElement(a);null!=this.previewElement&&this.checkEventSource&&mxClient.IS_SVG&&(this.previewElement.style.pointerEvents="none");this.isGuidesEnabled()&&null!=this.previewElement&&(this.currentGuide=new mxGuide(a,a.graphHandler.getGuideStates()));this.highlightDropTargets&&(this.currentHighlight=new mxCellHighlight(a,mxConstants.DROP_TARGET_COLOR));a.addListener(mxEvent.FIRE_MOUSE_EVENT,
+this.eventConsumer)};mxDragSource.prototype.dragExit=function(a,b){this.currentPoint=this.currentDropTarget=null;a.isMouseDown=!1;a.removeListener(this.eventConsumer);null!=this.previewElement&&(null!=this.previewElement.parentNode&&this.previewElement.parentNode.removeChild(this.previewElement),this.previewElement=null);null!=this.currentGuide&&(this.currentGuide.destroy(),this.currentGuide=null);null!=this.currentHighlight&&(this.currentHighlight.destroy(),this.currentHighlight=null)};
+mxDragSource.prototype.dragOver=function(a,b){var c=mxUtils.getOffset(a.container),d=mxUtils.getScrollOrigin(a.container),e=mxEvent.getClientX(b)-c.x+d.x-a.panDx,c=mxEvent.getClientY(b)-c.y+d.y-a.panDy;a.autoScroll&&(null==this.autoscroll||this.autoscroll)&&a.scrollPointToVisible(e,c,a.autoExtend);null!=this.currentHighlight&&a.isDropEnabled()&&(this.currentDropTarget=this.getDropTarget(a,e,c,b),d=a.getView().getState(this.currentDropTarget),this.currentHighlight.highlight(d));if(null!=this.previewElement){null==
+this.previewElement.parentNode&&(a.container.appendChild(this.previewElement),this.previewElement.style.zIndex="3",this.previewElement.style.position="absolute");var d=this.isGridEnabled()&&a.isGridEnabledEvent(b),f=!0;if(null!=this.currentGuide&&this.currentGuide.isEnabledForEvent(b))var f=parseInt(this.previewElement.style.width),g=parseInt(this.previewElement.style.height),f=new mxRectangle(0,0,f,g),c=new mxPoint(e,c),c=this.currentGuide.move(f,c,d),f=!1,e=c.x,c=c.y;else if(d)var d=a.view.scale,
+g=a.view.translate,k=a.gridSize/2,e=(a.snap(e/d-g.x-k)+g.x)*d,c=(a.snap(c/d-g.y-k)+g.y)*d;null!=this.currentGuide&&f&&this.currentGuide.hide();null!=this.previewOffset&&(e+=this.previewOffset.x,c+=this.previewOffset.y);this.previewElement.style.left=Math.round(e)+"px";this.previewElement.style.top=Math.round(c)+"px";this.previewElement.style.visibility="visible"}this.currentPoint=new mxPoint(e,c)};
+mxDragSource.prototype.drop=function(a,b,c,d,e){this.dropHandler.apply(this,arguments);"hidden"!=a.container.style.visibility&&a.container.focus()};function mxToolbar(a){this.container=a}mxToolbar.prototype=new mxEventSource;mxToolbar.prototype.constructor=mxToolbar;mxToolbar.prototype.container=null;mxToolbar.prototype.enabled=!0;mxToolbar.prototype.noReset=!1;mxToolbar.prototype.updateDefaultMode=!0;
+mxToolbar.prototype.addItem=function(a,b,c,d,e,f){var g=document.createElement(null!=b?"img":"button"),k=e||(null!=f?"mxToolbarMode":"mxToolbarItem");g.className=k;g.setAttribute("src",b);null!=a&&(null!=b?g.setAttribute("title",a):mxUtils.write(g,a));this.container.appendChild(g);null!=c&&(mxEvent.addListener(g,"click",c),mxClient.IS_TOUCH&&mxEvent.addListener(g,"touchend",c));a=mxUtils.bind(this,function(a){null!=d?g.setAttribute("src",b):g.style.backgroundColor=""});mxEvent.addGestureListeners(g,
+mxUtils.bind(this,function(a){null!=d?g.setAttribute("src",d):g.style.backgroundColor="gray";if(null!=f){null==this.menu&&(this.menu=new mxPopupMenu,this.menu.init());var b=this.currentImg;this.menu.isMenuShowing()&&this.menu.hideMenu();b!=g&&(this.currentImg=g,this.menu.factoryMethod=f,b=new mxPoint(g.offsetLeft,g.offsetTop+g.offsetHeight),this.menu.popup(b.x,b.y,null,a),this.menu.isMenuShowing()&&(g.className=k+"Selected",this.menu.hideMenu=function(){mxPopupMenu.prototype.hideMenu.apply(this);
+g.className=k;this.currentImg=null}))}}),null,a);mxEvent.addListener(g,"mouseout",a);return g};mxToolbar.prototype.addCombo=function(a){var b=document.createElement("div");b.style.display="inline";b.className="mxToolbarComboContainer";var c=document.createElement("select");c.className=a||"mxToolbarCombo";b.appendChild(c);this.container.appendChild(b);return c};
+mxToolbar.prototype.addActionCombo=function(a,b){var c=document.createElement("select");c.className=b||"mxToolbarCombo";this.addOption(c,a,null);mxEvent.addListener(c,"change",function(a){var b=c.options[c.selectedIndex];c.selectedIndex=0;null!=b.funct&&b.funct(a)});this.container.appendChild(c);return c};mxToolbar.prototype.addOption=function(a,b,c){var d=document.createElement("option");mxUtils.writeln(d,b);"function"==typeof c?d.funct=c:d.setAttribute("value",c);a.appendChild(d);return d};
+mxToolbar.prototype.addSwitchMode=function(a,b,c,d,e){var f=document.createElement("img");f.initialClassName=e||"mxToolbarMode";f.className=f.initialClassName;f.setAttribute("src",b);f.altIcon=d;null!=a&&f.setAttribute("title",a);mxEvent.addListener(f,"click",mxUtils.bind(this,function(a){a=this.selectedMode.altIcon;null!=a?(this.selectedMode.altIcon=this.selectedMode.getAttribute("src"),this.selectedMode.setAttribute("src",a)):this.selectedMode.className=this.selectedMode.initialClassName;this.updateDefaultMode&&
+(this.defaultMode=f);this.selectedMode=f;a=f.altIcon;null!=a?(f.altIcon=f.getAttribute("src"),f.setAttribute("src",a)):f.className=f.initialClassName+"Selected";this.fireEvent(new mxEventObject(mxEvent.SELECT));c()}));this.container.appendChild(f);null==this.defaultMode&&(this.defaultMode=f,this.selectMode(f),c());return f};
+mxToolbar.prototype.addMode=function(a,b,c,d,e,f){f=null!=f?f:!0;var g=document.createElement(null!=b?"img":"button");g.initialClassName=e||"mxToolbarMode";g.className=g.initialClassName;g.setAttribute("src",b);g.altIcon=d;null!=a&&g.setAttribute("title",a);this.enabled&&f&&(mxEvent.addListener(g,"click",mxUtils.bind(this,function(a){this.selectMode(g,c);this.noReset=!1})),mxEvent.addListener(g,"dblclick",mxUtils.bind(this,function(a){this.selectMode(g,c);this.noReset=!0})),null==this.defaultMode&&
+(this.defaultMode=g,this.defaultFunction=c,this.selectMode(g,c)));this.container.appendChild(g);return g};
+mxToolbar.prototype.selectMode=function(a,b){if(this.selectedMode!=a){if(null!=this.selectedMode){var c=this.selectedMode.altIcon;null!=c?(this.selectedMode.altIcon=this.selectedMode.getAttribute("src"),this.selectedMode.setAttribute("src",c)):this.selectedMode.className=this.selectedMode.initialClassName}this.selectedMode=a;c=this.selectedMode.altIcon;null!=c?(this.selectedMode.altIcon=this.selectedMode.getAttribute("src"),this.selectedMode.setAttribute("src",c)):this.selectedMode.className=this.selectedMode.initialClassName+
+"Selected";this.fireEvent(new mxEventObject(mxEvent.SELECT,"function",b))}};mxToolbar.prototype.resetMode=function(a){!a&&this.noReset||this.selectedMode==this.defaultMode||this.selectMode(this.defaultMode,this.defaultFunction)};mxToolbar.prototype.addSeparator=function(a){return this.addItem(null,a,null)};mxToolbar.prototype.addBreak=function(){mxUtils.br(this.container)};
+mxToolbar.prototype.addLine=function(){var a=document.createElement("hr");a.style.marginRight="6px";a.setAttribute("size","1");this.container.appendChild(a)};mxToolbar.prototype.destroy=function(){mxEvent.release(this.container);this.selectedMode=this.defaultFunction=this.defaultMode=this.container=null;null!=this.menu&&this.menu.destroy()};function mxUndoableEdit(a,b){this.source=a;this.changes=[];this.significant=null!=b?b:!0}mxUndoableEdit.prototype.source=null;
+mxUndoableEdit.prototype.changes=null;mxUndoableEdit.prototype.significant=null;mxUndoableEdit.prototype.undone=!1;mxUndoableEdit.prototype.redone=!1;mxUndoableEdit.prototype.isEmpty=function(){return 0==this.changes.length};mxUndoableEdit.prototype.isSignificant=function(){return this.significant};mxUndoableEdit.prototype.add=function(a){this.changes.push(a)};mxUndoableEdit.prototype.notify=function(){};mxUndoableEdit.prototype.die=function(){};
+mxUndoableEdit.prototype.undo=function(){if(!this.undone){this.source.fireEvent(new mxEventObject(mxEvent.START_EDIT));for(var a=this.changes.length-1;0<=a;a--){var b=this.changes[a];null!=b.execute?b.execute():null!=b.undo&&b.undo();this.source.fireEvent(new mxEventObject(mxEvent.EXECUTED,"change",b))}this.undone=!0;this.redone=!1;this.source.fireEvent(new mxEventObject(mxEvent.END_EDIT))}this.notify()};
+mxUndoableEdit.prototype.redo=function(){if(!this.redone){this.source.fireEvent(new mxEventObject(mxEvent.START_EDIT));for(var a=this.changes.length,b=0;b<a;b++){var c=this.changes[b];null!=c.execute?c.execute():null!=c.redo&&c.redo();this.source.fireEvent(new mxEventObject(mxEvent.EXECUTED,"change",c))}this.undone=!1;this.redone=!0;this.source.fireEvent(new mxEventObject(mxEvent.END_EDIT))}this.notify()};function mxUndoManager(a){this.size=null!=a?a:100;this.clear()}mxUndoManager.prototype=new mxEventSource;
+mxUndoManager.prototype.constructor=mxUndoManager;mxUndoManager.prototype.size=null;mxUndoManager.prototype.history=null;mxUndoManager.prototype.indexOfNextAdd=0;mxUndoManager.prototype.isEmpty=function(){return 0==this.history.length};mxUndoManager.prototype.clear=function(){this.history=[];this.indexOfNextAdd=0;this.fireEvent(new mxEventObject(mxEvent.CLEAR))};mxUndoManager.prototype.canUndo=function(){return 0<this.indexOfNextAdd};
+mxUndoManager.prototype.undo=function(){for(;0<this.indexOfNextAdd;){var a=this.history[--this.indexOfNextAdd];a.undo();if(a.isSignificant()){this.fireEvent(new mxEventObject(mxEvent.UNDO,"edit",a));break}}};mxUndoManager.prototype.canRedo=function(){return this.indexOfNextAdd<this.history.length};
+mxUndoManager.prototype.redo=function(){for(var a=this.history.length;this.indexOfNextAdd<a;){var b=this.history[this.indexOfNextAdd++];b.redo();if(b.isSignificant()){this.fireEvent(new mxEventObject(mxEvent.REDO,"edit",b));break}}};mxUndoManager.prototype.undoableEditHappened=function(a){this.trim();0<this.size&&this.size==this.history.length&&this.history.shift();this.history.push(a);this.indexOfNextAdd=this.history.length;this.fireEvent(new mxEventObject(mxEvent.ADD,"edit",a))};
+mxUndoManager.prototype.trim=function(){if(this.history.length>this.indexOfNextAdd)for(var a=this.history.splice(this.indexOfNextAdd,this.history.length-this.indexOfNextAdd),b=0;b<a.length;b++)a[b].die()};var mxUrlConverter=function(){};mxUrlConverter.prototype.enabled=!0;mxUrlConverter.prototype.baseUrl=null;mxUrlConverter.prototype.baseDomain=null;
+mxUrlConverter.prototype.updateBaseUrl=function(){this.baseDomain=location.protocol+"//"+location.host;this.baseUrl=this.baseDomain+location.pathname;var a=this.baseUrl.lastIndexOf("/");0<a&&(this.baseUrl=this.baseUrl.substring(0,a+1))};mxUrlConverter.prototype.isEnabled=function(){return this.enabled};mxUrlConverter.prototype.setEnabled=function(a){this.enabled=a};mxUrlConverter.prototype.getBaseUrl=function(){return this.baseUrl};mxUrlConverter.prototype.setBaseUrl=function(a){this.baseUrl=a};
+mxUrlConverter.prototype.getBaseDomain=function(){return this.baseDomain};mxUrlConverter.prototype.setBaseDomain=function(a){this.baseDomain=a};mxUrlConverter.prototype.isRelativeUrl=function(a){return"//"!=a.substring(0,2)&&"http://"!=a.substring(0,7)&&"https://"!=a.substring(0,8)&&"data:image"!=a.substring(0,10)&&"file://"!=a.substring(0,7)};
+mxUrlConverter.prototype.convert=function(a){this.isEnabled()&&this.isRelativeUrl(a)&&(null==this.getBaseUrl()&&this.updateBaseUrl(),a="/"==a.charAt(0)?this.getBaseDomain()+a:this.getBaseUrl()+a);return a};
+function mxPanningManager(a){this.thread=null;this.active=!1;this.dy=this.dx=this.t0y=this.t0x=this.tdy=this.tdx=0;this.scrollbars=!1;this.scrollTop=this.scrollLeft=0;this.mouseListener={mouseDown:function(a,b){},mouseMove:function(a,b){},mouseUp:mxUtils.bind(this,function(a,b){this.active&&this.stop()})};a.addMouseListener(this.mouseListener);this.mouseUpListener=mxUtils.bind(this,function(){this.active&&this.stop()});mxEvent.addListener(document,"mouseup",this.mouseUpListener);var b=mxUtils.bind(this,
+function(){this.scrollbars=mxUtils.hasScrollbars(a.container);this.scrollLeft=a.container.scrollLeft;this.scrollTop=a.container.scrollTop;return window.setInterval(mxUtils.bind(this,function(){this.tdx-=this.dx;this.tdy-=this.dy;this.scrollbars?(a.panGraph(-a.container.scrollLeft-Math.ceil(this.dx),-a.container.scrollTop-Math.ceil(this.dy)),a.panDx=this.scrollLeft-a.container.scrollLeft,a.panDy=this.scrollTop-a.container.scrollTop,a.fireEvent(new mxEventObject(mxEvent.PAN))):a.panGraph(this.getDx(),
+this.getDy())}),this.delay)});this.isActive=function(){return active};this.getDx=function(){return Math.round(this.tdx)};this.getDy=function(){return Math.round(this.tdy)};this.start=function(){this.t0x=a.view.translate.x;this.t0y=a.view.translate.y;this.active=!0};this.panTo=function(c,d,e,f){this.active||this.start();this.scrollLeft=a.container.scrollLeft;this.scrollTop=a.container.scrollTop;var g=a.container;this.dx=c+(null!=e?e:0)-g.scrollLeft-g.clientWidth;this.dx=0>this.dx&&Math.abs(this.dx)<
+this.border?this.border+this.dx:this.handleMouseOut?Math.max(this.dx,0):0;0==this.dx&&(this.dx=c-g.scrollLeft,this.dx=0<this.dx&&this.dx<this.border?this.dx-this.border:this.handleMouseOut?Math.min(0,this.dx):0);this.dy=d+(null!=f?f:0)-g.scrollTop-g.clientHeight;this.dy=0>this.dy&&Math.abs(this.dy)<this.border?this.border+this.dy:this.handleMouseOut?Math.max(this.dy,0):0;0==this.dy&&(this.dy=d-g.scrollTop,this.dy=0<this.dy&&this.dy<this.border?this.dy-this.border:this.handleMouseOut?Math.min(0,this.dy):
+0);0!=this.dx||0!=this.dy?(this.dx*=this.damper,this.dy*=this.damper,null==this.thread&&(this.thread=b())):null!=this.thread&&(window.clearInterval(this.thread),this.thread=null)};this.stop=function(){if(this.active)if(this.active=!1,null!=this.thread&&(window.clearInterval(this.thread),this.thread=null),this.tdy=this.tdx=0,this.scrollbars)a.panDx=0,a.panDy=0,a.fireEvent(new mxEventObject(mxEvent.PAN));else{var b=a.panDx,d=a.panDy;if(0!=b||0!=d)a.panGraph(0,0),a.view.setTranslate(this.t0x+b/a.view.scale,
+this.t0y+d/a.view.scale)}};this.destroy=function(){a.removeMouseListener(this.mouseListener);mxEvent.removeListener(document,"mouseup",this.mouseUpListener)}}mxPanningManager.prototype.damper=1/6;mxPanningManager.prototype.delay=10;mxPanningManager.prototype.handleMouseOut=!0;mxPanningManager.prototype.border=0;function mxPopupMenu(a){this.factoryMethod=a;null!=a&&this.init()}mxPopupMenu.prototype=new mxEventSource;mxPopupMenu.prototype.constructor=mxPopupMenu;
+mxPopupMenu.prototype.submenuImage=mxClient.imageBasePath+"/submenu.gif";mxPopupMenu.prototype.zIndex=10006;mxPopupMenu.prototype.factoryMethod=null;mxPopupMenu.prototype.useLeftButtonForPopup=!1;mxPopupMenu.prototype.enabled=!0;mxPopupMenu.prototype.itemCount=0;mxPopupMenu.prototype.autoExpand=!1;mxPopupMenu.prototype.smartSeparators=!1;mxPopupMenu.prototype.labels=!0;
+mxPopupMenu.prototype.init=function(){this.table=document.createElement("table");this.table.className="mxPopupMenu";this.tbody=document.createElement("tbody");this.table.appendChild(this.tbody);this.div=document.createElement("div");this.div.className="mxPopupMenu";this.div.style.display="inline";this.div.style.zIndex=this.zIndex;this.div.appendChild(this.table);mxEvent.disableContextMenu(this.div)};mxPopupMenu.prototype.isEnabled=function(){return this.enabled};
+mxPopupMenu.prototype.setEnabled=function(a){this.enabled=a};mxPopupMenu.prototype.isPopupTrigger=function(a){return a.isPopupTrigger()||this.useLeftButtonForPopup&&mxEvent.isLeftMouseButton(a.getEvent())};
+mxPopupMenu.prototype.addItem=function(a,b,c,d,e,f,g){d=d||this;this.itemCount++;d.willAddSeparator&&(d.containsItems&&this.addSeparator(d,!0),d.willAddSeparator=!1);d.containsItems=!0;var k=document.createElement("tr");k.className="mxPopupMenuItem";var l=document.createElement("td");l.className="mxPopupMenuIcon";null!=b?(e=document.createElement("img"),e.src=b,l.appendChild(e)):null!=e&&(b=document.createElement("div"),b.className=e,l.appendChild(b));k.appendChild(l);this.labels&&(l=document.createElement("td"),
+l.className="mxPopupMenuItem"+(null==f||f?"":" mxDisabled"),mxUtils.write(l,a),l.align="left",k.appendChild(l),a=document.createElement("td"),a.className="mxPopupMenuItem"+(null==f||f?"":" mxDisabled"),a.style.paddingRight="6px",a.style.textAlign="right",k.appendChild(a),null==d.div&&this.createSubmenu(d));d.tbody.appendChild(k);if(0!=g&&0!=f){var m=null;mxEvent.addGestureListeners(k,mxUtils.bind(this,function(a){this.eventReceiver=k;d.activeRow!=k&&d.activeRow!=d&&(null!=d.activeRow&&null!=d.activeRow.div.parentNode&&
+this.hideSubmenu(d),null!=k.div&&(this.showSubmenu(d,k),d.activeRow=k));if(mxClient.IS_QUIRKS||8==document.documentMode)m=document.selection.createRange();mxEvent.consume(a)}),mxUtils.bind(this,function(a){d.activeRow!=k&&d.activeRow!=d&&(null!=d.activeRow&&null!=d.activeRow.div.parentNode&&this.hideSubmenu(d),this.autoExpand&&null!=k.div&&(this.showSubmenu(d,k),d.activeRow=k));k.className="mxPopupMenuItemHover"}),mxUtils.bind(this,function(a){if(this.eventReceiver==k){d.activeRow!=k&&this.hideMenu();
+if(null!=m){try{m.select()}catch(p){}m=null}null!=c&&c(a)}this.eventReceiver=null;mxEvent.consume(a)}));mxEvent.addListener(k,"mouseout",mxUtils.bind(this,function(a){k.className="mxPopupMenuItem"}))}return k};mxPopupMenu.prototype.addCheckmark=function(a,b){var c=a.firstChild.nextSibling;c.style.backgroundImage="url('"+b+"')";c.style.backgroundRepeat="no-repeat";c.style.backgroundPosition="2px 50%"};
+mxPopupMenu.prototype.createSubmenu=function(a){a.table=document.createElement("table");a.table.className="mxPopupMenu";a.tbody=document.createElement("tbody");a.table.appendChild(a.tbody);a.div=document.createElement("div");a.div.className="mxPopupMenu";a.div.style.position="absolute";a.div.style.display="inline";a.div.style.zIndex=this.zIndex;a.div.appendChild(a.table);var b=document.createElement("img");b.setAttribute("src",this.submenuImage);td=a.firstChild.nextSibling.nextSibling;td.appendChild(b)};
+mxPopupMenu.prototype.showSubmenu=function(a,b){if(null!=b.div){b.div.style.left=a.div.offsetLeft+b.offsetLeft+b.offsetWidth-1+"px";b.div.style.top=a.div.offsetTop+b.offsetTop+"px";document.body.appendChild(b.div);var c=parseInt(b.div.offsetLeft),d=parseInt(b.div.offsetWidth),e=mxUtils.getDocumentScrollOrigin(document),f=document.documentElement;c+d>e.x+(document.body.clientWidth||f.clientWidth)&&(b.div.style.left=Math.max(0,a.div.offsetLeft-d+(mxClient.IS_IE?6:-6))+"px");mxUtils.fit(b.div)}};
+mxPopupMenu.prototype.addSeparator=function(a,b){a=a||this;if(this.smartSeparators&&!b)a.willAddSeparator=!0;else if(null!=a.tbody){a.willAddSeparator=!1;var c=document.createElement("tr"),d=document.createElement("td");d.className="mxPopupMenuIcon";d.style.padding="0 0 0 0px";c.appendChild(d);d=document.createElement("td");d.style.padding="0 0 0 0px";d.setAttribute("colSpan","2");var e=document.createElement("hr");e.setAttribute("size","1");d.appendChild(e);c.appendChild(d);a.tbody.appendChild(c)}};
+mxPopupMenu.prototype.popup=function(a,b,c,d){if(null!=this.div&&null!=this.tbody&&null!=this.factoryMethod){this.div.style.left=a+"px";for(this.div.style.top=b+"px";null!=this.tbody.firstChild;)mxEvent.release(this.tbody.firstChild),this.tbody.removeChild(this.tbody.firstChild);this.itemCount=0;this.factoryMethod(this,c,d);0<this.itemCount&&(this.showMenu(),this.fireEvent(new mxEventObject(mxEvent.SHOW)))}};
+mxPopupMenu.prototype.isMenuShowing=function(){return null!=this.div&&this.div.parentNode==document.body};mxPopupMenu.prototype.showMenu=function(){9<=document.documentMode&&(this.div.style.filter="none");document.body.appendChild(this.div);mxUtils.fit(this.div)};mxPopupMenu.prototype.hideMenu=function(){null!=this.div&&(null!=this.div.parentNode&&this.div.parentNode.removeChild(this.div),this.hideSubmenu(this),this.containsItems=!1,this.fireEvent(new mxEventObject(mxEvent.HIDE)))};
+mxPopupMenu.prototype.hideSubmenu=function(a){null!=a.activeRow&&(this.hideSubmenu(a.activeRow),null!=a.activeRow.div.parentNode&&a.activeRow.div.parentNode.removeChild(a.activeRow.div),a.activeRow=null)};mxPopupMenu.prototype.destroy=function(){null!=this.div&&(mxEvent.release(this.div),null!=this.div.parentNode&&this.div.parentNode.removeChild(this.div),this.div=null)};
+function mxAutoSaveManager(a){this.changeHandler=mxUtils.bind(this,function(a,c){this.isEnabled()&&this.graphModelChanged(c.getProperty("edit").changes)});this.setGraph(a)}mxAutoSaveManager.prototype=new mxEventSource;mxAutoSaveManager.prototype.constructor=mxAutoSaveManager;mxAutoSaveManager.prototype.graph=null;mxAutoSaveManager.prototype.autoSaveDelay=10;mxAutoSaveManager.prototype.autoSaveThrottle=2;mxAutoSaveManager.prototype.autoSaveThreshold=5;mxAutoSaveManager.prototype.ignoredChanges=0;
+mxAutoSaveManager.prototype.lastSnapshot=0;mxAutoSaveManager.prototype.enabled=!0;mxAutoSaveManager.prototype.changeHandler=null;mxAutoSaveManager.prototype.isEnabled=function(){return this.enabled};mxAutoSaveManager.prototype.setEnabled=function(a){this.enabled=a};mxAutoSaveManager.prototype.setGraph=function(a){null!=this.graph&&this.graph.getModel().removeListener(this.changeHandler);this.graph=a;null!=this.graph&&this.graph.getModel().addListener(mxEvent.CHANGE,this.changeHandler)};
+mxAutoSaveManager.prototype.save=function(){};mxAutoSaveManager.prototype.graphModelChanged=function(a){a=((new Date).getTime()-this.lastSnapshot)/1E3;a>this.autoSaveDelay||this.ignoredChanges>=this.autoSaveThreshold&&a>this.autoSaveThrottle?(this.save(),this.reset()):this.ignoredChanges++};mxAutoSaveManager.prototype.reset=function(){this.lastSnapshot=(new Date).getTime();this.ignoredChanges=0};mxAutoSaveManager.prototype.destroy=function(){this.setGraph(null)};
+function mxAnimation(a){this.delay=null!=a?a:20}mxAnimation.prototype=new mxEventSource;mxAnimation.prototype.constructor=mxAnimation;mxAnimation.prototype.delay=null;mxAnimation.prototype.thread=null;mxAnimation.prototype.isRunning=function(){return null!=this.thread};mxAnimation.prototype.startAnimation=function(){null==this.thread&&(this.thread=window.setInterval(mxUtils.bind(this,this.updateAnimation),this.delay))};mxAnimation.prototype.updateAnimation=function(){this.fireEvent(new mxEventObject(mxEvent.EXECUTE))};
+mxAnimation.prototype.stopAnimation=function(){null!=this.thread&&(window.clearInterval(this.thread),this.thread=null,this.fireEvent(new mxEventObject(mxEvent.DONE)))};function mxMorphing(a,b,c,d){mxAnimation.call(this,d);this.graph=a;this.steps=null!=b?b:6;this.ease=null!=c?c:1.5}mxMorphing.prototype=new mxAnimation;mxMorphing.prototype.constructor=mxMorphing;mxMorphing.prototype.graph=null;mxMorphing.prototype.steps=null;mxMorphing.prototype.step=0;mxMorphing.prototype.ease=null;
+mxMorphing.prototype.cells=null;mxMorphing.prototype.updateAnimation=function(){mxAnimation.prototype.updateAnimation.apply(this,arguments);var a=new mxCellStatePreview(this.graph);if(null!=this.cells)for(var b=0;b<this.cells.length;b++)this.animateCell(this.cells[b],a,!1);else this.animateCell(this.graph.getModel().getRoot(),a,!0);this.show(a);(a.isEmpty()||this.step++>=this.steps)&&this.stopAnimation()};mxMorphing.prototype.show=function(a){a.show()};
+mxMorphing.prototype.animateCell=function(a,b,c){var d=this.graph.getView().getState(a),e=null;if(null!=d&&(e=this.getDelta(d),this.graph.getModel().isVertex(a)&&(0!=e.x||0!=e.y))){var f=this.graph.view.getTranslate(),g=this.graph.view.getScale();e.x+=f.x*g;e.y+=f.y*g;b.moveState(d,-e.x/this.ease,-e.y/this.ease)}if(c&&!this.stopRecursion(d,e))for(d=this.graph.getModel().getChildCount(a),e=0;e<d;e++)this.animateCell(this.graph.getModel().getChildAt(a,e),b,c)};
+mxMorphing.prototype.stopRecursion=function(a,b){return null!=b&&(0!=b.x||0!=b.y)};mxMorphing.prototype.getDelta=function(a){var b=this.getOriginForCell(a.cell),c=this.graph.getView().getTranslate(),d=this.graph.getView().getScale();return new mxPoint((b.x-(a.x/d-c.x))*d,(b.y-(a.y/d-c.y))*d)};
+mxMorphing.prototype.getOriginForCell=function(a){var b=null;if(null!=a){var c=this.graph.getModel().getParent(a);a=this.graph.getCellGeometry(a);b=this.getOriginForCell(c);null!=a&&(a.relative?(c=this.graph.getCellGeometry(c),null!=c&&(b.x+=a.x*c.width,b.y+=a.y*c.height)):(b.x+=a.x,b.y+=a.y))}null==b&&(b=this.graph.view.getTranslate(),b=new mxPoint(-b.x,-b.y));return b};function mxImageBundle(a){this.images=[];this.alt=null!=a?a:!1}mxImageBundle.prototype.images=null;
+mxImageBundle.prototype.images=null;mxImageBundle.prototype.putImage=function(a,b,c){this.images[a]={value:b,fallback:c}};mxImageBundle.prototype.getImage=function(a){var b=null;null!=a&&(a=this.images[a],null!=a&&(b=this.alt?a.fallback:a.value));return b};function mxImageExport(){}mxImageExport.prototype.includeOverlays=!1;
+mxImageExport.prototype.drawState=function(a,b){null!=a&&(this.visitStatesRecursive(a,b,mxUtils.bind(this,function(){this.drawCellState.apply(this,arguments)})),this.includeOverlays&&this.visitStatesRecursive(a,b,mxUtils.bind(this,function(){this.drawOverlays.apply(this,arguments)})))};
+mxImageExport.prototype.visitStatesRecursive=function(a,b,c){if(null!=a){c(a,b);for(var d=a.view.graph,e=d.model.getChildCount(a.cell),f=0;f<e;f++){var g=d.view.getState(d.model.getChildAt(a.cell,f));this.visitStatesRecursive(g,b,c)}}};mxImageExport.prototype.getLinkForCellState=function(a,b){return null};mxImageExport.prototype.drawCellState=function(a,b){var c=this.getLinkForCellState(a,b);null!=c&&b.setLink(c);this.drawShape(a,b);this.drawText(a,b);null!=c&&b.setLink(null)};
+mxImageExport.prototype.drawShape=function(a,b){a.shape instanceof mxShape&&a.shape.checkBounds()&&(b.save(),a.shape.paint(b),b.restore())};mxImageExport.prototype.drawText=function(a,b){null!=a.text&&a.text.checkBounds()&&(b.save(),a.text.paint(b),b.restore())};mxImageExport.prototype.drawOverlays=function(a,b){null!=a.overlays&&a.overlays.visit(function(a,d){d instanceof mxShape&&d.paint(b)})};function mxAbstractCanvas2D(){this.converter=this.createUrlConverter();this.reset()}
+mxAbstractCanvas2D.prototype.state=null;mxAbstractCanvas2D.prototype.states=null;mxAbstractCanvas2D.prototype.path=null;mxAbstractCanvas2D.prototype.rotateHtml=!0;mxAbstractCanvas2D.prototype.lastX=0;mxAbstractCanvas2D.prototype.lastY=0;mxAbstractCanvas2D.prototype.moveOp="M";mxAbstractCanvas2D.prototype.lineOp="L";mxAbstractCanvas2D.prototype.quadOp="Q";mxAbstractCanvas2D.prototype.curveOp="C";mxAbstractCanvas2D.prototype.closeOp="Z";mxAbstractCanvas2D.prototype.pointerEvents=!1;
+mxAbstractCanvas2D.prototype.createUrlConverter=function(){return new mxUrlConverter};mxAbstractCanvas2D.prototype.reset=function(){this.state=this.createState();this.states=[]};
+mxAbstractCanvas2D.prototype.createState=function(){return{dx:0,dy:0,scale:1,alpha:1,fillAlpha:1,strokeAlpha:1,fillColor:null,gradientFillAlpha:1,gradientColor:null,gradientAlpha:1,gradientDirection:null,strokeColor:null,strokeWidth:1,dashed:!1,dashPattern:"3 3",fixDash:!1,lineCap:"flat",lineJoin:"miter",miterLimit:10,fontColor:"#000000",fontBackgroundColor:null,fontBorderColor:null,fontSize:mxConstants.DEFAULT_FONTSIZE,fontFamily:mxConstants.DEFAULT_FONTFAMILY,fontStyle:0,shadow:!1,shadowColor:mxConstants.SHADOWCOLOR,
+shadowAlpha:mxConstants.SHADOW_OPACITY,shadowDx:mxConstants.SHADOW_OFFSET_X,shadowDy:mxConstants.SHADOW_OFFSET_Y,rotation:0,rotationCx:0,rotationCy:0}};mxAbstractCanvas2D.prototype.format=function(a){return Math.round(parseFloat(a))};
+mxAbstractCanvas2D.prototype.addOp=function(){if(null!=this.path&&(this.path.push(arguments[0]),2<arguments.length))for(var a=this.state,b=2;b<arguments.length;b+=2)this.lastX=arguments[b-1],this.lastY=arguments[b],this.path.push(this.format((this.lastX+a.dx)*a.scale)),this.path.push(this.format((this.lastY+a.dy)*a.scale))};mxAbstractCanvas2D.prototype.rotatePoint=function(a,b,c,d,e){c*=Math.PI/180;return mxUtils.getRotatedPoint(new mxPoint(a,b),Math.cos(c),Math.sin(c),new mxPoint(d,e))};
+mxAbstractCanvas2D.prototype.save=function(){this.states.push(this.state);this.state=mxUtils.clone(this.state)};mxAbstractCanvas2D.prototype.restore=function(){0<this.states.length&&(this.state=this.states.pop())};mxAbstractCanvas2D.prototype.setLink=function(a){};mxAbstractCanvas2D.prototype.scale=function(a){this.state.scale*=a;this.state.strokeWidth*=a};mxAbstractCanvas2D.prototype.translate=function(a,b){this.state.dx+=a;this.state.dy+=b};mxAbstractCanvas2D.prototype.rotate=function(a,b,c,d,e){};
+mxAbstractCanvas2D.prototype.setAlpha=function(a){this.state.alpha=a};mxAbstractCanvas2D.prototype.setFillAlpha=function(a){this.state.fillAlpha=a};mxAbstractCanvas2D.prototype.setStrokeAlpha=function(a){this.state.strokeAlpha=a};mxAbstractCanvas2D.prototype.setFillColor=function(a){a==mxConstants.NONE&&(a=null);this.state.fillColor=a;this.state.gradientColor=null};
+mxAbstractCanvas2D.prototype.setGradient=function(a,b,c,d,e,f,g,k,l){c=this.state;c.fillColor=a;c.gradientFillAlpha=null!=k?k:1;c.gradientColor=b;c.gradientAlpha=null!=l?l:1;c.gradientDirection=g};mxAbstractCanvas2D.prototype.setStrokeColor=function(a){a==mxConstants.NONE&&(a=null);this.state.strokeColor=a};mxAbstractCanvas2D.prototype.setStrokeWidth=function(a){this.state.strokeWidth=a};mxAbstractCanvas2D.prototype.setDashed=function(a,b){this.state.dashed=a;this.state.fixDash=b};
+mxAbstractCanvas2D.prototype.setDashPattern=function(a){this.state.dashPattern=a};mxAbstractCanvas2D.prototype.setLineCap=function(a){this.state.lineCap=a};mxAbstractCanvas2D.prototype.setLineJoin=function(a){this.state.lineJoin=a};mxAbstractCanvas2D.prototype.setMiterLimit=function(a){this.state.miterLimit=a};mxAbstractCanvas2D.prototype.setFontColor=function(a){a==mxConstants.NONE&&(a=null);this.state.fontColor=a};
+mxAbstractCanvas2D.prototype.setFontBackgroundColor=function(a){a==mxConstants.NONE&&(a=null);this.state.fontBackgroundColor=a};mxAbstractCanvas2D.prototype.setFontBorderColor=function(a){a==mxConstants.NONE&&(a=null);this.state.fontBorderColor=a};mxAbstractCanvas2D.prototype.setFontSize=function(a){this.state.fontSize=parseFloat(a)};mxAbstractCanvas2D.prototype.setFontFamily=function(a){this.state.fontFamily=a};
+mxAbstractCanvas2D.prototype.setFontStyle=function(a){null==a&&(a=0);this.state.fontStyle=a};mxAbstractCanvas2D.prototype.setShadow=function(a){this.state.shadow=a};mxAbstractCanvas2D.prototype.setShadowColor=function(a){a==mxConstants.NONE&&(a=null);this.state.shadowColor=a};mxAbstractCanvas2D.prototype.setShadowAlpha=function(a){this.state.shadowAlpha=a};mxAbstractCanvas2D.prototype.setShadowOffset=function(a,b){this.state.shadowDx=a;this.state.shadowDy=b};
+mxAbstractCanvas2D.prototype.begin=function(){this.lastY=this.lastX=0;this.path=[]};mxAbstractCanvas2D.prototype.moveTo=function(a,b){this.addOp(this.moveOp,a,b)};mxAbstractCanvas2D.prototype.lineTo=function(a,b){this.addOp(this.lineOp,a,b)};mxAbstractCanvas2D.prototype.quadTo=function(a,b,c,d){this.addOp(this.quadOp,a,b,c,d)};mxAbstractCanvas2D.prototype.curveTo=function(a,b,c,d,e,f){this.addOp(this.curveOp,a,b,c,d,e,f)};
+mxAbstractCanvas2D.prototype.arcTo=function(a,b,c,d,e,f,g){a=mxUtils.arcToCurves(this.lastX,this.lastY,a,b,c,d,e,f,g);if(null!=a)for(b=0;b<a.length;b+=6)this.curveTo(a[b],a[b+1],a[b+2],a[b+3],a[b+4],a[b+5])};mxAbstractCanvas2D.prototype.close=function(a,b,c,d,e,f){this.addOp(this.closeOp)};mxAbstractCanvas2D.prototype.end=function(){};function mxXmlCanvas2D(a){mxAbstractCanvas2D.call(this);this.root=a;this.writeDefaults()}mxUtils.extend(mxXmlCanvas2D,mxAbstractCanvas2D);
+mxXmlCanvas2D.prototype.textEnabled=!0;mxXmlCanvas2D.prototype.compressed=!0;
+mxXmlCanvas2D.prototype.writeDefaults=function(){var a;a=this.createElement("fontfamily");a.setAttribute("family",mxConstants.DEFAULT_FONTFAMILY);this.root.appendChild(a);a=this.createElement("fontsize");a.setAttribute("size",mxConstants.DEFAULT_FONTSIZE);this.root.appendChild(a);a=this.createElement("shadowcolor");a.setAttribute("color",mxConstants.SHADOWCOLOR);this.root.appendChild(a);a=this.createElement("shadowalpha");a.setAttribute("alpha",mxConstants.SHADOW_OPACITY);this.root.appendChild(a);
+a=this.createElement("shadowoffset");a.setAttribute("dx",mxConstants.SHADOW_OFFSET_X);a.setAttribute("dy",mxConstants.SHADOW_OFFSET_Y);this.root.appendChild(a)};mxXmlCanvas2D.prototype.format=function(a){return parseFloat(parseFloat(a).toFixed(2))};mxXmlCanvas2D.prototype.createElement=function(a){return this.root.ownerDocument.createElement(a)};mxXmlCanvas2D.prototype.save=function(){this.compressed&&mxAbstractCanvas2D.prototype.save.apply(this,arguments);this.root.appendChild(this.createElement("save"))};
+mxXmlCanvas2D.prototype.restore=function(){this.compressed&&mxAbstractCanvas2D.prototype.restore.apply(this,arguments);this.root.appendChild(this.createElement("restore"))};mxXmlCanvas2D.prototype.scale=function(a){var b=this.createElement("scale");b.setAttribute("scale",a);this.root.appendChild(b)};mxXmlCanvas2D.prototype.translate=function(a,b){var c=this.createElement("translate");c.setAttribute("dx",this.format(a));c.setAttribute("dy",this.format(b));this.root.appendChild(c)};
+mxXmlCanvas2D.prototype.rotate=function(a,b,c,d,e){var f=this.createElement("rotate");if(0!=a||b||c)f.setAttribute("theta",this.format(a)),f.setAttribute("flipH",b?"1":"0"),f.setAttribute("flipV",c?"1":"0"),f.setAttribute("cx",this.format(d)),f.setAttribute("cy",this.format(e)),this.root.appendChild(f)};
+mxXmlCanvas2D.prototype.setAlpha=function(a){if(this.compressed){if(this.state.alpha==a)return;mxAbstractCanvas2D.prototype.setAlpha.apply(this,arguments)}var b=this.createElement("alpha");b.setAttribute("alpha",this.format(a));this.root.appendChild(b)};mxXmlCanvas2D.prototype.setFillAlpha=function(a){if(this.compressed){if(this.state.fillAlpha==a)return;mxAbstractCanvas2D.prototype.setFillAlpha.apply(this,arguments)}var b=this.createElement("fillalpha");b.setAttribute("alpha",this.format(a));this.root.appendChild(b)};
+mxXmlCanvas2D.prototype.setStrokeAlpha=function(a){if(this.compressed){if(this.state.strokeAlpha==a)return;mxAbstractCanvas2D.prototype.setStrokeAlpha.apply(this,arguments)}var b=this.createElement("strokealpha");b.setAttribute("alpha",this.format(a));this.root.appendChild(b)};
+mxXmlCanvas2D.prototype.setFillColor=function(a){a==mxConstants.NONE&&(a=null);if(this.compressed){if(this.state.fillColor==a)return;mxAbstractCanvas2D.prototype.setFillColor.apply(this,arguments)}var b=this.createElement("fillcolor");b.setAttribute("color",null!=a?a:mxConstants.NONE);this.root.appendChild(b)};
+mxXmlCanvas2D.prototype.setGradient=function(a,b,c,d,e,f,g,k,l){if(null!=a&&null!=b){mxAbstractCanvas2D.prototype.setGradient.apply(this,arguments);var m=this.createElement("gradient");m.setAttribute("c1",a);m.setAttribute("c2",b);m.setAttribute("x",this.format(c));m.setAttribute("y",this.format(d));m.setAttribute("w",this.format(e));m.setAttribute("h",this.format(f));null!=g&&m.setAttribute("direction",g);null!=k&&m.setAttribute("alpha1",k);null!=l&&m.setAttribute("alpha2",l);this.root.appendChild(m)}};
+mxXmlCanvas2D.prototype.setStrokeColor=function(a){a==mxConstants.NONE&&(a=null);if(this.compressed){if(this.state.strokeColor==a)return;mxAbstractCanvas2D.prototype.setStrokeColor.apply(this,arguments)}var b=this.createElement("strokecolor");b.setAttribute("color",null!=a?a:mxConstants.NONE);this.root.appendChild(b)};
+mxXmlCanvas2D.prototype.setStrokeWidth=function(a){if(this.compressed){if(this.state.strokeWidth==a)return;mxAbstractCanvas2D.prototype.setStrokeWidth.apply(this,arguments)}var b=this.createElement("strokewidth");b.setAttribute("width",this.format(a));this.root.appendChild(b)};
+mxXmlCanvas2D.prototype.setDashed=function(a,b){if(this.compressed){if(this.state.dashed==a)return;mxAbstractCanvas2D.prototype.setDashed.apply(this,arguments)}var c=this.createElement("dashed");c.setAttribute("dashed",a?"1":"0");null!=b&&c.setAttribute("fixDash",b?"1":"0");this.root.appendChild(c)};
+mxXmlCanvas2D.prototype.setDashPattern=function(a){if(this.compressed){if(this.state.dashPattern==a)return;mxAbstractCanvas2D.prototype.setDashPattern.apply(this,arguments)}var b=this.createElement("dashpattern");b.setAttribute("pattern",a);this.root.appendChild(b)};mxXmlCanvas2D.prototype.setLineCap=function(a){if(this.compressed){if(this.state.lineCap==a)return;mxAbstractCanvas2D.prototype.setLineCap.apply(this,arguments)}var b=this.createElement("linecap");b.setAttribute("cap",a);this.root.appendChild(b)};
+mxXmlCanvas2D.prototype.setLineJoin=function(a){if(this.compressed){if(this.state.lineJoin==a)return;mxAbstractCanvas2D.prototype.setLineJoin.apply(this,arguments)}var b=this.createElement("linejoin");b.setAttribute("join",a);this.root.appendChild(b)};mxXmlCanvas2D.prototype.setMiterLimit=function(a){if(this.compressed){if(this.state.miterLimit==a)return;mxAbstractCanvas2D.prototype.setMiterLimit.apply(this,arguments)}var b=this.createElement("miterlimit");b.setAttribute("limit",a);this.root.appendChild(b)};
+mxXmlCanvas2D.prototype.setFontColor=function(a){if(this.textEnabled){a==mxConstants.NONE&&(a=null);if(this.compressed){if(this.state.fontColor==a)return;mxAbstractCanvas2D.prototype.setFontColor.apply(this,arguments)}var b=this.createElement("fontcolor");b.setAttribute("color",null!=a?a:mxConstants.NONE);this.root.appendChild(b)}};
+mxXmlCanvas2D.prototype.setFontBackgroundColor=function(a){if(this.textEnabled){a==mxConstants.NONE&&(a=null);if(this.compressed){if(this.state.fontBackgroundColor==a)return;mxAbstractCanvas2D.prototype.setFontBackgroundColor.apply(this,arguments)}var b=this.createElement("fontbackgroundcolor");b.setAttribute("color",null!=a?a:mxConstants.NONE);this.root.appendChild(b)}};
+mxXmlCanvas2D.prototype.setFontBorderColor=function(a){if(this.textEnabled){a==mxConstants.NONE&&(a=null);if(this.compressed){if(this.state.fontBorderColor==a)return;mxAbstractCanvas2D.prototype.setFontBorderColor.apply(this,arguments)}var b=this.createElement("fontbordercolor");b.setAttribute("color",null!=a?a:mxConstants.NONE);this.root.appendChild(b)}};
+mxXmlCanvas2D.prototype.setFontSize=function(a){if(this.textEnabled){if(this.compressed){if(this.state.fontSize==a)return;mxAbstractCanvas2D.prototype.setFontSize.apply(this,arguments)}var b=this.createElement("fontsize");b.setAttribute("size",a);this.root.appendChild(b)}};
+mxXmlCanvas2D.prototype.setFontFamily=function(a){if(this.textEnabled){if(this.compressed){if(this.state.fontFamily==a)return;mxAbstractCanvas2D.prototype.setFontFamily.apply(this,arguments)}var b=this.createElement("fontfamily");b.setAttribute("family",a);this.root.appendChild(b)}};
+mxXmlCanvas2D.prototype.setFontStyle=function(a){if(this.textEnabled){null==a&&(a=0);if(this.compressed){if(this.state.fontStyle==a)return;mxAbstractCanvas2D.prototype.setFontStyle.apply(this,arguments)}var b=this.createElement("fontstyle");b.setAttribute("style",a);this.root.appendChild(b)}};
+mxXmlCanvas2D.prototype.setShadow=function(a){if(this.compressed){if(this.state.shadow==a)return;mxAbstractCanvas2D.prototype.setShadow.apply(this,arguments)}var b=this.createElement("shadow");b.setAttribute("enabled",a?"1":"0");this.root.appendChild(b)};
+mxXmlCanvas2D.prototype.setShadowColor=function(a){if(this.compressed){a==mxConstants.NONE&&(a=null);if(this.state.shadowColor==a)return;mxAbstractCanvas2D.prototype.setShadowColor.apply(this,arguments)}var b=this.createElement("shadowcolor");b.setAttribute("color",null!=a?a:mxConstants.NONE);this.root.appendChild(b)};
+mxXmlCanvas2D.prototype.setShadowAlpha=function(a){if(this.compressed){if(this.state.shadowAlpha==a)return;mxAbstractCanvas2D.prototype.setShadowAlpha.apply(this,arguments)}var b=this.createElement("shadowalpha");b.setAttribute("alpha",a);this.root.appendChild(b)};
+mxXmlCanvas2D.prototype.setShadowOffset=function(a,b){if(this.compressed){if(this.state.shadowDx==a&&this.state.shadowDy==b)return;mxAbstractCanvas2D.prototype.setShadowOffset.apply(this,arguments)}var c=this.createElement("shadowoffset");c.setAttribute("dx",a);c.setAttribute("dy",b);this.root.appendChild(c)};
+mxXmlCanvas2D.prototype.rect=function(a,b,c,d){var e=this.createElement("rect");e.setAttribute("x",this.format(a));e.setAttribute("y",this.format(b));e.setAttribute("w",this.format(c));e.setAttribute("h",this.format(d));this.root.appendChild(e)};
+mxXmlCanvas2D.prototype.roundrect=function(a,b,c,d,e,f){var g=this.createElement("roundrect");g.setAttribute("x",this.format(a));g.setAttribute("y",this.format(b));g.setAttribute("w",this.format(c));g.setAttribute("h",this.format(d));g.setAttribute("dx",this.format(e));g.setAttribute("dy",this.format(f));this.root.appendChild(g)};
+mxXmlCanvas2D.prototype.ellipse=function(a,b,c,d){var e=this.createElement("ellipse");e.setAttribute("x",this.format(a));e.setAttribute("y",this.format(b));e.setAttribute("w",this.format(c));e.setAttribute("h",this.format(d));this.root.appendChild(e)};
+mxXmlCanvas2D.prototype.image=function(a,b,c,d,e,f,g,k){e=this.converter.convert(e);var l=this.createElement("image");l.setAttribute("x",this.format(a));l.setAttribute("y",this.format(b));l.setAttribute("w",this.format(c));l.setAttribute("h",this.format(d));l.setAttribute("src",e);l.setAttribute("aspect",f?"1":"0");l.setAttribute("flipH",g?"1":"0");l.setAttribute("flipV",k?"1":"0");this.root.appendChild(l)};
+mxXmlCanvas2D.prototype.begin=function(){this.root.appendChild(this.createElement("begin"));this.lastY=this.lastX=0};mxXmlCanvas2D.prototype.moveTo=function(a,b){var c=this.createElement("move");c.setAttribute("x",this.format(a));c.setAttribute("y",this.format(b));this.root.appendChild(c);this.lastX=a;this.lastY=b};
+mxXmlCanvas2D.prototype.lineTo=function(a,b){var c=this.createElement("line");c.setAttribute("x",this.format(a));c.setAttribute("y",this.format(b));this.root.appendChild(c);this.lastX=a;this.lastY=b};mxXmlCanvas2D.prototype.quadTo=function(a,b,c,d){var e=this.createElement("quad");e.setAttribute("x1",this.format(a));e.setAttribute("y1",this.format(b));e.setAttribute("x2",this.format(c));e.setAttribute("y2",this.format(d));this.root.appendChild(e);this.lastX=c;this.lastY=d};
+mxXmlCanvas2D.prototype.curveTo=function(a,b,c,d,e,f){var g=this.createElement("curve");g.setAttribute("x1",this.format(a));g.setAttribute("y1",this.format(b));g.setAttribute("x2",this.format(c));g.setAttribute("y2",this.format(d));g.setAttribute("x3",this.format(e));g.setAttribute("y3",this.format(f));this.root.appendChild(g);this.lastX=e;this.lastY=f};mxXmlCanvas2D.prototype.close=function(){this.root.appendChild(this.createElement("close"))};
+mxXmlCanvas2D.prototype.text=function(a,b,c,d,e,f,g,k,l,m,n,p,q){if(this.textEnabled&&null!=e){mxUtils.isNode(e)&&(e=mxUtils.getOuterHtml(e));var r=this.createElement("text");r.setAttribute("x",this.format(a));r.setAttribute("y",this.format(b));r.setAttribute("w",this.format(c));r.setAttribute("h",this.format(d));r.setAttribute("str",e);null!=f&&r.setAttribute("align",f);null!=g&&r.setAttribute("valign",g);r.setAttribute("wrap",k?"1":"0");null==l&&(l="");r.setAttribute("format",l);null!=m&&r.setAttribute("overflow",
+m);null!=n&&r.setAttribute("clip",n?"1":"0");null!=p&&r.setAttribute("rotation",p);null!=q&&r.setAttribute("dir",q);this.root.appendChild(r)}};mxXmlCanvas2D.prototype.stroke=function(){this.root.appendChild(this.createElement("stroke"))};mxXmlCanvas2D.prototype.fill=function(){this.root.appendChild(this.createElement("fill"))};mxXmlCanvas2D.prototype.fillAndStroke=function(){this.root.appendChild(this.createElement("fillstroke"))};
+function mxSvgCanvas2D(a,b){mxAbstractCanvas2D.call(this);this.root=a;this.gradients=[];this.defs=null;this.styleEnabled=null!=b?b:!1;var c=null;if(a.ownerDocument!=document)for(c=a;null!=c&&"svg"!=c.nodeName;)c=c.parentNode;null!=c&&(0<c.getElementsByTagName("defs").length&&(this.defs=c.getElementsByTagName("defs")[0]),null==this.defs&&(this.defs=this.createElement("defs"),null!=c.firstChild?c.insertBefore(this.defs,c.firstChild):c.appendChild(this.defs)),this.styleEnabled&&this.defs.appendChild(this.createStyle()))}
+mxUtils.extend(mxSvgCanvas2D,mxAbstractCanvas2D);(function(){mxSvgCanvas2D.prototype.useDomParser=!mxClient.IS_IE&&"function"===typeof DOMParser&&"function"===typeof XMLSerializer;if(mxSvgCanvas2D.prototype.useDomParser)try{var a=(new DOMParser).parseFromString("test text","text/html");mxSvgCanvas2D.prototype.useDomParser=null!=a}catch(b){mxSvgCanvas2D.prototype.useDomParser=!1}})();mxSvgCanvas2D.prototype.node=null;mxSvgCanvas2D.prototype.matchHtmlAlignment=!0;
+mxSvgCanvas2D.prototype.textEnabled=!0;mxSvgCanvas2D.prototype.foEnabled=!0;mxSvgCanvas2D.prototype.foAltText="[Object]";mxSvgCanvas2D.prototype.foOffset=0;mxSvgCanvas2D.prototype.textOffset=0;mxSvgCanvas2D.prototype.imageOffset=0;mxSvgCanvas2D.prototype.strokeTolerance=0;mxSvgCanvas2D.prototype.minStrokeWidth=1;mxSvgCanvas2D.prototype.refCount=0;mxSvgCanvas2D.prototype.blockImagePointerEvents=!1;mxSvgCanvas2D.prototype.lineHeightCorrection=1;mxSvgCanvas2D.prototype.pointerEventsValue="all";
+mxSvgCanvas2D.prototype.fontMetricsPadding=10;mxSvgCanvas2D.prototype.cacheOffsetSize=!0;mxSvgCanvas2D.prototype.format=function(a){return parseFloat(parseFloat(a).toFixed(2))};mxSvgCanvas2D.prototype.getBaseUrl=function(){var a=window.location.href,b=a.lastIndexOf("#");0<b&&(a=a.substring(0,b));return a};mxSvgCanvas2D.prototype.reset=function(){mxAbstractCanvas2D.prototype.reset.apply(this,arguments);this.gradients=[]};
+mxSvgCanvas2D.prototype.createStyle=function(a){a=this.createElement("style");a.setAttribute("type","text/css");mxUtils.write(a,"svg{font-family:"+mxConstants.DEFAULT_FONTFAMILY+";font-size:"+mxConstants.DEFAULT_FONTSIZE+";fill:none;stroke-miterlimit:10}");return a};
+mxSvgCanvas2D.prototype.createElement=function(a,b){if(null!=this.root.ownerDocument.createElementNS)return this.root.ownerDocument.createElementNS(b||mxConstants.NS_SVG,a);var c=this.root.ownerDocument.createElement(a);null!=b&&c.setAttribute("xmlns",b);return c};
+mxSvgCanvas2D.prototype.createAlternateContent=function(a,b,c,d,e,f,g,k,l,m,n,p,q){return null!=this.foAltText?(a=this.state,b=this.createElement("text"),b.setAttribute("x",Math.round(d/2)),b.setAttribute("y",Math.round((e+a.fontSize)/2)),b.setAttribute("fill",a.fontColor||"black"),b.setAttribute("text-anchor","middle"),b.setAttribute("font-size",a.fontSize+"px"),b.setAttribute("font-family",a.fontFamily),(a.fontStyle&mxConstants.FONT_BOLD)==mxConstants.FONT_BOLD&&b.setAttribute("font-weight","bold"),
+(a.fontStyle&mxConstants.FONT_ITALIC)==mxConstants.FONT_ITALIC&&b.setAttribute("font-style","italic"),(a.fontStyle&mxConstants.FONT_UNDERLINE)==mxConstants.FONT_UNDERLINE&&b.setAttribute("text-decoration","underline"),mxUtils.write(b,this.foAltText),b):null};
+mxSvgCanvas2D.prototype.createGradientId=function(a,b,c,d,e){"#"==a.charAt(0)&&(a=a.substring(1));"#"==b.charAt(0)&&(b=b.substring(1));a=a.toLowerCase()+"-"+c;b=b.toLowerCase()+"-"+d;c=null;null==e||e==mxConstants.DIRECTION_SOUTH?c="s":e==mxConstants.DIRECTION_EAST?c="e":(d=a,a=b,b=d,e==mxConstants.DIRECTION_NORTH?c="s":e==mxConstants.DIRECTION_WEST&&(c="e"));return"mx-gradient-"+a+"-"+b+"-"+c};
+mxSvgCanvas2D.prototype.getSvgGradient=function(a,b,c,d,e){var f=this.createGradientId(a,b,c,d,e),g=this.gradients[f];if(null==g){var k=this.root.ownerSVGElement,l=0,m=f+"-"+l;if(null!=k)for(g=k.ownerDocument.getElementById(m);null!=g&&g.ownerSVGElement!=k;)m=f+"-"+l++,g=k.ownerDocument.getElementById(m);else m="id"+ ++this.refCount;null==g&&(g=this.createSvgGradient(a,b,c,d,e),g.setAttribute("id",m),null!=this.defs?this.defs.appendChild(g):k.appendChild(g));this.gradients[f]=g}return g.getAttribute("id")};
+mxSvgCanvas2D.prototype.createSvgGradient=function(a,b,c,d,e){var f=this.createElement("linearGradient");f.setAttribute("x1","0%");f.setAttribute("y1","0%");f.setAttribute("x2","0%");f.setAttribute("y2","0%");null==e||e==mxConstants.DIRECTION_SOUTH?f.setAttribute("y2","100%"):e==mxConstants.DIRECTION_EAST?f.setAttribute("x2","100%"):e==mxConstants.DIRECTION_NORTH?f.setAttribute("y1","100%"):e==mxConstants.DIRECTION_WEST&&f.setAttribute("x1","100%");c=1>c?";stop-opacity:"+c:"";e=this.createElement("stop");
+e.setAttribute("offset","0%");e.setAttribute("style","stop-color:"+a+c);f.appendChild(e);c=1>d?";stop-opacity:"+d:"";e=this.createElement("stop");e.setAttribute("offset","100%");e.setAttribute("style","stop-color:"+b+c);f.appendChild(e);return f};
+mxSvgCanvas2D.prototype.addNode=function(a,b){var c=this.node,d=this.state;if(null!=c){if("path"==c.nodeName)if(null!=this.path&&0<this.path.length)c.setAttribute("d",this.path.join(" "));else return;a&&null!=d.fillColor?this.updateFill():this.styleEnabled||("ellipse"==c.nodeName&&mxClient.IS_FF?c.setAttribute("fill","transparent"):c.setAttribute("fill","none"),a=!1);b&&null!=d.strokeColor?this.updateStroke():this.styleEnabled||c.setAttribute("stroke","none");null!=d.transform&&0<d.transform.length&&
+c.setAttribute("transform",d.transform);d.shadow&&this.root.appendChild(this.createShadow(c));0<this.strokeTolerance&&!a&&this.root.appendChild(this.createTolerance(c));!this.pointerEvents||"path"==c.nodeName&&this.path[this.path.length-1]!=this.closeOp?this.pointerEvents||null!=this.originalRoot||c.setAttribute("pointer-events","none"):c.setAttribute("pointer-events",this.pointerEventsValue);("rect"!=c.nodeName&&"path"!=c.nodeName&&"ellipse"!=c.nodeName||"none"!=c.getAttribute("fill")&&"transparent"!=
+c.getAttribute("fill")||"none"!=c.getAttribute("stroke")||"none"!=c.getAttribute("pointer-events"))&&this.root.appendChild(c);this.node=null}};
+mxSvgCanvas2D.prototype.updateFill=function(){var a=this.state;(1>a.alpha||1>a.fillAlpha)&&this.node.setAttribute("fill-opacity",a.alpha*a.fillAlpha);if(null!=a.fillColor)if(null!=a.gradientColor)if(a=this.getSvgGradient(a.fillColor,a.gradientColor,a.gradientFillAlpha,a.gradientAlpha,a.gradientDirection),mxClient.IS_CHROME_APP||mxClient.IS_IE||mxClient.IS_IE11||mxClient.IS_EDGE||this.root.ownerDocument!=document)this.node.setAttribute("fill","url(#"+a+")");else{var b=this.getBaseUrl().replace(/([\(\)])/g,
+"\\$1");this.node.setAttribute("fill","url("+b+"#"+a+")")}else this.node.setAttribute("fill",a.fillColor.toLowerCase())};mxSvgCanvas2D.prototype.getCurrentStrokeWidth=function(){return Math.max(this.minStrokeWidth,Math.max(.01,this.format(this.state.strokeWidth*this.state.scale)))};
+mxSvgCanvas2D.prototype.updateStroke=function(){var a=this.state;this.node.setAttribute("stroke",a.strokeColor.toLowerCase());(1>a.alpha||1>a.strokeAlpha)&&this.node.setAttribute("stroke-opacity",a.alpha*a.strokeAlpha);var b=this.getCurrentStrokeWidth();1!=b&&this.node.setAttribute("stroke-width",b);"path"==this.node.nodeName&&this.updateStrokeAttributes();a.dashed&&this.node.setAttribute("stroke-dasharray",this.createDashPattern((a.fixDash?1:a.strokeWidth)*a.scale))};
+mxSvgCanvas2D.prototype.updateStrokeAttributes=function(){var a=this.state;null!=a.lineJoin&&"miter"!=a.lineJoin&&this.node.setAttribute("stroke-linejoin",a.lineJoin);if(null!=a.lineCap){var b=a.lineCap;"flat"==b&&(b="butt");"butt"!=b&&this.node.setAttribute("stroke-linecap",b)}null==a.miterLimit||this.styleEnabled&&10==a.miterLimit||this.node.setAttribute("stroke-miterlimit",a.miterLimit)};
+mxSvgCanvas2D.prototype.createDashPattern=function(a){var b=[];if("string"===typeof this.state.dashPattern){var c=this.state.dashPattern.split(" ");if(0<c.length)for(var d=0;d<c.length;d++)b[d]=Number(c[d])*a}return b.join(" ")};
+mxSvgCanvas2D.prototype.createTolerance=function(a){a=a.cloneNode(!0);var b=parseFloat(a.getAttribute("stroke-width")||1)+this.strokeTolerance;a.setAttribute("pointer-events","stroke");a.setAttribute("visibility","hidden");a.removeAttribute("stroke-dasharray");a.setAttribute("stroke-width",b);a.setAttribute("fill","none");a.setAttribute("stroke",mxClient.IS_OT?"none":"white");return a};
+mxSvgCanvas2D.prototype.createShadow=function(a){a=a.cloneNode(!0);var b=this.state;"none"==a.getAttribute("fill")||mxClient.IS_FF&&"transparent"==a.getAttribute("fill")||a.setAttribute("fill",b.shadowColor);"none"!=a.getAttribute("stroke")&&a.setAttribute("stroke",b.shadowColor);a.setAttribute("transform","translate("+this.format(b.shadowDx*b.scale)+","+this.format(b.shadowDy*b.scale)+")"+(b.transform||""));a.setAttribute("opacity",b.shadowAlpha);return a};
+mxSvgCanvas2D.prototype.setLink=function(a){if(null==a)this.root=this.originalRoot;else{this.originalRoot=this.root;var b=this.createElement("a");null==b.setAttributeNS||this.root.ownerDocument!=document&&null==document.documentMode?b.setAttribute("xlink:href",a):b.setAttributeNS(mxConstants.NS_XLINK,"xlink:href",a);this.root.appendChild(b);this.root=b}};
+mxSvgCanvas2D.prototype.rotate=function(a,b,c,d,e){if(0!=a||b||c){var f=this.state;d+=f.dx;e+=f.dy;d*=f.scale;e*=f.scale;f.transform=f.transform||"";if(b&&c)a+=180;else if(b!=c){var g=b?d:0,k=b?-1:1,l=c?e:0,m=c?-1:1;f.transform+="translate("+this.format(g)+","+this.format(l)+")scale("+this.format(k)+","+this.format(m)+")translate("+this.format(-g)+","+this.format(-l)+")"}if(b?!c:c)a*=-1;0!=a&&(f.transform+="rotate("+this.format(a)+","+this.format(d)+","+this.format(e)+")");f.rotation+=a;f.rotationCx=
+d;f.rotationCy=e}};mxSvgCanvas2D.prototype.begin=function(){mxAbstractCanvas2D.prototype.begin.apply(this,arguments);this.node=this.createElement("path")};mxSvgCanvas2D.prototype.rect=function(a,b,c,d){var e=this.state,f=this.createElement("rect");f.setAttribute("x",this.format((a+e.dx)*e.scale));f.setAttribute("y",this.format((b+e.dy)*e.scale));f.setAttribute("width",this.format(c*e.scale));f.setAttribute("height",this.format(d*e.scale));this.node=f};
+mxSvgCanvas2D.prototype.roundrect=function(a,b,c,d,e,f){this.rect(a,b,c,d);0<e&&this.node.setAttribute("rx",this.format(e*this.state.scale));0<f&&this.node.setAttribute("ry",this.format(f*this.state.scale))};mxSvgCanvas2D.prototype.ellipse=function(a,b,c,d){var e=this.state,f=this.createElement("ellipse");f.setAttribute("cx",Math.round((a+c/2+e.dx)*e.scale));f.setAttribute("cy",Math.round((b+d/2+e.dy)*e.scale));f.setAttribute("rx",c/2*e.scale);f.setAttribute("ry",d/2*e.scale);this.node=f};
+mxSvgCanvas2D.prototype.image=function(a,b,c,d,e,f,g,k){e=this.converter.convert(e);f=null!=f?f:!0;g=null!=g?g:!1;k=null!=k?k:!1;var l=this.state;a+=l.dx;b+=l.dy;var m=this.createElement("image");m.setAttribute("x",this.format(a*l.scale)+this.imageOffset);m.setAttribute("y",this.format(b*l.scale)+this.imageOffset);m.setAttribute("width",this.format(c*l.scale));m.setAttribute("height",this.format(d*l.scale));null==m.setAttributeNS?m.setAttribute("xlink:href",e):m.setAttributeNS(mxConstants.NS_XLINK,
+"xlink:href",e);f||m.setAttribute("preserveAspectRatio","none");(1>l.alpha||1>l.fillAlpha)&&m.setAttribute("opacity",l.alpha*l.fillAlpha);e=this.state.transform||"";if(g||k){var n=f=1,p=0,q=0;g&&(f=-1,p=-c-2*a);k&&(n=-1,q=-d-2*b);e+="scale("+f+","+n+")translate("+p*l.scale+","+q*l.scale+")"}0<e.length&&m.setAttribute("transform",e);this.pointerEvents||m.setAttribute("pointer-events","none");this.root.appendChild(m);this.blockImagePointerEvents&&(m.setAttribute("style","pointer-events:none"),m=this.createElement("rect"),
+m.setAttribute("visibility","hidden"),m.setAttribute("pointer-events","fill"),m.setAttribute("x",this.format(a*l.scale)),m.setAttribute("y",this.format(b*l.scale)),m.setAttribute("width",this.format(c*l.scale)),m.setAttribute("height",this.format(d*l.scale)),this.root.appendChild(m))};
+mxSvgCanvas2D.prototype.convertHtml=function(a){if(this.useDomParser){var b=(new DOMParser).parseFromString(a,"text/html");null!=b&&(a=(new XMLSerializer).serializeToString(b.body),"<body"==a.substring(0,5)&&(a=a.substring(a.indexOf(">",5)+1)),"</body>"==a.substring(a.length-7,a.length)&&(a=a.substring(0,a.length-7)))}else{if(null!=document.implementation&&null!=document.implementation.createDocument){var b=document.implementation.createDocument("http://www.w3.org/1999/xhtml","html",null),c=b.createElement("body");
+b.documentElement.appendChild(c);var d=document.createElement("div");d.innerHTML=a;for(a=d.firstChild;null!=a;)d=a.nextSibling,c.appendChild(b.adoptNode(a)),a=d;return c.innerHTML}b=document.createElement("textarea");b.innerHTML=a.replace(/&amp;/g,"&amp;amp;").replace(/&#60;/g,"&amp;lt;").replace(/&#62;/g,"&amp;gt;").replace(/&lt;/g,"&amp;lt;").replace(/&gt;/g,"&amp;gt;").replace(/</g,"&lt;").replace(/>/g,"&gt;");a=b.value.replace(/&/g,"&amp;").replace(/&amp;lt;/g,"&lt;").replace(/&amp;gt;/g,"&gt;").replace(/&amp;amp;/g,
+"&amp;").replace(/<br>/g,"<br />").replace(/<hr>/g,"<hr />").replace(/(<img[^>]+)>/gm,"$1 />")}return a};
+mxSvgCanvas2D.prototype.createDiv=function(a,b,c,d,e){c=this.state;d="display:inline-block;font-size:"+c.fontSize+"px;font-family:"+c.fontFamily+";color:"+c.fontColor+";line-height:"+(mxConstants.ABSOLUTE_LINE_HEIGHT?c.fontSize*mxConstants.LINE_HEIGHT+"px":mxConstants.LINE_HEIGHT*this.lineHeightCorrection)+";"+d;(c.fontStyle&mxConstants.FONT_BOLD)==mxConstants.FONT_BOLD&&(d+="font-weight:bold;");(c.fontStyle&mxConstants.FONT_ITALIC)==mxConstants.FONT_ITALIC&&(d+="font-style:italic;");(c.fontStyle&
+mxConstants.FONT_UNDERLINE)==mxConstants.FONT_UNDERLINE&&(d+="text-decoration:underline;");b==mxConstants.ALIGN_CENTER?d+="text-align:center;":b==mxConstants.ALIGN_RIGHT&&(d+="text-align:right;");b="";null!=c.fontBackgroundColor&&(b+="background-color:"+c.fontBackgroundColor+";");null!=c.fontBorderColor&&(b+="border:1px solid "+c.fontBorderColor+";");mxUtils.isNode(a)||(a=this.convertHtml(a),"fill"!=e&&"width"!=e?a='<div xmlns="http://www.w3.org/1999/xhtml" style="display:inline-block;text-align:inherit;text-decoration:inherit;'+
+b+'">'+a+"</div>":d+=b);if(!mxClient.IS_IE&&document.createElementNS)return e=document.createElementNS("http://www.w3.org/1999/xhtml","div"),e.setAttribute("style",d),mxUtils.isNode(a)?this.root.ownerDocument!=document?e.appendChild(a.cloneNode(!0)):e.appendChild(a):e.innerHTML=a,e;mxUtils.isNode(a)&&this.root.ownerDocument!=document&&(a=a.outerHTML);return mxUtils.parseXml('<div xmlns="http://www.w3.org/1999/xhtml" style="'+d+'">'+a+"</div>").documentElement};
+mxSvgCanvas2D.prototype.invalidateCachedOffsetSize=function(a){delete a.firstChild.mxCachedOffsetWidth;delete a.firstChild.mxCachedFinalOffsetWidth;delete a.firstChild.mxCachedFinalOffsetHeight};
+mxSvgCanvas2D.prototype.updateText=function(a,b,c,d,e,f,g,k,l,m,n){if(null!=n&&null!=n.firstChild&&null!=n.firstChild.firstChild&&null!=n.firstChild.firstChild.firstChild){n=n.firstChild;var p=n.firstChild,q=p.firstChild;m=null!=m?m:0;var r=this.state;a+=r.dx;b+=r.dy;l?(q.style.maxHeight=Math.round(d)+"px",q.style.maxWidth=Math.round(c)+"px"):"fill"==k?(q.style.width=Math.round(c+1)+"px",q.style.height=Math.round(d+1)+"px"):"width"==k&&(q.style.width=Math.round(c+1)+"px",0<d&&(q.style.maxHeight=Math.round(d)+
+"px"));g&&0<c&&(q.style.width=Math.round(c+1)+"px");var t,u=q;null!=u.firstChild&&"DIV"==u.firstChild.nodeName&&(u=u.firstChild);var x=null!=n.mxCachedOffsetWidth?n.mxCachedOffsetWidth:u.offsetWidth;t=x+0;g&&"fill"!=k&&(l&&(t=Math.min(t,c)),q.style.width=Math.round(t+1)+"px");t=null!=n.mxCachedFinalOffsetWidth?n.mxCachedFinalOffsetWidth:u.offsetWidth;g=null!=n.mxCachedFinalOffsetHeight?n.mxCachedFinalOffsetHeight:u.offsetHeight;this.cacheOffsetSize&&(n.mxCachedOffsetWidth=x,n.mxCachedFinalOffsetWidth=
+t,n.mxCachedFinalOffsetHeight=g);t+=0;g-=2;l&&(g=Math.min(g,d),t=Math.min(t,c));"width"==k?d=g:"fill"!=k&&(c=t,d=g);g=l=0;e==mxConstants.ALIGN_CENTER?l-=c/2:e==mxConstants.ALIGN_RIGHT&&(l-=c);a+=l;f==mxConstants.ALIGN_MIDDLE?g-=d/2:f==mxConstants.ALIGN_BOTTOM&&(g-=d);"fill"!=k&&mxClient.IS_FF&&mxClient.IS_WIN&&(g-=2);b+=g;e=1!=r.scale?"scale("+r.scale+")":"";0!=r.rotation&&this.rotateHtml?(e+="rotate("+r.rotation+","+c/2+","+d/2+")",b=this.rotatePoint((a+c/2)*r.scale,(b+d/2)*r.scale,r.rotation,r.rotationCx,
+r.rotationCy),a=b.x-c*r.scale/2,b=b.y-d*r.scale/2):(a*=r.scale,b*=r.scale);0!=m&&(e+="rotate("+m+","+-l+","+-g+")");n.setAttribute("transform","translate("+Math.round(a)+","+Math.round(b)+")"+e);p.setAttribute("width",Math.round(Math.max(1,c)));p.setAttribute("height",Math.round(Math.max(1,d)))}};
+mxSvgCanvas2D.prototype.text=function(a,b,c,d,e,f,g,k,l,m,n,p,q){if(this.textEnabled&&null!=e){p=null!=p?p:0;var r=this.state;a+=r.dx;b+=r.dy;if(this.foEnabled&&"html"==l){var t="vertical-align:top;";n?t+="overflow:hidden;max-height:"+Math.round(d)+"px;max-width:"+Math.round(c)+"px;":"fill"==m?t+="width:"+Math.round(c+1)+"px;height:"+Math.round(d+1)+"px;overflow:hidden;":"width"==m&&(t+="width:"+Math.round(c+1)+"px;",0<d&&(t+="max-height:"+Math.round(d)+"px;overflow:hidden;"));var t=k&&0<c?t+("width:"+
+Math.round(c+1)+"px;white-space:normal;word-wrap:"+mxConstants.WORD_WRAP+";"):t+"white-space:nowrap;",u=this.createElement("g");1>r.alpha&&u.setAttribute("opacity",r.alpha);var x=this.createElement("foreignObject");x.setAttribute("style","overflow:visible;");x.setAttribute("pointer-events","all");t=this.createDiv(e,f,g,t,m);if(null!=t){null!=q&&t.setAttribute("dir",q);u.appendChild(x);this.root.appendChild(u);var y,A;q=y=2;if(!mxClient.IS_IE||9!=document.documentMode&&mxClient.IS_SVG){this.root.ownerDocument!=
+document?(t.style.visibility="hidden",document.body.appendChild(t)):x.appendChild(t);var z=t;null!=z.firstChild&&"DIV"==z.firstChild.nodeName&&(z=z.firstChild,k&&"break-word"==t.style.wordWrap&&(z.style.width="100%"));v=z.offsetWidth;0==v&&t.parentNode==x&&(t.style.visibility="hidden",document.body.appendChild(t),v=z.offsetWidth);this.cacheOffsetSize&&(u.mxCachedOffsetWidth=v);!n&&k&&0<c&&this.root.ownerDocument!=document&&"fill"!=m&&"width"!=m&&(B=t.style.whiteSpace,t.style.whiteSpace="nowrap",v<
+z.offsetWidth&&(t.style.whiteSpace=B));y=v+y-1;k&&"fill"!=m&&"width"!=m&&(n&&(y=Math.min(y,c)),t.style.width=y+"px");y=z.offsetWidth;A=z.offsetHeight;this.cacheOffsetSize&&(u.mxCachedFinalOffsetWidth=y,u.mxCachedFinalOffsetHeight=A);A-=q;t.parentNode!=x&&(x.appendChild(t),t.style.visibility="")}else{z=document.createElement("div");z.style.cssText=t.getAttribute("style");z.style.display=mxClient.IS_QUIRKS?"inline":"inline-block";z.style.position="absolute";z.style.visibility="hidden";A=document.createElement("div");
+A.style.display=mxClient.IS_QUIRKS?"inline":"inline-block";A.style.wordWrap=mxConstants.WORD_WRAP;A.innerHTML=mxUtils.isNode(e)?e.outerHTML:e;z.appendChild(A);document.body.appendChild(z);8!=document.documentMode&&9!=document.documentMode&&null!=r.fontBorderColor&&(y+=2,q+=2);if(k&&0<c){var v=A.offsetWidth;padDx=0;if(!n&&k&&0<c&&this.root.ownerDocument!=document&&"fill"!=m){var B=z.style.whiteSpace;A.style.whiteSpace="nowrap";v<A.offsetWidth&&(z.style.whiteSpace=B)}n&&(v=Math.min(v,c));z.style.width=
+v+"px";y=A.offsetWidth+y+padDx;A=A.offsetHeight+q;z.style.display="inline-block";z.style.position="";z.style.visibility="";z.style.width=y+"px";t.setAttribute("style",z.style.cssText)}else y=A.offsetWidth+y,A=A.offsetHeight+q;z.parentNode.removeChild(z);x.appendChild(t)}n&&(A=Math.min(A,d),y=Math.min(y,c));"width"==m?d=A:"fill"!=m&&(c=y,d=A);1>r.alpha&&u.setAttribute("opacity",r.alpha);q=t=0;f==mxConstants.ALIGN_CENTER?t-=c/2:f==mxConstants.ALIGN_RIGHT&&(t-=c);a+=t;g==mxConstants.ALIGN_MIDDLE?q-=
+d/2:g==mxConstants.ALIGN_BOTTOM&&(q-=d);"fill"!=m&&mxClient.IS_FF&&mxClient.IS_WIN&&(q-=2);b+=q;z=1!=r.scale?"scale("+r.scale+")":"";0!=r.rotation&&this.rotateHtml?(z+="rotate("+r.rotation+","+c/2+","+d/2+")",b=this.rotatePoint((a+c/2)*r.scale,(b+d/2)*r.scale,r.rotation,r.rotationCx,r.rotationCy),a=b.x-c*r.scale/2,b=b.y-d*r.scale/2):(a*=r.scale,b*=r.scale);0!=p&&(z+="rotate("+p+","+-t+","+-q+")");u.setAttribute("transform","translate("+(Math.round(a)+this.foOffset)+","+(Math.round(b)+this.foOffset)+
+")"+z);x.setAttribute("width",Math.round(Math.max(1,c)));x.setAttribute("height",Math.round(Math.max(1,d)));this.root.ownerDocument!=document&&(a=this.createAlternateContent(x,a,b,c,d,e,f,g,k,l,m,n,p),null!=a&&(x.setAttribute("requiredFeatures","http://www.w3.org/TR/SVG11/feature#Extensibility"),c=this.createElement("switch"),c.appendChild(x),c.appendChild(a),u.appendChild(c)))}}else this.plainText(a,b,c,d,e,f,g,k,m,n,p,q)}};
+mxSvgCanvas2D.prototype.createClip=function(a,b,c,d){a=Math.round(a);b=Math.round(b);c=Math.round(c);d=Math.round(d);for(var e="mx-clip-"+a+"-"+b+"-"+c+"-"+d,f=0,g=e+"-"+f;null!=document.getElementById(g);)g=e+"-"+ ++f;clip=this.createElement("clipPath");clip.setAttribute("id",g);e=this.createElement("rect");e.setAttribute("x",a);e.setAttribute("y",b);e.setAttribute("width",c);e.setAttribute("height",d);clip.appendChild(e);return clip};
+mxSvgCanvas2D.prototype.plainText=function(a,b,c,d,e,f,g,k,l,m,n,p){n=null!=n?n:0;k=this.state;var q=k.fontSize,r=this.createElement("g"),t=k.transform||"";this.updateFont(r);0!=n&&(t+="rotate("+n+","+this.format(a*k.scale)+","+this.format(b*k.scale)+")");null!=p&&r.setAttribute("direction",p);m&&0<c&&0<d&&(p=a,n=b,f==mxConstants.ALIGN_CENTER?p-=c/2:f==mxConstants.ALIGN_RIGHT&&(p-=c),"fill"!=l&&(g==mxConstants.ALIGN_MIDDLE?n-=d/2:g==mxConstants.ALIGN_BOTTOM&&(n-=d)),n=this.createClip(p*k.scale-2,
+n*k.scale-2,c*k.scale+4,d*k.scale+4),null!=this.defs?this.defs.appendChild(n):this.root.appendChild(n),mxClient.IS_CHROME_APP||mxClient.IS_IE||mxClient.IS_IE11||mxClient.IS_EDGE||this.root.ownerDocument!=document?r.setAttribute("clip-path","url(#"+n.getAttribute("id")+")"):(p=this.getBaseUrl().replace(/([\(\)])/g,"\\$1"),r.setAttribute("clip-path","url("+p+"#"+n.getAttribute("id")+")")));n=f==mxConstants.ALIGN_RIGHT?"end":f==mxConstants.ALIGN_CENTER?"middle":"start";"start"!=n&&r.setAttribute("text-anchor",
+n);this.styleEnabled&&q==mxConstants.DEFAULT_FONTSIZE||r.setAttribute("font-size",q*k.scale+"px");0<t.length&&r.setAttribute("transform",t);1>k.alpha&&r.setAttribute("opacity",k.alpha);t=e.split("\n");p=Math.round(q*mxConstants.LINE_HEIGHT);var u=q+(t.length-1)*p;n=b+q-1;g==mxConstants.ALIGN_MIDDLE?"fill"==l?n-=d/2:(m=(this.matchHtmlAlignment&&m&&0<d?Math.min(u,d):u)/2,n-=m+1):g==mxConstants.ALIGN_BOTTOM&&("fill"==l?n-=d:(m=this.matchHtmlAlignment&&m&&0<d?Math.min(u,d):u,n-=m+2));for(m=0;m<t.length;m++)0<
+t[m].length&&0<mxUtils.trim(t[m]).length&&(q=this.createElement("text"),q.setAttribute("x",this.format(a*k.scale)+this.textOffset),q.setAttribute("y",this.format(n*k.scale)+this.textOffset),mxUtils.write(q,t[m]),r.appendChild(q)),n+=p;this.root.appendChild(r);this.addTextBackground(r,e,a,b,c,"fill"==l?d:u,f,g,l)};
+mxSvgCanvas2D.prototype.updateFont=function(a){var b=this.state;a.setAttribute("fill",b.fontColor);this.styleEnabled&&b.fontFamily==mxConstants.DEFAULT_FONTFAMILY||a.setAttribute("font-family",b.fontFamily);(b.fontStyle&mxConstants.FONT_BOLD)==mxConstants.FONT_BOLD&&a.setAttribute("font-weight","bold");(b.fontStyle&mxConstants.FONT_ITALIC)==mxConstants.FONT_ITALIC&&a.setAttribute("font-style","italic");(b.fontStyle&mxConstants.FONT_UNDERLINE)==mxConstants.FONT_UNDERLINE&&a.setAttribute("text-decoration",
+"underline")};
+mxSvgCanvas2D.prototype.addTextBackground=function(a,b,c,d,e,f,g,k,l){var m=this.state;if(null!=m.fontBackgroundColor||null!=m.fontBorderColor){var n=null;if("fill"==l||"width"==l)g==mxConstants.ALIGN_CENTER?c-=e/2:g==mxConstants.ALIGN_RIGHT&&(c-=e),k==mxConstants.ALIGN_MIDDLE?d-=f/2:k==mxConstants.ALIGN_BOTTOM&&(d-=f),n=new mxRectangle((c+1)*m.scale,d*m.scale,(e-2)*m.scale,(f+2)*m.scale);else if(null!=a.getBBox&&this.root.ownerDocument==document)try{var n=a.getBBox(),p=mxClient.IS_IE&&mxClient.IS_SVG,
+n=new mxRectangle(n.x,n.y+(p?0:1),n.width,n.height+(p?1:0))}catch(q){}else n=document.createElement("div"),n.style.lineHeight=mxConstants.ABSOLUTE_LINE_HEIGHT?m.fontSize*mxConstants.LINE_HEIGHT+"px":mxConstants.LINE_HEIGHT,n.style.fontSize=m.fontSize+"px",n.style.fontFamily=m.fontFamily,n.style.whiteSpace="nowrap",n.style.position="absolute",n.style.visibility="hidden",n.style.display=mxClient.IS_QUIRKS?"inline":"inline-block",n.style.zoom="1",(m.fontStyle&mxConstants.FONT_BOLD)==mxConstants.FONT_BOLD&&
+(n.style.fontWeight="bold"),(m.fontStyle&mxConstants.FONT_ITALIC)==mxConstants.FONT_ITALIC&&(n.style.fontStyle="italic"),b=mxUtils.htmlEntities(b,!1),n.innerHTML=b.replace(/\n/g,"<br/>"),document.body.appendChild(n),e=n.offsetWidth,f=n.offsetHeight,n.parentNode.removeChild(n),g==mxConstants.ALIGN_CENTER?c-=e/2:g==mxConstants.ALIGN_RIGHT&&(c-=e),k==mxConstants.ALIGN_MIDDLE?d-=f/2:k==mxConstants.ALIGN_BOTTOM&&(d-=f),n=new mxRectangle((c+1)*m.scale,(d+2)*m.scale,e*m.scale,(f+1)*m.scale);null!=n&&(b=
+this.createElement("rect"),b.setAttribute("fill",m.fontBackgroundColor||"none"),b.setAttribute("stroke",m.fontBorderColor||"none"),b.setAttribute("x",Math.floor(n.x-1)),b.setAttribute("y",Math.floor(n.y-1)),b.setAttribute("width",Math.ceil(n.width+2)),b.setAttribute("height",Math.ceil(n.height)),m=null!=m.fontBorderColor?Math.max(1,this.format(m.scale)):0,b.setAttribute("stroke-width",m),this.root.ownerDocument==document&&1==mxUtils.mod(m,2)&&b.setAttribute("transform","translate(0.5, 0.5)"),a.insertBefore(b,
+a.firstChild))}};mxSvgCanvas2D.prototype.stroke=function(){this.addNode(!1,!0)};mxSvgCanvas2D.prototype.fill=function(){this.addNode(!0,!1)};mxSvgCanvas2D.prototype.fillAndStroke=function(){this.addNode(!0,!0)};var mxVmlCanvas2D=function(a){mxAbstractCanvas2D.call(this);this.root=a};mxUtils.extend(mxVmlCanvas2D,mxAbstractCanvas2D);mxVmlCanvas2D.prototype.node=null;mxVmlCanvas2D.prototype.textEnabled=!0;mxVmlCanvas2D.prototype.moveOp="m";mxVmlCanvas2D.prototype.lineOp="l";
+mxVmlCanvas2D.prototype.curveOp="c";mxVmlCanvas2D.prototype.closeOp="x";mxVmlCanvas2D.prototype.rotatedHtmlBackground="";mxVmlCanvas2D.prototype.vmlScale=1;mxVmlCanvas2D.prototype.createElement=function(a){return document.createElement(a)};mxVmlCanvas2D.prototype.createVmlElement=function(a){return this.createElement(mxClient.VML_PREFIX+":"+a)};
+mxVmlCanvas2D.prototype.addNode=function(a,b){var c=this.node,d=this.state;if(null!=c){if("shape"==c.nodeName)if(null!=this.path&&0<this.path.length)c.path=this.path.join(" ")+" e",c.style.width=this.root.style.width,c.style.height=this.root.style.height,c.coordsize=parseInt(c.style.width)+" "+parseInt(c.style.height);else return;c.strokeweight=this.format(Math.max(1,d.strokeWidth*d.scale/this.vmlScale))+"px";d.shadow&&this.root.appendChild(this.createShadow(c,a&&null!=d.fillColor,b&&null!=d.strokeColor));
+b&&null!=d.strokeColor?(c.stroked="true",c.strokecolor=d.strokeColor):c.stroked="false";c.appendChild(this.createStroke());a&&null!=d.fillColor?c.appendChild(this.createFill()):!this.pointerEvents||"shape"==c.nodeName&&this.path[this.path.length-1]!=this.closeOp?c.filled="false":c.appendChild(this.createTransparentFill());this.root.appendChild(c)}};
+mxVmlCanvas2D.prototype.createTransparentFill=function(){var a=this.createVmlElement("fill");a.src=mxClient.imageBasePath+"/transparent.gif";a.type="tile";return a};
+mxVmlCanvas2D.prototype.createFill=function(){var a=this.state,b=this.createVmlElement("fill");b.color=a.fillColor;if(null!=a.gradientColor){b.type="gradient";b.method="none";b.color2=a.gradientColor;var c=180-a.rotation,c=a.gradientDirection==mxConstants.DIRECTION_WEST?c-(90+("x"==this.root.style.flip?180:0)):a.gradientDirection==mxConstants.DIRECTION_EAST?c+(90+("x"==this.root.style.flip?180:0)):a.gradientDirection==mxConstants.DIRECTION_NORTH?c-(180+("y"==this.root.style.flip?-180:0)):c+("y"==
+this.root.style.flip?-180:0);if("x"==this.root.style.flip||"y"==this.root.style.flip)c*=-1;b.angle=mxUtils.mod(c,360);b.opacity=a.alpha*a.gradientFillAlpha*100+"%";b.setAttribute(mxClient.OFFICE_PREFIX+":opacity2",a.alpha*a.gradientAlpha*100+"%")}else if(1>a.alpha||1>a.fillAlpha)b.opacity=a.alpha*a.fillAlpha*100+"%";return b};
+mxVmlCanvas2D.prototype.createStroke=function(){var a=this.state,b=this.createVmlElement("stroke");b.endcap=a.lineCap||"flat";b.joinstyle=a.lineJoin||"miter";b.miterlimit=a.miterLimit||"10";if(1>a.alpha||1>a.strokeAlpha)b.opacity=a.alpha*a.strokeAlpha*100+"%";a.dashed&&(b.dashstyle=this.getVmlDashStyle());return b};mxVmlCanvas2D.prototype.getVmlDashStyle=function(){var a="dash";if("string"===typeof this.state.dashPattern){var b=this.state.dashPattern.split(" ");0<b.length&&1==b[0]&&(a="0 2")}return a};
+mxVmlCanvas2D.prototype.createShadow=function(a,b,c){var d=this.state,e=Math.PI/180*-d.rotation,f=Math.cos(e),e=Math.sin(e),g=d.shadowDx*d.scale,k=d.shadowDy*d.scale;"x"==this.root.style.flip?g*=-1:"y"==this.root.style.flip&&(k*=-1);var l=a.cloneNode(!0);l.style.marginLeft=Math.round(g*f-k*e)+"px";l.style.marginTop=Math.round(g*e+k*f)+"px";8==document.documentMode&&(l.strokeweight=a.strokeweight,"shape"==a.nodeName&&(l.path=this.path.join(" ")+" e",l.style.width=this.root.style.width,l.style.height=
+this.root.style.height,l.coordsize=parseInt(a.style.width)+" "+parseInt(a.style.height)));c?(l.strokecolor=d.shadowColor,l.appendChild(this.createShadowStroke())):l.stroked="false";b?l.appendChild(this.createShadowFill()):l.filled="false";return l};mxVmlCanvas2D.prototype.createShadowFill=function(){var a=this.createVmlElement("fill");a.color=this.state.shadowColor;a.opacity=this.state.alpha*this.state.shadowAlpha*100+"%";return a};
+mxVmlCanvas2D.prototype.createShadowStroke=function(){var a=this.createStroke();a.opacity=this.state.alpha*this.state.shadowAlpha*100+"%";return a};mxVmlCanvas2D.prototype.rotate=function(a,b,c,d,e){b&&c?a+=180:b?this.root.style.flip="x":c&&(this.root.style.flip="y");if(b?!c:c)a*=-1;this.root.style.rotation=a;this.state.rotation+=a;this.state.rotationCx=d;this.state.rotationCy=e};
+mxVmlCanvas2D.prototype.begin=function(){mxAbstractCanvas2D.prototype.begin.apply(this,arguments);this.node=this.createVmlElement("shape");this.node.style.position="absolute"};
+mxVmlCanvas2D.prototype.quadTo=function(a,b,c,d){var e=this.state,f=(this.lastX+e.dx)*e.scale,g=(this.lastY+e.dy)*e.scale;a=(a+e.dx)*e.scale;b=(b+e.dy)*e.scale;c=(c+e.dx)*e.scale;d=(d+e.dy)*e.scale;var g=g+2/3*(b-g),k=c+2/3*(a-c);b=d+2/3*(b-d);this.path.push("c "+this.format(f+2/3*(a-f))+" "+this.format(g)+" "+this.format(k)+" "+this.format(b)+" "+this.format(c)+" "+this.format(d));this.lastX=c/e.scale-e.dx;this.lastY=d/e.scale-e.dy};
+mxVmlCanvas2D.prototype.createRect=function(a,b,c,d,e){var f=this.state;a=this.createVmlElement(a);a.style.position="absolute";a.style.left=this.format((b+f.dx)*f.scale)+"px";a.style.top=this.format((c+f.dy)*f.scale)+"px";a.style.width=this.format(d*f.scale)+"px";a.style.height=this.format(e*f.scale)+"px";return a};mxVmlCanvas2D.prototype.rect=function(a,b,c,d){this.node=this.createRect("rect",a,b,c,d)};
+mxVmlCanvas2D.prototype.roundrect=function(a,b,c,d,e,f){this.node=this.createRect("roundrect",a,b,c,d);this.node.setAttribute("arcsize",Math.max(100*e/c,100*f/d)+"%")};mxVmlCanvas2D.prototype.ellipse=function(a,b,c,d){this.node=this.createRect("oval",a,b,c,d)};
+mxVmlCanvas2D.prototype.image=function(a,b,c,d,e,f,g,k){f?(a=this.createRect("rect",a,b,c,d),a.stroked="false",b=this.createVmlElement("fill"),b.aspect=f?"atmost":"ignore",b.rotate="true",b.type="frame",b.src=e,a.appendChild(b)):(a=this.createRect("image",a,b,c,d),a.src=e);g&&k?a.style.rotation="180":g?a.style.flip="x":k&&(a.style.flip="y");if(1>this.state.alpha||1>this.state.fillAlpha)a.style.filter+="alpha(opacity="+this.state.alpha*this.state.fillAlpha*100+")";this.root.appendChild(a)};
+mxVmlCanvas2D.prototype.createDiv=function(a,b,c,d){c=this.createElement("div");var e=this.state,f="";null!=e.fontBackgroundColor&&(f+="background-color:"+e.fontBackgroundColor+";");null!=e.fontBorderColor&&(f+="border:1px solid "+e.fontBorderColor+";");mxUtils.isNode(a)?c.appendChild(a):"fill"!=d&&"width"!=d?(d=this.createElement("div"),d.style.cssText=f,d.style.display=mxClient.IS_QUIRKS?"inline":"inline-block",d.style.zoom="1",d.style.textDecoration="inherit",d.innerHTML=a,c.appendChild(d)):(c.style.cssText=
+f,c.innerHTML=a);a=c.style;a.fontSize=e.fontSize/this.vmlScale+"px";a.fontFamily=e.fontFamily;a.color=e.fontColor;a.verticalAlign="top";a.textAlign=b||"left";a.lineHeight=mxConstants.ABSOLUTE_LINE_HEIGHT?e.fontSize*mxConstants.LINE_HEIGHT/this.vmlScale+"px":mxConstants.LINE_HEIGHT;(e.fontStyle&mxConstants.FONT_BOLD)==mxConstants.FONT_BOLD&&(a.fontWeight="bold");(e.fontStyle&mxConstants.FONT_ITALIC)==mxConstants.FONT_ITALIC&&(a.fontStyle="italic");(e.fontStyle&mxConstants.FONT_UNDERLINE)==mxConstants.FONT_UNDERLINE&&
+(a.textDecoration="underline");return c};
+mxVmlCanvas2D.prototype.text=function(a,b,c,d,e,f,g,k,l,m,n,p,q){if(this.textEnabled&&null!=e){var r=this.state;if("html"==l){null!=r.rotation&&(b=this.rotatePoint(a,b,r.rotation,r.rotationCx,r.rotationCy),a=b.x,b=b.y);8!=document.documentMode||mxClient.IS_EM?(a*=r.scale,b*=r.scale):(a+=r.dx,b+=r.dy,"fill"!=m&&g==mxConstants.ALIGN_TOP&&--b);l=8!=document.documentMode||mxClient.IS_EM?this.createElement("div"):this.createVmlElement("group");l.style.position="absolute";l.style.display="inline";l.style.left=
+this.format(a)+"px";l.style.top=this.format(b)+"px";l.style.zoom=r.scale;var t=this.createElement("div");t.style.position="relative";t.style.display="inline";var u=mxUtils.getAlignmentAsPoint(f,g),x=u.x,u=u.y;e=this.createDiv(e,f,g,m);f=this.createElement("div");null!=q&&e.setAttribute("dir",q);if(k&&0<c){if(n||(e.style.width=Math.round(c)+"px"),e.style.wordWrap=mxConstants.WORD_WRAP,e.style.whiteSpace="normal","break-word"==e.style.wordWrap){var y=e;null!=y.firstChild&&"DIV"==y.firstChild.nodeName&&
+(y.firstChild.style.width="100%")}}else e.style.whiteSpace="nowrap";p=r.rotation+(p||0);this.rotateHtml&&0!=p?(f.style.display="inline",f.style.zoom="1",f.appendChild(e),8!=document.documentMode||mxClient.IS_EM||"DIV"==this.root.nodeName?l.appendChild(f):(t.appendChild(f),l.appendChild(t))):8!=document.documentMode||mxClient.IS_EM?(e.style.display="inline",l.appendChild(e)):(t.appendChild(e),l.appendChild(t));"DIV"!=this.root.nodeName?(q=this.createVmlElement("rect"),q.stroked="false",q.filled="false",
+q.appendChild(l),this.root.appendChild(q)):this.root.appendChild(l);n?(e.style.overflow="hidden",e.style.width=Math.round(c)+"px",mxClient.IS_QUIRKS||(e.style.maxHeight=Math.round(d)+"px")):"fill"==m?(e.style.overflow="hidden",e.style.width=Math.max(0,c)+1+"px",e.style.height=Math.max(0,d)+1+"px"):"width"==m&&(e.style.overflow="hidden",e.style.width=Math.max(0,c)+1+"px",e.style.maxHeight=Math.max(0,d)+1+"px");if(this.rotateHtml&&0!=p){y=Math.PI/180*p;p=parseFloat(parseFloat(Math.cos(y)).toFixed(8));
+q=parseFloat(parseFloat(Math.sin(-y)).toFixed(8));y%=2*Math.PI;0>y&&(y+=2*Math.PI);y%=Math.PI;y>Math.PI/2&&(y=Math.PI-y);g=Math.cos(y);var A=Math.sin(y);8!=document.documentMode||mxClient.IS_EM||(e.style.display="inline-block",f.style.display="inline-block",t.style.display="inline-block");e.style.visibility="hidden";e.style.position="absolute";document.body.appendChild(e);t=e;null!=t.firstChild&&"DIV"==t.firstChild.nodeName&&(t=t.firstChild);y=t.offsetWidth+3;t=t.offsetHeight;n?(c=Math.min(c,y),t=
+Math.min(t,d)):c=y;k&&(e.style.width=c+"px");mxClient.IS_QUIRKS&&(n||"width"==m)&&t>d&&(t=d,e.style.height=t+"px");d=t;n=(d-d*g+c*-A)/2-q*c*(x+.5)+p*d*(u+.5);k=(c-c*g+d*-A)/2+p*c*(x+.5)+q*d*(u+.5);"group"==l.nodeName&&"DIV"==this.root.nodeName?(m=this.createElement("div"),m.style.display="inline-block",m.style.position="absolute",m.style.left=this.format(a+(k-c/2)*r.scale)+"px",m.style.top=this.format(b+(n-d/2)*r.scale)+"px",l.parentNode.appendChild(m),m.appendChild(l)):(r=8!=document.documentMode||
+mxClient.IS_EM?r.scale:1,l.style.left=this.format(a+(k-c/2)*r)+"px",l.style.top=this.format(b+(n-d/2)*r)+"px");f.style.filter="progid:DXImageTransform.Microsoft.Matrix(M11="+p+", M12="+q+", M21="+-q+", M22="+p+", sizingMethod='auto expand')";f.style.backgroundColor=this.rotatedHtmlBackground;1>this.state.alpha&&(f.style.filter+="alpha(opacity="+100*this.state.alpha+")");f.appendChild(e);e.style.position="";e.style.visibility=""}else 8!=document.documentMode||mxClient.IS_EM?(e.style.verticalAlign=
+"top",1>this.state.alpha&&(l.style.filter="alpha(opacity="+100*this.state.alpha+")"),r=e.parentNode,e.style.visibility="hidden",document.body.appendChild(e),c=e.offsetWidth,t=e.offsetHeight,mxClient.IS_QUIRKS&&n&&t>d&&(t=d,e.style.height=t+"px"),d=t,e.style.visibility="",r.appendChild(e),l.style.left=this.format(a+c*x*this.state.scale)+"px",l.style.top=this.format(b+d*u*this.state.scale)+"px"):(1>this.state.alpha&&(e.style.filter="alpha(opacity="+100*this.state.alpha+")"),t.style.left=100*x+"%",t.style.top=
+100*u+"%")}else this.plainText(a,b,c,d,mxUtils.htmlEntities(e,!1),f,g,k,l,m,n,p,q)}};
+mxVmlCanvas2D.prototype.plainText=function(a,b,c,d,e,f,g,k,l,m,n,p,q){k=this.state;a=(a+k.dx)*k.scale;b=(b+k.dy)*k.scale;c=this.createVmlElement("shape");c.style.width="1px";c.style.height="1px";c.stroked="false";d=this.createVmlElement("fill");d.color=k.fontColor;d.opacity=100*k.alpha+"%";c.appendChild(d);d=this.createVmlElement("path");d.textpathok="true";d.v="m "+this.format(0)+" "+this.format(0)+" l "+this.format(1)+" "+this.format(0);c.appendChild(d);d=this.createVmlElement("textpath");d.style.cssText=
+"v-text-align:"+f;d.style.align=f;d.style.fontFamily=k.fontFamily;d.string=e;d.on="true";f=k.fontSize*k.scale/this.vmlScale;d.style.fontSize=f+"px";(k.fontStyle&mxConstants.FONT_BOLD)==mxConstants.FONT_BOLD&&(d.style.fontWeight="bold");(k.fontStyle&mxConstants.FONT_ITALIC)==mxConstants.FONT_ITALIC&&(d.style.fontStyle="italic");(k.fontStyle&mxConstants.FONT_UNDERLINE)==mxConstants.FONT_UNDERLINE&&(d.style.textDecoration="underline");e=e.split("\n");k=f+(e.length-1)*f*mxConstants.LINE_HEIGHT;f=e=0;
+g==mxConstants.ALIGN_BOTTOM?f=-k/2:g!=mxConstants.ALIGN_MIDDLE&&(f=k/2);null!=p&&(c.style.rotation=p,g=Math.PI/180*p,e=Math.sin(g)*f,f*=Math.cos(g));c.appendChild(d);c.style.left=this.format(a-e)+"px";c.style.top=this.format(b+f)+"px";this.root.appendChild(c)};mxVmlCanvas2D.prototype.stroke=function(){this.addNode(!1,!0)};mxVmlCanvas2D.prototype.fill=function(){this.addNode(!0,!1)};mxVmlCanvas2D.prototype.fillAndStroke=function(){this.addNode(!0,!0)};
+function mxGuide(a,b){this.graph=a;this.setStates(b)}mxGuide.prototype.graph=null;mxGuide.prototype.states=null;mxGuide.prototype.horizontal=!0;mxGuide.prototype.vertical=!0;mxGuide.prototype.guideX=null;mxGuide.prototype.guideY=null;mxGuide.prototype.setStates=function(a){this.states=a};mxGuide.prototype.isEnabledForEvent=function(a){return!0};mxGuide.prototype.getGuideTolerance=function(){return this.graph.gridSize/2};
+mxGuide.prototype.createGuideShape=function(a){a=new mxPolyline([],mxConstants.GUIDE_COLOR,mxConstants.GUIDE_STROKEWIDTH);a.isDashed=!0;return a};
+mxGuide.prototype.move=function(a,b,c){if(null!=this.states&&(this.horizontal||this.vertical)&&null!=a&&null!=b){var d=function(b){b+=this.graph.panDy;var c=!1;Math.abs(b-F)<y?(l=b-a.getCenterY(),y=Math.abs(b-F),c=!0):Math.abs(b-B)<y?(l=b-a.y,y=Math.abs(b-B),c=!0):Math.abs(b-C)<y&&(l=b-a.y-a.height,y=Math.abs(b-C),c=!0);c&&(r=D,t=Math.round(b-this.graph.panDy),null==this.guideY&&(this.guideY=this.createGuideShape(!1),this.guideY.dialect=this.graph.dialect!=mxConstants.DIALECT_SVG?mxConstants.DIALECT_VML:
+mxConstants.DIALECT_SVG,this.guideY.pointerEvents=!1,this.guideY.init(this.graph.getView().getOverlayPane())));q=q||c},e=function(b,c){b+=this.graph.panDx;var d=!1;Math.abs(b-v)<x?(k=b-a.getCenterX(),x=Math.abs(b-v),d=!0):Math.abs(b-A)<x?(k=b-a.x,x=Math.abs(b-A),d=!0):Math.abs(b-z)<x&&(k=b-a.x-a.width,x=Math.abs(b-z),d=!0);d&&(n=c,p=Math.round(b-this.graph.panDx),null==this.guideX&&(this.guideX=this.createGuideShape(!0),this.guideX.dialect=this.graph.dialect!=mxConstants.DIALECT_SVG?mxConstants.DIALECT_VML:
+mxConstants.DIALECT_SVG,this.guideX.pointerEvents=!1,this.guideX.init(this.graph.getView().getOverlayPane())));m=m||d},f=this.graph.getView().translate,g=this.graph.getView().scale,k=b.x,l=b.y,m=!1,n=null,p=null,q=!1,r=null,t=null,u=this.getGuideTolerance(),x=u,y=u,u=a.clone();u.x+=b.x;u.y+=b.y;var A=u.x,z=u.x+u.width,v=u.getCenterX(),B=u.y,C=u.y+u.height,F=u.getCenterY();for(b=0;b<this.states.length;b++){var D=this.states[b];null!=D&&(this.horizontal&&(e.call(this,D.getCenterX(),D),e.call(this,D.x,
+D),e.call(this,D.x+D.width,D)),this.vertical&&(d.call(this,D.getCenterY(),D),d.call(this,D.y,D),d.call(this,D.y+D.height,D)))}c&&(m||(c=a.x-(this.graph.snap(a.x/g-f.x)+f.x)*g,k=this.graph.snap(k/g)*g-c),q||(f=a.y-(this.graph.snap(a.y/g-f.y)+f.y)*g,l=this.graph.snap(l/g)*g-f));g=this.graph.container;m||null==this.guideX?null!=this.guideX&&(null!=n&&null!=a&&(minY=Math.min(a.y+l-this.graph.panDy,n.y),maxY=Math.max(a.y+a.height+l-this.graph.panDy,n.y+n.height)),this.guideX.points=null!=minY&&null!=maxY?
+[new mxPoint(p,minY),new mxPoint(p,maxY)]:[new mxPoint(p,-this.graph.panDy),new mxPoint(p,g.scrollHeight-3-this.graph.panDy)],this.guideX.stroke=this.getGuideColor(n,!0),this.guideX.node.style.visibility="visible",this.guideX.redraw()):this.guideX.node.style.visibility="hidden";q||null==this.guideY?null!=this.guideY&&(null!=r&&null!=a&&(minX=Math.min(a.x+k-this.graph.panDx,r.x),maxX=Math.max(a.x+a.width+k-this.graph.panDx,r.x+r.width)),this.guideY.points=null!=minX&&null!=maxX?[new mxPoint(minX,t),
+new mxPoint(maxX,t)]:[new mxPoint(-this.graph.panDx,t),new mxPoint(g.scrollWidth-3-this.graph.panDx,t)],this.guideY.stroke=this.getGuideColor(r,!1),this.guideY.node.style.visibility="visible",this.guideY.redraw()):this.guideY.node.style.visibility="hidden";b=new mxPoint(k,l)}return b};mxGuide.prototype.getGuideColor=function(a,b){return mxConstants.GUIDE_COLOR};mxGuide.prototype.hide=function(){this.setVisible(!1)};
+mxGuide.prototype.setVisible=function(a){null!=this.guideX&&(this.guideX.node.style.visibility=a?"visible":"hidden");null!=this.guideY&&(this.guideY.node.style.visibility=a?"visible":"hidden")};mxGuide.prototype.destroy=function(){null!=this.guideX&&(this.guideX.destroy(),this.guideX=null);null!=this.guideY&&(this.guideY.destroy(),this.guideY=null)};function mxStencil(a){this.desc=a;this.parseDescription();this.parseConstraints()}mxStencil.defaultLocalized=!1;mxStencil.allowEval=!1;
+mxStencil.prototype.desc=null;mxStencil.prototype.constraints=null;mxStencil.prototype.aspect=null;mxStencil.prototype.w0=null;mxStencil.prototype.h0=null;mxStencil.prototype.bgNode=null;mxStencil.prototype.fgNode=null;mxStencil.prototype.strokewidth=null;
+mxStencil.prototype.parseDescription=function(){this.fgNode=this.desc.getElementsByTagName("foreground")[0];this.bgNode=this.desc.getElementsByTagName("background")[0];this.w0=Number(this.desc.getAttribute("w")||100);this.h0=Number(this.desc.getAttribute("h")||100);var a=this.desc.getAttribute("aspect");this.aspect=null!=a?a:"variable";a=this.desc.getAttribute("strokewidth");this.strokewidth=null!=a?a:"1"};
+mxStencil.prototype.parseConstraints=function(){var a=this.desc.getElementsByTagName("connections")[0];if(null!=a&&(a=mxUtils.getChildNodes(a),null!=a&&0<a.length)){this.constraints=[];for(var b=0;b<a.length;b++)this.constraints.push(this.parseConstraint(a[b]))}};mxStencil.prototype.parseConstraint=function(a){var b=Number(a.getAttribute("x")),c=Number(a.getAttribute("y")),d="1"==a.getAttribute("perimeter");a=a.getAttribute("name");return new mxConnectionConstraint(new mxPoint(b,c),d,a)};
+mxStencil.prototype.evaluateTextAttribute=function(a,b,c){b=this.evaluateAttribute(a,b,c);a=a.getAttribute("localized");if(mxStencil.defaultLocalized&&null==a||"1"==a)b=mxResources.get(b);return b};mxStencil.prototype.evaluateAttribute=function(a,b,c){b=a.getAttribute(b);null==b&&(a=mxUtils.getTextContent(a),null!=a&&mxStencil.allowEval&&(a=mxUtils.eval(a),"function"==typeof a&&(b=a(c))));return b};
+mxStencil.prototype.drawShape=function(a,b,c,d,e,f){var g=mxUtils.getValue(b.style,mxConstants.STYLE_DIRECTION,null),g=this.computeAspect(b.style,c,d,e,f,g),k=Math.min(g.width,g.height),k="inherit"==this.strokewidth?Number(mxUtils.getNumber(b.style,mxConstants.STYLE_STROKEWIDTH,1)):Number(this.strokewidth)*k;a.setStrokeWidth(k);this.drawChildren(a,b,c,d,e,f,this.bgNode,g,!1);this.drawChildren(a,b,c,d,e,f,this.fgNode,g,!0)};
+mxStencil.prototype.drawChildren=function(a,b,c,d,e,f,g,k,l){if(null!=g&&0<e&&0<f)for(c=g.firstChild;null!=c;)c.nodeType==mxConstants.NODETYPE_ELEMENT&&this.drawNode(a,b,c,k,l),c=c.nextSibling};
+mxStencil.prototype.computeAspect=function(a,b,c,d,e,f){a=b;b=d/this.w0;var g=e/this.h0;if(f=f==mxConstants.DIRECTION_NORTH||f==mxConstants.DIRECTION_SOUTH){g=d/this.h0;b=e/this.w0;var k=(d-e)/2;a+=k;c-=k}"fixed"==this.aspect&&(b=g=Math.min(b,g),f?(a+=(e-this.w0*b)/2,c+=(d-this.h0*g)/2):(a+=(d-this.w0*b)/2,c+=(e-this.h0*g)/2));return new mxRectangle(a,c,b,g)};
+mxStencil.prototype.drawNode=function(a,b,c,d,e){var f=c.nodeName,g=d.x,k=d.y,l=d.width,m=d.height,n=Math.min(l,m);if("save"==f)a.save();else if("restore"==f)a.restore();else if("path"==f)for(a.begin(),c=c.firstChild;null!=c;)c.nodeType==mxConstants.NODETYPE_ELEMENT&&this.drawNode(a,b,c,d,e),c=c.nextSibling;else if("close"==f)a.close();else if("move"==f)a.moveTo(g+Number(c.getAttribute("x"))*l,k+Number(c.getAttribute("y"))*m);else if("line"==f)a.lineTo(g+Number(c.getAttribute("x"))*l,k+Number(c.getAttribute("y"))*
+m);else if("quad"==f)a.quadTo(g+Number(c.getAttribute("x1"))*l,k+Number(c.getAttribute("y1"))*m,g+Number(c.getAttribute("x2"))*l,k+Number(c.getAttribute("y2"))*m);else if("curve"==f)a.curveTo(g+Number(c.getAttribute("x1"))*l,k+Number(c.getAttribute("y1"))*m,g+Number(c.getAttribute("x2"))*l,k+Number(c.getAttribute("y2"))*m,g+Number(c.getAttribute("x3"))*l,k+Number(c.getAttribute("y3"))*m);else if("arc"==f)a.arcTo(Number(c.getAttribute("rx"))*l,Number(c.getAttribute("ry"))*m,Number(c.getAttribute("x-axis-rotation")),
+Number(c.getAttribute("large-arc-flag")),Number(c.getAttribute("sweep-flag")),g+Number(c.getAttribute("x"))*l,k+Number(c.getAttribute("y"))*m);else if("rect"==f)a.rect(g+Number(c.getAttribute("x"))*l,k+Number(c.getAttribute("y"))*m,Number(c.getAttribute("w"))*l,Number(c.getAttribute("h"))*m);else if("roundrect"==f)b=Number(c.getAttribute("arcsize")),0==b&&(b=100*mxConstants.RECTANGLE_ROUNDING_FACTOR),n=Number(c.getAttribute("w"))*l,d=Number(c.getAttribute("h"))*m,b=Number(b)/100,b=Math.min(n*b,d*
+b),a.roundrect(g+Number(c.getAttribute("x"))*l,k+Number(c.getAttribute("y"))*m,n,d,b,b);else if("ellipse"==f)a.ellipse(g+Number(c.getAttribute("x"))*l,k+Number(c.getAttribute("y"))*m,Number(c.getAttribute("w"))*l,Number(c.getAttribute("h"))*m);else if("image"==f)b.outline||(b=this.evaluateAttribute(c,"src",b),a.image(g+Number(c.getAttribute("x"))*l,k+Number(c.getAttribute("y"))*m,Number(c.getAttribute("w"))*l,Number(c.getAttribute("h"))*m,b,!1,"1"==c.getAttribute("flipH"),"1"==c.getAttribute("flipV")));
+else if("text"==f){if(!b.outline){n=this.evaluateTextAttribute(c,"str",b);d="1"==c.getAttribute("vertical")?-90:0;if("0"==c.getAttribute("align-shape")){var p=b.rotation,q=1==mxUtils.getValue(b.style,mxConstants.STYLE_FLIPH,0);b=1==mxUtils.getValue(b.style,mxConstants.STYLE_FLIPV,0);d=q&&b?d-p:q||b?d+p:d-p}d-=c.getAttribute("rotation");a.text(g+Number(c.getAttribute("x"))*l,k+Number(c.getAttribute("y"))*m,0,0,n,c.getAttribute("align")||"left",c.getAttribute("valign")||"top",!1,"",null,!1,d)}}else if("include-shape"==
+f)p=mxStencilRegistry.getStencil(c.getAttribute("name")),null!=p&&(g+=Number(c.getAttribute("x"))*l,k+=Number(c.getAttribute("y"))*m,n=Number(c.getAttribute("w"))*l,d=Number(c.getAttribute("h"))*m,p.drawShape(a,b,g,k,n,d));else if("fillstroke"==f)a.fillAndStroke();else if("fill"==f)a.fill();else if("stroke"==f)a.stroke();else if("strokewidth"==f)l="1"==c.getAttribute("fixed")?1:n,a.setStrokeWidth(Number(c.getAttribute("width"))*l);else if("dashed"==f)a.setDashed("1"==c.getAttribute("dashed"));else if("dashpattern"==
+f){if(c=c.getAttribute("pattern"),null!=c){c=c.split(" ");l=[];for(m=0;m<c.length;m++)0<c[m].length&&l.push(Number(c[m])*n);c=l.join(" ");a.setDashPattern(c)}}else"strokecolor"==f?a.setStrokeColor(c.getAttribute("color")):"linecap"==f?a.setLineCap(c.getAttribute("cap")):"linejoin"==f?a.setLineJoin(c.getAttribute("join")):"miterlimit"==f?a.setMiterLimit(Number(c.getAttribute("limit"))):"fillcolor"==f?a.setFillColor(c.getAttribute("color")):"alpha"==f?a.setAlpha(c.getAttribute("alpha")):"fontcolor"==
+f?a.setFontColor(c.getAttribute("color")):"fontstyle"==f?a.setFontStyle(c.getAttribute("style")):"fontfamily"==f?a.setFontFamily(c.getAttribute("family")):"fontsize"==f&&a.setFontSize(Number(c.getAttribute("size"))*n);!e||"fillstroke"!=f&&"fill"!=f&&"stroke"!=f||a.setShadow(!1)};function mxShape(a){this.stencil=a;this.initStyles()}mxShape.prototype.dialect=null;mxShape.prototype.scale=1;mxShape.prototype.antiAlias=!0;mxShape.prototype.minSvgStrokeWidth=1;mxShape.prototype.bounds=null;
+mxShape.prototype.points=null;mxShape.prototype.node=null;mxShape.prototype.state=null;mxShape.prototype.style=null;mxShape.prototype.boundingBox=null;mxShape.prototype.stencil=null;mxShape.prototype.svgStrokeTolerance=8;mxShape.prototype.pointerEvents=!0;mxShape.prototype.svgPointerEvents="all";mxShape.prototype.shapePointerEvents=!1;mxShape.prototype.stencilPointerEvents=!1;mxShape.prototype.vmlScale=1;mxShape.prototype.outline=!1;mxShape.prototype.visible=!0;
+mxShape.prototype.useSvgBoundingBox=!1;mxShape.prototype.init=function(a){null==this.node&&(this.node=this.create(a),null!=a&&a.appendChild(this.node))};mxShape.prototype.initStyles=function(a){this.strokewidth=1;this.rotation=0;this.strokeOpacity=this.fillOpacity=this.opacity=100;this.flipV=this.flipH=!1};mxShape.prototype.isParseVml=function(){return!0};mxShape.prototype.isHtmlAllowed=function(){return!1};
+mxShape.prototype.getSvgScreenOffset=function(){return 1==mxUtils.mod(Math.max(1,Math.round((this.stencil&&"inherit"!=this.stencil.strokewidth?Number(this.stencil.strokewidth):this.strokewidth)*this.scale)),2)?.5:0};mxShape.prototype.create=function(a){return null!=a&&null!=a.ownerSVGElement?this.createSvg(a):8==document.documentMode||!mxClient.IS_VML||this.dialect!=mxConstants.DIALECT_VML&&this.isHtmlAllowed()?this.createHtml(a):this.createVml(a)};
+mxShape.prototype.createSvg=function(){return document.createElementNS(mxConstants.NS_SVG,"g")};mxShape.prototype.createVml=function(){var a=document.createElement(mxClient.VML_PREFIX+":group");a.style.position="absolute";return a};mxShape.prototype.createHtml=function(){var a=document.createElement("div");a.style.position="absolute";return a};mxShape.prototype.reconfigure=function(){this.redraw()};
+mxShape.prototype.redraw=function(){this.updateBoundsFromPoints();this.visible&&this.checkBounds()?(this.node.style.visibility="visible",this.clear(),"DIV"!=this.node.nodeName||!this.isHtmlAllowed()&&mxClient.IS_VML?this.redrawShape():this.redrawHtmlShape(),this.updateBoundingBox()):(this.node.style.visibility="hidden",this.boundingBox=null)};
+mxShape.prototype.clear=function(){if(null!=this.node.ownerSVGElement)for(;null!=this.node.lastChild;)this.node.removeChild(this.node.lastChild);else this.node.style.cssText="position:absolute;"+(null!=this.cursor?"cursor:"+this.cursor+";":""),this.node.innerHTML=""};
+mxShape.prototype.updateBoundsFromPoints=function(){var a=this.points;if(null!=a&&0<a.length&&null!=a[0]){this.bounds=new mxRectangle(Number(a[0].x),Number(a[0].y),1,1);for(var b=1;b<this.points.length;b++)null!=a[b]&&this.bounds.add(new mxRectangle(Number(a[b].x),Number(a[b].y),1,1))}};
+mxShape.prototype.getLabelBounds=function(a){var b=mxUtils.getValue(this.style,mxConstants.STYLE_DIRECTION,mxConstants.DIRECTION_EAST),c=a;b!=mxConstants.DIRECTION_SOUTH&&b!=mxConstants.DIRECTION_NORTH&&null!=this.state&&null!=this.state.text&&this.state.text.isPaintBoundsInverted()&&(c=c.clone(),b=c.width,c.width=c.height,c.height=b);c=this.getLabelMargins(c);if(null!=c){var d="1"==mxUtils.getValue(this.style,mxConstants.STYLE_FLIPH,!1),e="1"==mxUtils.getValue(this.style,mxConstants.STYLE_FLIPV,
+!1);null!=this.state&&null!=this.state.text&&this.state.text.isPaintBoundsInverted()&&(b=c.x,c.x=c.height,c.height=c.width,c.width=c.y,c.y=b,b=d,d=e,e=b);return mxUtils.getDirectedBounds(a,c,this.style,d,e)}return a};mxShape.prototype.getLabelMargins=function(a){return null};
+mxShape.prototype.checkBounds=function(){return!isNaN(this.scale)&&isFinite(this.scale)&&0<this.scale&&null!=this.bounds&&!isNaN(this.bounds.x)&&!isNaN(this.bounds.y)&&!isNaN(this.bounds.width)&&!isNaN(this.bounds.height)&&0<this.bounds.width&&0<this.bounds.height};mxShape.prototype.createVmlGroup=function(){var a=document.createElement(mxClient.VML_PREFIX+":group");a.style.position="absolute";a.style.width=this.node.style.width;a.style.height=this.node.style.height;return a};
+mxShape.prototype.redrawShape=function(){var a=this.createCanvas();null!=a&&(a.pointerEvents=this.pointerEvents,this.paint(a),this.node!=a.root&&this.node.insertAdjacentHTML("beforeend",a.root.outerHTML),"DIV"==this.node.nodeName&&8==document.documentMode&&(this.node.style.filter="",mxUtils.addTransparentBackgroundFilter(this.node)),this.destroyCanvas(a))};
+mxShape.prototype.createCanvas=function(){var a=null;null!=this.node.ownerSVGElement?a=this.createSvgCanvas():mxClient.IS_VML&&(this.updateVmlContainer(),a=this.createVmlCanvas());null!=a&&this.outline&&(a.setStrokeWidth(this.strokewidth),a.setStrokeColor(this.stroke),null!=this.isDashed&&a.setDashed(this.isDashed),a.setStrokeWidth=function(){},a.setStrokeColor=function(){},a.setFillColor=function(){},a.setGradient=function(){},a.setDashed=function(){});return a};
+mxShape.prototype.createSvgCanvas=function(){var a=new mxSvgCanvas2D(this.node,!1);a.strokeTolerance=this.pointerEvents?this.svgStrokeTolerance:0;a.pointerEventsValue=this.svgPointerEvents;a.blockImagePointerEvents=mxClient.IS_FF;var b=this.getSvgScreenOffset();0!=b?this.node.setAttribute("transform","translate("+b+","+b+")"):this.node.removeAttribute("transform");a.minStrokeWidth=this.minSvgStrokeWidth;this.antiAlias||(a.format=function(a){return Math.round(parseFloat(a))});return a};
+mxShape.prototype.createVmlCanvas=function(){var a=8==document.documentMode&&this.isParseVml()?this.createVmlGroup():this.node,b=new mxVmlCanvas2D(a,!1);""!=a.tagUrn&&(a.coordsize=Math.max(1,Math.round(this.bounds.width))*this.vmlScale+","+Math.max(1,Math.round(this.bounds.height))*this.vmlScale,b.scale(this.vmlScale),b.vmlScale=this.vmlScale);a=this.scale;b.translate(-Math.round(this.bounds.x/a),-Math.round(this.bounds.y/a));return b};
+mxShape.prototype.updateVmlContainer=function(){this.node.style.left=Math.round(this.bounds.x)+"px";this.node.style.top=Math.round(this.bounds.y)+"px";var a=Math.max(1,Math.round(this.bounds.height));this.node.style.width=Math.max(1,Math.round(this.bounds.width))+"px";this.node.style.height=a+"px";this.node.style.overflow="visible"};mxShape.prototype.redrawHtmlShape=function(){this.updateHtmlBounds(this.node);this.updateHtmlFilters(this.node);this.updateHtmlColors(this.node)};
+mxShape.prototype.updateHtmlFilters=function(a){var b="";100>this.opacity&&(b+="alpha(opacity="+this.opacity+")");this.isShadow&&(b+="progid:DXImageTransform.Microsoft.dropShadow (OffX='"+Math.round(mxConstants.SHADOW_OFFSET_X*this.scale)+"', OffY='"+Math.round(mxConstants.SHADOW_OFFSET_Y*this.scale)+"', Color='"+mxConstants.VML_SHADOWCOLOR+"')");if(null!=this.fill&&this.fill!=mxConstants.NONE&&this.gradient&&this.gradient!=mxConstants.NONE){var c=this.fill,d=this.gradient,e="0",f={east:0,south:1,
+west:2,north:3},g=null!=this.direction?f[this.direction]:0;null!=this.gradientDirection&&(g=mxUtils.mod(g+f[this.gradientDirection]-1,4));1==g?(e="1",f=c,c=d,d=f):2==g?(f=c,c=d,d=f):3==g&&(e="1");b+="progid:DXImageTransform.Microsoft.gradient(startColorStr='"+c+"', endColorStr='"+d+"', gradientType='"+e+"')"}a.style.filter=b};
+mxShape.prototype.updateHtmlColors=function(a){var b=this.stroke;null!=b&&b!=mxConstants.NONE?(a.style.borderColor=b,this.isDashed?a.style.borderStyle="dashed":0<this.strokewidth&&(a.style.borderStyle="solid"),a.style.borderWidth=Math.max(1,Math.ceil(this.strokewidth*this.scale))+"px"):a.style.borderWidth="0px";b=this.outline?null:this.fill;null!=b&&b!=mxConstants.NONE?(a.style.backgroundColor=b,a.style.backgroundImage="none"):this.pointerEvents?a.style.backgroundColor="transparent":8==document.documentMode?
+mxUtils.addTransparentBackgroundFilter(a):this.setTransparentBackgroundImage(a)};
+mxShape.prototype.updateHtmlBounds=function(a){var b=9<=document.documentMode?0:Math.ceil(this.strokewidth*this.scale);a.style.borderWidth=Math.max(1,b)+"px";a.style.overflow="hidden";a.style.left=Math.round(this.bounds.x-b/2)+"px";a.style.top=Math.round(this.bounds.y-b/2)+"px";"CSS1Compat"==document.compatMode&&(b=-b);a.style.width=Math.round(Math.max(0,this.bounds.width+b))+"px";a.style.height=Math.round(Math.max(0,this.bounds.height+b))+"px"};
+mxShape.prototype.destroyCanvas=function(a){if(a instanceof mxSvgCanvas2D){for(var b in a.gradients){var c=a.gradients[b];null!=c&&(c.mxRefCount=(c.mxRefCount||0)+1)}this.releaseSvgGradients(this.oldGradients);this.oldGradients=a.gradients}};
+mxShape.prototype.paint=function(a){var b=!1;if(null!=a&&this.outline){var c=a.stroke;a.stroke=function(){b=!0;c.apply(this,arguments)};var d=a.fillAndStroke;a.fillAndStroke=function(){b=!0;d.apply(this,arguments)}}var e=this.scale,f=this.bounds.x/e,g=this.bounds.y/e,k=this.bounds.width/e,l=this.bounds.height/e;if(this.isPaintBoundsInverted())var m=(k-l)/2,f=f+m,g=g-m,m=k,k=l,l=m;this.updateTransform(a,f,g,k,l);this.configureCanvas(a,f,g,k,l);m=null;if(null==this.stencil&&null==this.points&&this.shapePointerEvents||
+null!=this.stencil&&this.stencilPointerEvents){var n=this.createBoundingBox();this.dialect==mxConstants.DIALECT_SVG?(m=this.createTransparentSvgRectangle(n.x,n.y,n.width,n.height),this.node.appendChild(m)):(n=a.createRect("rect",n.x/e,n.y/e,n.width/e,n.height/e),n.appendChild(a.createTransparentFill()),n.stroked="false",a.root.appendChild(n))}if(null!=this.stencil)this.stencil.drawShape(a,this,f,g,k,l);else if(a.setStrokeWidth(this.strokewidth),null!=this.points){for(var n=[],p=0;p<this.points.length;p++)null!=
+this.points[p]&&n.push(new mxPoint(this.points[p].x/e,this.points[p].y/e));this.paintEdgeShape(a,n)}else this.paintVertexShape(a,f,g,k,l);null!=m&&null!=a.state&&null!=a.state.transform&&m.setAttribute("transform",a.state.transform);null!=a&&this.outline&&!b&&(a.rect(f,g,k,l),a.stroke())};
+mxShape.prototype.configureCanvas=function(a,b,c,d,e){var f=null;null!=this.style&&(f=this.style.dashPattern);a.setAlpha(this.opacity/100);a.setFillAlpha(this.fillOpacity/100);a.setStrokeAlpha(this.strokeOpacity/100);null!=this.isShadow&&a.setShadow(this.isShadow);null!=this.isDashed&&a.setDashed(this.isDashed,null!=this.style?1==mxUtils.getValue(this.style,mxConstants.STYLE_FIX_DASH,!1):!1);null!=f&&a.setDashPattern(f);null!=this.fill&&this.fill!=mxConstants.NONE&&this.gradient&&this.gradient!=mxConstants.NONE?
+(b=this.getGradientBounds(a,b,c,d,e),a.setGradient(this.fill,this.gradient,b.x,b.y,b.width,b.height,this.gradientDirection)):a.setFillColor(this.fill);a.setStrokeColor(this.stroke)};mxShape.prototype.getGradientBounds=function(a,b,c,d,e){return new mxRectangle(b,c,d,e)};mxShape.prototype.updateTransform=function(a,b,c,d,e){a.scale(this.scale);a.rotate(this.getShapeRotation(),this.flipH,this.flipV,b+d/2,c+e/2)};
+mxShape.prototype.paintVertexShape=function(a,b,c,d,e){this.paintBackground(a,b,c,d,e);a.setShadow(!1);this.paintForeground(a,b,c,d,e)};mxShape.prototype.paintBackground=function(a,b,c,d,e){};mxShape.prototype.paintForeground=function(a,b,c,d,e){};mxShape.prototype.paintEdgeShape=function(a,b){};
+mxShape.prototype.getArcSize=function(a,b){var c;"1"==mxUtils.getValue(this.style,mxConstants.STYLE_ABSOLUTE_ARCSIZE,0)?c=Math.min(a/2,Math.min(b/2,mxUtils.getValue(this.style,mxConstants.STYLE_ARCSIZE,mxConstants.LINE_ARCSIZE)/2)):(c=mxUtils.getValue(this.style,mxConstants.STYLE_ARCSIZE,100*mxConstants.RECTANGLE_ROUNDING_FACTOR)/100,c=Math.min(a*c,b*c));return c};
+mxShape.prototype.paintGlassEffect=function(a,b,c,d,e,f){var g=Math.ceil(this.strokewidth/2);a.setGradient("#ffffff","#ffffff",b,c,d,.6*e,"south",.9,.1);a.begin();f+=2*g;this.isRounded?(a.moveTo(b-g+f,c-g),a.quadTo(b-g,c-g,b-g,c-g+f),a.lineTo(b-g,c+.4*e),a.quadTo(b+.5*d,c+.7*e,b+d+g,c+.4*e),a.lineTo(b+d+g,c-g+f),a.quadTo(b+d+g,c-g,b+d+g-f,c-g)):(a.moveTo(b-g,c-g),a.lineTo(b-g,c+.4*e),a.quadTo(b+.5*d,c+.7*e,b+d+g,c+.4*e),a.lineTo(b+d+g,c-g));a.close();a.fill()};
+mxShape.prototype.addPoints=function(a,b,c,d,e,f,g){if(null!=b&&0<b.length){g=null!=g?g:!0;var k=b[b.length-1];if(e&&c){b=b.slice();var l=b[0],l=new mxPoint(k.x+(l.x-k.x)/2,k.y+(l.y-k.y)/2);b.splice(0,0,l)}var m=b[0],l=1;for(g?a.moveTo(m.x,m.y):a.lineTo(m.x,m.y);l<(e?b.length:b.length-1);){g=b[mxUtils.mod(l,b.length)];var n=m.x-g.x,m=m.y-g.y;if(c&&(0!=n||0!=m)&&(null==f||0>mxUtils.indexOf(f,l-1))){var p=Math.sqrt(n*n+m*m);a.lineTo(g.x+n*Math.min(d,p/2)/p,g.y+m*Math.min(d,p/2)/p);for(m=b[mxUtils.mod(l+
+1,b.length)];l<b.length-2&&0==Math.round(m.x-g.x)&&0==Math.round(m.y-g.y);)m=b[mxUtils.mod(l+2,b.length)],l++;n=m.x-g.x;m=m.y-g.y;p=Math.max(1,Math.sqrt(n*n+m*m));n=g.x+n*Math.min(d,p/2)/p;m=g.y+m*Math.min(d,p/2)/p;a.quadTo(g.x,g.y,n,m);g=new mxPoint(n,m)}else a.lineTo(g.x,g.y);m=g;l++}e?a.close():a.lineTo(k.x,k.y)}};
+mxShape.prototype.resetStyles=function(){this.initStyles();this.spacing=0;delete this.fill;delete this.gradient;delete this.gradientDirection;delete this.stroke;delete this.startSize;delete this.endSize;delete this.startArrow;delete this.endArrow;delete this.direction;delete this.isShadow;delete this.isDashed;delete this.isRounded;delete this.glass};
+mxShape.prototype.apply=function(a){this.state=a;this.style=a.style;if(null!=this.style){this.fill=mxUtils.getValue(this.style,mxConstants.STYLE_FILLCOLOR,this.fill);this.gradient=mxUtils.getValue(this.style,mxConstants.STYLE_GRADIENTCOLOR,this.gradient);this.gradientDirection=mxUtils.getValue(this.style,mxConstants.STYLE_GRADIENT_DIRECTION,this.gradientDirection);this.opacity=mxUtils.getValue(this.style,mxConstants.STYLE_OPACITY,this.opacity);this.fillOpacity=mxUtils.getValue(this.style,mxConstants.STYLE_FILL_OPACITY,
+this.fillOpacity);this.strokeOpacity=mxUtils.getValue(this.style,mxConstants.STYLE_STROKE_OPACITY,this.strokeOpacity);this.stroke=mxUtils.getValue(this.style,mxConstants.STYLE_STROKECOLOR,this.stroke);this.strokewidth=mxUtils.getNumber(this.style,mxConstants.STYLE_STROKEWIDTH,this.strokewidth);this.spacing=mxUtils.getValue(this.style,mxConstants.STYLE_SPACING,this.spacing);this.startSize=mxUtils.getNumber(this.style,mxConstants.STYLE_STARTSIZE,this.startSize);this.endSize=mxUtils.getNumber(this.style,
+mxConstants.STYLE_ENDSIZE,this.endSize);this.startArrow=mxUtils.getValue(this.style,mxConstants.STYLE_STARTARROW,this.startArrow);this.endArrow=mxUtils.getValue(this.style,mxConstants.STYLE_ENDARROW,this.endArrow);this.rotation=mxUtils.getValue(this.style,mxConstants.STYLE_ROTATION,this.rotation);this.direction=mxUtils.getValue(this.style,mxConstants.STYLE_DIRECTION,this.direction);this.flipH=1==mxUtils.getValue(this.style,mxConstants.STYLE_FLIPH,0);this.flipV=1==mxUtils.getValue(this.style,mxConstants.STYLE_FLIPV,
+0);null!=this.stencil&&(this.flipH=1==mxUtils.getValue(this.style,"stencilFlipH",0)||this.flipH,this.flipV=1==mxUtils.getValue(this.style,"stencilFlipV",0)||this.flipV);if(this.direction==mxConstants.DIRECTION_NORTH||this.direction==mxConstants.DIRECTION_SOUTH)a=this.flipH,this.flipH=this.flipV,this.flipV=a;this.isShadow=1==mxUtils.getValue(this.style,mxConstants.STYLE_SHADOW,this.isShadow);this.isDashed=1==mxUtils.getValue(this.style,mxConstants.STYLE_DASHED,this.isDashed);this.isRounded=1==mxUtils.getValue(this.style,
+mxConstants.STYLE_ROUNDED,this.isRounded);this.glass=1==mxUtils.getValue(this.style,mxConstants.STYLE_GLASS,this.glass);this.fill==mxConstants.NONE&&(this.fill=null);this.gradient==mxConstants.NONE&&(this.gradient=null);this.stroke==mxConstants.NONE&&(this.stroke=null)}};mxShape.prototype.setCursor=function(a){null==a&&(a="");this.cursor=a;null!=this.node&&(this.node.style.cursor=a)};mxShape.prototype.getCursor=function(){return this.cursor};
+mxShape.prototype.updateBoundingBox=function(){if(this.useSvgBoundingBox&&null!=this.node&&null!=this.node.ownerSVGElement)try{var a=this.node.getBBox();if(0<a.width&&0<a.height){this.boundingBox=new mxRectangle(a.x,a.y,a.width,a.height);this.boundingBox.grow(this.strokewidth*this.scale/2);return}}catch(c){}if(null!=this.bounds){a=this.createBoundingBox();if(null!=a){this.augmentBoundingBox(a);var b=this.getShapeRotation();0!=b&&(a=mxUtils.getBoundingBox(a,b))}this.boundingBox=a}};
+mxShape.prototype.createBoundingBox=function(){var a=this.bounds.clone();(null!=this.stencil&&(this.direction==mxConstants.DIRECTION_NORTH||this.direction==mxConstants.DIRECTION_SOUTH)||this.isPaintBoundsInverted())&&a.rotate90();return a};mxShape.prototype.augmentBoundingBox=function(a){this.isShadow&&(a.width+=Math.ceil(mxConstants.SHADOW_OFFSET_X*this.scale),a.height+=Math.ceil(mxConstants.SHADOW_OFFSET_Y*this.scale));a.grow(this.strokewidth*this.scale/2)};
+mxShape.prototype.isPaintBoundsInverted=function(){return null==this.stencil&&(this.direction==mxConstants.DIRECTION_NORTH||this.direction==mxConstants.DIRECTION_SOUTH)};mxShape.prototype.getRotation=function(){return null!=this.rotation?this.rotation:0};mxShape.prototype.getTextRotation=function(){var a=this.getRotation();1!=mxUtils.getValue(this.style,mxConstants.STYLE_HORIZONTAL,1)&&(a+=mxText.prototype.verticalTextRotation);return a};
+mxShape.prototype.getShapeRotation=function(){var a=this.getRotation();null!=this.direction&&(this.direction==mxConstants.DIRECTION_NORTH?a+=270:this.direction==mxConstants.DIRECTION_WEST?a+=180:this.direction==mxConstants.DIRECTION_SOUTH&&(a+=90));return a};
+mxShape.prototype.createTransparentSvgRectangle=function(a,b,c,d){var e=document.createElementNS(mxConstants.NS_SVG,"rect");e.setAttribute("x",a);e.setAttribute("y",b);e.setAttribute("width",c);e.setAttribute("height",d);e.setAttribute("fill","none");e.setAttribute("stroke","none");e.setAttribute("pointer-events","all");return e};mxShape.prototype.setTransparentBackgroundImage=function(a){a.style.backgroundImage="url('"+mxClient.imageBasePath+"/transparent.gif')"};
+mxShape.prototype.releaseSvgGradients=function(a){if(null!=a)for(var b in a){var c=a[b];null!=c&&(c.mxRefCount=(c.mxRefCount||0)-1,0==c.mxRefCount&&null!=c.parentNode&&c.parentNode.removeChild(c))}};mxShape.prototype.destroy=function(){null!=this.node&&(mxEvent.release(this.node),null!=this.node.parentNode&&this.node.parentNode.removeChild(this.node),this.node=null);this.releaseSvgGradients(this.oldGradients);this.oldGradients=null};
+var mxStencilRegistry={stencils:{},addStencil:function(a,b){mxStencilRegistry.stencils[a]=b},getStencil:function(a){return mxStencilRegistry.stencils[a]}},mxMarker={markers:[],addMarker:function(a,b){mxMarker.markers[a]=b},createMarker:function(a,b,c,d,e,f,g,k,l,m){var n=mxMarker.markers[c];return null!=n?n(a,b,c,d,e,f,g,k,l,m):null}};
+(function(){function a(a){a=null!=a?a:2;return function(b,c,d,k,l,m,n,p,q,r){c=l*q*1.118;p=m*q*1.118;l*=n+q;m*=n+q;var e=k.clone();e.x-=c;e.y-=p;n=d!=mxConstants.ARROW_CLASSIC&&d!=mxConstants.ARROW_CLASSIC_THIN?1:.75;k.x+=-l*n-c;k.y+=-m*n-p;return function(){b.begin();b.moveTo(e.x,e.y);b.lineTo(e.x-l-m/a,e.y-m+l/a);d!=mxConstants.ARROW_CLASSIC&&d!=mxConstants.ARROW_CLASSIC_THIN||b.lineTo(e.x-3*l/4,e.y-3*m/4);b.lineTo(e.x+m/a-l,e.y-m-l/a);b.close();r?b.fillAndStroke():b.stroke()}}}function b(a){a=
+null!=a?a:2;return function(b,c,d,k,l,m,n,p,q,r){c=l*q*1.118;d=m*q*1.118;l*=n+q;m*=n+q;var e=k.clone();e.x-=c;e.y-=d;k.x+=2*-c;k.y+=2*-d;return function(){b.begin();b.moveTo(e.x-l-m/a,e.y-m+l/a);b.lineTo(e.x,e.y);b.lineTo(e.x+m/a-l,e.y-m-l/a);b.stroke()}}}function c(a,b,c,g,k,l,m,n,p,q){n=c==mxConstants.ARROW_DIAMOND?.7071:.9862;b=k*p*n;n*=l*p;k*=m+p;l*=m+p;var d=g.clone();d.x-=b;d.y-=n;g.x+=-k-b;g.y+=-l-n;var e=c==mxConstants.ARROW_DIAMOND?2:3.4;return function(){a.begin();a.moveTo(d.x,d.y);a.lineTo(d.x-
+k/2-l/e,d.y+k/e-l/2);a.lineTo(d.x-k,d.y-l);a.lineTo(d.x-k/2+l/e,d.y-l/2-k/e);a.close();q?a.fillAndStroke():a.stroke()}}mxMarker.addMarker("classic",a(2));mxMarker.addMarker("classicThin",a(3));mxMarker.addMarker("block",a(2));mxMarker.addMarker("blockThin",a(3));mxMarker.addMarker("open",b(2));mxMarker.addMarker("openThin",b(3));mxMarker.addMarker("oval",function(a,b,c,g,k,l,m,n,p,q){var d=m/2,e=g.clone();g.x-=k*d;g.y-=l*d;return function(){a.ellipse(e.x-d,e.y-d,m,m);q?a.fillAndStroke():a.stroke()}});
+mxMarker.addMarker("diamond",c);mxMarker.addMarker("diamondThin",c)})();function mxActor(a,b,c,d){mxShape.call(this);this.bounds=a;this.fill=b;this.stroke=c;this.strokewidth=null!=d?d:1}mxUtils.extend(mxActor,mxShape);mxActor.prototype.paintVertexShape=function(a,b,c,d,e){a.translate(b,c);a.begin();this.redrawPath(a,b,c,d,e);a.fillAndStroke()};
+mxActor.prototype.redrawPath=function(a,b,c,d,e){b=d/3;a.moveTo(0,e);a.curveTo(0,3*e/5,0,2*e/5,d/2,2*e/5);a.curveTo(d/2-b,2*e/5,d/2-b,0,d/2,0);a.curveTo(d/2+b,0,d/2+b,2*e/5,d/2,2*e/5);a.curveTo(d,2*e/5,d,3*e/5,d,e);a.close()};function mxCloud(a,b,c,d){mxActor.call(this);this.bounds=a;this.fill=b;this.stroke=c;this.strokewidth=null!=d?d:1}mxUtils.extend(mxCloud,mxActor);
+mxCloud.prototype.redrawPath=function(a,b,c,d,e){a.moveTo(.25*d,.25*e);a.curveTo(.05*d,.25*e,0,.5*e,.16*d,.55*e);a.curveTo(0,.66*e,.18*d,.9*e,.31*d,.8*e);a.curveTo(.4*d,e,.7*d,e,.8*d,.8*e);a.curveTo(d,.8*e,d,.6*e,.875*d,.5*e);a.curveTo(d,.3*e,.8*d,.1*e,.625*d,.2*e);a.curveTo(.5*d,.05*e,.3*d,.05*e,.25*d,.25*e);a.close()};function mxRectangleShape(a,b,c,d){mxShape.call(this);this.bounds=a;this.fill=b;this.stroke=c;this.strokewidth=null!=d?d:1}mxUtils.extend(mxRectangleShape,mxShape);
+mxRectangleShape.prototype.isHtmlAllowed=function(){var a=!0;null!=this.style&&(a="1"==mxUtils.getValue(this.style,mxConstants.STYLE_POINTER_EVENTS,"1"));return!this.isRounded&&!this.glass&&0==this.rotation&&(a||null!=this.fill&&this.fill!=mxConstants.NONE)};
+mxRectangleShape.prototype.paintBackground=function(a,b,c,d,e){var f=!0;null!=this.style&&(f="1"==mxUtils.getValue(this.style,mxConstants.STYLE_POINTER_EVENTS,"1"));if(f||null!=this.fill&&this.fill!=mxConstants.NONE||null!=this.stroke&&this.stroke!=mxConstants.NONE)f||null!=this.fill&&this.fill!=mxConstants.NONE||(a.pointerEvents=!1),this.isRounded?("1"==mxUtils.getValue(this.style,mxConstants.STYLE_ABSOLUTE_ARCSIZE,0)?f=Math.min(d/2,Math.min(e/2,mxUtils.getValue(this.style,mxConstants.STYLE_ARCSIZE,
+mxConstants.LINE_ARCSIZE)/2)):(f=mxUtils.getValue(this.style,mxConstants.STYLE_ARCSIZE,100*mxConstants.RECTANGLE_ROUNDING_FACTOR)/100,f=Math.min(d*f,e*f)),a.roundrect(b,c,d,e,f,f)):a.rect(b,c,d,e),a.fillAndStroke()};mxRectangleShape.prototype.paintForeground=function(a,b,c,d,e){this.glass&&!this.outline&&null!=this.fill&&this.fill!=mxConstants.NONE&&this.paintGlassEffect(a,b,c,d,e,this.getArcSize(d+this.strokewidth,e+this.strokewidth))};
+function mxEllipse(a,b,c,d){mxShape.call(this);this.bounds=a;this.fill=b;this.stroke=c;this.strokewidth=null!=d?d:1}mxUtils.extend(mxEllipse,mxShape);mxEllipse.prototype.paintVertexShape=function(a,b,c,d,e){a.ellipse(b,c,d,e);a.fillAndStroke()};function mxDoubleEllipse(a,b,c,d){mxShape.call(this);this.bounds=a;this.fill=b;this.stroke=c;this.strokewidth=null!=d?d:1}mxUtils.extend(mxDoubleEllipse,mxShape);mxDoubleEllipse.prototype.vmlScale=10;
+mxDoubleEllipse.prototype.paintBackground=function(a,b,c,d,e){a.ellipse(b,c,d,e);a.fillAndStroke()};mxDoubleEllipse.prototype.paintForeground=function(a,b,c,d,e){if(!this.outline){var f=mxUtils.getValue(this.style,mxConstants.STYLE_MARGIN,Math.min(3+this.strokewidth,Math.min(d/5,e/5)));d-=2*f;e-=2*f;0<d&&0<e&&a.ellipse(b+f,c+f,d,e);a.stroke()}};
+mxDoubleEllipse.prototype.getLabelBounds=function(a){var b=mxUtils.getValue(this.style,mxConstants.STYLE_MARGIN,Math.min(3+this.strokewidth,Math.min(a.width/5/this.scale,a.height/5/this.scale)))*this.scale;return new mxRectangle(a.x+b,a.y+b,a.width-2*b,a.height-2*b)};function mxRhombus(a,b,c,d){mxShape.call(this);this.bounds=a;this.fill=b;this.stroke=c;this.strokewidth=null!=d?d:1}mxUtils.extend(mxRhombus,mxShape);
+mxRhombus.prototype.paintVertexShape=function(a,b,c,d,e){var f=d/2,g=e/2,k=mxUtils.getValue(this.style,mxConstants.STYLE_ARCSIZE,mxConstants.LINE_ARCSIZE)/2;a.begin();this.addPoints(a,[new mxPoint(b+f,c),new mxPoint(b+d,c+g),new mxPoint(b+f,c+e),new mxPoint(b,c+g)],this.isRounded,k,!0);a.fillAndStroke()};function mxPolyline(a,b,c){mxShape.call(this);this.points=a;this.stroke=b;this.strokewidth=null!=c?c:1}mxUtils.extend(mxPolyline,mxShape);mxPolyline.prototype.getRotation=function(){return 0};
+mxPolyline.prototype.getShapeRotation=function(){return 0};mxPolyline.prototype.isPaintBoundsInverted=function(){return!1};mxPolyline.prototype.paintEdgeShape=function(a,b){null==this.style||1!=this.style[mxConstants.STYLE_CURVED]?this.paintLine(a,b,this.isRounded):this.paintCurvedLine(a,b)};mxPolyline.prototype.paintLine=function(a,b,c){var d=mxUtils.getValue(this.style,mxConstants.STYLE_ARCSIZE,mxConstants.LINE_ARCSIZE)/2;a.begin();this.addPoints(a,b,c,d,!1);a.stroke()};
+mxPolyline.prototype.paintCurvedLine=function(a,b){a.begin();var c=b[0],d=b.length;a.moveTo(c.x,c.y);for(c=1;c<d-2;c++){var e=b[c],f=b[c+1];a.quadTo(e.x,e.y,(e.x+f.x)/2,(e.y+f.y)/2)}e=b[d-2];f=b[d-1];a.quadTo(e.x,e.y,f.x,f.y);a.stroke()};
+function mxArrow(a,b,c,d,e,f,g){mxShape.call(this);this.points=a;this.fill=b;this.stroke=c;this.strokewidth=null!=d?d:1;this.arrowWidth=null!=e?e:mxConstants.ARROW_WIDTH;this.spacing=null!=f?f:mxConstants.ARROW_SPACING;this.endSize=null!=g?g:mxConstants.ARROW_SIZE}mxUtils.extend(mxArrow,mxShape);mxArrow.prototype.augmentBoundingBox=function(a){mxShape.prototype.augmentBoundingBox.apply(this,arguments);a.grow((Math.max(this.arrowWidth,this.endSize)/2+this.strokewidth)*this.scale)};
+mxArrow.prototype.paintEdgeShape=function(a,b){var c=mxConstants.ARROW_SPACING,d=mxConstants.ARROW_WIDTH,e=b[0],f=b[b.length-1],g=f.x-e.x,k=f.y-e.y,l=Math.sqrt(g*g+k*k),m=l-2*c-mxConstants.ARROW_SIZE,g=g/l,k=k/l,l=d*k/3,d=-d*g/3,n=e.x-l/2+c*g,e=e.y-d/2+c*k,p=n+l,q=e+d,r=p+m*g,m=q+m*k,t=r+l,u=m+d,x=t-3*l,y=u-3*d;a.begin();a.moveTo(n,e);a.lineTo(p,q);a.lineTo(r,m);a.lineTo(t,u);a.lineTo(f.x-c*g,f.y-c*k);a.lineTo(x,y);a.lineTo(x+l,y+d);a.close();a.fillAndStroke()};
+function mxArrowConnector(a,b,c,d,e,f,g){mxShape.call(this);this.points=a;this.fill=b;this.stroke=c;this.strokewidth=null!=d?d:1;this.arrowWidth=null!=e?e:mxConstants.ARROW_WIDTH;this.arrowSpacing=null!=f?f:mxConstants.ARROW_SPACING;this.startSize=mxConstants.ARROW_SIZE/5;this.endSize=mxConstants.ARROW_SIZE/5}mxUtils.extend(mxArrowConnector,mxShape);mxArrowConnector.prototype.useSvgBoundingBox=!0;
+mxArrowConnector.prototype.resetStyles=function(){mxShape.prototype.resetStyles.apply(this,arguments);this.arrowSpacing=mxConstants.ARROW_SPACING};mxArrowConnector.prototype.apply=function(a){mxShape.prototype.apply.apply(this,arguments);null!=this.style&&(this.startSize=3*mxUtils.getNumber(this.style,mxConstants.STYLE_STARTSIZE,mxConstants.ARROW_SIZE/5),this.endSize=3*mxUtils.getNumber(this.style,mxConstants.STYLE_ENDSIZE,mxConstants.ARROW_SIZE/5))};
+mxArrowConnector.prototype.augmentBoundingBox=function(a){mxShape.prototype.augmentBoundingBox.apply(this,arguments);var b=this.getEdgeWidth();this.isMarkerStart()&&(b=Math.max(b,this.getStartArrowWidth()));this.isMarkerEnd()&&(b=Math.max(b,this.getEndArrowWidth()));a.grow((b/2+this.strokewidth)*this.scale)};
+mxArrowConnector.prototype.paintEdgeShape=function(a,b){var c=this.strokewidth;this.outline&&(c=Math.max(1,mxUtils.getNumber(this.style,mxConstants.STYLE_STROKEWIDTH,this.strokewidth)));for(var d=this.getStartArrowWidth()+c,e=this.getEndArrowWidth()+c,f=this.outline?this.getEdgeWidth()+c:this.getEdgeWidth(),g=this.isOpenEnded(),k=this.isMarkerStart(),l=this.isMarkerEnd(),m=g?0:this.arrowSpacing+c/2,n=this.startSize+c,c=this.endSize+c,p=this.isArrowRounded(),q=b[b.length-1],r=1;r<b.length-1&&b[r].x==
+b[0].x&&b[r].y==b[0].y;)r++;var t=b[r].x-b[0].x,r=b[r].y-b[0].y,u=Math.sqrt(t*t+r*r);if(0!=u){var x=t/u,y,A=x,z=r/u,v,B=z,u=f*z,C=-f*x,F=[];p?a.setLineJoin("round"):2<b.length&&a.setMiterLimit(1.42);a.begin();t=x;r=z;if(k&&!g)this.paintMarker(a,b[0].x,b[0].y,x,z,n,d,f,m,!0);else{y=b[0].x+u/2+m*x;v=b[0].y+C/2+m*z;var D=b[0].x-u/2+m*x,I=b[0].y-C/2+m*z;g?(a.moveTo(y,v),F.push(function(){a.lineTo(D,I)})):(a.moveTo(D,I),a.lineTo(y,v))}for(var E=v=y=0,u=0;u<b.length-2;u++)if(C=mxUtils.relativeCcw(b[u].x,
+b[u].y,b[u+1].x,b[u+1].y,b[u+2].x,b[u+2].y),y=b[u+2].x-b[u+1].x,v=b[u+2].y-b[u+1].y,E=Math.sqrt(y*y+v*v),0!=E&&(A=y/E,B=v/E,tmp=Math.max(Math.sqrt((x*A+z*B+1)/2),.04),y=x+A,v=z+B,E=Math.sqrt(y*y+v*v),0!=E)){y/=E;v/=E;var E=Math.max(tmp,Math.min(this.strokewidth/200+.04,.35)),E=0!=C&&p?Math.max(.1,E):Math.max(tmp,.06),G=b[u+1].x+v*f/2/E,H=b[u+1].y-y*f/2/E;v=b[u+1].x-v*f/2/E;y=b[u+1].y+y*f/2/E;0!=C&&p?-1==C?(C=v+B*f,E=y-A*f,a.lineTo(v+z*f,y-x*f),a.quadTo(G,H,C,E),function(b,c){F.push(function(){a.lineTo(b,
+c)})}(v,y)):(a.lineTo(G,H),function(b,c){var d=G-z*f,e=H+x*f,g=G-B*f,k=H+A*f;F.push(function(){a.quadTo(b,c,d,e)});F.push(function(){a.lineTo(g,k)})}(v,y)):(a.lineTo(G,H),function(b,c){F.push(function(){a.lineTo(b,c)})}(v,y));x=A;z=B}u=f*B;C=-f*A;if(l&&!g)this.paintMarker(a,q.x,q.y,-x,-z,c,e,f,m,!1);else{a.lineTo(q.x-m*A+u/2,q.y-m*B+C/2);var J=q.x-m*A-u/2,K=q.y-m*B-C/2;g?(a.moveTo(J,K),F.splice(0,0,function(){a.moveTo(J,K)})):a.lineTo(J,K)}for(u=F.length-1;0<=u;u--)F[u]();g?(a.end(),a.stroke()):(a.close(),
+a.fillAndStroke());a.setShadow(!1);a.setMiterLimit(4);p&&a.setLineJoin("flat");2<b.length&&(a.setMiterLimit(4),k&&!g&&(a.begin(),this.paintMarker(a,b[0].x,b[0].y,t,r,n,d,f,m,!0),a.stroke(),a.end()),l&&!g&&(a.begin(),this.paintMarker(a,q.x,q.y,-x,-z,c,e,f,m,!0),a.stroke(),a.end()))}};
+mxArrowConnector.prototype.paintMarker=function(a,b,c,d,e,f,g,k,l,m){g=k/g;var n=k*e/2;k=-k*d/2;var p=(l+f)*d;f=(l+f)*e;m?a.moveTo(b-n+p,c-k+f):a.lineTo(b-n+p,c-k+f);a.lineTo(b-n/g+p,c-k/g+f);a.lineTo(b+l*d,c+l*e);a.lineTo(b+n/g+p,c+k/g+f);a.lineTo(b+n+p,c+k+f)};mxArrowConnector.prototype.isArrowRounded=function(){return this.isRounded};mxArrowConnector.prototype.getStartArrowWidth=function(){return mxConstants.ARROW_WIDTH};mxArrowConnector.prototype.getEndArrowWidth=function(){return mxConstants.ARROW_WIDTH};
+mxArrowConnector.prototype.getEdgeWidth=function(){return mxConstants.ARROW_WIDTH/3};mxArrowConnector.prototype.isOpenEnded=function(){return!1};mxArrowConnector.prototype.isMarkerStart=function(){return mxUtils.getValue(this.style,mxConstants.STYLE_STARTARROW,mxConstants.NONE)!=mxConstants.NONE};mxArrowConnector.prototype.isMarkerEnd=function(){return mxUtils.getValue(this.style,mxConstants.STYLE_ENDARROW,mxConstants.NONE)!=mxConstants.NONE};
+function mxText(a,b,c,d,e,f,g,k,l,m,n,p,q,r,t,u,x,y,A,z,v){mxShape.call(this);this.value=a;this.bounds=b;this.color=null!=e?e:"black";this.align=null!=c?c:mxConstants.ALIGN_CENTER;this.valign=null!=d?d:mxConstants.ALIGN_MIDDLE;this.family=null!=f?f:mxConstants.DEFAULT_FONTFAMILY;this.size=null!=g?g:mxConstants.DEFAULT_FONTSIZE;this.fontStyle=null!=k?k:mxConstants.DEFAULT_FONTSTYLE;this.spacing=parseInt(l||2);this.spacingTop=this.spacing+parseInt(m||0);this.spacingRight=this.spacing+parseInt(n||0);
+this.spacingBottom=this.spacing+parseInt(p||0);this.spacingLeft=this.spacing+parseInt(q||0);this.horizontal=null!=r?r:!0;this.background=t;this.border=u;this.wrap=null!=x?x:!1;this.clipped=null!=y?y:!1;this.overflow=null!=A?A:"visible";this.labelPadding=null!=z?z:0;this.textDirection=v;this.rotation=0;this.updateMargin()}mxUtils.extend(mxText,mxShape);mxText.prototype.baseSpacingTop=0;mxText.prototype.baseSpacingBottom=0;mxText.prototype.baseSpacingLeft=0;mxText.prototype.baseSpacingRight=0;
+mxText.prototype.replaceLinefeeds=!0;mxText.prototype.verticalTextRotation=-90;mxText.prototype.ignoreClippedStringSize=!0;mxText.prototype.ignoreStringSize=!1;mxText.prototype.textWidthPadding=8!=document.documentMode||mxClient.IS_EM?3:4;mxText.prototype.lastValue=null;mxText.prototype.cacheEnabled=!0;mxText.prototype.isParseVml=function(){return!1};mxText.prototype.isHtmlAllowed=function(){return 8!=document.documentMode||mxClient.IS_EM};mxText.prototype.getSvgScreenOffset=function(){return 0};
+mxText.prototype.checkBounds=function(){return!isNaN(this.scale)&&isFinite(this.scale)&&0<this.scale&&null!=this.bounds&&!isNaN(this.bounds.x)&&!isNaN(this.bounds.y)&&!isNaN(this.bounds.width)&&!isNaN(this.bounds.height)};
+mxText.prototype.paint=function(a,b){var c=this.scale,d=this.bounds.x/c,e=this.bounds.y/c,f=this.bounds.width/c,c=this.bounds.height/c;this.updateTransform(a,d,e,f,c);this.configureCanvas(a,d,e,f,c);var g=null!=this.state?this.state.unscaledWidth:null;if(b)null==this.node.firstChild||null!=g&&this.lastUnscaledWidth==g||a.invalidateCachedOffsetSize(this.node),a.updateText(d,e,f,c,this.align,this.valign,this.wrap,this.overflow,this.clipped,this.getTextRotation(),this.node);else{var k=mxUtils.isNode(this.value)||
+this.dialect==mxConstants.DIALECT_STRICTHTML,l=k||a instanceof mxVmlCanvas2D?"html":"",m=this.value;k||"html"!=l||(m=mxUtils.htmlEntities(m,!1));"html"!=l||mxUtils.isNode(this.value)||(m=mxUtils.replaceTrailingNewlines(m,"<div><br></div>"));var m=!mxUtils.isNode(this.value)&&this.replaceLinefeeds&&"html"==l?m.replace(/\n/g,"<br/>"):m,n=this.textDirection;n!=mxConstants.TEXT_DIRECTION_AUTO||k||(n=this.getAutoDirection());n!=mxConstants.TEXT_DIRECTION_LTR&&n!=mxConstants.TEXT_DIRECTION_RTL&&(n=null);
+a.text(d,e,f,c,m,this.align,this.valign,this.wrap,l,this.overflow,this.clipped,this.getTextRotation(),n)}this.lastUnscaledWidth=g};
+mxText.prototype.redraw=function(){if(this.visible&&this.checkBounds()&&this.cacheEnabled&&this.lastValue==this.value&&(mxUtils.isNode(this.value)||this.dialect==mxConstants.DIALECT_STRICTHTML))if("DIV"!=this.node.nodeName||!this.isHtmlAllowed()&&mxClient.IS_VML){var a=this.createCanvas();null!=a&&null!=a.updateText&&null!=a.invalidateCachedOffsetSize?(this.paint(a,!0),this.destroyCanvas(a),this.updateBoundingBox()):mxShape.prototype.redraw.apply(this,arguments)}else this.updateSize(this.node,null==
+this.state||null==this.state.view.textDiv),mxClient.IS_IE&&(null==document.documentMode||8>=document.documentMode)?this.updateHtmlFilter():this.updateHtmlTransform(),this.updateBoundingBox();else mxShape.prototype.redraw.apply(this,arguments),mxUtils.isNode(this.value)||this.dialect==mxConstants.DIALECT_STRICTHTML?this.lastValue=this.value:this.lastValue=null};
+mxText.prototype.resetStyles=function(){mxShape.prototype.resetStyles.apply(this,arguments);this.color="black";this.align=mxConstants.ALIGN_CENTER;this.valign=mxConstants.ALIGN_MIDDLE;this.family=mxConstants.DEFAULT_FONTFAMILY;this.size=mxConstants.DEFAULT_FONTSIZE;this.fontStyle=mxConstants.DEFAULT_FONTSTYLE;this.spacingLeft=this.spacingBottom=this.spacingRight=this.spacingTop=this.spacing=2;this.horizontal=!0;delete this.background;delete this.border;this.textDirection=mxConstants.DEFAULT_TEXT_DIRECTION;
+delete this.margin};
+mxText.prototype.apply=function(a){var b=this.spacing;mxShape.prototype.apply.apply(this,arguments);null!=this.style&&(this.fontStyle=mxUtils.getValue(this.style,mxConstants.STYLE_FONTSTYLE,this.fontStyle),this.family=mxUtils.getValue(this.style,mxConstants.STYLE_FONTFAMILY,this.family),this.size=mxUtils.getValue(this.style,mxConstants.STYLE_FONTSIZE,this.size),this.color=mxUtils.getValue(this.style,mxConstants.STYLE_FONTCOLOR,this.color),this.align=mxUtils.getValue(this.style,mxConstants.STYLE_ALIGN,
+this.align),this.valign=mxUtils.getValue(this.style,mxConstants.STYLE_VERTICAL_ALIGN,this.valign),this.spacing=parseInt(mxUtils.getValue(this.style,mxConstants.STYLE_SPACING,this.spacing)),this.spacingTop=parseInt(mxUtils.getValue(this.style,mxConstants.STYLE_SPACING_TOP,this.spacingTop-b))+this.spacing,this.spacingRight=parseInt(mxUtils.getValue(this.style,mxConstants.STYLE_SPACING_RIGHT,this.spacingRight-b))+this.spacing,this.spacingBottom=parseInt(mxUtils.getValue(this.style,mxConstants.STYLE_SPACING_BOTTOM,
+this.spacingBottom-b))+this.spacing,this.spacingLeft=parseInt(mxUtils.getValue(this.style,mxConstants.STYLE_SPACING_LEFT,this.spacingLeft-b))+this.spacing,this.horizontal=mxUtils.getValue(this.style,mxConstants.STYLE_HORIZONTAL,this.horizontal),this.background=mxUtils.getValue(this.style,mxConstants.STYLE_LABEL_BACKGROUNDCOLOR,this.background),this.border=mxUtils.getValue(this.style,mxConstants.STYLE_LABEL_BORDERCOLOR,this.border),this.textDirection=mxUtils.getValue(this.style,mxConstants.STYLE_TEXT_DIRECTION,
+mxConstants.DEFAULT_TEXT_DIRECTION),this.opacity=mxUtils.getValue(this.style,mxConstants.STYLE_TEXT_OPACITY,100),this.updateMargin());this.flipH=this.flipV=null};mxText.prototype.getAutoDirection=function(){var a=/[A-Za-z\u05d0-\u065f\u066a-\u06ef\u06fa-\u07ff\ufb1d-\ufdff\ufe70-\ufefc]/.exec(this.value);return null!=a&&0<a.length&&"z"<a[0]?mxConstants.TEXT_DIRECTION_RTL:mxConstants.TEXT_DIRECTION_LTR};
+mxText.prototype.updateBoundingBox=function(){var a=this.node;this.boundingBox=this.bounds.clone();var b=this.getTextRotation(),c=null!=this.style?mxUtils.getValue(this.style,mxConstants.STYLE_LABEL_POSITION,mxConstants.ALIGN_CENTER):null,d=null!=this.style?mxUtils.getValue(this.style,mxConstants.STYLE_VERTICAL_LABEL_POSITION,mxConstants.ALIGN_MIDDLE):null;if(!(this.ignoreStringSize||null==a||"fill"==this.overflow||this.clipped&&this.ignoreClippedStringSize&&c==mxConstants.ALIGN_CENTER&&d==mxConstants.ALIGN_MIDDLE)){d=
+c=null;if(null!=a.ownerSVGElement)if(null!=a.firstChild&&null!=a.firstChild.firstChild&&"foreignObject"==a.firstChild.firstChild.nodeName)a=a.firstChild.firstChild,c=parseInt(a.getAttribute("width"))*this.scale,d=parseInt(a.getAttribute("height"))*this.scale;else try{var e=a.getBBox();"string"==typeof this.value&&0==mxUtils.trim(this.value)?this.boundingBox=null:this.boundingBox=0==e.width&&0==e.height?null:new mxRectangle(e.x,e.y,e.width,e.height);return}catch(f){}else{c=null!=this.state?this.state.view.textDiv:
+null;if(null==this.offsetWidth||null==this.offsetHeight)null!=c&&(this.updateFont(c),this.updateSize(c,!1),this.updateInnerHtml(c),a=c),e=a,8!=document.documentMode||mxClient.IS_EM?null!=e.firstChild&&"DIV"==e.firstChild.nodeName&&(e=e.firstChild):(d=Math.round(this.bounds.width/this.scale),this.wrap&&0<d?(a.style.wordWrap=mxConstants.WORD_WRAP,a.style.whiteSpace="normal","break-word"!=a.style.wordWrap&&(a=e.getElementsByTagName("div"),0<a.length&&(e=a[a.length-1]),c=e.offsetWidth+2,a=this.node.getElementsByTagName("div"),
+this.clipped&&(c=Math.min(d,c)),1<a.length&&(a[a.length-2].style.width=c+"px"))):a.style.whiteSpace="nowrap"),this.offsetWidth=e.offsetWidth+this.textWidthPadding,this.offsetHeight=e.offsetHeight;c=this.offsetWidth*this.scale;d=this.offsetHeight*this.scale}null!=c&&null!=d&&(this.boundingBox=new mxRectangle(this.bounds.x,this.bounds.y,c,d))}null!=this.boundingBox&&(0!=b?(b=mxUtils.getBoundingBox(new mxRectangle(this.margin.x*this.boundingBox.width,this.margin.y*this.boundingBox.height,this.boundingBox.width,
+this.boundingBox.height),b,new mxPoint(0,0)),this.unrotatedBoundingBox=mxRectangle.fromRectangle(this.boundingBox),this.unrotatedBoundingBox.x+=this.margin.x*this.unrotatedBoundingBox.width,this.unrotatedBoundingBox.y+=this.margin.y*this.unrotatedBoundingBox.height,this.boundingBox.x+=b.x,this.boundingBox.y+=b.y,this.boundingBox.width=b.width,this.boundingBox.height=b.height):(this.boundingBox.x+=this.margin.x*this.boundingBox.width,this.boundingBox.y+=this.margin.y*this.boundingBox.height,this.unrotatedBoundingBox=
+null))};mxText.prototype.getShapeRotation=function(){return 0};mxText.prototype.getTextRotation=function(){return null!=this.state&&null!=this.state.shape?this.state.shape.getTextRotation():0};mxText.prototype.isPaintBoundsInverted=function(){return!this.horizontal&&null!=this.state&&this.state.view.graph.model.isVertex(this.state.cell)};
+mxText.prototype.configureCanvas=function(a,b,c,d,e){mxShape.prototype.configureCanvas.apply(this,arguments);a.setFontColor(this.color);a.setFontBackgroundColor(this.background);a.setFontBorderColor(this.border);a.setFontFamily(this.family);a.setFontSize(this.size);a.setFontStyle(this.fontStyle)};
+mxText.prototype.updateVmlContainer=function(){this.node.style.left=Math.round(this.bounds.x)+"px";this.node.style.top=Math.round(this.bounds.y)+"px";this.node.style.width="1px";this.node.style.height="1px";this.node.style.overflow="visible"};
+mxText.prototype.redrawHtmlShape=function(){var a=this.node.style;a.whiteSpace="normal";a.overflow="";a.width="";a.height="";this.updateValue();this.updateFont(this.node);this.updateSize(this.node,null==this.state||null==this.state.view.textDiv);this.offsetHeight=this.offsetWidth=null;mxClient.IS_IE&&(null==document.documentMode||8>=document.documentMode)?this.updateHtmlFilter():this.updateHtmlTransform()};
+mxText.prototype.updateHtmlTransform=function(){var a=this.getTextRotation(),b=this.node.style,c=this.margin.x,d=this.margin.y;0!=a?(mxUtils.setPrefixedStyle(b,"transformOrigin",100*-c+"% "+100*-d+"%"),mxUtils.setPrefixedStyle(b,"transform","translate("+100*c+"%,"+100*d+"%)scale("+this.scale+") rotate("+a+"deg)")):(mxUtils.setPrefixedStyle(b,"transformOrigin","0% 0%"),mxUtils.setPrefixedStyle(b,"transform","scale("+this.scale+")translate("+100*c+"%,"+100*d+"%)"));b.left=Math.round(this.bounds.x-Math.ceil(c*
+("fill"!=this.overflow&&"width"!=this.overflow?3:1)))+"px";b.top=Math.round(this.bounds.y-d*("fill"!=this.overflow?3:1))+"px";b.opacity=100>this.opacity?this.opacity/100:""};
+mxText.prototype.updateInnerHtml=function(a){if(mxUtils.isNode(this.value))a.innerHTML=this.value.outerHTML;else{var b=this.value;this.dialect!=mxConstants.DIALECT_STRICTHTML&&(b=mxUtils.htmlEntities(b,!1));b=mxUtils.replaceTrailingNewlines(b,"<div>&nbsp;</div>");b=this.replaceLinefeeds?b.replace(/\n/g,"<br/>"):b;a.innerHTML='<div style="display:inline-block;_display:inline;">'+b+"</div>"}};
+mxText.prototype.updateHtmlFilter=function(){var a=this.node.style,b=this.margin.x,c=this.margin.y,d=this.scale;mxUtils.setOpacity(this.node,this.opacity);var e,f=0,g=null!=this.state?this.state.view.textDiv:null,k=this.node;if(null!=g){g.style.overflow="";g.style.height="";g.style.width="";this.updateFont(g);this.updateSize(g,!1);this.updateInnerHtml(g);var l=Math.round(this.bounds.width/this.scale);this.wrap&&0<l?(g.style.whiteSpace="normal",g.style.wordWrap=mxConstants.WORD_WRAP,e=l,this.clipped&&
+(e=Math.min(e,this.bounds.width)),g.style.width=e+"px"):g.style.whiteSpace="nowrap";k=g;null!=k.firstChild&&"DIV"==k.firstChild.nodeName&&(k=k.firstChild,this.wrap&&"break-word"==g.style.wordWrap&&(k.style.width="100%"));!this.clipped&&this.wrap&&0<l&&(e=k.offsetWidth+this.textWidthPadding,g.style.width=e+"px");f=k.offsetHeight+2;mxClient.IS_QUIRKS&&null!=this.border&&this.border!=mxConstants.NONE&&(f+=3)}else null!=k.firstChild&&"DIV"==k.firstChild.nodeName&&(k=k.firstChild,f=k.offsetHeight);e=k.offsetWidth+
+this.textWidthPadding;this.clipped&&(f=Math.min(f,this.bounds.height));l=this.bounds.width/d;g=this.bounds.height/d;"fill"==this.overflow?(f=g,e=l):"width"==this.overflow&&(f=k.scrollHeight,e=l);this.offsetWidth=e;this.offsetHeight=f;mxClient.IS_QUIRKS&&(this.clipped||"width"==this.overflow&&0<g)?(g=Math.min(g,f),a.height=Math.round(g)+"px"):g=f;"fill"!=this.overflow&&"width"!=this.overflow&&(this.clipped&&(e=Math.min(l,e)),l=e,mxClient.IS_QUIRKS&&this.clipped||this.wrap)&&(a.width=Math.round(l)+
+"px");var g=g*d,l=l*d,m=this.getTextRotation()*(Math.PI/180);e=parseFloat(parseFloat(Math.cos(m)).toFixed(8));f=parseFloat(parseFloat(Math.sin(-m)).toFixed(8));m%=2*Math.PI;0>m&&(m+=2*Math.PI);m%=Math.PI;m>Math.PI/2&&(m=Math.PI-m);var k=Math.cos(m),n=Math.sin(-m),b=l*-(b+.5),p=g*-(c+.5);0!=m&&(c="progid:DXImageTransform.Microsoft.Matrix(M11="+e+", M12="+f+", M21="+-f+", M22="+e+", sizingMethod='auto expand')",a.filter=null!=a.filter&&0<a.filter.length?a.filter+(" "+c):c);c=0;"fill"!=this.overflow&&
+mxClient.IS_QUIRKS&&(c=this.valign==mxConstants.ALIGN_TOP?c-1:this.valign==mxConstants.ALIGN_BOTTOM?c+2:c+1);a.zoom=d;a.left=Math.round(this.bounds.x+((l-l*k+g*n)/2-e*b-f*p)-l/2)+"px";a.top=Math.round(this.bounds.y+((g-g*k+l*n)/2+f*b-e*p)-g/2+c)+"px"};
+mxText.prototype.updateValue=function(){if(mxUtils.isNode(this.value))this.node.innerHTML="",this.node.appendChild(this.value);else{var a=this.value;this.dialect!=mxConstants.DIALECT_STRICTHTML&&(a=mxUtils.htmlEntities(a,!1));var a=mxUtils.replaceTrailingNewlines(a,"<div><br></div>"),a=this.replaceLinefeeds?a.replace(/\n/g,"<br/>"):a,b=null!=this.background&&this.background!=mxConstants.NONE?this.background:null,c=null!=this.border&&this.border!=mxConstants.NONE?this.border:null;if("fill"==this.overflow||
+"width"==this.overflow)null!=b&&(this.node.style.backgroundColor=b),null!=c&&(this.node.style.border="1px solid "+c);else{var d="";null!=b&&(d+="background-color:"+b+";");null!=c&&(d+="border:1px solid "+c+";");a='<div style="zoom:1;'+d+"display:inline-block;_display:inline;text-decoration:inherit;padding-bottom:1px;padding-right:1px;line-height:"+(mxConstants.ABSOLUTE_LINE_HEIGHT?this.size*mxConstants.LINE_HEIGHT+"px":mxConstants.LINE_HEIGHT)+'">'+a+"</div>"}this.node.innerHTML=a;a=this.node.getElementsByTagName("div");
+0<a.length&&(b=this.textDirection,b==mxConstants.TEXT_DIRECTION_AUTO&&this.dialect!=mxConstants.DIALECT_STRICTHTML&&(b=this.getAutoDirection()),b==mxConstants.TEXT_DIRECTION_LTR||b==mxConstants.TEXT_DIRECTION_RTL?a[a.length-1].setAttribute("dir",b):a[a.length-1].removeAttribute("dir"))}};
+mxText.prototype.updateFont=function(a){a=a.style;a.lineHeight=mxConstants.ABSOLUTE_LINE_HEIGHT?this.size*mxConstants.LINE_HEIGHT+"px":mxConstants.LINE_HEIGHT;a.fontSize=this.size+"px";a.fontFamily=this.family;a.verticalAlign="top";a.color=this.color;a.fontWeight=(this.fontStyle&mxConstants.FONT_BOLD)==mxConstants.FONT_BOLD?"bold":"";a.fontStyle=(this.fontStyle&mxConstants.FONT_ITALIC)==mxConstants.FONT_ITALIC?"italic":"";a.textDecoration=(this.fontStyle&mxConstants.FONT_UNDERLINE)==mxConstants.FONT_UNDERLINE?
+"underline":"";a.textAlign=this.align==mxConstants.ALIGN_CENTER?"center":this.align==mxConstants.ALIGN_RIGHT?"right":"left"};
+mxText.prototype.updateSize=function(a,b){var c=Math.max(0,Math.round(this.bounds.width/this.scale)),d=Math.max(0,Math.round(this.bounds.height/this.scale)),e=a.style;this.clipped?(e.overflow="hidden",mxClient.IS_QUIRKS?e.width=c+"px":(e.maxHeight=d+"px",e.maxWidth=c+"px")):"fill"==this.overflow?(e.width=c+1+"px",e.height=d+1+"px",e.overflow="hidden"):"width"==this.overflow&&(e.width=c+1+"px",e.maxHeight=d+1+"px",e.overflow="hidden");if(this.wrap&&0<c){if(e.wordWrap=mxConstants.WORD_WRAP,e.whiteSpace=
+"normal",e.width=c+"px",b&&"fill"!=this.overflow&&"width"!=this.overflow){d=a;null!=d.firstChild&&"DIV"==d.firstChild.nodeName&&(d=d.firstChild,"break-word"==a.style.wordWrap&&(d.style.width="100%"));var f=d.offsetWidth;if(0==f){var g=a.parentNode;a.style.visibility="hidden";document.body.appendChild(a);f=d.offsetWidth;a.style.visibility="";g.appendChild(a)}f+=3;this.clipped&&(f=Math.min(f,c));e.width=f+"px"}}else e.whiteSpace="nowrap"};
+mxText.prototype.updateMargin=function(){this.margin=mxUtils.getAlignmentAsPoint(this.align,this.valign)};
+mxText.prototype.getSpacing=function(){return new mxPoint(this.align==mxConstants.ALIGN_CENTER?(this.spacingLeft-this.spacingRight)/2:this.align==mxConstants.ALIGN_RIGHT?-this.spacingRight-this.baseSpacingRight:this.spacingLeft+this.baseSpacingLeft,this.valign==mxConstants.ALIGN_MIDDLE?(this.spacingTop-this.spacingBottom)/2:this.valign==mxConstants.ALIGN_BOTTOM?-this.spacingBottom-this.baseSpacingBottom:this.spacingTop+this.baseSpacingTop)};function mxTriangle(){mxActor.call(this)}
+mxUtils.extend(mxTriangle,mxActor);mxTriangle.prototype.redrawPath=function(a,b,c,d,e){b=mxUtils.getValue(this.style,mxConstants.STYLE_ARCSIZE,mxConstants.LINE_ARCSIZE)/2;this.addPoints(a,[new mxPoint(0,0),new mxPoint(d,.5*e),new mxPoint(0,e)],this.isRounded,b,!0)};function mxHexagon(){mxActor.call(this)}mxUtils.extend(mxHexagon,mxActor);
+mxHexagon.prototype.redrawPath=function(a,b,c,d,e){b=mxUtils.getValue(this.style,mxConstants.STYLE_ARCSIZE,mxConstants.LINE_ARCSIZE)/2;this.addPoints(a,[new mxPoint(.25*d,0),new mxPoint(.75*d,0),new mxPoint(d,.5*e),new mxPoint(.75*d,e),new mxPoint(.25*d,e),new mxPoint(0,.5*e)],this.isRounded,b,!0)};function mxLine(a,b,c){mxShape.call(this);this.bounds=a;this.stroke=b;this.strokewidth=null!=c?c:1}mxUtils.extend(mxLine,mxShape);
+mxLine.prototype.paintVertexShape=function(a,b,c,d,e){c+=e/2;a.begin();a.moveTo(b,c);a.lineTo(b+d,c);a.stroke()};function mxImageShape(a,b,c,d,e){mxShape.call(this);this.bounds=a;this.image=b;this.fill=c;this.stroke=d;this.strokewidth=null!=e?e:1;this.shadow=!1}mxUtils.extend(mxImageShape,mxRectangleShape);mxImageShape.prototype.preserveImageAspect=!0;mxImageShape.prototype.getSvgScreenOffset=function(){return 0};
+mxImageShape.prototype.apply=function(a){mxShape.prototype.apply.apply(this,arguments);this.gradient=this.stroke=this.fill=null;null!=this.style&&(this.preserveImageAspect=1==mxUtils.getNumber(this.style,mxConstants.STYLE_IMAGE_ASPECT,1),this.flipH=this.flipH||1==mxUtils.getValue(this.style,"imageFlipH",0),this.flipV=this.flipV||1==mxUtils.getValue(this.style,"imageFlipV",0))};mxImageShape.prototype.isHtmlAllowed=function(){return!this.preserveImageAspect};
+mxImageShape.prototype.createHtml=function(){var a=document.createElement("div");a.style.position="absolute";return a};
+mxImageShape.prototype.paintVertexShape=function(a,b,c,d,e){if(null!=this.image){var f=mxUtils.getValue(this.style,mxConstants.STYLE_IMAGE_BACKGROUND,null),g=mxUtils.getValue(this.style,mxConstants.STYLE_IMAGE_BORDER,null);null!=f&&(a.setFillColor(f),a.setStrokeColor(g),a.rect(b,c,d,e),a.fillAndStroke());a.image(b,c,d,e,this.image,this.preserveImageAspect,!1,!1);g=mxUtils.getValue(this.style,mxConstants.STYLE_IMAGE_BORDER,null);null!=g&&(a.setShadow(!1),a.setStrokeColor(g),a.rect(b,c,d,e),a.stroke())}else mxRectangleShape.prototype.paintBackground.apply(this,
+arguments)};
+mxImageShape.prototype.redrawHtmlShape=function(){this.node.style.left=Math.round(this.bounds.x)+"px";this.node.style.top=Math.round(this.bounds.y)+"px";this.node.style.width=Math.max(0,Math.round(this.bounds.width))+"px";this.node.style.height=Math.max(0,Math.round(this.bounds.height))+"px";this.node.innerHTML="";if(null!=this.image){var a=mxUtils.getValue(this.style,mxConstants.STYLE_IMAGE_BACKGROUND,""),b=mxUtils.getValue(this.style,mxConstants.STYLE_IMAGE_BORDER,"");this.node.style.backgroundColor=a;
+this.node.style.borderColor=b;a=document.createElement(mxClient.IS_IE6||(null==document.documentMode||8>=document.documentMode)&&0!=this.rotation?mxClient.VML_PREFIX+":image":"img");a.setAttribute("border","0");a.style.position="absolute";a.src=this.image;b=100>this.opacity?"alpha(opacity="+this.opacity+")":"";this.node.style.filter=b;this.flipH&&this.flipV?b+="progid:DXImageTransform.Microsoft.BasicImage(rotation=2)":this.flipH?b+="progid:DXImageTransform.Microsoft.BasicImage(mirror=1)":this.flipV&&
+(b+="progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)");a.style.filter!=b&&(a.style.filter=b);"image"==a.nodeName?a.style.rotation=this.rotation:0!=this.rotation?mxUtils.setPrefixedStyle(a.style,"transform","rotate("+this.rotation+"deg)"):mxUtils.setPrefixedStyle(a.style,"transform","");a.style.width=this.node.style.width;a.style.height=this.node.style.height;this.node.style.backgroundImage="";this.node.appendChild(a)}else this.setTransparentBackgroundImage(this.node)};
+function mxLabel(a,b,c,d){mxRectangleShape.call(this,a,b,c,d)}mxUtils.extend(mxLabel,mxRectangleShape);mxLabel.prototype.imageSize=mxConstants.DEFAULT_IMAGESIZE;mxLabel.prototype.spacing=2;mxLabel.prototype.indicatorSize=10;mxLabel.prototype.indicatorSpacing=2;mxLabel.prototype.init=function(a){mxShape.prototype.init.apply(this,arguments);null!=this.indicatorShape&&(this.indicator=new this.indicatorShape,this.indicator.dialect=this.dialect,this.indicator.init(this.node))};
+mxLabel.prototype.redraw=function(){null!=this.indicator&&(this.indicator.fill=this.indicatorColor,this.indicator.stroke=this.indicatorStrokeColor,this.indicator.gradient=this.indicatorGradientColor,this.indicator.direction=this.indicatorDirection);mxShape.prototype.redraw.apply(this,arguments)};mxLabel.prototype.isHtmlAllowed=function(){return mxRectangleShape.prototype.isHtmlAllowed.apply(this,arguments)&&null==this.indicatorColor&&null==this.indicatorShape};
+mxLabel.prototype.paintForeground=function(a,b,c,d,e){this.paintImage(a,b,c,d,e);this.paintIndicator(a,b,c,d,e);mxRectangleShape.prototype.paintForeground.apply(this,arguments)};mxLabel.prototype.paintImage=function(a,b,c,d,e){null!=this.image&&(b=this.getImageBounds(b,c,d,e),a.image(b.x,b.y,b.width,b.height,this.image,!1,!1,!1))};
+mxLabel.prototype.getImageBounds=function(a,b,c,d){var e=mxUtils.getValue(this.style,mxConstants.STYLE_IMAGE_ALIGN,mxConstants.ALIGN_LEFT),f=mxUtils.getValue(this.style,mxConstants.STYLE_IMAGE_VERTICAL_ALIGN,mxConstants.ALIGN_MIDDLE),g=mxUtils.getNumber(this.style,mxConstants.STYLE_IMAGE_WIDTH,mxConstants.DEFAULT_IMAGESIZE),k=mxUtils.getNumber(this.style,mxConstants.STYLE_IMAGE_HEIGHT,mxConstants.DEFAULT_IMAGESIZE),l=mxUtils.getNumber(this.style,mxConstants.STYLE_SPACING,this.spacing)+5;a=e==mxConstants.ALIGN_CENTER?
+a+(c-g)/2:e==mxConstants.ALIGN_RIGHT?a+(c-g-l):a+l;b=f==mxConstants.ALIGN_TOP?b+l:f==mxConstants.ALIGN_BOTTOM?b+(d-k-l):b+(d-k)/2;return new mxRectangle(a,b,g,k)};mxLabel.prototype.paintIndicator=function(a,b,c,d,e){null!=this.indicator?(this.indicator.bounds=this.getIndicatorBounds(b,c,d,e),this.indicator.paint(a)):null!=this.indicatorImage&&(b=this.getIndicatorBounds(b,c,d,e),a.image(b.x,b.y,b.width,b.height,this.indicatorImage,!1,!1,!1))};
+mxLabel.prototype.getIndicatorBounds=function(a,b,c,d){var e=mxUtils.getValue(this.style,mxConstants.STYLE_IMAGE_ALIGN,mxConstants.ALIGN_LEFT),f=mxUtils.getValue(this.style,mxConstants.STYLE_IMAGE_VERTICAL_ALIGN,mxConstants.ALIGN_MIDDLE),g=mxUtils.getNumber(this.style,mxConstants.STYLE_INDICATOR_WIDTH,this.indicatorSize),k=mxUtils.getNumber(this.style,mxConstants.STYLE_INDICATOR_HEIGHT,this.indicatorSize),l=this.spacing+5;a=e==mxConstants.ALIGN_RIGHT?a+(c-g-l):e==mxConstants.ALIGN_CENTER?a+(c-g)/
+2:a+l;b=f==mxConstants.ALIGN_BOTTOM?b+(d-k-l):f==mxConstants.ALIGN_TOP?b+l:b+(d-k)/2;return new mxRectangle(a,b,g,k)};
+mxLabel.prototype.redrawHtmlShape=function(){for(mxRectangleShape.prototype.redrawHtmlShape.apply(this,arguments);this.node.hasChildNodes();)this.node.removeChild(this.node.lastChild);if(null!=this.image){var a=document.createElement("img");a.style.position="relative";a.setAttribute("border","0");var b=this.getImageBounds(this.bounds.x,this.bounds.y,this.bounds.width,this.bounds.height);b.x-=this.bounds.x;b.y-=this.bounds.y;a.style.left=Math.round(b.x)+"px";a.style.top=Math.round(b.y)+"px";a.style.width=
+Math.round(b.width)+"px";a.style.height=Math.round(b.height)+"px";a.src=this.image;this.node.appendChild(a)}};function mxCylinder(a,b,c,d){mxShape.call(this);this.bounds=a;this.fill=b;this.stroke=c;this.strokewidth=null!=d?d:1}mxUtils.extend(mxCylinder,mxShape);mxCylinder.prototype.maxHeight=40;mxCylinder.prototype.svgStrokeTolerance=0;
+mxCylinder.prototype.paintVertexShape=function(a,b,c,d,e){a.translate(b,c);a.begin();this.redrawPath(a,b,c,d,e,!1);a.fillAndStroke();a.setShadow(!1);a.begin();this.redrawPath(a,b,c,d,e,!0);a.stroke()};
+mxCylinder.prototype.redrawPath=function(a,b,c,d,e,f){b=Math.min(this.maxHeight,Math.round(e/5));if(f&&null!=this.fill||!f&&null==this.fill)a.moveTo(0,b),a.curveTo(0,2*b,d,2*b,d,b),f||(a.stroke(),a.begin());f||(a.moveTo(0,b),a.curveTo(0,-b/3,d,-b/3,d,b),a.lineTo(d,e-b),a.curveTo(d,e+b/3,0,e+b/3,0,e-b),a.close())};function mxConnector(a,b,c){mxPolyline.call(this,a,b,c)}mxUtils.extend(mxConnector,mxPolyline);
+mxConnector.prototype.updateBoundingBox=function(){this.useSvgBoundingBox=null!=this.style&&1==this.style[mxConstants.STYLE_CURVED];mxShape.prototype.updateBoundingBox.apply(this,arguments)};mxConnector.prototype.paintEdgeShape=function(a,b){var c=this.createMarker(a,b,!0),d=this.createMarker(a,b,!1);mxPolyline.prototype.paintEdgeShape.apply(this,arguments);a.setFillColor(this.stroke);a.setShadow(!1);a.setDashed(!1);null!=c&&c();null!=d&&d()};
+mxConnector.prototype.createMarker=function(a,b,c){var d=null,e=b.length,f=mxUtils.getValue(this.style,c?mxConstants.STYLE_STARTARROW:mxConstants.STYLE_ENDARROW),g=c?b[1]:b[e-2],k=c?b[0]:b[e-1];if(null!=f&&null!=g&&null!=k){for(d=1;d<e-1&&0==Math.round(g.x-k.x)&&0==Math.round(g.y-k.y);)g=c?b[1+d]:b[e-2-d],d++;b=k.x-g.x;e=k.y-g.y;d=Math.max(1,Math.sqrt(b*b+e*e));g=b/d;b=e/d;e=mxUtils.getNumber(this.style,c?mxConstants.STYLE_STARTSIZE:mxConstants.STYLE_ENDSIZE,mxConstants.DEFAULT_MARKERSIZE);d=mxMarker.createMarker(a,
+this,f,k,g,b,e,c,this.strokewidth,0!=this.style[c?mxConstants.STYLE_STARTFILL:mxConstants.STYLE_ENDFILL])}return d};
+mxConnector.prototype.augmentBoundingBox=function(a){mxShape.prototype.augmentBoundingBox.apply(this,arguments);var b=0;mxUtils.getValue(this.style,mxConstants.STYLE_STARTARROW,mxConstants.NONE)!=mxConstants.NONE&&(b=mxUtils.getNumber(this.style,mxConstants.STYLE_STARTSIZE,mxConstants.DEFAULT_MARKERSIZE)+1);mxUtils.getValue(this.style,mxConstants.STYLE_ENDARROW,mxConstants.NONE)!=mxConstants.NONE&&(b=Math.max(b,mxUtils.getNumber(this.style,mxConstants.STYLE_ENDSIZE,mxConstants.DEFAULT_MARKERSIZE))+
+1);a.grow(b*this.scale)};function mxSwimlane(a,b,c,d){mxShape.call(this);this.bounds=a;this.fill=b;this.stroke=c;this.strokewidth=null!=d?d:1}mxUtils.extend(mxSwimlane,mxShape);mxSwimlane.prototype.imageSize=16;mxSwimlane.prototype.getTitleSize=function(){return Math.max(0,mxUtils.getValue(this.style,mxConstants.STYLE_STARTSIZE,mxConstants.DEFAULT_STARTSIZE))};
+mxSwimlane.prototype.getLabelBounds=function(a){var b=this.getTitleSize();a=new mxRectangle(a.x,a.y,a.width,a.height);var c=this.isHorizontal(),d=1==mxUtils.getValue(this.style,mxConstants.STYLE_FLIPH,0),e=1==mxUtils.getValue(this.style,mxConstants.STYLE_FLIPV,0),f=this.direction==mxConstants.DIRECTION_NORTH||this.direction==mxConstants.DIRECTION_SOUTH,c=c==!f,d=!c&&d!=(this.direction==mxConstants.DIRECTION_SOUTH||this.direction==mxConstants.DIRECTION_WEST),e=c&&e!=(this.direction==mxConstants.DIRECTION_SOUTH||
+this.direction==mxConstants.DIRECTION_WEST);if(f){b=Math.min(a.width,b*this.scale);if(d||e)a.x+=a.width-b;a.width=b}else{b=Math.min(a.height,b*this.scale);if(d||e)a.y+=a.height-b;a.height=b}return a};mxSwimlane.prototype.getGradientBounds=function(a,b,c,d,e){a=this.getTitleSize();if(this.isHorizontal())return a=Math.min(a,e),new mxRectangle(b,c,d,a);a=Math.min(a,d);return new mxRectangle(b,c,a,e)};
+mxSwimlane.prototype.getArcSize=function(a,b,c){a=mxUtils.getValue(this.style,mxConstants.STYLE_ARCSIZE,100*mxConstants.RECTANGLE_ROUNDING_FACTOR)/100;return c*a*3};mxSwimlane.prototype.isHorizontal=function(){return 1==mxUtils.getValue(this.style,mxConstants.STYLE_HORIZONTAL,1)};
+mxSwimlane.prototype.paintVertexShape=function(a,b,c,d,e){var f=this.getTitleSize(),g=mxUtils.getValue(this.style,mxConstants.STYLE_SWIMLANE_FILLCOLOR,mxConstants.NONE),k=1==mxUtils.getValue(this.style,mxConstants.STYLE_SWIMLANE_LINE,1),l=0,f=this.isHorizontal()?Math.min(f,e):Math.min(f,d);a.translate(b,c);this.isRounded?(l=this.getArcSize(d,e,f),this.paintRoundedSwimlane(a,b,c,d,e,f,l,g,k)):this.paintSwimlane(a,b,c,d,e,f,g,k);g=mxUtils.getValue(this.style,mxConstants.STYLE_SEPARATORCOLOR,mxConstants.NONE);
+this.paintSeparator(a,b,c,d,e,f,g);null!=this.image&&(e=this.getImageBounds(b,c,d,e),a.image(e.x-b,e.y-c,e.width,e.height,this.image,!1,!1,!1));this.glass&&(a.setShadow(!1),this.paintGlassEffect(a,0,0,d,f,l))};
+mxSwimlane.prototype.paintSwimlane=function(a,b,c,d,e,f,g,k){g!=mxConstants.NONE&&(a.save(),a.setFillColor(g),a.rect(0,0,d,e),a.fillAndStroke(),a.restore(),a.setShadow(!1));a.begin();0==f&&(f=e);this.isHorizontal()?(a.moveTo(0,f),a.lineTo(0,0),a.lineTo(d,0),a.lineTo(d,f),(k||f>=e)&&a.close(),a.fillAndStroke(),f<e&&g==mxConstants.NONE&&(a.pointerEvents=!1,a.begin(),a.moveTo(0,f),a.lineTo(0,e),a.lineTo(d,e),a.lineTo(d,f),a.stroke())):(a.moveTo(f,0),a.lineTo(0,0),a.lineTo(0,e),a.lineTo(f,e),(k||f>=d)&&
+a.close(),a.fillAndStroke(),f<d&&g==mxConstants.NONE&&(a.pointerEvents=!1,a.begin(),a.moveTo(f,0),a.lineTo(d,0),a.lineTo(d,e),a.lineTo(f,e),a.stroke()))};
+mxSwimlane.prototype.paintRoundedSwimlane=function(a,b,c,d,e,f,g,k,l){g=Math.min(e-f,Math.min(f,g));k!=mxConstants.NONE&&(a.save(),a.setFillColor(k),a.roundrect(0,0,d,e,g,g),a.fillAndStroke(),a.restore(),a.setShadow(!1));a.begin();this.isHorizontal()?(a.moveTo(d,f),a.lineTo(d,g),a.quadTo(d,0,d-Math.min(d/2,g),0),a.lineTo(Math.min(d/2,g),0),a.quadTo(0,0,0,g),a.lineTo(0,f),(l||f>=e)&&a.close(),a.fillAndStroke(),f<e&&k==mxConstants.NONE&&(a.pointerEvents=!1,a.begin(),a.moveTo(0,f),a.lineTo(0,e-g),a.quadTo(0,
+e,Math.min(d/2,g),e),a.lineTo(d-Math.min(d/2,g),e),a.quadTo(d,e,d,e-g),a.lineTo(d,f),a.stroke())):(a.moveTo(f,0),a.lineTo(g,0),a.quadTo(0,0,0,Math.min(e/2,g)),a.lineTo(0,e-Math.min(e/2,g)),a.quadTo(0,e,g,e),a.lineTo(f,e),(l||f>=d)&&a.close(),a.fillAndStroke(),f<d&&k==mxConstants.NONE&&(a.pointerEvents=!1,a.begin(),a.moveTo(f,e),a.lineTo(d-g,e),a.quadTo(d,e,d,e-Math.min(e/2,g)),a.lineTo(d,Math.min(e/2,g)),a.quadTo(d,0,d-g,0),a.lineTo(f,0),a.stroke()))};
+mxSwimlane.prototype.paintSeparator=function(a,b,c,d,e,f,g){g!=mxConstants.NONE&&(a.setStrokeColor(g),a.setDashed(!0),a.begin(),this.isHorizontal()?(a.moveTo(d,f),a.lineTo(d,e)):(a.moveTo(f,0),a.lineTo(d,0)),a.stroke(),a.setDashed(!1))};mxSwimlane.prototype.getImageBounds=function(a,b,c,d){return this.isHorizontal()?new mxRectangle(a+c-this.imageSize,b,this.imageSize,this.imageSize):new mxRectangle(a,b,this.imageSize,this.imageSize)};function mxGraphLayout(a){this.graph=a}
+mxGraphLayout.prototype.graph=null;mxGraphLayout.prototype.useBoundingBox=!0;mxGraphLayout.prototype.parent=null;mxGraphLayout.prototype.moveCell=function(a,b,c){};mxGraphLayout.prototype.execute=function(a){};mxGraphLayout.prototype.getGraph=function(){return this.graph};mxGraphLayout.prototype.getConstraint=function(a,b,c,d){c=this.graph.view.getState(b);b=null!=c?c.style:this.graph.getCellStyle(b);return null!=b?b[a]:null};
+mxGraphLayout.traverse=function(a,b,c,d,e){if(null!=c&&null!=a&&(b=null!=b?b:!0,e=e||new mxDictionary,!e.get(a)&&(e.put(a,!0),d=c(a,d),null==d||d))&&(d=this.graph.model.getEdgeCount(a),0<d))for(var f=0;f<d;f++){var g=this.graph.model.getEdgeAt(a,f),k=this.graph.model.getTerminal(g,!0)==a;if(!b||k)k=this.graph.view.getVisibleTerminal(g,!k),this.traverse(k,b,c,g,e)}};
+mxGraphLayout.prototype.isAncestor=function(a,b,c){if(!c)return this.graph.model.getParent(cell)==a;if(b==a)return!1;for(;null!=b&&b!=a;)b=this.graph.model.getParent(b);return b==a};mxGraphLayout.prototype.isVertexMovable=function(a){return this.graph.isCellMovable(a)};mxGraphLayout.prototype.isVertexIgnored=function(a){return!this.graph.getModel().isVertex(a)||!this.graph.isCellVisible(a)};
+mxGraphLayout.prototype.isEdgeIgnored=function(a){var b=this.graph.getModel();return!b.isEdge(a)||!this.graph.isCellVisible(a)||null==b.getTerminal(a,!0)||null==b.getTerminal(a,!1)};mxGraphLayout.prototype.setEdgeStyleEnabled=function(a,b){this.graph.setCellStyles(mxConstants.STYLE_NOEDGESTYLE,b?"0":"1",[a])};mxGraphLayout.prototype.setOrthogonalEdge=function(a,b){this.graph.setCellStyles(mxConstants.STYLE_ORTHOGONAL,b?"1":"0",[a])};
+mxGraphLayout.prototype.getParentOffset=function(a){var b=new mxPoint;if(null!=a&&a!=this.parent){var c=this.graph.getModel();if(c.isAncestor(this.parent,a))for(var d=c.getGeometry(a);a!=this.parent;)b.x+=d.x,b.y+=d.y,a=c.getParent(a),d=c.getGeometry(a)}return b};
+mxGraphLayout.prototype.setEdgePoints=function(a,b){if(null!=a){var c=this.graph.model,d=c.getGeometry(a);null==d?(d=new mxGeometry,d.setRelative(!0)):d=d.clone();if(null!=this.parent&&null!=b)for(var e=c.getParent(a),e=this.getParentOffset(e),f=0;f<b.length;f++)b[f].x-=e.x,b[f].y-=e.y;d.points=b;c.setGeometry(a,d)}};
+mxGraphLayout.prototype.setVertexLocation=function(a,b,c){var d=this.graph.getModel(),e=d.getGeometry(a),f=null;if(null!=e){f=new mxRectangle(b,c,e.width,e.height);if(this.useBoundingBox){var g=this.graph.getView().getState(a);if(null!=g&&null!=g.text&&null!=g.text.boundingBox){var k=this.graph.getView().scale,l=g.text.boundingBox;g.text.boundingBox.x<g.x&&(b+=(g.x-l.x)/k,f.width=l.width);g.text.boundingBox.y<g.y&&(c+=(g.y-l.y)/k,f.height=l.height)}}null!=this.parent&&(g=d.getParent(a),null!=g&&g!=
+this.parent&&(g=this.getParentOffset(g),b-=g.x,c-=g.y));if(e.x!=b||e.y!=c)e=e.clone(),e.x=b,e.y=c,d.setGeometry(a,e)}return f};
+mxGraphLayout.prototype.getVertexBounds=function(a){var b=this.graph.getModel().getGeometry(a);if(this.useBoundingBox){var c=this.graph.getView().getState(a);if(null!=c&&null!=c.text&&null!=c.text.boundingBox)var d=this.graph.getView().scale,e=c.text.boundingBox,f=Math.max(c.x-e.x,0)/d,g=Math.max(c.y-e.y,0)/d,b=new mxRectangle(b.x-f,b.y-g,b.width+f+Math.max(e.x+e.width-(c.x+c.width),0)/d,b.height+g+Math.max(e.y+e.height-(c.y+c.height),0)/d)}null!=this.parent&&(a=this.graph.getModel().getParent(a),
+b=b.clone(),null!=a&&a!=this.parent&&(a=this.getParentOffset(a),b.x+=a.x,b.y+=a.y));return new mxRectangle(b.x,b.y,b.width,b.height)};mxGraphLayout.prototype.arrangeGroups=function(a,b,c,d,e,f){return this.graph.updateGroupBounds(a,b,!0,c,d,e,f)};function WeightedCellSorter(a,b){this.cell=a;this.weightedValue=b}WeightedCellSorter.prototype.weightedValue=0;WeightedCellSorter.prototype.nudge=!1;WeightedCellSorter.prototype.visited=!1;WeightedCellSorter.prototype.rankIndex=null;
+WeightedCellSorter.prototype.cell=null;WeightedCellSorter.prototype.compare=function(a,b){return null!=a&&null!=b?b.weightedValue>a.weightedValue?-1:b.weightedValue<a.weightedValue?1:b.nudge?-1:1:0};function mxStackLayout(a,b,c,d,e,f){mxGraphLayout.call(this,a);this.horizontal=null!=b?b:!0;this.spacing=null!=c?c:0;this.x0=null!=d?d:0;this.y0=null!=e?e:0;this.border=null!=f?f:0}mxStackLayout.prototype=new mxGraphLayout;mxStackLayout.prototype.constructor=mxStackLayout;
+mxStackLayout.prototype.horizontal=null;mxStackLayout.prototype.spacing=null;mxStackLayout.prototype.x0=null;mxStackLayout.prototype.y0=null;mxStackLayout.prototype.border=0;mxStackLayout.prototype.marginTop=0;mxStackLayout.prototype.marginLeft=0;mxStackLayout.prototype.marginRight=0;mxStackLayout.prototype.marginBottom=0;mxStackLayout.prototype.keepFirstLocation=!1;mxStackLayout.prototype.fill=!1;mxStackLayout.prototype.resizeParent=!1;mxStackLayout.prototype.resizeParentMax=!1;
+mxStackLayout.prototype.resizeLast=!1;mxStackLayout.prototype.wrap=null;mxStackLayout.prototype.borderCollapse=!0;mxStackLayout.prototype.isHorizontal=function(){return this.horizontal};
+mxStackLayout.prototype.moveCell=function(a,b,c){var d=this.graph.getModel(),e=d.getParent(a),f=this.isHorizontal();if(null!=a&&null!=e){var g=0,k=d.getChildCount(e);c=f?b:c;b=this.graph.getView().getState(e);null!=b&&(c-=f?b.x:b.y);c/=this.graph.view.scale;for(b=0;b<k;b++){var l=d.getChildAt(e,b);if(l!=a&&(l=d.getGeometry(l),null!=l)){l=f?l.x+l.width/2:l.y+l.height/2;if(g<=c&&l>c)break;g=l}}f=e.getIndex(a);f=Math.max(0,b-(b>f?1:0));d.add(e,a,f)}};
+mxStackLayout.prototype.getParentSize=function(a){var b=this.graph.getModel(),c=b.getGeometry(a);null!=this.graph.container&&(null==c&&b.isLayer(a)||a==this.graph.getView().currentRoot)&&(c=new mxRectangle(0,0,this.graph.container.offsetWidth-1,this.graph.container.offsetHeight-1));return c};
+mxStackLayout.prototype.execute=function(a){if(null!=a){var b=this.getParentSize(a),c=this.isHorizontal(),d=this.graph.getModel(),e=null;null!=b&&(e=c?b.height-this.marginTop-this.marginBottom:b.width-this.marginLeft-this.marginRight);var e=e-2*this.border,f=this.x0+this.border+this.marginLeft,g=this.y0+this.border+this.marginTop;if(this.graph.isSwimlane(a)){var k=this.graph.getCellStyle(a),l=mxUtils.getNumber(k,mxConstants.STYLE_STARTSIZE,mxConstants.DEFAULT_STARTSIZE),k=1==mxUtils.getValue(k,mxConstants.STYLE_HORIZONTAL,
+!0);null!=b&&(l=k?Math.min(l,b.height):Math.min(l,b.width));c==k&&(e-=l);k?g+=l:f+=l}d.beginUpdate();try{for(var l=0,k=null,m=0,n=null,p=d.getChildCount(a),q=0;q<p;q++){var r=d.getChildAt(a,q);if(!this.isVertexIgnored(r)&&this.isVertexMovable(r)){var t=d.getGeometry(r);if(null!=t){t=t.clone();null!=this.wrap&&null!=k&&(c&&k.x+k.width+t.width+2*this.spacing>this.wrap||!c&&k.y+k.height+t.height+2*this.spacing>this.wrap)&&(k=null,c?g+=l+this.spacing:f+=l+this.spacing,l=0);var l=Math.max(l,c?t.height:
+t.width),u=0;if(!this.borderCollapse)var x=this.graph.getCellStyle(r),u=mxUtils.getNumber(x,mxConstants.STYLE_STROKEWIDTH,1);null!=k?c?t.x=m+this.spacing+Math.floor(u/2):t.y=m+this.spacing+Math.floor(u/2):this.keepFirstLocation||(c?t.x=f:t.y=g);c?t.y=g:t.x=f;this.fill&&null!=e&&(c?t.height=e:t.width=e);this.setChildGeometry(r,t);n=r;k=t;m=c?k.x+k.width+Math.floor(u/2):k.y+k.height+Math.floor(u/2)}}}this.resizeParent&&null!=b&&null!=k&&!this.graph.isCellCollapsed(a)?this.updateParentGeometry(a,b,k):
+this.resizeLast&&null!=b&&null!=k&&null!=n&&(c?k.width=b.width-k.x-this.spacing-this.marginRight-this.marginLeft:k.height=b.height-k.y-this.spacing-this.marginBottom,this.setChildGeometry(n,k))}finally{d.endUpdate()}}};mxStackLayout.prototype.setChildGeometry=function(a,b){var c=this.graph.getCellGeometry(a);null!=c&&b.x==c.x&&b.y==c.y&&b.width==c.width&&b.height==c.height||this.graph.getModel().setGeometry(a,b)};
+mxStackLayout.prototype.updateParentGeometry=function(a,b,c){var d=this.isHorizontal(),e=this.graph.getModel(),f=b.clone();d?(c=c.x+c.width+this.marginRight+this.border,f.width=this.resizeParentMax?Math.max(f.width,c):c):(c=c.y+c.height+this.marginBottom+this.border,f.height=this.resizeParentMax?Math.max(f.height,c):c);b.x==f.x&&b.y==f.y&&b.width==f.width&&b.height==f.height||e.setGeometry(a,f)};
+function mxPartitionLayout(a,b,c,d){mxGraphLayout.call(this,a);this.horizontal=null!=b?b:!0;this.spacing=c||0;this.border=d||0}mxPartitionLayout.prototype=new mxGraphLayout;mxPartitionLayout.prototype.constructor=mxPartitionLayout;mxPartitionLayout.prototype.horizontal=null;mxPartitionLayout.prototype.spacing=null;mxPartitionLayout.prototype.border=null;mxPartitionLayout.prototype.resizeVertices=!0;mxPartitionLayout.prototype.isHorizontal=function(){return this.horizontal};
+mxPartitionLayout.prototype.moveCell=function(a,b,c){c=this.graph.getModel();var d=c.getParent(a);if(null!=a&&null!=d){var e,f=0,g=c.getChildCount(d);for(e=0;e<g;e++){var k=c.getChildAt(d,e),k=this.getVertexBounds(k);if(null!=k){k=k.x+k.width/2;if(f<b&&k>b)break;f=k}}b=d.getIndex(a);b=Math.max(0,e-(e>b?1:0));c.add(d,a,b)}};
+mxPartitionLayout.prototype.execute=function(a){var b=this.isHorizontal(),c=this.graph.getModel(),d=c.getGeometry(a);null!=this.graph.container&&(null==d&&c.isLayer(a)||a==this.graph.getView().currentRoot)&&(d=new mxRectangle(0,0,this.graph.container.offsetWidth-1,this.graph.container.offsetHeight-1));if(null!=d){for(var e=[],f=c.getChildCount(a),g=0;g<f;g++){var k=c.getChildAt(a,g);!this.isVertexIgnored(k)&&this.isVertexMovable(k)&&e.push(k)}f=e.length;if(0<f){var l=this.border,m=this.border,n=b?
+d.height:d.width,n=n-2*this.border;a=this.graph.isSwimlane(a)?this.graph.getStartSize(a):new mxRectangle;n-=b?a.height:a.width;l+=a.width;m+=a.height;a=this.border+(f-1)*this.spacing;d=b?(d.width-l-a)/f:(d.height-m-a)/f;if(0<d){c.beginUpdate();try{for(g=0;g<f;g++){var k=e[g],p=c.getGeometry(k);null!=p&&(p=p.clone(),p.x=l,p.y=m,b?(this.resizeVertices&&(p.width=d,p.height=n),l+=d+this.spacing):(this.resizeVertices&&(p.height=d,p.width=n),m+=d+this.spacing),c.setGeometry(k,p))}}finally{c.endUpdate()}}}}};
+function mxCompactTreeLayout(a,b,c){mxGraphLayout.call(this,a);this.horizontal=null!=b?b:!0;this.invert=null!=c?c:!1}mxCompactTreeLayout.prototype=new mxGraphLayout;mxCompactTreeLayout.prototype.constructor=mxCompactTreeLayout;mxCompactTreeLayout.prototype.horizontal=null;mxCompactTreeLayout.prototype.invert=null;mxCompactTreeLayout.prototype.resizeParent=!0;mxCompactTreeLayout.prototype.maintainParentLocation=!1;mxCompactTreeLayout.prototype.groupPadding=10;
+mxCompactTreeLayout.prototype.groupPaddingTop=0;mxCompactTreeLayout.prototype.groupPaddingRight=0;mxCompactTreeLayout.prototype.groupPaddingBottom=0;mxCompactTreeLayout.prototype.groupPaddingLeft=0;mxCompactTreeLayout.prototype.parentsChanged=null;mxCompactTreeLayout.prototype.moveTree=!1;mxCompactTreeLayout.prototype.visited=null;mxCompactTreeLayout.prototype.levelDistance=10;mxCompactTreeLayout.prototype.nodeDistance=20;mxCompactTreeLayout.prototype.resetEdges=!0;
+mxCompactTreeLayout.prototype.prefHozEdgeSep=5;mxCompactTreeLayout.prototype.prefVertEdgeOff=4;mxCompactTreeLayout.prototype.minEdgeJetty=8;mxCompactTreeLayout.prototype.channelBuffer=4;mxCompactTreeLayout.prototype.edgeRouting=!0;mxCompactTreeLayout.prototype.sortEdges=!1;mxCompactTreeLayout.prototype.alignRanks=!1;mxCompactTreeLayout.prototype.maxRankHeight=null;mxCompactTreeLayout.prototype.root=null;mxCompactTreeLayout.prototype.node=null;
+mxCompactTreeLayout.prototype.isVertexIgnored=function(a){return mxGraphLayout.prototype.isVertexIgnored.apply(this,arguments)||0==this.graph.getConnections(a).length};mxCompactTreeLayout.prototype.isHorizontal=function(){return this.horizontal};
+mxCompactTreeLayout.prototype.execute=function(a,b){this.parent=a;var c=this.graph.getModel();if(null==b)if(0<this.graph.getEdges(a,c.getParent(a),this.invert,!this.invert,!1).length)this.root=a;else{var d=this.graph.findTreeRoots(a,!0,this.invert);if(0<d.length)for(var e=0;e<d.length;e++)if(!this.isVertexIgnored(d[e])&&0<this.graph.getEdges(d[e],null,this.invert,!this.invert,!1).length){this.root=d[e];break}}else this.root=b;if(null!=this.root){this.parentsChanged=this.resizeParent?{}:null;this.parentY=
+this.parentX=null;if(a!=this.root&&null!=c.isVertex(a)&&this.maintainParentLocation){var f=this.graph.getCellGeometry(a);null!=f&&(this.parentX=f.x,this.parentY=f.y)}c.beginUpdate();try{if(this.visited={},this.node=this.dfs(this.root,a),this.alignRanks&&(this.maxRankHeight=[],this.findRankHeights(this.node,0),this.setCellHeights(this.node,0)),null!=this.node){this.layout(this.node);var g=this.graph.gridSize,d=g;if(!this.moveTree){var k=this.getVertexBounds(this.root);null!=k&&(g=k.x,d=k.y)}k=null;
+k=this.isHorizontal()?this.horizontalLayout(this.node,g,d):this.verticalLayout(this.node,null,g,d);if(null!=k){var l=e=0;0>k.x&&(e=Math.abs(g-k.x));0>k.y&&(l=Math.abs(d-k.y));0==e&&0==l||this.moveNode(this.node,e,l);this.resizeParent&&this.adjustParents();this.edgeRouting&&this.localEdgeProcessing(this.node)}null!=this.parentX&&null!=this.parentY&&(f=this.graph.getCellGeometry(a),null!=f&&(f=f.clone(),f.x=this.parentX,f.y=this.parentY,c.setGeometry(a,f)))}}finally{c.endUpdate()}}};
+mxCompactTreeLayout.prototype.moveNode=function(a,b,c){a.x+=b;a.y+=c;this.apply(a);for(a=a.child;null!=a;)this.moveNode(a,b,c),a=a.next};
+mxCompactTreeLayout.prototype.sortOutgoingEdges=function(a,b){var c=new mxDictionary;b.sort(function(b,e){var d=b.getTerminal(b.getTerminal(!1)==a),g=c.get(d);null==g&&(g=mxCellPath.create(d).split(mxCellPath.PATH_SEPARATOR),c.put(d,g));var d=e.getTerminal(e.getTerminal(!1)==a),k=c.get(d);null==k&&(k=mxCellPath.create(d).split(mxCellPath.PATH_SEPARATOR),c.put(d,k));return mxCellPath.compare(g,k)})};
+mxCompactTreeLayout.prototype.findRankHeights=function(a,b){if(null==this.maxRankHeight[b]||this.maxRankHeight[b]<a.height)this.maxRankHeight[b]=a.height;for(var c=a.child;null!=c;)this.findRankHeights(c,b+1),c=c.next};mxCompactTreeLayout.prototype.setCellHeights=function(a,b){null!=this.maxRankHeight[b]&&this.maxRankHeight[b]>a.height&&(a.height=this.maxRankHeight[b]);for(var c=a.child;null!=c;)this.setCellHeights(c,b+1),c=c.next};
+mxCompactTreeLayout.prototype.dfs=function(a,b){var c=mxCellPath.create(a),d=null;if(null!=a&&null==this.visited[c]&&!this.isVertexIgnored(a)){this.visited[c]=a;var d=this.createNode(a),c=this.graph.getModel(),e=null,f=this.graph.getEdges(a,b,this.invert,!this.invert,!1,!0),g=this.graph.getView();this.sortEdges&&this.sortOutgoingEdges(a,f);for(var k=0;k<f.length;k++){var l=f[k];if(!this.isEdgeIgnored(l)){this.resetEdges&&this.setEdgePoints(l,null);this.edgeRouting&&(this.setEdgeStyleEnabled(l,!1),
+this.setEdgePoints(l,null));var m=g.getState(l),l=null!=m?m.getVisibleTerminal(this.invert):g.getVisibleTerminal(l,this.invert),m=this.dfs(l,b);null!=m&&null!=c.getGeometry(l)&&(null==e?d.child=m:e.next=m,e=m)}}}return d};mxCompactTreeLayout.prototype.layout=function(a){if(null!=a){for(var b=a.child;null!=b;)this.layout(b),b=b.next;null!=a.child?this.attachParent(a,this.join(a)):this.layoutLeaf(a)}};
+mxCompactTreeLayout.prototype.horizontalLayout=function(a,b,c,d){a.x+=b+a.offsetX;a.y+=c+a.offsetY;d=this.apply(a,d);b=a.child;if(null!=b){d=this.horizontalLayout(b,a.x,a.y,d);c=a.y+b.offsetY;for(var e=b.next;null!=e;)d=this.horizontalLayout(e,a.x+b.offsetX,c,d),c+=e.offsetY,e=e.next}return d};
+mxCompactTreeLayout.prototype.verticalLayout=function(a,b,c,d,e){a.x+=c+a.offsetY;a.y+=d+a.offsetX;e=this.apply(a,e);b=a.child;if(null!=b)for(e=this.verticalLayout(b,a,a.x,a.y,e),c=a.x+b.offsetY,d=b.next;null!=d;)e=this.verticalLayout(d,a,c,a.y+b.offsetX,e),c+=d.offsetY,d=d.next;return e};
+mxCompactTreeLayout.prototype.attachParent=function(a,b){var c=this.nodeDistance+this.levelDistance,d=(b-a.width)/2-this.nodeDistance,e=d+a.width+2*this.nodeDistance-b;a.child.offsetX=c+a.height;a.child.offsetY=e;a.contour.upperHead=this.createLine(a.height,0,this.createLine(c,e,a.contour.upperHead));a.contour.lowerHead=this.createLine(a.height,0,this.createLine(c,d,a.contour.lowerHead))};
+mxCompactTreeLayout.prototype.layoutLeaf=function(a){var b=2*this.nodeDistance;a.contour.upperTail=this.createLine(a.height+b,0);a.contour.upperHead=a.contour.upperTail;a.contour.lowerTail=this.createLine(0,-a.width-b);a.contour.lowerHead=this.createLine(a.height+b,0,a.contour.lowerTail)};
+mxCompactTreeLayout.prototype.join=function(a){var b=2*this.nodeDistance,c=a.child;a.contour=c.contour;for(var d=c.width+b,e=d,c=c.next;null!=c;){var f=this.merge(a.contour,c.contour);c.offsetY=f+d;c.offsetX=0;d=c.width+b;e+=f+d;c=c.next}return e};
+mxCompactTreeLayout.prototype.merge=function(a,b){for(var c=0,d=0,e=0,f=a.lowerHead,g=b.upperHead;null!=g&&null!=f;){var k=this.offset(c,d,g.dx,g.dy,f.dx,f.dy),d=d+k,e=e+k;c+g.dx<=f.dx?(c+=g.dx,d+=g.dy,g=g.next):(c-=f.dx,d-=f.dy,f=f.next)}null!=g?(c=this.bridge(a.upperTail,0,0,g,c,d),a.upperTail=null!=c.next?b.upperTail:c,a.lowerTail=b.lowerTail):(c=this.bridge(b.lowerTail,c,d,f,0,0),null==c.next&&(a.lowerTail=c));a.lowerHead=b.lowerHead;return e};
+mxCompactTreeLayout.prototype.offset=function(a,b,c,d,e,f){if(e<=a||0>=a+c)return 0;a=0<e*d-c*f?0>a?a*d/c-b:0<a?a*f/e-b:-b:e<a+c?f-(b+(e-a)*d/c):e>a+c?(c+a)*f/e-(b+d):f-(b+d);return 0<a?a:0};mxCompactTreeLayout.prototype.bridge=function(a,b,c,d,e,f){b=e+d.dx-b;0==d.dx?e=d.dy:(e=b*d.dy,e/=d.dx);b=this.createLine(b,e,d.next);a.next=this.createLine(0,f+d.dy-e-c,b);return b};
+mxCompactTreeLayout.prototype.createNode=function(a){var b={};b.cell=a;b.x=0;b.y=0;b.width=0;b.height=0;a=this.getVertexBounds(a);null!=a&&(this.isHorizontal()?(b.width=a.height,b.height=a.width):(b.width=a.width,b.height=a.height));b.offsetX=0;b.offsetY=0;b.contour={};return b};
+mxCompactTreeLayout.prototype.apply=function(a,b){var c=this.graph.getModel(),d=a.cell,e=c.getGeometry(d);null!=d&&null!=e&&(this.isVertexMovable(d)&&(e=this.setVertexLocation(d,a.x,a.y),this.resizeParent&&(c=c.getParent(d),d=mxCellPath.create(c),null==this.parentsChanged[d]&&(this.parentsChanged[d]=c))),b=null==b?new mxRectangle(e.x,e.y,e.width,e.height):new mxRectangle(Math.min(b.x,e.x),Math.min(b.y,e.y),Math.max(b.x+b.width,e.x+e.width),Math.max(b.y+b.height,e.y+e.height)));return b};
+mxCompactTreeLayout.prototype.createLine=function(a,b,c){var d={};d.dx=a;d.dy=b;d.next=c;return d};mxCompactTreeLayout.prototype.adjustParents=function(){var a=[],b;for(b in this.parentsChanged)a.push(this.parentsChanged[b]);this.arrangeGroups(mxUtils.sortCells(a,!0),this.groupPadding,this.groupPaddingTop,this.groupPaddingRight,this.groupPaddingBottom,this.groupPaddingLeft)};
+mxCompactTreeLayout.prototype.localEdgeProcessing=function(a){this.processNodeOutgoing(a);for(a=a.child;null!=a;)this.localEdgeProcessing(a),a=a.next};
+mxCompactTreeLayout.prototype.processNodeOutgoing=function(a){for(var b=a.child,c=a.cell,d=0,e=[];null!=b;){d++;var f=b.x;this.horizontal&&(f=b.y);e.push(new WeightedCellSorter(b,f));b=b.next}e.sort(WeightedCellSorter.prototype.compare);var f=a.width,g=(d+1)*this.prefHozEdgeSep;f>g+2*this.prefHozEdgeSep&&(f-=2*this.prefHozEdgeSep);a=f/d;b=a/2;f>g+2*this.prefHozEdgeSep&&(b+=this.prefHozEdgeSep);for(var f=this.minEdgeJetty-this.prefVertEdgeOff,g=this.getVertexBounds(c),k=0;k<e.length;k++){for(var l=
+e[k].cell.cell,m=this.getVertexBounds(l),l=this.graph.getEdgesBetween(c,l,!1),n=[],p,q,r=0;r<l.length;r++)this.horizontal?(p=g.x+g.width,q=g.y+b,n.push(new mxPoint(p,q)),p=g.x+g.width+f,n.push(new mxPoint(p,q)),q=m.y+m.height/2):(p=g.x+b,q=g.y+g.height,n.push(new mxPoint(p,q)),q=g.y+g.height+f,n.push(new mxPoint(p,q)),p=m.x+m.width/2),n.push(new mxPoint(p,q)),this.setEdgePoints(l[r],n);k<d/2?f+=this.prefVertEdgeOff:k>d/2&&(f-=this.prefVertEdgeOff);b+=a}};
+function mxRadialTreeLayout(a){mxCompactTreeLayout.call(this,a,!1)}mxUtils.extend(mxRadialTreeLayout,mxCompactTreeLayout);mxRadialTreeLayout.prototype.angleOffset=.5;mxRadialTreeLayout.prototype.rootx=0;mxRadialTreeLayout.prototype.rooty=0;mxRadialTreeLayout.prototype.levelDistance=120;mxRadialTreeLayout.prototype.nodeDistance=10;mxRadialTreeLayout.prototype.autoRadius=!1;mxRadialTreeLayout.prototype.sortEdges=!1;mxRadialTreeLayout.prototype.rowMinX=[];mxRadialTreeLayout.prototype.rowMaxX=[];
+mxRadialTreeLayout.prototype.rowMinCenX=[];mxRadialTreeLayout.prototype.rowMaxCenX=[];mxRadialTreeLayout.prototype.rowRadi=[];mxRadialTreeLayout.prototype.row=[];mxRadialTreeLayout.prototype.isVertexIgnored=function(a){return mxGraphLayout.prototype.isVertexIgnored.apply(this,arguments)||0==this.graph.getConnections(a).length};
+mxRadialTreeLayout.prototype.execute=function(a,b){this.parent=a;this.edgeRouting=this.useBoundingBox=!1;mxCompactTreeLayout.prototype.execute.apply(this,arguments);var c=null,d=this.getVertexBounds(this.root);this.centerX=d.x+d.width/2;this.centerY=d.y+d.height/2;for(var e in this.visited){var f=this.getVertexBounds(this.visited[e]),c=null!=c?c:f.clone();c.add(f)}this.calcRowDims([this.node],0);for(var g=0,k=0,c=0;c<this.row.length;c++)e=(this.rowMaxX[c]-this.centerX-this.nodeDistance)/this.rowRadi[c],
+g=Math.max(g,(this.centerX-this.rowMinX[c]-this.nodeDistance)/this.rowRadi[c]),k=Math.max(k,e);for(c=0;c<this.row.length;c++){var l=this.centerX-this.nodeDistance-g*this.rowRadi[c],m=this.centerX+this.nodeDistance+k*this.rowRadi[c]-l;for(e=0;e<this.row[c].length;e++)f=this.row[c],d=f[e],f=this.getVertexBounds(d.cell),d.theta=(f.x+f.width/2-l)/m*Math.PI*2}for(c=this.row.length-2;0<=c;c--)for(f=this.row[c],e=0;e<f.length;e++){d=f[e];g=d.child;for(l=k=0;null!=g;)l+=g.theta,k++,g=g.next;0<k&&(g=l/k,g>
+d.theta&&e<f.length-1?d.theta=Math.min(g,f[e+1].theta-Math.PI/10):g<d.theta&&0<e&&(d.theta=Math.max(g,f[e-1].theta+Math.PI/10)))}for(c=0;c<this.row.length;c++)for(e=0;e<this.row[c].length;e++)f=this.row[c],d=f[e],f=this.getVertexBounds(d.cell),this.setVertexLocation(d.cell,this.centerX-f.width/2+this.rowRadi[c]*Math.cos(d.theta),this.centerY-f.height/2+this.rowRadi[c]*Math.sin(d.theta))};
+mxRadialTreeLayout.prototype.calcRowDims=function(a,b){if(null!=a&&0!=a.length){this.rowMinX[b]=this.centerX;this.rowMaxX[b]=this.centerX;this.rowMinCenX[b]=this.centerX;this.rowMaxCenX[b]=this.centerX;this.row[b]=[];for(var c=!1,d=0;d<a.length;d++)for(var e=null!=a[d]?a[d].child:null;null!=e;){var f=this.getVertexBounds(e.cell);this.rowMinX[b]=Math.min(f.x,this.rowMinX[b]);this.rowMaxX[b]=Math.max(f.x+f.width,this.rowMaxX[b]);this.rowMinCenX[b]=Math.min(f.x+f.width/2,this.rowMinCenX[b]);this.rowMaxCenX[b]=
+Math.max(f.x+f.width/2,this.rowMaxCenX[b]);this.rowRadi[b]=f.y-this.getVertexBounds(this.root).y;null!=e.child&&(c=!0);this.row[b].push(e);e=e.next}c&&this.calcRowDims(this.row[b],b+1)}};function mxFastOrganicLayout(a){mxGraphLayout.call(this,a)}mxFastOrganicLayout.prototype=new mxGraphLayout;mxFastOrganicLayout.prototype.constructor=mxFastOrganicLayout;mxFastOrganicLayout.prototype.useInputOrigin=!0;mxFastOrganicLayout.prototype.resetEdges=!0;mxFastOrganicLayout.prototype.disableEdgeStyle=!0;
+mxFastOrganicLayout.prototype.forceConstant=50;mxFastOrganicLayout.prototype.forceConstantSquared=0;mxFastOrganicLayout.prototype.minDistanceLimit=2;mxFastOrganicLayout.prototype.maxDistanceLimit=500;mxFastOrganicLayout.prototype.minDistanceLimitSquared=4;mxFastOrganicLayout.prototype.initialTemp=200;mxFastOrganicLayout.prototype.temperature=0;mxFastOrganicLayout.prototype.maxIterations=0;mxFastOrganicLayout.prototype.iteration=0;mxFastOrganicLayout.prototype.allowedToRun=!0;
+mxFastOrganicLayout.prototype.isVertexIgnored=function(a){return mxGraphLayout.prototype.isVertexIgnored.apply(this,arguments)||0==this.graph.getConnections(a).length};
+mxFastOrganicLayout.prototype.execute=function(a){var b=this.graph.getModel();this.vertexArray=[];for(var c=this.graph.getChildVertices(a),d=0;d<c.length;d++)this.isVertexIgnored(c[d])||this.vertexArray.push(c[d]);var e=this.useInputOrigin?this.graph.getBoundingBoxFromGeometry(this.vertexArray):null,f=this.vertexArray.length;this.indices=[];this.dispX=[];this.dispY=[];this.cellLocation=[];this.isMoveable=[];this.neighbours=[];this.radius=[];this.radiusSquared=[];.001>this.forceConstant&&(this.forceConstant=
+.001);this.forceConstantSquared=this.forceConstant*this.forceConstant;for(d=0;d<this.vertexArray.length;d++){var g=this.vertexArray[d];this.cellLocation[d]=[];var k=mxObjectIdentity.get(g);this.indices[k]=d;var l=this.getVertexBounds(g),m=l.width,n=l.height,p=l.x,q=l.y;this.cellLocation[d][0]=p+m/2;this.cellLocation[d][1]=q+n/2;this.radius[d]=Math.min(m,n);this.radiusSquared[d]=this.radius[d]*this.radius[d]}b.beginUpdate();try{for(d=0;d<f;d++){this.dispX[d]=0;this.dispY[d]=0;this.isMoveable[d]=this.isVertexMovable(this.vertexArray[d]);
+var r=this.graph.getConnections(this.vertexArray[d],a),c=this.graph.getOpposites(r,this.vertexArray[d]);this.neighbours[d]=[];for(m=0;m<c.length;m++){this.resetEdges&&this.graph.resetEdge(r[m]);this.disableEdgeStyle&&this.setEdgeStyleEnabled(r[m],!1);var k=mxObjectIdentity.get(c[m]),t=this.indices[k];this.neighbours[d][m]=null!=t?t:d}}this.temperature=this.initialTemp;0==this.maxIterations&&(this.maxIterations=20*Math.sqrt(f));for(this.iteration=0;this.iteration<this.maxIterations;this.iteration++){if(!this.allowedToRun)return;
+this.calcRepulsion();this.calcAttraction();this.calcPositions();this.reduceTemperature()}a=c=null;for(d=0;d<this.vertexArray.length;d++)g=this.vertexArray[d],this.isVertexMovable(g)&&(l=this.getVertexBounds(g),null!=l&&(this.cellLocation[d][0]-=l.width/2,this.cellLocation[d][1]-=l.height/2,p=this.graph.snap(Math.round(this.cellLocation[d][0])),q=this.graph.snap(Math.round(this.cellLocation[d][1])),this.setVertexLocation(g,p,q),c=null==c?p:Math.min(c,p),a=null==a?q:Math.min(a,q)));d=-(c||0)+1;g=-(a||
+0)+1;null!=e&&(d+=e.x,g+=e.y);this.graph.moveCells(this.vertexArray,d,g)}finally{b.endUpdate()}};mxFastOrganicLayout.prototype.calcPositions=function(){for(var a=0;a<this.vertexArray.length;a++)if(this.isMoveable[a]){var b=Math.sqrt(this.dispX[a]*this.dispX[a]+this.dispY[a]*this.dispY[a]);.001>b&&(b=.001);var c=this.dispX[a]/b*Math.min(b,this.temperature),b=this.dispY[a]/b*Math.min(b,this.temperature);this.dispX[a]=0;this.dispY[a]=0;this.cellLocation[a][0]+=c;this.cellLocation[a][1]+=b}};
+mxFastOrganicLayout.prototype.calcAttraction=function(){for(var a=0;a<this.vertexArray.length;a++)for(var b=0;b<this.neighbours[a].length;b++){var c=this.neighbours[a][b];if(a!=c&&this.isMoveable[a]&&this.isMoveable[c]){var d=this.cellLocation[a][0]-this.cellLocation[c][0],e=this.cellLocation[a][1]-this.cellLocation[c][1],f=d*d+e*e-this.radiusSquared[a]-this.radiusSquared[c];f<this.minDistanceLimitSquared&&(f=this.minDistanceLimitSquared);var g=Math.sqrt(f),f=f/this.forceConstant,d=d/g*f,e=e/g*f;
+this.dispX[a]-=d;this.dispY[a]-=e;this.dispX[c]+=d;this.dispY[c]+=e}}};
+mxFastOrganicLayout.prototype.calcRepulsion=function(){for(var a=this.vertexArray.length,b=0;b<a;b++)for(var c=b;c<a;c++){if(!this.allowedToRun)return;if(c!=b&&this.isMoveable[b]&&this.isMoveable[c]){var d=this.cellLocation[b][0]-this.cellLocation[c][0],e=this.cellLocation[b][1]-this.cellLocation[c][1];0==d&&(d=.01+Math.random());0==e&&(e=.01+Math.random());var f=Math.sqrt(d*d+e*e),g=f-this.radius[b]-this.radius[c];g>this.maxDistanceLimit||(g<this.minDistanceLimit&&(g=this.minDistanceLimit),g=this.forceConstantSquared/
+g,d=d/f*g,e=e/f*g,this.dispX[b]+=d,this.dispY[b]+=e,this.dispX[c]-=d,this.dispY[c]-=e)}}};mxFastOrganicLayout.prototype.reduceTemperature=function(){this.temperature=this.initialTemp*(1-this.iteration/this.maxIterations)};function mxCircleLayout(a,b){mxGraphLayout.call(this,a);this.radius=null!=b?b:100}mxCircleLayout.prototype=new mxGraphLayout;mxCircleLayout.prototype.constructor=mxCircleLayout;mxCircleLayout.prototype.radius=null;mxCircleLayout.prototype.moveCircle=!1;
+mxCircleLayout.prototype.x0=0;mxCircleLayout.prototype.y0=0;mxCircleLayout.prototype.resetEdges=!0;mxCircleLayout.prototype.disableEdgeStyle=!0;
+mxCircleLayout.prototype.execute=function(a){var b=this.graph.getModel();b.beginUpdate();try{for(var c=0,d=null,e=null,f=[],g=b.getChildCount(a),k=0;k<g;k++){var l=b.getChildAt(a,k);if(this.isVertexIgnored(l))this.isEdgeIgnored(l)||(this.resetEdges&&this.graph.resetEdge(l),this.disableEdgeStyle&&this.setEdgeStyleEnabled(l,!1));else{f.push(l);var m=this.getVertexBounds(l),d=null==d?m.y:Math.min(d,m.y),e=null==e?m.x:Math.min(e,m.x),c=Math.max(c,Math.max(m.width,m.height))}}var n=this.getRadius(f.length,
+c);this.moveCircle&&(e=this.x0,d=this.y0);this.circle(f,n,e,d)}finally{b.endUpdate()}};mxCircleLayout.prototype.getRadius=function(a,b){return Math.max(a*b/Math.PI,this.radius)};mxCircleLayout.prototype.circle=function(a,b,c,d){for(var e=a.length,f=2*Math.PI/e,g=0;g<e;g++)this.isVertexMovable(a[g])&&this.setVertexLocation(a[g],Math.round(c+b+b*Math.sin(g*f)),Math.round(d+b+b*Math.cos(g*f)))};function mxParallelEdgeLayout(a){mxGraphLayout.call(this,a)}mxParallelEdgeLayout.prototype=new mxGraphLayout;
+mxParallelEdgeLayout.prototype.constructor=mxParallelEdgeLayout;mxParallelEdgeLayout.prototype.spacing=20;mxParallelEdgeLayout.prototype.execute=function(a){a=this.findParallels(a);this.graph.model.beginUpdate();try{for(var b in a){var c=a[b];1<c.length&&this.layout(c)}}finally{this.graph.model.endUpdate()}};
+mxParallelEdgeLayout.prototype.findParallels=function(a){for(var b=this.graph.getModel(),c=[],d=b.getChildCount(a),e=0;e<d;e++){var f=b.getChildAt(a,e);if(!this.isEdgeIgnored(f)){var g=this.getEdgeId(f);null!=g&&(null==c[g]&&(c[g]=[]),c[g].push(f))}}return c};mxParallelEdgeLayout.prototype.getEdgeId=function(a){var b=this.graph.getView(),c=b.getVisibleTerminal(a,!0);a=b.getVisibleTerminal(a,!1);return null!=c&&null!=a?(c=mxObjectIdentity.get(c),a=mxObjectIdentity.get(a),c>a?a+"-"+c:c+"-"+a):null};
+mxParallelEdgeLayout.prototype.layout=function(a){var b=a[0],c=this.graph.getView(),d=this.graph.getModel(),e=d.getGeometry(c.getVisibleTerminal(b,!0)),d=d.getGeometry(c.getVisibleTerminal(b,!1));if(e==d)for(var b=e.x+e.width+this.spacing,c=e.y+e.height/2,f=0;f<a.length;f++)this.route(a[f],b,c),b+=this.spacing;else if(null!=e&&null!=d){var b=e.x+e.width/2,c=e.y+e.height/2,f=d.x+d.width/2-b,g=d.y+d.height/2-c,d=Math.sqrt(f*f+g*g);if(0<d)for(e=g*this.spacing/d,d=f*this.spacing/d,b=b+f/2+e*(a.length-
+1)/2,c=c+g/2-d*(a.length-1)/2,f=0;f<a.length;f++)this.route(a[f],b,c),b-=e,c+=d}};mxParallelEdgeLayout.prototype.route=function(a,b,c){this.graph.isCellMovable(a)&&this.setEdgePoints(a,[new mxPoint(b,c)])};function mxCompositeLayout(a,b,c){mxGraphLayout.call(this,a);this.layouts=b;this.master=c}mxCompositeLayout.prototype=new mxGraphLayout;mxCompositeLayout.prototype.constructor=mxCompositeLayout;mxCompositeLayout.prototype.layouts=null;mxCompositeLayout.prototype.master=null;
+mxCompositeLayout.prototype.moveCell=function(a,b,c){null!=this.master?this.master.move.apply(this.master,arguments):this.layouts[0].move.apply(this.layouts[0],arguments)};mxCompositeLayout.prototype.execute=function(a){var b=this.graph.getModel();b.beginUpdate();try{for(var c=0;c<this.layouts.length;c++)this.layouts[c].execute.apply(this.layouts[c],arguments)}finally{b.endUpdate()}};function mxEdgeLabelLayout(a,b){mxGraphLayout.call(this,a)}mxEdgeLabelLayout.prototype=new mxGraphLayout;
+mxEdgeLabelLayout.prototype.constructor=mxEdgeLabelLayout;mxEdgeLabelLayout.prototype.execute=function(a){for(var b=this.graph.view,c=this.graph.getModel(),d=[],e=[],f=c.getChildCount(a),g=0;g<f;g++){var k=c.getChildAt(a,g),l=b.getState(k);null!=l&&(this.isVertexIgnored(k)?this.isEdgeIgnored(k)||d.push(l):e.push(l))}this.placeLabels(e,d)};
+mxEdgeLabelLayout.prototype.placeLabels=function(a,b){var c=this.graph.getModel();c.beginUpdate();try{for(var d=0;d<b.length;d++){var e=b[d];if(null!=e&&null!=e.text&&null!=e.text.boundingBox)for(var f=0;f<a.length;f++){var g=a[f];null!=g&&this.avoid(e,g)}}}finally{c.endUpdate()}};
+mxEdgeLabelLayout.prototype.avoid=function(a,b){var c=this.graph.getModel(),d=a.text.boundingBox;if(mxUtils.intersects(d,b)){var e=-d.y-d.height+b.y,f=-d.y+b.y+b.height,e=Math.abs(e)<Math.abs(f)?e:f,f=-d.x-d.width+b.x,d=-d.x+b.x+b.width,d=Math.abs(f)<Math.abs(d)?f:d;Math.abs(d)<Math.abs(e)?e=0:d=0;f=c.getGeometry(a.cell);null!=f&&(f=f.clone(),null!=f.offset?(f.offset.x+=d,f.offset.y+=e):f.offset=new mxPoint(d,e),c.setGeometry(a.cell,f))}};
+function mxGraphAbstractHierarchyCell(){this.x=[];this.y=[];this.temp=[]}mxGraphAbstractHierarchyCell.prototype.maxRank=-1;mxGraphAbstractHierarchyCell.prototype.minRank=-1;mxGraphAbstractHierarchyCell.prototype.x=null;mxGraphAbstractHierarchyCell.prototype.y=null;mxGraphAbstractHierarchyCell.prototype.width=0;mxGraphAbstractHierarchyCell.prototype.height=0;mxGraphAbstractHierarchyCell.prototype.nextLayerConnectedCells=null;mxGraphAbstractHierarchyCell.prototype.previousLayerConnectedCells=null;
+mxGraphAbstractHierarchyCell.prototype.temp=null;mxGraphAbstractHierarchyCell.prototype.getNextLayerConnectedCells=function(a){return null};mxGraphAbstractHierarchyCell.prototype.getPreviousLayerConnectedCells=function(a){return null};mxGraphAbstractHierarchyCell.prototype.isEdge=function(){return!1};mxGraphAbstractHierarchyCell.prototype.isVertex=function(){return!1};mxGraphAbstractHierarchyCell.prototype.getGeneralPurposeVariable=function(a){return null};
+mxGraphAbstractHierarchyCell.prototype.setGeneralPurposeVariable=function(a,b){return null};mxGraphAbstractHierarchyCell.prototype.setX=function(a,b){this.isVertex()?this.x[0]=b:this.isEdge()&&(this.x[a-this.minRank-1]=b)};mxGraphAbstractHierarchyCell.prototype.getX=function(a){return this.isVertex()?this.x[0]:this.isEdge()?this.x[a-this.minRank-1]:0};mxGraphAbstractHierarchyCell.prototype.setY=function(a,b){this.isVertex()?this.y[0]=b:this.isEdge()&&(this.y[a-this.minRank-1]=b)};
+function mxGraphHierarchyNode(a){mxGraphAbstractHierarchyCell.apply(this,arguments);this.cell=a;this.id=mxObjectIdentity.get(a);this.connectsAsTarget=[];this.connectsAsSource=[]}mxGraphHierarchyNode.prototype=new mxGraphAbstractHierarchyCell;mxGraphHierarchyNode.prototype.constructor=mxGraphHierarchyNode;mxGraphHierarchyNode.prototype.cell=null;mxGraphHierarchyNode.prototype.id=null;mxGraphHierarchyNode.prototype.connectsAsTarget=null;mxGraphHierarchyNode.prototype.connectsAsSource=null;
+mxGraphHierarchyNode.prototype.hashCode=!1;mxGraphHierarchyNode.prototype.getRankValue=function(a){return this.maxRank};mxGraphHierarchyNode.prototype.getNextLayerConnectedCells=function(a){if(null==this.nextLayerConnectedCells){this.nextLayerConnectedCells=[];this.nextLayerConnectedCells[0]=[];for(var b=0;b<this.connectsAsTarget.length;b++){var c=this.connectsAsTarget[b];-1==c.maxRank||c.maxRank==a+1?this.nextLayerConnectedCells[0].push(c.source):this.nextLayerConnectedCells[0].push(c)}}return this.nextLayerConnectedCells[0]};
+mxGraphHierarchyNode.prototype.getPreviousLayerConnectedCells=function(a){if(null==this.previousLayerConnectedCells){this.previousLayerConnectedCells=[];this.previousLayerConnectedCells[0]=[];for(var b=0;b<this.connectsAsSource.length;b++){var c=this.connectsAsSource[b];-1==c.minRank||c.minRank==a-1?this.previousLayerConnectedCells[0].push(c.target):this.previousLayerConnectedCells[0].push(c)}}return this.previousLayerConnectedCells[0]};mxGraphHierarchyNode.prototype.isVertex=function(){return!0};
+mxGraphHierarchyNode.prototype.getGeneralPurposeVariable=function(a){return this.temp[0]};mxGraphHierarchyNode.prototype.setGeneralPurposeVariable=function(a,b){this.temp[0]=b};mxGraphHierarchyNode.prototype.isAncestor=function(a){if(null!=a&&null!=this.hashCode&&null!=a.hashCode&&this.hashCode.length<a.hashCode.length){if(this.hashCode==a.hashCode)return!0;if(null==this.hashCode||null==this.hashCode)return!1;for(var b=0;b<this.hashCode.length;b++)if(this.hashCode[b]!=a.hashCode[b])return!1;return!0}return!1};
+mxGraphHierarchyNode.prototype.getCoreCell=function(){return this.cell};function mxGraphHierarchyEdge(a){mxGraphAbstractHierarchyCell.apply(this,arguments);this.edges=a;this.ids=[];for(var b=0;b<a.length;b++)this.ids.push(mxObjectIdentity.get(a[b]))}mxGraphHierarchyEdge.prototype=new mxGraphAbstractHierarchyCell;mxGraphHierarchyEdge.prototype.constructor=mxGraphHierarchyEdge;mxGraphHierarchyEdge.prototype.edges=null;mxGraphHierarchyEdge.prototype.ids=null;mxGraphHierarchyEdge.prototype.source=null;
+mxGraphHierarchyEdge.prototype.target=null;mxGraphHierarchyEdge.prototype.isReversed=!1;mxGraphHierarchyEdge.prototype.invert=function(a){a=this.source;this.source=this.target;this.target=a;this.isReversed=!this.isReversed};
+mxGraphHierarchyEdge.prototype.getNextLayerConnectedCells=function(a){if(null==this.nextLayerConnectedCells){this.nextLayerConnectedCells=[];for(var b=0;b<this.temp.length;b++)this.nextLayerConnectedCells[b]=[],b==this.temp.length-1?this.nextLayerConnectedCells[b].push(this.source):this.nextLayerConnectedCells[b].push(this)}return this.nextLayerConnectedCells[a-this.minRank-1]};
+mxGraphHierarchyEdge.prototype.getPreviousLayerConnectedCells=function(a){if(null==this.previousLayerConnectedCells){this.previousLayerConnectedCells=[];for(var b=0;b<this.temp.length;b++)this.previousLayerConnectedCells[b]=[],0==b?this.previousLayerConnectedCells[b].push(this.target):this.previousLayerConnectedCells[b].push(this)}return this.previousLayerConnectedCells[a-this.minRank-1]};mxGraphHierarchyEdge.prototype.isEdge=function(){return!0};
+mxGraphHierarchyEdge.prototype.getGeneralPurposeVariable=function(a){return this.temp[a-this.minRank-1]};mxGraphHierarchyEdge.prototype.setGeneralPurposeVariable=function(a,b){this.temp[a-this.minRank-1]=b};mxGraphHierarchyEdge.prototype.getCoreCell=function(){return null!=this.edges&&0<this.edges.length?this.edges[0]:null};
+function mxGraphHierarchyModel(a,b,c,d,e){a.getGraph();this.tightenToSource=e;this.roots=c;this.parent=d;this.vertexMapper=new mxDictionary;this.edgeMapper=new mxDictionary;this.maxRank=0;c=[];null==b&&(b=this.graph.getChildVertices(d));this.maxRank=this.SOURCESCANSTARTRANK;this.createInternalCells(a,b,c);for(d=0;d<b.length;d++){e=c[d].connectsAsSource;for(var f=0;f<e.length;f++){var g=e[f],k=g.edges;if(null!=k&&0<k.length){var k=k[0],l=a.getVisibleTerminal(k,!1),l=this.vertexMapper.get(l);c[d]==
+l&&(l=a.getVisibleTerminal(k,!0),l=this.vertexMapper.get(l));null!=l&&c[d]!=l&&(g.target=l,0==l.connectsAsTarget.length&&(l.connectsAsTarget=[]),0>mxUtils.indexOf(l.connectsAsTarget,g)&&l.connectsAsTarget.push(g))}}c[d].temp[0]=1}}mxGraphHierarchyModel.prototype.maxRank=null;mxGraphHierarchyModel.prototype.vertexMapper=null;mxGraphHierarchyModel.prototype.edgeMapper=null;mxGraphHierarchyModel.prototype.ranks=null;mxGraphHierarchyModel.prototype.roots=null;mxGraphHierarchyModel.prototype.parent=null;
+mxGraphHierarchyModel.prototype.dfsCount=0;mxGraphHierarchyModel.prototype.SOURCESCANSTARTRANK=1E8;mxGraphHierarchyModel.prototype.tightenToSource=!1;
+mxGraphHierarchyModel.prototype.createInternalCells=function(a,b,c){for(var d=a.getGraph(),e=0;e<b.length;e++){c[e]=new mxGraphHierarchyNode(b[e]);this.vertexMapper.put(b[e],c[e]);var f=a.getEdges(b[e]);c[e].connectsAsSource=[];for(var g=0;g<f.length;g++){var k=a.getVisibleTerminal(f[g],!1);if(k!=b[e]&&a.graph.model.isVertex(k)&&!a.isVertexIgnored(k)){var l=a.getEdgesBetween(b[e],k,!1),k=a.getEdgesBetween(b[e],k,!0);if(null!=l&&0<l.length&&null==this.edgeMapper.get(l[0])&&2*k.length>=l.length){for(var k=
+new mxGraphHierarchyEdge(l),m=0;m<l.length;m++){var n=l[m];this.edgeMapper.put(n,k);d.resetEdge(n);a.disableEdgeStyle&&(a.setEdgeStyleEnabled(n,!1),a.setOrthogonalEdge(n,!0))}k.source=c[e];0>mxUtils.indexOf(c[e].connectsAsSource,k)&&c[e].connectsAsSource.push(k)}}}c[e].temp[0]=0}};
+mxGraphHierarchyModel.prototype.initialRank=function(){var a=[];if(null!=this.roots)for(var b=0;b<this.roots.length;b++){var c=this.vertexMapper.get(this.roots[b]);null!=c&&a.push(c)}for(var d=this.vertexMapper.getValues(),b=0;b<d.length;b++)d[b].temp[0]=-1;for(var e=a.slice();0<a.length;){var c=a[0],f,g;f=c.connectsAsTarget;g=c.connectsAsSource;for(var k=!0,l=this.SOURCESCANSTARTRANK,b=0;b<f.length;b++){var m=f[b];if(5270620==m.temp[0])m=m.source,l=Math.min(l,m.temp[0]-1);else{k=!1;break}}if(k){c.temp[0]=
+l;this.maxRank=Math.min(this.maxRank,l);if(null!=g)for(b=0;b<g.length;b++)m=g[b],m.temp[0]=5270620,m=m.target,-1==m.temp[0]&&(a.push(m),m.temp[0]=-2);a.shift()}else if(b=a.shift(),a.push(c),b==c&&1==a.length)break}for(b=0;b<d.length;b++)d[b].temp[0]-=this.maxRank;for(b=0;b<e.length;b++)for(c=e[b],a=0,f=c.connectsAsSource,d=0;d<f.length;d++)m=f[d],m=m.target,c.temp[0]=Math.max(a,m.temp[0]+1),a=c.temp[0];this.maxRank=this.SOURCESCANSTARTRANK-this.maxRank};
+mxGraphHierarchyModel.prototype.fixRanks=function(){var a=[];this.ranks=[];for(var b=0;b<this.maxRank+1;b++)a[b]=[],this.ranks[b]=a[b];var c=null;if(null!=this.roots)for(var d=this.roots,c=[],b=0;b<d.length;b++){var e=this.vertexMapper.get(d[b]);c[b]=e}this.visit(function(b,c,d,e,m){0==m&&0>c.maxRank&&0>c.minRank&&(a[c.temp[0]].push(c),c.maxRank=c.temp[0],c.minRank=c.temp[0],c.temp[0]=a[c.maxRank].length-1);if(null!=b&&null!=d&&1<b.maxRank-c.maxRank)for(d.maxRank=b.maxRank,d.minRank=c.maxRank,d.temp=
+[],d.x=[],d.y=[],b=d.minRank+1;b<d.maxRank;b++)a[b].push(d),d.setGeneralPurposeVariable(b,a[b].length-1)},c,!1,null)};mxGraphHierarchyModel.prototype.visit=function(a,b,c,d){if(null!=b){for(var e=0;e<b.length;e++){var f=b[e];null!=f&&(null==d&&(d={}),c?(f.hashCode=[],f.hashCode[0]=this.dfsCount,f.hashCode[1]=e,this.extendedDfs(null,f,null,a,d,f.hashCode,e,0)):this.dfs(null,f,null,a,d,0))}this.dfsCount++}};
+mxGraphHierarchyModel.prototype.dfs=function(a,b,c,d,e,f){if(null!=b){var g=b.id;if(null==e[g])for(e[g]=b,d(a,b,c,f,0),a=b.connectsAsSource.slice(),c=0;c<a.length;c++)g=a[c],this.dfs(b,g.target,g,d,e,f+1);else d(a,b,c,f,1)}};
+mxGraphHierarchyModel.prototype.extendedDfs=function(a,b,c,d,e,f,g,k){if(null!=b)if(null==a||null!=b.hashCode&&b.hashCode[0]==a.hashCode[0]||(f=a.hashCode.length+1,b.hashCode=a.hashCode.slice(),b.hashCode[f-1]=g),g=b.id,null==e[g])for(e[g]=b,d(a,b,c,k,0),a=b.connectsAsSource.slice(),c=0;c<a.length;c++)g=a[c],this.extendedDfs(b,g.target,g,d,e,b.hashCode,c,k+1);else d(a,b,c,k,1)};
+function mxSwimlaneModel(a,b,c,d,e){a.getGraph();this.tightenToSource=e;this.roots=c;this.parent=d;this.vertexMapper=new mxDictionary;this.edgeMapper=new mxDictionary;this.maxRank=0;c=[];null==b&&(b=this.graph.getChildVertices(d));this.maxRank=this.SOURCESCANSTARTRANK;this.createInternalCells(a,b,c);for(d=0;d<b.length;d++){e=c[d].connectsAsSource;for(var f=0;f<e.length;f++){var g=e[f],k=g.edges;if(null!=k&&0<k.length){var k=k[0],l=a.getVisibleTerminal(k,!1),l=this.vertexMapper.get(l);c[d]==l&&(l=
+a.getVisibleTerminal(k,!0),l=this.vertexMapper.get(l));null!=l&&c[d]!=l&&(g.target=l,0==l.connectsAsTarget.length&&(l.connectsAsTarget=[]),0>mxUtils.indexOf(l.connectsAsTarget,g)&&l.connectsAsTarget.push(g))}}c[d].temp[0]=1}}mxSwimlaneModel.prototype.maxRank=null;mxSwimlaneModel.prototype.vertexMapper=null;mxSwimlaneModel.prototype.edgeMapper=null;mxSwimlaneModel.prototype.ranks=null;mxSwimlaneModel.prototype.roots=null;mxSwimlaneModel.prototype.parent=null;mxSwimlaneModel.prototype.dfsCount=0;
+mxSwimlaneModel.prototype.SOURCESCANSTARTRANK=1E8;mxSwimlaneModel.prototype.tightenToSource=!1;mxSwimlaneModel.prototype.ranksPerGroup=null;
+mxSwimlaneModel.prototype.createInternalCells=function(a,b,c){for(var d=a.getGraph(),e=a.swimlanes,f=0;f<b.length;f++){c[f]=new mxGraphHierarchyNode(b[f]);this.vertexMapper.put(b[f],c[f]);c[f].swimlaneIndex=-1;for(var g=0;g<e.length;g++)if(d.model.getParent(b[f])==e[g]){c[f].swimlaneIndex=g;break}g=a.getEdges(b[f]);c[f].connectsAsSource=[];for(var k=0;k<g.length;k++){var l=a.getVisibleTerminal(g[k],!1);if(l!=b[f]&&a.graph.model.isVertex(l)&&!a.isVertexIgnored(l)){var m=a.getEdgesBetween(b[f],l,!1),
+l=a.getEdgesBetween(b[f],l,!0);if(null!=m&&0<m.length&&null==this.edgeMapper.get(m[0])&&2*l.length>=m.length){for(var l=new mxGraphHierarchyEdge(m),n=0;n<m.length;n++){var p=m[n];this.edgeMapper.put(p,l);d.resetEdge(p);a.disableEdgeStyle&&(a.setEdgeStyleEnabled(p,!1),a.setOrthogonalEdge(p,!0))}l.source=c[f];0>mxUtils.indexOf(c[f].connectsAsSource,l)&&c[f].connectsAsSource.push(l)}}}c[f].temp[0]=0}};
+mxSwimlaneModel.prototype.initialRank=function(){this.ranksPerGroup=[];var a=[],b={};if(null!=this.roots)for(var c=0;c<this.roots.length;c++){var d=this.vertexMapper.get(this.roots[c]);this.maxChainDfs(null,d,null,b,0);null!=d&&a.push(d)}d=[];b=[];for(c=this.ranksPerGroup.length-1;0<=c;c--)d[c]=c==this.ranksPerGroup.length-1?0:b[c+1]+1,b[c]=d[c]+this.ranksPerGroup[c];this.maxRank=b[0];d=this.vertexMapper.getValues();for(c=0;c<d.length;c++)d[c].temp[0]=-1;for(a.slice();0<a.length;){var d=a[0],e,f;
+e=d.connectsAsTarget;f=d.connectsAsSource;for(var g=!0,k=b[0],c=0;c<e.length;c++){var l=e[c];if(5270620==l.temp[0])l=l.source,k=Math.min(k,l.temp[0]-1);else{g=!1;break}}if(g){k>b[d.swimlaneIndex]&&(k=b[d.swimlaneIndex]);d.temp[0]=k;if(null!=f)for(c=0;c<f.length;c++)l=f[c],l.temp[0]=5270620,l=l.target,-1==l.temp[0]&&(a.push(l),l.temp[0]=-2);a.shift()}else if(c=a.shift(),a.push(d),c==d&&1==a.length)break}};
+mxSwimlaneModel.prototype.maxChainDfs=function(a,b,c,d,e){if(null!=b&&(a=mxCellPath.create(b.cell),null==d[a])){d[a]=b;a=b.swimlaneIndex;if(null==this.ranksPerGroup[a]||this.ranksPerGroup[a]<e)this.ranksPerGroup[a]=e;a=b.connectsAsSource.slice();for(c=0;c<a.length;c++){var f=a[c],g=f.target;b.swimlaneIndex<g.swimlaneIndex?this.maxChainDfs(b,g,f,mxUtils.clone(d,null,!0),0):b.swimlaneIndex==g.swimlaneIndex&&this.maxChainDfs(b,g,f,mxUtils.clone(d,null,!0),e+1)}}};
+mxSwimlaneModel.prototype.fixRanks=function(){var a=[];this.ranks=[];for(var b=0;b<this.maxRank+1;b++)a[b]=[],this.ranks[b]=a[b];var c=null;if(null!=this.roots)for(var d=this.roots,c=[],b=0;b<d.length;b++){var e=this.vertexMapper.get(d[b]);c[b]=e}this.visit(function(b,c,d,e,m){0==m&&0>c.maxRank&&0>c.minRank&&(a[c.temp[0]].push(c),c.maxRank=c.temp[0],c.minRank=c.temp[0],c.temp[0]=a[c.maxRank].length-1);if(null!=b&&null!=d&&1<b.maxRank-c.maxRank)for(d.maxRank=b.maxRank,d.minRank=c.maxRank,d.temp=[],
+d.x=[],d.y=[],b=d.minRank+1;b<d.maxRank;b++)a[b].push(d),d.setGeneralPurposeVariable(b,a[b].length-1)},c,!1,null)};mxSwimlaneModel.prototype.visit=function(a,b,c,d){if(null!=b){for(var e=0;e<b.length;e++){var f=b[e];null!=f&&(null==d&&(d={}),c?(f.hashCode=[],f.hashCode[0]=this.dfsCount,f.hashCode[1]=e,this.extendedDfs(null,f,null,a,d,f.hashCode,e,0)):this.dfs(null,f,null,a,d,0))}this.dfsCount++}};
+mxSwimlaneModel.prototype.dfs=function(a,b,c,d,e,f){if(null!=b){var g=b.id;if(null==e[g])for(e[g]=b,d(a,b,c,f,0),a=b.connectsAsSource.slice(),c=0;c<a.length;c++)g=a[c],this.dfs(b,g.target,g,d,e,f+1);else d(a,b,c,f,1)}};
+mxSwimlaneModel.prototype.extendedDfs=function(a,b,c,d,e,f,g,k){if(null!=b)if(null==a||null!=b.hashCode&&b.hashCode[0]==a.hashCode[0]||(f=a.hashCode.length+1,b.hashCode=a.hashCode.slice(),b.hashCode[f-1]=g),g=b.id,null==e[g]){e[g]=b;d(a,b,c,k,0);a=b.connectsAsSource.slice();c=b.connectsAsTarget.slice();for(g=0;g<a.length;g++){f=a[g];var l=f.target;b.swimlaneIndex<=l.swimlaneIndex&&this.extendedDfs(b,l,f,d,e,b.hashCode,g,k+1)}for(g=0;g<c.length;g++)f=c[g],l=f.source,b.swimlaneIndex<l.swimlaneIndex&&
+this.extendedDfs(b,l,f,d,e,b.hashCode,g,k+1)}else d(a,b,c,k,1)};function mxHierarchicalLayoutStage(){}mxHierarchicalLayoutStage.prototype.execute=function(a){};function mxMedianHybridCrossingReduction(a){this.layout=a}mxMedianHybridCrossingReduction.prototype=new mxHierarchicalLayoutStage;mxMedianHybridCrossingReduction.prototype.constructor=mxMedianHybridCrossingReduction;mxMedianHybridCrossingReduction.prototype.layout=null;mxMedianHybridCrossingReduction.prototype.maxIterations=24;
+mxMedianHybridCrossingReduction.prototype.nestedBestRanks=null;mxMedianHybridCrossingReduction.prototype.currentBestCrossings=0;mxMedianHybridCrossingReduction.prototype.iterationsWithoutImprovement=0;mxMedianHybridCrossingReduction.prototype.maxNoImprovementIterations=2;
+mxMedianHybridCrossingReduction.prototype.execute=function(a){a=this.layout.getModel();this.nestedBestRanks=[];for(var b=0;b<a.ranks.length;b++)this.nestedBestRanks[b]=a.ranks[b].slice();for(var c=0,d=this.calculateCrossings(a),b=0;b<this.maxIterations&&c<this.maxNoImprovementIterations;b++){this.weightedMedian(b,a);this.transpose(b,a);var e=this.calculateCrossings(a);if(e<d)for(d=e,e=c=0;e<this.nestedBestRanks.length;e++)for(var f=a.ranks[e],g=0;g<f.length;g++){var k=f[g];this.nestedBestRanks[e][k.getGeneralPurposeVariable(e)]=
+k}else for(c++,e=0;e<this.nestedBestRanks.length;e++)for(f=a.ranks[e],g=0;g<f.length;g++)k=f[g],k.setGeneralPurposeVariable(e,g);if(0==d)break}c=[];d=[];for(b=0;b<a.maxRank+1;b++)d[b]=[],c[b]=d[b];for(b=0;b<this.nestedBestRanks.length;b++)for(e=0;e<this.nestedBestRanks[b].length;e++)d[b].push(this.nestedBestRanks[b][e]);a.ranks=c};mxMedianHybridCrossingReduction.prototype.calculateCrossings=function(a){for(var b=a.ranks.length,c=0,d=1;d<b;d++)c+=this.calculateRankCrossing(d,a);return c};
+mxMedianHybridCrossingReduction.prototype.calculateRankCrossing=function(a,b){for(var c=0,d=b.ranks[a],e=b.ranks[a-1],f=[],g=0;g<d.length;g++){for(var k=d[g],l=k.getGeneralPurposeVariable(a),k=k.getPreviousLayerConnectedCells(a),m=[],n=0;n<k.length;n++){var p=k[n].getGeneralPurposeVariable(a-1);m.push(p)}m.sort(function(a,b){return a-b});f[l]=m}d=[];for(g=0;g<f.length;g++)d=d.concat(f[g]);for(f=1;f<e.length;)f<<=1;l=2*f-1;--f;e=[];for(g=0;g<l;++g)e[g]=0;for(g=0;g<d.length;g++)for(l=d[g]+f,++e[l];0<
+l;)l%2&&(c+=e[l+1]),l=l-1>>1,++e[l];return c};
+mxMedianHybridCrossingReduction.prototype.transpose=function(a,b){for(var c=!0,d=0;c&&10>d++;)for(var e=1==a%2&&1==d%2,c=!1,f=0;f<b.ranks.length;f++){for(var g=b.ranks[f],k=[],l=0;l<g.length;l++){var m=g[l],n=m.getGeneralPurposeVariable(f);0>n&&(n=l);k[n]=m}for(var p=null,q=null,r,t,u=null,x=null,y,A=null,l=0;l<g.length-1;l++){if(0==l){y=k[l];m=y.getNextLayerConnectedCells(f);n=y.getPreviousLayerConnectedCells(f);r=[];t=[];for(var z=0;z<m.length;z++)r[z]=m[z].getGeneralPurposeVariable(f+1);for(z=
+0;z<n.length;z++)t[z]=n[z].getGeneralPurposeVariable(f-1)}else m=p,n=q,r=u,t=x,y=A;A=k[l+1];p=A.getNextLayerConnectedCells(f);q=A.getPreviousLayerConnectedCells(f);u=[];x=[];for(z=0;z<p.length;z++)u[z]=p[z].getGeneralPurposeVariable(f+1);for(z=0;z<q.length;z++)x[z]=q[z].getGeneralPurposeVariable(f-1);for(var v=0,B=0,z=0;z<r.length;z++)for(var C=0;C<u.length;C++)r[z]>u[C]&&v++,r[z]<u[C]&&B++;for(z=0;z<t.length;z++)for(C=0;C<x.length;C++)t[z]>x[C]&&v++,t[z]<x[C]&&B++;if(B<v||B==v&&e)p=y.getGeneralPurposeVariable(f),
+y.setGeneralPurposeVariable(f,A.getGeneralPurposeVariable(f)),A.setGeneralPurposeVariable(f,p),p=m,q=n,u=r,x=t,A=y,e||(c=!0)}}};mxMedianHybridCrossingReduction.prototype.weightedMedian=function(a,b){var c=0==a%2;if(c)for(var d=b.maxRank-1;0<=d;d--)this.medianRank(d,c);else for(d=1;d<b.maxRank;d++)this.medianRank(d,c)};
+mxMedianHybridCrossingReduction.prototype.medianRank=function(a,b){for(var c=this.nestedBestRanks[a].length,d=[],e=[],f=0;f<c;f++){var g=this.nestedBestRanks[a][f],k=new MedianCellSorter;k.cell=g;var l;l=b?g.getNextLayerConnectedCells(a):g.getPreviousLayerConnectedCells(a);var m;m=b?a+1:a-1;null!=l&&0!=l.length?(k.medianValue=this.medianValue(l,m),d.push(k)):e[g.getGeneralPurposeVariable(a)]=!0}d.sort(MedianCellSorter.prototype.compare);for(f=0;f<c;f++)null==e[f]&&(g=d.shift().cell,g.setGeneralPurposeVariable(a,
+f))};mxMedianHybridCrossingReduction.prototype.medianValue=function(a,b){for(var c=[],d=0,e=0;e<a.length;e++){var f=a[e];c[d++]=f.getGeneralPurposeVariable(b)}c.sort(function(a,b){return a-b});if(1==d%2)return c[Math.floor(d/2)];if(2==d)return(c[0]+c[1])/2;e=d/2;f=c[e-1]-c[0];d=c[d-1]-c[e];return(c[e-1]*d+c[e]*f)/(f+d)};function MedianCellSorter(){}MedianCellSorter.prototype.medianValue=0;MedianCellSorter.prototype.cell=!1;
+MedianCellSorter.prototype.compare=function(a,b){return null!=a&&null!=b?b.medianValue>a.medianValue?-1:b.medianValue<a.medianValue?1:0:0};function mxMinimumCycleRemover(a){this.layout=a}mxMinimumCycleRemover.prototype=new mxHierarchicalLayoutStage;mxMinimumCycleRemover.prototype.constructor=mxMinimumCycleRemover;mxMinimumCycleRemover.prototype.layout=null;
+mxMinimumCycleRemover.prototype.execute=function(a){a=this.layout.getModel();for(var b={},c=a.vertexMapper.getValues(),d={},e=0;e<c.length;e++)d[c[e].id]=c[e];c=null;if(null!=a.roots)for(var f=a.roots,c=[],e=0;e<f.length;e++)c[e]=a.vertexMapper.get(f[e]);a.visit(function(a,c,e,f,n){c.isAncestor(a)&&(e.invert(),mxUtils.remove(e,a.connectsAsSource),a.connectsAsTarget.push(e),mxUtils.remove(e,c.connectsAsTarget),c.connectsAsSource.push(e));b[c.id]=c;delete d[c.id]},c,!0,null);e=mxUtils.clone(b,null,
+!0);a.visit(function(a,c,e,f,n){c.isAncestor(a)&&(e.invert(),mxUtils.remove(e,a.connectsAsSource),c.connectsAsSource.push(e),a.connectsAsTarget.push(e),mxUtils.remove(e,c.connectsAsTarget));b[c.id]=c;delete d[c.id]},d,!0,e)};function mxCoordinateAssignment(a,b,c,d,e,f){this.layout=a;this.intraCellSpacing=b;this.interRankCellSpacing=c;this.orientation=d;this.initialX=e;this.parallelEdgeSpacing=f}mxCoordinateAssignment.prototype=new mxHierarchicalLayoutStage;
+mxCoordinateAssignment.prototype.constructor=mxCoordinateAssignment;mxCoordinateAssignment.prototype.layout=null;mxCoordinateAssignment.prototype.intraCellSpacing=30;mxCoordinateAssignment.prototype.interRankCellSpacing=100;mxCoordinateAssignment.prototype.parallelEdgeSpacing=10;mxCoordinateAssignment.prototype.maxIterations=8;mxCoordinateAssignment.prototype.prefHozEdgeSep=5;mxCoordinateAssignment.prototype.prefVertEdgeOff=2;mxCoordinateAssignment.prototype.minEdgeJetty=12;
+mxCoordinateAssignment.prototype.channelBuffer=4;mxCoordinateAssignment.prototype.jettyPositions=null;mxCoordinateAssignment.prototype.orientation=mxConstants.DIRECTION_NORTH;mxCoordinateAssignment.prototype.initialX=null;mxCoordinateAssignment.prototype.limitX=null;mxCoordinateAssignment.prototype.currentXDelta=null;mxCoordinateAssignment.prototype.widestRank=null;mxCoordinateAssignment.prototype.rankTopY=null;mxCoordinateAssignment.prototype.rankBottomY=null;
+mxCoordinateAssignment.prototype.widestRankValue=null;mxCoordinateAssignment.prototype.rankWidths=null;mxCoordinateAssignment.prototype.rankY=null;mxCoordinateAssignment.prototype.fineTuning=!0;mxCoordinateAssignment.prototype.nextLayerConnectedCache=null;mxCoordinateAssignment.prototype.previousLayerConnectedCache=null;mxCoordinateAssignment.prototype.groupPadding=10;
+mxCoordinateAssignment.prototype.printStatus=function(){var a=this.layout.getModel();mxLog.show();mxLog.writeln("======Coord assignment debug=======");for(var b=0;b<a.ranks.length;b++){mxLog.write("Rank ",b," : ");for(var c=a.ranks[b],d=0;d<c.length;d++)mxLog.write(c[d].getGeneralPurposeVariable(b)," ");mxLog.writeln()}mxLog.writeln("====================================")};
+mxCoordinateAssignment.prototype.execute=function(a){this.jettyPositions={};a=this.layout.getModel();this.currentXDelta=0;this.initialCoords(this.layout.getGraph(),a);this.fineTuning&&this.minNode(a);var b=1E8;if(this.fineTuning)for(var c=0;c<this.maxIterations;c++){0!=c&&(this.medianPos(c,a),this.minNode(a));if(this.currentXDelta<b){for(var d=0;d<a.ranks.length;d++)for(var e=a.ranks[d],f=0;f<e.length;f++){var g=e[f];g.setX(d,g.getGeneralPurposeVariable(d))}b=this.currentXDelta}else for(d=0;d<a.ranks.length;d++)for(e=
+a.ranks[d],f=0;f<e.length;f++)g=e[f],g.setGeneralPurposeVariable(d,g.getX(d));this.minPath(this.layout.getGraph(),a);this.currentXDelta=0}this.setCellLocations(this.layout.getGraph(),a)};
+mxCoordinateAssignment.prototype.minNode=function(a){for(var b=[],c=new mxDictionary,d=[],e=0;e<=a.maxRank;e++){d[e]=a.ranks[e];for(var f=0;f<d[e].length;f++){var g=d[e][f],k=new WeightedCellSorter(g,e);k.rankIndex=f;k.visited=!0;b.push(k);c.put(g,k)}}a=10*b.length;for(f=0;0<b.length&&f<=a;){var g=b.shift(),e=g.cell,l=g.weightedValue,m=parseInt(g.rankIndex),k=e.getNextLayerConnectedCells(l),n=e.getPreviousLayerConnectedCells(l),p=k.length,q=n.length,r=this.medianXValue(k,l+1),t=this.medianXValue(n,
+l-1),u=p+q,x=e.getGeneralPurposeVariable(l),y=x;0<u&&(y=(r*p+t*q)/u);p=!1;y<x-1?0==m?(e.setGeneralPurposeVariable(l,y),p=!0):(m=d[l][m-1],x=m.getGeneralPurposeVariable(l),x=x+m.width/2+this.intraCellSpacing+e.width/2,x<y?(e.setGeneralPurposeVariable(l,y),p=!0):x<e.getGeneralPurposeVariable(l)-1&&(e.setGeneralPurposeVariable(l,x),p=!0)):y>x+1&&(m==d[l].length-1?(e.setGeneralPurposeVariable(l,y),p=!0):(m=d[l][m+1],x=m.getGeneralPurposeVariable(l),x=x-m.width/2-this.intraCellSpacing-e.width/2,x>y?(e.setGeneralPurposeVariable(l,
+y),p=!0):x>e.getGeneralPurposeVariable(l)+1&&(e.setGeneralPurposeVariable(l,x),p=!0)));if(p){for(e=0;e<k.length;e++)l=k[e],l=c.get(l),null!=l&&0==l.visited&&(l.visited=!0,b.push(l));for(e=0;e<n.length;e++)l=n[e],l=c.get(l),null!=l&&0==l.visited&&(l.visited=!0,b.push(l))}g.visited=!1;f++}};mxCoordinateAssignment.prototype.medianPos=function(a,b){if(0==a%2)for(var c=b.maxRank;0<c;c--)this.rankMedianPosition(c-1,b,c);else for(c=0;c<b.maxRank-1;c++)this.rankMedianPosition(c+1,b,c)};
+mxCoordinateAssignment.prototype.rankMedianPosition=function(a,b,c){b=b.ranks[a];for(var d=[],e={},f=0;f<b.length;f++){var g=b[f];d[f]=new WeightedCellSorter;d[f].cell=g;d[f].rankIndex=f;e[g.id]=d[f];var k;k=c<a?g.getPreviousLayerConnectedCells(a):g.getNextLayerConnectedCells(a);d[f].weightedValue=this.calculatedWeightedValue(g,k)}d.sort(WeightedCellSorter.prototype.compare);for(f=0;f<d.length;f++){var l,g=d[f].cell;l=0;k=c<a?g.getPreviousLayerConnectedCells(a).slice():g.getNextLayerConnectedCells(a).slice();
+null!=k&&(l=k.length,l=0<l?this.medianXValue(k,c):g.getGeneralPurposeVariable(a));var m=0;k=-1E8;for(var n=d[f].rankIndex-1;0<=n;){var p=e[b[n].id];if(null!=p){var q=p.cell;p.visited?(k=q.getGeneralPurposeVariable(a)+q.width/2+this.intraCellSpacing+m+g.width/2,n=-1):(m+=q.width+this.intraCellSpacing,n--)}}m=0;q=1E8;for(n=d[f].rankIndex+1;n<d.length;)if(p=e[b[n].id],null!=p){var r=p.cell;p.visited?(q=r.getGeneralPurposeVariable(a)-r.width/2-this.intraCellSpacing-m-g.width/2,n=d.length):(m+=r.width+
+this.intraCellSpacing,n++)}l>=k&&l<=q?g.setGeneralPurposeVariable(a,l):l<k?(g.setGeneralPurposeVariable(a,k),this.currentXDelta+=k-l):l>q&&(g.setGeneralPurposeVariable(a,q),this.currentXDelta+=l-q);d[f].visited=!0}};mxCoordinateAssignment.prototype.calculatedWeightedValue=function(a,b){for(var c=0,d=0;d<b.length;d++){var e=b[d];a.isVertex()&&e.isVertex()?c++:c=a.isEdge()&&e.isEdge()?c+8:c+2}return c};
+mxCoordinateAssignment.prototype.medianXValue=function(a,b){if(0==a.length)return 0;for(var c=[],d=0;d<a.length;d++)c[d]=a[d].getGeneralPurposeVariable(b);c.sort(function(a,b){return a-b});if(1==a.length%2)return c[Math.floor(a.length/2)];d=a.length/2;return(c[d-1]+c[d])/2};
+mxCoordinateAssignment.prototype.initialCoords=function(a,b){this.calculateWidestRank(a,b);for(var c=this.widestRank;0<=c;c--)c<b.maxRank&&this.rankCoordinates(c,a,b);for(c=this.widestRank+1;c<=b.maxRank;c++)0<c&&this.rankCoordinates(c,a,b)};
+mxCoordinateAssignment.prototype.rankCoordinates=function(a,b,c){b=c.ranks[a];c=this.initialX+(this.widestRankValue-this.rankWidths[a])/2;for(var d=!1,e=0;e<b.length;e++){var f=b[e];if(f.isVertex()){var g=this.layout.getVertexBounds(f.cell);null!=g?this.orientation==mxConstants.DIRECTION_NORTH||this.orientation==mxConstants.DIRECTION_SOUTH?(f.width=g.width,f.height=g.height):(f.width=g.height,f.height=g.width):d=!0}else f.isEdge()&&(g=1,null!=f.edges?g=f.edges.length:mxLog.warn("edge.edges is null"),
+f.width=(g-1)*this.parallelEdgeSpacing);c+=f.width/2;f.setX(a,c);f.setGeneralPurposeVariable(a,c);c+=f.width/2;c+=this.intraCellSpacing}1==d&&mxLog.warn("At least one cell has no bounds")};
+mxCoordinateAssignment.prototype.calculateWidestRank=function(a,b){var c=-this.interRankCellSpacing,d=0;this.rankWidths=[];this.rankY=[];for(var e=b.maxRank;0<=e;e--){for(var f=0,g=b.ranks[e],k=this.initialX,l=!1,m=0;m<g.length;m++){var n=g[m];if(n.isVertex()){var p=this.layout.getVertexBounds(n.cell);null!=p?this.orientation==mxConstants.DIRECTION_NORTH||this.orientation==mxConstants.DIRECTION_SOUTH?(n.width=p.width,n.height=p.height):(n.width=p.height,n.height=p.width):l=!0;f=Math.max(f,n.height)}else n.isEdge()&&
+(p=1,null!=n.edges?p=n.edges.length:mxLog.warn("edge.edges is null"),n.width=(p-1)*this.parallelEdgeSpacing);k+=n.width/2;n.setX(e,k);n.setGeneralPurposeVariable(e,k);k+=n.width/2;k+=this.intraCellSpacing;k>this.widestRankValue&&(this.widestRankValue=k,this.widestRank=e);this.rankWidths[e]=k}1==l&&mxLog.warn("At least one cell has no bounds");this.rankY[e]=c;k=f/2+d/2+this.interRankCellSpacing;d=f;c=this.orientation==mxConstants.DIRECTION_NORTH||this.orientation==mxConstants.DIRECTION_WEST?c+k:c-
+k;for(m=0;m<g.length;m++)g[m].setY(e,c)}};
+mxCoordinateAssignment.prototype.minPath=function(a,b){for(var c=b.edgeMapper.getValues(),d=0;d<c.length;d++){var e=c[d];if(!(1>e.maxRank-e.minRank-1)){for(var f=e.getGeneralPurposeVariable(e.minRank+1),g=!0,k=0,l=e.minRank+2;l<e.maxRank;l++){var m=e.getGeneralPurposeVariable(l);f!=m?(g=!1,f=m):k++}if(!g){for(var g=f=0,m=[],n=[],p=e.getGeneralPurposeVariable(e.minRank+1),l=e.minRank+1;l<e.maxRank-1;l++){var q=e.getX(l+1);p==q?(m[l-e.minRank-1]=p,f++):this.repositionValid(b,e,l+1,p)?(m[l-e.minRank-
+1]=p,f++):p=m[l-e.minRank-1]=q}p=e.getX(l);for(l=e.maxRank-1;l>e.minRank+1;l--)q=e.getX(l-1),p==q?(n[l-e.minRank-2]=p,g++):this.repositionValid(b,e,l-1,p)?(n[l-e.minRank-2]=p,g++):(n[l-e.minRank-2]=e.getX(l-1),p=q);if(g>k||f>k)if(g>=f)for(l=e.maxRank-2;l>e.minRank;l--)e.setX(l,n[l-e.minRank-1]);else if(f>g)for(l=e.minRank+2;l<e.maxRank;l++)e.setX(l,m[l-e.minRank-2])}}}};
+mxCoordinateAssignment.prototype.repositionValid=function(a,b,c,d){a=a.ranks[c];for(var e=-1,f=0;f<a.length;f++)if(b==a[f]){e=f;break}if(0>e)return!1;f=b.getGeneralPurposeVariable(c);if(d<f){if(0==e)return!0;a=a[e-1];c=a.getGeneralPurposeVariable(c);c=c+a.width/2+this.intraCellSpacing+b.width/2;if(!(c<=d))return!1}else if(d>f){if(e==a.length-1)return!0;a=a[e+1];c=a.getGeneralPurposeVariable(c);c=c-a.width/2-this.intraCellSpacing-b.width/2;if(!(c>=d))return!1}return!0};
+mxCoordinateAssignment.prototype.setCellLocations=function(a,b){this.rankTopY=[];this.rankBottomY=[];for(var c=0;c<b.ranks.length;c++)this.rankTopY[c]=Number.MAX_VALUE,this.rankBottomY[c]=-Number.MAX_VALUE;for(var d=b.vertexMapper.getValues(),c=0;c<d.length;c++)this.setVertexLocation(d[c]);this.layout.edgeStyle!=mxHierarchicalEdgeStyle.ORTHOGONAL&&this.layout.edgeStyle!=mxHierarchicalEdgeStyle.POLYLINE&&this.layout.edgeStyle!=mxHierarchicalEdgeStyle.CURVE||this.localEdgeProcessing(b);d=b.edgeMapper.getValues();
+for(c=0;c<d.length;c++)this.setEdgePosition(d[c])};
+mxCoordinateAssignment.prototype.localEdgeProcessing=function(a){for(var b=0;b<a.ranks.length;b++)for(var c=a.ranks[b],d=0;d<c.length;d++){var e=c[d];if(e.isVertex())for(var f=e.getPreviousLayerConnectedCells(b),g=b-1,k=0;2>k;k++){if(-1<g&&g<a.ranks.length&&null!=f&&0<f.length){for(var l=[],m=0;m<f.length;m++){var n=new WeightedCellSorter(f[m],f[m].getX(g));l.push(n)}l.sort(WeightedCellSorter.prototype.compare);for(var n=e.x[0]-e.width/2,p=n+e.width,q=f=0,g=[],m=0;m<l.length;m++){var r=l[m].cell,
+t;if(r.isVertex()){t=0==k?e.connectsAsSource:e.connectsAsTarget;for(var u=0;u<t.length;u++)if(t[u].source==r||t[u].target==r)f+=t[u].edges.length,q++,g.push(t[u])}else f+=r.edges.length,q++,g.push(r)}e.width>(f+1)*this.prefHozEdgeSep+2*this.prefHozEdgeSep&&(n+=this.prefHozEdgeSep,p-=this.prefHozEdgeSep);l=(p-n)/f;n+=l/2;p=this.minEdgeJetty-this.prefVertEdgeOff;for(m=0;m<g.length;m++)for(q=g[m].edges.length,r=this.jettyPositions[g[m].ids[0]],null==r&&(r=[],this.jettyPositions[g[m].ids[0]]=r),m<f/2?
+p+=this.prefVertEdgeOff:m>f/2&&(p-=this.prefVertEdgeOff),t=0;t<q;t++)r[4*t+2*k]=n,n+=l,r[4*t+2*k+1]=p}f=e.getNextLayerConnectedCells(b);g=b+1}}};
+mxCoordinateAssignment.prototype.setEdgePosition=function(a){var b=0;if(101207!=a.temp[0]){var c=a.maxRank,d=a.minRank;c==d&&(c=a.source.maxRank,d=a.target.minRank);for(var e=0,f=this.jettyPositions[a.ids[0]],g=a.isReversed?a.target.cell:a.source.cell,k=this.layout.graph,l=this.orientation==mxConstants.DIRECTION_EAST||this.orientation==mxConstants.DIRECTION_SOUTH,m=0;m<a.edges.length;m++){var n=a.edges[m],p=this.layout.getVisibleTerminal(n,!0),q=[],r=a.isReversed;p!=g&&(r=!r);if(null!=f){var t=r?
+2:0,u=r?l?this.rankBottomY[d]:this.rankTopY[d]:l?this.rankTopY[c]:this.rankBottomY[c],x=f[4*e+1+t];r!=l&&(x=-x);var u=u+x,t=f[4*e+t],y=k.model.getTerminal(n,!0);this.layout.isPort(y)&&k.model.getParent(y)==p&&(t=k.view.getState(y),t=null!=t?t.x:p.geometry.x+a.source.width*y.geometry.x);this.orientation==mxConstants.DIRECTION_NORTH||this.orientation==mxConstants.DIRECTION_SOUTH?(q.push(new mxPoint(t,u)),this.layout.edgeStyle==mxHierarchicalEdgeStyle.CURVE&&q.push(new mxPoint(t,u+x))):(q.push(new mxPoint(u,
+t)),this.layout.edgeStyle==mxHierarchicalEdgeStyle.CURVE&&q.push(new mxPoint(u+x,t)))}t=a.x.length-1;u=x=-1;p=a.maxRank-1;r&&(t=0,x=a.x.length,u=1,p=a.minRank+1);for(;a.maxRank!=a.minRank&&t!=x;t+=u){var y=a.x[t]+b,A=(this.rankTopY[p]+this.rankBottomY[p+1])/2,z=(this.rankTopY[p-1]+this.rankBottomY[p])/2;if(r)var v=A,A=z,z=v;this.orientation==mxConstants.DIRECTION_NORTH||this.orientation==mxConstants.DIRECTION_SOUTH?(q.push(new mxPoint(y,A)),q.push(new mxPoint(y,z))):(q.push(new mxPoint(A,y)),q.push(new mxPoint(z,
+y)));this.limitX=Math.max(this.limitX,y);p+=u}null!=f&&(t=r?2:0,u=r?l?this.rankTopY[c]:this.rankBottomY[c]:l?this.rankBottomY[d]:this.rankTopY[d],x=f[4*e+3-t],r!=l&&(x=-x),u-=x,t=f[4*e+2-t],r=k.model.getTerminal(n,!1),p=this.layout.getVisibleTerminal(n,!1),this.layout.isPort(r)&&k.model.getParent(r)==p&&(t=k.view.getState(r),t=null!=t?t.x:p.geometry.x+a.target.width*r.geometry.x),this.orientation==mxConstants.DIRECTION_NORTH||this.orientation==mxConstants.DIRECTION_SOUTH?(this.layout.edgeStyle==mxHierarchicalEdgeStyle.CURVE&&
+q.push(new mxPoint(t,u-x)),q.push(new mxPoint(t,u))):(this.layout.edgeStyle==mxHierarchicalEdgeStyle.CURVE&&q.push(new mxPoint(u-x,t)),q.push(new mxPoint(u,t))));a.isReversed&&this.processReversedEdge(a,n);this.layout.setEdgePoints(n,q);b=0==b?this.parallelEdgeSpacing:0<b?-b:-b+this.parallelEdgeSpacing;e++}a.temp[0]=101207}};
+mxCoordinateAssignment.prototype.setVertexLocation=function(a){var b=a.cell,c=a.x[0]-a.width/2,d=a.y[0]-a.height/2;this.rankTopY[a.minRank]=Math.min(this.rankTopY[a.minRank],d);this.rankBottomY[a.minRank]=Math.max(this.rankBottomY[a.minRank],d+a.height);this.orientation==mxConstants.DIRECTION_NORTH||this.orientation==mxConstants.DIRECTION_SOUTH?this.layout.setVertexLocation(b,c,d):this.layout.setVertexLocation(b,d,c);this.limitX=Math.max(this.limitX,c+a.width)};
+mxCoordinateAssignment.prototype.processReversedEdge=function(a,b){};function mxSwimlaneOrdering(a){this.layout=a}mxSwimlaneOrdering.prototype=new mxHierarchicalLayoutStage;mxSwimlaneOrdering.prototype.constructor=mxSwimlaneOrdering;mxSwimlaneOrdering.prototype.layout=null;
+mxSwimlaneOrdering.prototype.execute=function(a){a=this.layout.getModel();var b=mxUtils.clone(a.vertexMapper,null,!0),c=null;if(null!=a.roots)for(var d=a.roots,c=[],e=0;e<d.length;e++)mxCellPath.create(d[e]),c[e]=a.vertexMapper.get(d[e]);a.visit(function(a,c,d,e,m){e=null!=a&&a.swimlaneIndex==c.swimlaneIndex&&c.isAncestor(a);m=null!=a&&null!=d&&a.swimlaneIndex<c.swimlaneIndex&&d.source==c;e?(d.invert(),mxUtils.remove(d,a.connectsAsSource),c.connectsAsSource.push(d),a.connectsAsTarget.push(d),mxUtils.remove(d,
+c.connectsAsTarget)):m&&(d.invert(),mxUtils.remove(d,a.connectsAsTarget),c.connectsAsTarget.push(d),a.connectsAsSource.push(d),mxUtils.remove(d,c.connectsAsSource));a=mxCellPath.create(c.cell);delete b[a]},c,!0,null)};function mxHierarchicalLayout(a,b,c){mxGraphLayout.call(this,a);this.orientation=null!=b?b:mxConstants.DIRECTION_NORTH;this.deterministic=null!=c?c:!0}var mxHierarchicalEdgeStyle={ORTHOGONAL:1,POLYLINE:2,STRAIGHT:3,CURVE:4};mxHierarchicalLayout.prototype=new mxGraphLayout;
+mxHierarchicalLayout.prototype.constructor=mxHierarchicalLayout;mxHierarchicalLayout.prototype.roots=null;mxHierarchicalLayout.prototype.resizeParent=!1;mxHierarchicalLayout.prototype.maintainParentLocation=!1;mxHierarchicalLayout.prototype.moveParent=!1;mxHierarchicalLayout.prototype.parentBorder=0;mxHierarchicalLayout.prototype.intraCellSpacing=30;mxHierarchicalLayout.prototype.interRankCellSpacing=100;mxHierarchicalLayout.prototype.interHierarchySpacing=60;
+mxHierarchicalLayout.prototype.parallelEdgeSpacing=10;mxHierarchicalLayout.prototype.orientation=mxConstants.DIRECTION_NORTH;mxHierarchicalLayout.prototype.fineTuning=!0;mxHierarchicalLayout.prototype.tightenToSource=!0;mxHierarchicalLayout.prototype.disableEdgeStyle=!0;mxHierarchicalLayout.prototype.traverseAncestors=!0;mxHierarchicalLayout.prototype.model=null;mxHierarchicalLayout.prototype.edgesCache=null;mxHierarchicalLayout.prototype.edgeSourceTermCache=null;
+mxHierarchicalLayout.prototype.edgesTargetTermCache=null;mxHierarchicalLayout.prototype.edgeStyle=mxHierarchicalEdgeStyle.POLYLINE;mxHierarchicalLayout.prototype.getModel=function(){return this.model};
+mxHierarchicalLayout.prototype.execute=function(a,b){this.parent=a;var c=this.graph.model;this.edgesCache=new mxDictionary;this.edgeSourceTermCache=new mxDictionary;this.edgesTargetTermCache=new mxDictionary;null==b||b instanceof Array||(b=[b]);if(null!=b||null!=a){this.parentY=this.parentX=null;if(a!=this.root&&null!=c.isVertex(a)&&this.maintainParentLocation){var d=this.graph.getCellGeometry(a);null!=d&&(this.parentX=d.x,this.parentY=d.y)}if(null!=b){for(var e=[],f=0;f<b.length;f++)(null!=a?c.isAncestor(a,
+b[f]):1)&&c.isVertex(b[f])&&e.push(b[f]);this.roots=e}c.beginUpdate();try{this.run(a),this.resizeParent&&!this.graph.isCellCollapsed(a)&&this.graph.updateGroupBounds([a],this.parentBorder,this.moveParent),null!=this.parentX&&null!=this.parentY&&(d=this.graph.getCellGeometry(a),null!=d&&(d=d.clone(),d.x=this.parentX,d.y=this.parentY,c.setGeometry(a,d)))}finally{c.endUpdate()}}};
+mxHierarchicalLayout.prototype.findRoots=function(a,b){var c=[];if(null!=a&&null!=b){var d=this.graph.model,e=null,f=-1E5,g;for(g in b){var k=b[g];if(d.isVertex(k)&&this.graph.isCellVisible(k)){for(var l=this.getEdges(k),m=0,n=0,p=0;p<l.length;p++)this.getVisibleTerminal(l[p],!0)==k?m++:n++;0==n&&0<m&&c.push(k);l=m-n;l>f&&(f=l,e=k)}}0==c.length&&null!=e&&c.push(e)}return c};
+mxHierarchicalLayout.prototype.getEdges=function(a){var b=this.edgesCache.get(a);if(null!=b)return b;for(var c=this.graph.model,b=[],d=this.graph.isCellCollapsed(a),e=c.getChildCount(a),f=0;f<e;f++){var g=c.getChildAt(a,f);if(this.isPort(g))b=b.concat(c.getEdges(g,!0,!0));else if(d||!this.graph.isCellVisible(g))b=b.concat(c.getEdges(g,!0,!0))}b=b.concat(c.getEdges(a,!0,!0));c=[];for(f=0;f<b.length;f++)d=this.getVisibleTerminal(b[f],!0),e=this.getVisibleTerminal(b[f],!1),(d==e||d!=e&&(e==a&&(null==
+this.parent||this.isAncestor(this.parent,d,this.traverseAncestors))||d==a&&(null==this.parent||this.isAncestor(this.parent,e,this.traverseAncestors))))&&c.push(b[f]);this.edgesCache.put(a,c);return c};
+mxHierarchicalLayout.prototype.getVisibleTerminal=function(a,b){var c=this.edgesTargetTermCache;b&&(c=this.edgeSourceTermCache);var d=c.get(a);if(null!=d)return d;var d=this.graph.view.getState(a),e=null!=d?d.getVisibleTerminal(b):this.graph.view.getVisibleTerminal(a,b);null==e&&(e=null!=d?d.getVisibleTerminal(b):this.graph.view.getVisibleTerminal(a,b));null!=e&&(this.isPort(e)&&(e=this.graph.model.getParent(e)),c.put(a,e));return e};
+mxHierarchicalLayout.prototype.run=function(a){var b=[],c=[];if(null==this.roots&&null!=a){var d={};this.filterDescendants(a,d);this.roots=[];var e=!0,f;for(f in d)if(null!=d[f]){e=!1;break}for(;!e;){for(var g=this.findRoots(a,d),e=0;e<g.length;e++){var k={};b.push(k);this.traverse(g[e],!0,null,c,k,b,d)}for(e=0;e<g.length;e++)this.roots.push(g[e]);e=!0;for(f in d)if(null!=d[f]){e=!1;break}}}else for(e=0;e<this.roots.length;e++)k={},b.push(k),this.traverse(this.roots[e],!0,null,c,k,b,null);for(e=c=
+0;e<b.length;e++){k=b[e];d=[];for(f in k)d.push(k[f]);this.model=new mxGraphHierarchyModel(this,d,this.roots,a,this.tightenToSource);this.cycleStage(a);this.layeringStage();this.crossingStage(a);c=this.placementStage(c,a)}};
+mxHierarchicalLayout.prototype.filterDescendants=function(a,b){var c=this.graph.model;c.isVertex(a)&&a!=this.parent&&this.graph.isCellVisible(a)&&(b[mxObjectIdentity.get(a)]=a);if(this.traverseAncestors||a==this.parent&&this.graph.isCellVisible(a))for(var d=c.getChildCount(a),e=0;e<d;e++){var f=c.getChildAt(a,e);this.isPort(f)||this.filterDescendants(f,b)}};mxHierarchicalLayout.prototype.isPort=function(a){return a.geometry.relative?!0:!1};
+mxHierarchicalLayout.prototype.getEdgesBetween=function(a,b,c){c=null!=c?c:!1;for(var d=this.getEdges(a),e=[],f=0;f<d.length;f++){var g=this.getVisibleTerminal(d[f],!0),k=this.getVisibleTerminal(d[f],!1);(g==a&&k==b||!c&&g==b&&k==a)&&e.push(d[f])}return e};
+mxHierarchicalLayout.prototype.traverse=function(a,b,c,d,e,f,g){if(null!=a&&null!=d){var k=mxObjectIdentity.get(a);if(null==d[k]&&(null==g||null!=g[k])){null==e[k]&&(e[k]=a);null==d[k]&&(d[k]=a);null!==g&&delete g[k];var l=this.getEdges(a),k=[];for(c=0;c<l.length;c++)k[c]=this.getVisibleTerminal(l[c],!0)==a;for(c=0;c<l.length;c++)if(!b||k[c]){a=this.getVisibleTerminal(l[c],!k[c]);for(var m=1,n=0;n<l.length;n++)if(n!=c){var p=k[n];this.getVisibleTerminal(l[n],!p)==a&&(p?m++:m--)}0<=m&&(e=this.traverse(a,
+b,l[c],d,e,f,g))}}else if(null==e[k])for(c=0;c<f.length;c++)if(b=f[c],null!=b[k]){for(l in b)e[l]=b[l];f.splice(c,1);break}}return e};mxHierarchicalLayout.prototype.cycleStage=function(a){(new mxMinimumCycleRemover(this)).execute(a)};mxHierarchicalLayout.prototype.layeringStage=function(){this.model.initialRank();this.model.fixRanks()};mxHierarchicalLayout.prototype.crossingStage=function(a){(new mxMedianHybridCrossingReduction(this)).execute(a)};
+mxHierarchicalLayout.prototype.placementStage=function(a,b){var c=new mxCoordinateAssignment(this,this.intraCellSpacing,this.interRankCellSpacing,this.orientation,a,this.parallelEdgeSpacing);c.fineTuning=this.fineTuning;c.execute(b);return c.limitX+this.interHierarchySpacing};function mxSwimlaneLayout(a,b,c){mxGraphLayout.call(this,a);this.orientation=null!=b?b:mxConstants.DIRECTION_NORTH;this.deterministic=null!=c?c:!0}mxSwimlaneLayout.prototype=new mxGraphLayout;
+mxSwimlaneLayout.prototype.constructor=mxSwimlaneLayout;mxSwimlaneLayout.prototype.roots=null;mxSwimlaneLayout.prototype.swimlanes=null;mxSwimlaneLayout.prototype.dummyVertices=null;mxSwimlaneLayout.prototype.dummyVertexWidth=50;mxSwimlaneLayout.prototype.resizeParent=!1;mxSwimlaneLayout.prototype.maintainParentLocation=!1;mxSwimlaneLayout.prototype.moveParent=!1;mxSwimlaneLayout.prototype.parentBorder=30;mxSwimlaneLayout.prototype.intraCellSpacing=30;
+mxSwimlaneLayout.prototype.interRankCellSpacing=100;mxSwimlaneLayout.prototype.interHierarchySpacing=60;mxSwimlaneLayout.prototype.parallelEdgeSpacing=10;mxSwimlaneLayout.prototype.orientation=mxConstants.DIRECTION_NORTH;mxSwimlaneLayout.prototype.fineTuning=!0;mxSwimlaneLayout.prototype.tightenToSource=!0;mxSwimlaneLayout.prototype.disableEdgeStyle=!0;mxSwimlaneLayout.prototype.traverseAncestors=!0;mxSwimlaneLayout.prototype.model=null;mxSwimlaneLayout.prototype.edgesCache=null;
+mxHierarchicalLayout.prototype.edgeSourceTermCache=null;mxHierarchicalLayout.prototype.edgesTargetTermCache=null;mxHierarchicalLayout.prototype.edgeStyle=mxHierarchicalEdgeStyle.POLYLINE;mxSwimlaneLayout.prototype.getModel=function(){return this.model};
+mxSwimlaneLayout.prototype.execute=function(a,b){this.parent=a;var c=this.graph.model;this.edgesCache=new mxDictionary;this.edgeSourceTermCache=new mxDictionary;this.edgesTargetTermCache=new mxDictionary;if(!(null==b||1>b.length)){null==a&&(a=c.getParent(b[0]));this.parentY=this.parentX=null;if(a!=this.root&&null!=c.isVertex(a)&&this.maintainParentLocation){var d=this.graph.getCellGeometry(a);null!=d&&(this.parentX=d.x,this.parentY=d.y)}this.swimlanes=b;this.dummyVertices=[];for(var e=0;e<b.length;e++){var f=
+this.graph.getChildCells(b[e]);if(null==f||0==f.length)f=this.graph.insertVertex(b[e],null,null,0,0,this.dummyVertexWidth,0),this.dummyVertices.push(f)}c.beginUpdate();try{this.run(a),this.resizeParent&&!this.graph.isCellCollapsed(a)&&this.graph.updateGroupBounds([a],this.parentBorder,this.moveParent),null!=this.parentX&&null!=this.parentY&&(d=this.graph.getCellGeometry(a),null!=d&&(d=d.clone(),d.x=this.parentX,d.y=this.parentY,c.setGeometry(a,d))),this.graph.removeCells(this.dummyVertices)}finally{c.endUpdate()}}};
+mxSwimlaneLayout.prototype.updateGroupBounds=function(){var a=[],b=this.model,c;for(c in b.edgeMapper)for(var d=b.edgeMapper[c],e=0;e<d.edges.length;e++)a.push(d.edges[e]);a=this.graph.getBoundingBoxFromGeometry(a,!0);b=[];for(e=0;e<this.swimlanes.length;e++){var f=this.swimlanes[e];c=this.graph.getCellGeometry(f);if(null!=c){var g=this.graph.getChildCells(f),d=this.graph.isSwimlane(f)?this.graph.getStartSize(f):new mxRectangle,f=this.graph.getBoundingBoxFromGeometry(g);b[e]=f;d=f.y+c.y-d.height-
+this.parentBorder;c=f.y+c.y+f.height;null==a?a=new mxRectangle(0,d,0,c-d):(a.y=Math.min(a.y,d),a.height=Math.max(a.y+a.height,c)-a.y)}}for(e=0;e<this.swimlanes.length;e++)if(f=this.swimlanes[e],c=this.graph.getCellGeometry(f),null!=c){var g=this.graph.getChildCells(f),d=this.graph.isSwimlane(f)?this.graph.getStartSize(f):new mxRectangle,k=c.clone(),l=0==e?this.parentBorder:this.interRankCellSpacing/2;k.x+=b[e].x-d.width-l;k.y=k.y+a.y-c.y-this.parentBorder;k.width=b[e].width+d.width+this.interRankCellSpacing/
+2+l;k.height=a.height+d.height+2*this.parentBorder;this.graph.model.setGeometry(f,k);this.graph.moveCells(g,-b[e].x+d.width+l,c.y-a.y+this.parentBorder)}};
+mxSwimlaneLayout.prototype.findRoots=function(a,b){var c=[];if(null!=a&&null!=b){var d=this.graph.model,e=null,f=-1E5,g;for(g in b){var k=b[g];if(null!=k&&d.isVertex(k)&&this.graph.isCellVisible(k)&&d.isAncestor(a,k)){for(var l=this.getEdges(k),m=0,n=0,p=0;p<l.length;p++){var q=this.getVisibleTerminal(l[p],!0);q==k?(q=this.getVisibleTerminal(l[p],!1),d.isAncestor(a,q)&&m++):d.isAncestor(a,q)&&n++}0==n&&0<m&&c.push(k);l=m-n;l>f&&(f=l,e=k)}}0==c.length&&null!=e&&c.push(e)}return c};
+mxSwimlaneLayout.prototype.getEdges=function(a){var b=this.edgesCache.get(a);if(null!=b)return b;for(var c=this.graph.model,b=[],d=this.graph.isCellCollapsed(a),e=c.getChildCount(a),f=0;f<e;f++){var g=c.getChildAt(a,f);if(this.isPort(g))b=b.concat(c.getEdges(g,!0,!0));else if(d||!this.graph.isCellVisible(g))b=b.concat(c.getEdges(g,!0,!0))}b=b.concat(c.getEdges(a,!0,!0));c=[];for(f=0;f<b.length;f++)d=this.getVisibleTerminal(b[f],!0),e=this.getVisibleTerminal(b[f],!1),(d==e||d!=e&&(e==a&&(null==this.parent||
+this.graph.isValidAncestor(d,this.parent,this.traverseAncestors))||d==a&&(null==this.parent||this.graph.isValidAncestor(e,this.parent,this.traverseAncestors))))&&c.push(b[f]);this.edgesCache.put(a,c);return c};
+mxSwimlaneLayout.prototype.getVisibleTerminal=function(a,b){var c=this.edgesTargetTermCache;b&&(c=this.edgeSourceTermCache);var d=c.get(a);if(null!=d)return d;var d=this.graph.view.getState(a),e=null!=d?d.getVisibleTerminal(b):this.graph.view.getVisibleTerminal(a,b);null==e&&(e=null!=d?d.getVisibleTerminal(b):this.graph.view.getVisibleTerminal(a,b));null!=e&&(this.isPort(e)&&(e=this.graph.model.getParent(e)),c.put(a,e));return e};
+mxSwimlaneLayout.prototype.run=function(a){var b=[],c=[];if(null!=this.swimlanes&&0<this.swimlanes.length&&null!=a){for(var d={},e=0;e<this.swimlanes.length;e++)this.filterDescendants(this.swimlanes[e],d);this.roots=[];var e=!0,f;for(f in d)if(null!=d[f]){e=!1;break}for(var g=0;!e&&g<this.swimlanes.length;){var k=this.findRoots(this.swimlanes[g],d);if(0==k.length)g++;else{for(e=0;e<k.length;e++){var l={};b.push(l);this.traverse(k[e],!0,null,c,l,b,d,g)}for(e=0;e<k.length;e++)this.roots.push(k[e]);
+e=!0;for(f in d)if(null!=d[f]){e=!1;break}}}}else for(e=0;e<this.roots.length;e++)l={},b.push(l),this.traverse(this.roots[e],!0,null,c,l,b,null);b=[];for(f in c)b.push(c[f]);this.model=new mxSwimlaneModel(this,b,this.roots,a,this.tightenToSource);this.cycleStage(a);this.layeringStage();this.crossingStage(a);initialX=this.placementStage(0,a)};
+mxSwimlaneLayout.prototype.filterDescendants=function(a,b){var c=this.graph.model;c.isVertex(a)&&a!=this.parent&&c.getParent(a)!=this.parent&&this.graph.isCellVisible(a)&&(b[mxObjectIdentity.get(a)]=a);if(this.traverseAncestors||a==this.parent&&this.graph.isCellVisible(a))for(var d=c.getChildCount(a),e=0;e<d;e++){var f=c.getChildAt(a,e);this.isPort(f)||this.filterDescendants(f,b)}};mxSwimlaneLayout.prototype.isPort=function(a){return a.geometry.relative?!0:!1};
+mxSwimlaneLayout.prototype.getEdgesBetween=function(a,b,c){c=null!=c?c:!1;for(var d=this.getEdges(a),e=[],f=0;f<d.length;f++){var g=this.getVisibleTerminal(d[f],!0),k=this.getVisibleTerminal(d[f],!1);(g==a&&k==b||!c&&g==b&&k==a)&&e.push(d[f])}return e};
+mxSwimlaneLayout.prototype.traverse=function(a,b,c,d,e,f,g,k){if(null!=a&&null!=d){var l=mxObjectIdentity.get(a);if(null==d[l]&&(null==g||null!=g[l])){null==e[l]&&(e[l]=a);null==d[l]&&(d[l]=a);null!==g&&delete g[l];var m=this.getEdges(a),l=this.graph.model;for(c=0;c<m.length;c++){var n=this.getVisibleTerminal(m[c],!0),p=n==a;p&&(n=this.getVisibleTerminal(m[c],!1));var q;for(q=0;q<this.swimlanes.length&&!l.isAncestor(this.swimlanes[q],n);q++);q>=this.swimlanes.length||!(q>k||(!b||p)&&q==k)||(e=this.traverse(n,
+b,m[c],d,e,f,g,q))}}else if(null==e[l])for(c=0;c<f.length;c++)if(a=f[c],null!=a[l]){for(m in a)e[m]=a[m];f.splice(c,1);break}}return e};mxSwimlaneLayout.prototype.cycleStage=function(a){(new mxSwimlaneOrdering(this)).execute(a)};mxSwimlaneLayout.prototype.layeringStage=function(){this.model.initialRank();this.model.fixRanks()};mxSwimlaneLayout.prototype.crossingStage=function(a){(new mxMedianHybridCrossingReduction(this)).execute(a)};
+mxSwimlaneLayout.prototype.placementStage=function(a,b){var c=new mxCoordinateAssignment(this,this.intraCellSpacing,this.interRankCellSpacing,this.orientation,a,this.parallelEdgeSpacing);c.fineTuning=this.fineTuning;c.execute(b);return c.limitX+this.interHierarchySpacing};function mxGraphModel(a){this.currentEdit=this.createUndoableEdit();null!=a?this.setRoot(a):this.clear()}mxGraphModel.prototype=new mxEventSource;mxGraphModel.prototype.constructor=mxGraphModel;mxGraphModel.prototype.root=null;
+mxGraphModel.prototype.cells=null;mxGraphModel.prototype.maintainEdgeParent=!0;mxGraphModel.prototype.ignoreRelativeEdgeParent=!0;mxGraphModel.prototype.createIds=!0;mxGraphModel.prototype.prefix="";mxGraphModel.prototype.postfix="";mxGraphModel.prototype.nextId=0;mxGraphModel.prototype.currentEdit=null;mxGraphModel.prototype.updateLevel=0;mxGraphModel.prototype.endingUpdate=!1;mxGraphModel.prototype.clear=function(){this.setRoot(this.createRoot())};mxGraphModel.prototype.isCreateIds=function(){return this.createIds};
+mxGraphModel.prototype.setCreateIds=function(a){this.createIds=a};mxGraphModel.prototype.createRoot=function(){var a=new mxCell;a.insert(new mxCell);return a};mxGraphModel.prototype.getCell=function(a){return null!=this.cells?this.cells[a]:null};mxGraphModel.prototype.filterCells=function(a,b){var c=null;if(null!=a)for(var c=[],d=0;d<a.length;d++)b(a[d])&&c.push(a[d]);return c};mxGraphModel.prototype.getDescendants=function(a){return this.filterDescendants(null,a)};
+mxGraphModel.prototype.filterDescendants=function(a,b){var c=[];b=b||this.getRoot();(null==a||a(b))&&c.push(b);for(var d=this.getChildCount(b),e=0;e<d;e++)var f=this.getChildAt(b,e),c=c.concat(this.filterDescendants(a,f));return c};mxGraphModel.prototype.getRoot=function(a){var b=a||this.root;if(null!=a)for(;null!=a;)b=a,a=this.getParent(a);return b};mxGraphModel.prototype.setRoot=function(a){this.execute(new mxRootChange(this,a));return a};
+mxGraphModel.prototype.rootChanged=function(a){var b=this.root;this.root=a;this.nextId=0;this.cells=null;this.cellAdded(a);return b};mxGraphModel.prototype.isRoot=function(a){return null!=a&&this.root==a};mxGraphModel.prototype.isLayer=function(a){return this.isRoot(this.getParent(a))};mxGraphModel.prototype.isAncestor=function(a,b){for(;null!=b&&b!=a;)b=this.getParent(b);return b==a};mxGraphModel.prototype.contains=function(a){return this.isAncestor(this.root,a)};
+mxGraphModel.prototype.getParent=function(a){return null!=a?a.getParent():null};mxGraphModel.prototype.add=function(a,b,c){if(b!=a&&null!=a&&null!=b){null==c&&(c=this.getChildCount(a));var d=a!=this.getParent(b);this.execute(new mxChildChange(this,a,b,c));this.maintainEdgeParent&&d&&this.updateEdgeParents(b)}return b};
+mxGraphModel.prototype.cellAdded=function(a){if(null!=a){null==a.getId()&&this.createIds&&a.setId(this.createId(a));if(null!=a.getId()){var b=this.getCell(a.getId());if(b!=a){for(;null!=b;)a.setId(this.createId(a)),b=this.getCell(a.getId());null==this.cells&&(this.cells={});this.cells[a.getId()]=a}}mxUtils.isNumeric(a.getId())&&(this.nextId=Math.max(this.nextId,a.getId()));for(var b=this.getChildCount(a),c=0;c<b;c++)this.cellAdded(this.getChildAt(a,c))}};
+mxGraphModel.prototype.createId=function(a){a=this.nextId;this.nextId++;return this.prefix+a+this.postfix};mxGraphModel.prototype.updateEdgeParents=function(a,b){b=b||this.getRoot(a);for(var c=this.getChildCount(a),d=0;d<c;d++){var e=this.getChildAt(a,d);this.updateEdgeParents(e,b)}e=this.getEdgeCount(a);c=[];for(d=0;d<e;d++)c.push(this.getEdgeAt(a,d));for(d=0;d<c.length;d++)e=c[d],this.isAncestor(b,e)&&this.updateEdgeParent(e,b)};
+mxGraphModel.prototype.updateEdgeParent=function(a,b){for(var c=this.getTerminal(a,!0),d=this.getTerminal(a,!1);null!=c&&!this.isEdge(c)&&null!=c.geometry&&c.geometry.relative;)c=this.getParent(c);for(;null!=d&&this.ignoreRelativeEdgeParent&&!this.isEdge(d)&&null!=d.geometry&&d.geometry.relative;)d=this.getParent(d);if(this.isAncestor(b,c)&&this.isAncestor(b,d)&&(c=c==d?this.getParent(c):this.getNearestCommonAncestor(c,d),null!=c&&(this.getParent(c)!=this.root||this.isAncestor(c,a))&&this.getParent(a)!=
+c)){d=this.getGeometry(a);if(null!=d){var e=this.getOrigin(this.getParent(a)),f=this.getOrigin(c),g=f.x-e.x,e=f.y-e.y,d=d.clone();d.translate(-g,-e);this.setGeometry(a,d)}this.add(c,a,this.getChildCount(c))}};mxGraphModel.prototype.getOrigin=function(a){var b;null!=a?(b=this.getOrigin(this.getParent(a)),this.isEdge(a)||(a=this.getGeometry(a),null!=a&&(b.x+=a.x,b.y+=a.y))):b=new mxPoint;return b};
+mxGraphModel.prototype.getNearestCommonAncestor=function(a,b){if(null!=a&&null!=b){var c=mxCellPath.create(b);if(null!=c&&0<c.length){var d=a,e=mxCellPath.create(d);if(c.length<e.length)var d=b,f=e,e=c,c=f;for(;null!=d;){f=this.getParent(d);if(0==c.indexOf(e+mxCellPath.PATH_SEPARATOR)&&null!=f)return d;e=mxCellPath.getParentPath(e);d=f}}}return null};mxGraphModel.prototype.remove=function(a){a==this.root?this.setRoot(null):null!=this.getParent(a)&&this.execute(new mxChildChange(this,null,a));return a};
+mxGraphModel.prototype.cellRemoved=function(a){if(null!=a&&null!=this.cells){for(var b=this.getChildCount(a)-1;0<=b;b--)this.cellRemoved(this.getChildAt(a,b));null!=this.cells&&null!=a.getId()&&delete this.cells[a.getId()]}};mxGraphModel.prototype.parentForCellChanged=function(a,b,c){var d=this.getParent(a);null!=b?b==d&&d.getIndex(a)==c||b.insert(a,c):null!=d&&(c=d.getIndex(a),d.remove(c));this.contains(d)||null==b?null==b&&this.cellRemoved(a):this.cellAdded(a);return d};
+mxGraphModel.prototype.getChildCount=function(a){return null!=a?a.getChildCount():0};mxGraphModel.prototype.getChildAt=function(a,b){return null!=a?a.getChildAt(b):null};mxGraphModel.prototype.getChildren=function(a){return null!=a?a.children:null};mxGraphModel.prototype.getChildVertices=function(a){return this.getChildCells(a,!0,!1)};mxGraphModel.prototype.getChildEdges=function(a){return this.getChildCells(a,!1,!0)};
+mxGraphModel.prototype.getChildCells=function(a,b,c){b=null!=b?b:!1;c=null!=c?c:!1;for(var d=this.getChildCount(a),e=[],f=0;f<d;f++){var g=this.getChildAt(a,f);(!c&&!b||c&&this.isEdge(g)||b&&this.isVertex(g))&&e.push(g)}return e};mxGraphModel.prototype.getTerminal=function(a,b){return null!=a?a.getTerminal(b):null};
+mxGraphModel.prototype.setTerminal=function(a,b,c){var d=b!=this.getTerminal(a,c);this.execute(new mxTerminalChange(this,a,b,c));this.maintainEdgeParent&&d&&this.updateEdgeParent(a,this.getRoot());return b};mxGraphModel.prototype.setTerminals=function(a,b,c){this.beginUpdate();try{this.setTerminal(a,b,!0),this.setTerminal(a,c,!1)}finally{this.endUpdate()}};
+mxGraphModel.prototype.terminalForCellChanged=function(a,b,c){var d=this.getTerminal(a,c);null!=b?b.insertEdge(a,c):null!=d&&d.removeEdge(a,c);return d};mxGraphModel.prototype.getEdgeCount=function(a){return null!=a?a.getEdgeCount():0};mxGraphModel.prototype.getEdgeAt=function(a,b){return null!=a?a.getEdgeAt(b):null};mxGraphModel.prototype.getDirectedEdgeCount=function(a,b,c){for(var d=0,e=this.getEdgeCount(a),f=0;f<e;f++){var g=this.getEdgeAt(a,f);g!=c&&this.getTerminal(g,b)==a&&d++}return d};
+mxGraphModel.prototype.getConnections=function(a){return this.getEdges(a,!0,!0,!1)};mxGraphModel.prototype.getIncomingEdges=function(a){return this.getEdges(a,!0,!1,!1)};mxGraphModel.prototype.getOutgoingEdges=function(a){return this.getEdges(a,!1,!0,!1)};
+mxGraphModel.prototype.getEdges=function(a,b,c,d){b=null!=b?b:!0;c=null!=c?c:!0;d=null!=d?d:!0;for(var e=this.getEdgeCount(a),f=[],g=0;g<e;g++){var k=this.getEdgeAt(a,g),l=this.getTerminal(k,!0),m=this.getTerminal(k,!1);(d&&l==m||l!=m&&(b&&m==a||c&&l==a))&&f.push(k)}return f};
+mxGraphModel.prototype.getEdgesBetween=function(a,b,c){c=null!=c?c:!1;var d=this.getEdgeCount(a),e=this.getEdgeCount(b),f=a,g=d;e<d&&(g=e,f=b);d=[];for(e=0;e<g;e++){var k=this.getEdgeAt(f,e),l=this.getTerminal(k,!0),m=this.getTerminal(k,!1),n=m==a&&l==b;(l==a&&m==b||!c&&n)&&d.push(k)}return d};
+mxGraphModel.prototype.getOpposites=function(a,b,c,d){c=null!=c?c:!0;d=null!=d?d:!0;var e=[];if(null!=a)for(var f=0;f<a.length;f++){var g=this.getTerminal(a[f],!0),k=this.getTerminal(a[f],!1);g==b&&null!=k&&k!=b&&d?e.push(k):k==b&&null!=g&&g!=b&&c&&e.push(g)}return e};
+mxGraphModel.prototype.getTopmostCells=function(a){for(var b=new mxDictionary,c=[],d=0;d<a.length;d++)b.put(a[d],!0);for(d=0;d<a.length;d++){for(var e=a[d],f=!0,g=this.getParent(e);null!=g;){if(b.get(g)){f=!1;break}g=this.getParent(g)}f&&c.push(e)}return c};mxGraphModel.prototype.isVertex=function(a){return null!=a?a.isVertex():!1};mxGraphModel.prototype.isEdge=function(a){return null!=a?a.isEdge():!1};mxGraphModel.prototype.isConnectable=function(a){return null!=a?a.isConnectable():!1};
+mxGraphModel.prototype.getValue=function(a){return null!=a?a.getValue():null};mxGraphModel.prototype.setValue=function(a,b){this.execute(new mxValueChange(this,a,b));return b};mxGraphModel.prototype.valueForCellChanged=function(a,b){return a.valueChanged(b)};mxGraphModel.prototype.getGeometry=function(a){return null!=a?a.getGeometry():null};mxGraphModel.prototype.setGeometry=function(a,b){b!=this.getGeometry(a)&&this.execute(new mxGeometryChange(this,a,b));return b};
+mxGraphModel.prototype.geometryForCellChanged=function(a,b){var c=this.getGeometry(a);a.setGeometry(b);return c};mxGraphModel.prototype.getStyle=function(a){return null!=a?a.getStyle():null};mxGraphModel.prototype.setStyle=function(a,b){b!=this.getStyle(a)&&this.execute(new mxStyleChange(this,a,b));return b};mxGraphModel.prototype.styleForCellChanged=function(a,b){var c=this.getStyle(a);a.setStyle(b);return c};mxGraphModel.prototype.isCollapsed=function(a){return null!=a?a.isCollapsed():!1};
+mxGraphModel.prototype.setCollapsed=function(a,b){b!=this.isCollapsed(a)&&this.execute(new mxCollapseChange(this,a,b));return b};mxGraphModel.prototype.collapsedStateForCellChanged=function(a,b){var c=this.isCollapsed(a);a.setCollapsed(b);return c};mxGraphModel.prototype.isVisible=function(a){return null!=a?a.isVisible():!1};mxGraphModel.prototype.setVisible=function(a,b){b!=this.isVisible(a)&&this.execute(new mxVisibleChange(this,a,b));return b};
+mxGraphModel.prototype.visibleStateForCellChanged=function(a,b){var c=this.isVisible(a);a.setVisible(b);return c};mxGraphModel.prototype.execute=function(a){a.execute();this.beginUpdate();this.currentEdit.add(a);this.fireEvent(new mxEventObject(mxEvent.EXECUTE,"change",a));this.fireEvent(new mxEventObject(mxEvent.EXECUTED,"change",a));this.endUpdate()};mxGraphModel.prototype.beginUpdate=function(){this.updateLevel++;this.fireEvent(new mxEventObject(mxEvent.BEGIN_UPDATE));1==this.updateLevel&&this.fireEvent(new mxEventObject(mxEvent.START_EDIT))};
+mxGraphModel.prototype.endUpdate=function(){this.updateLevel--;0==this.updateLevel&&this.fireEvent(new mxEventObject(mxEvent.END_EDIT));if(!this.endingUpdate){this.endingUpdate=0==this.updateLevel;this.fireEvent(new mxEventObject(mxEvent.END_UPDATE,"edit",this.currentEdit));try{if(this.endingUpdate&&!this.currentEdit.isEmpty()){this.fireEvent(new mxEventObject(mxEvent.BEFORE_UNDO,"edit",this.currentEdit));var a=this.currentEdit;this.currentEdit=this.createUndoableEdit();a.notify();this.fireEvent(new mxEventObject(mxEvent.UNDO,
+"edit",a))}}finally{this.endingUpdate=!1}}};mxGraphModel.prototype.createUndoableEdit=function(a){var b=new mxUndoableEdit(this,null!=a?a:!0);b.notify=function(){b.source.fireEvent(new mxEventObject(mxEvent.CHANGE,"edit",b,"changes",b.changes));b.source.fireEvent(new mxEventObject(mxEvent.NOTIFY,"edit",b,"changes",b.changes))};return b};
+mxGraphModel.prototype.mergeChildren=function(a,b,c){c=null!=c?c:!0;this.beginUpdate();try{var d={};this.mergeChildrenImpl(a,b,c,d);for(var e in d){var f=d[e],g=this.getTerminal(f,!0);null!=g&&(g=d[mxCellPath.create(g)],this.setTerminal(f,g,!0));g=this.getTerminal(f,!1);null!=g&&(g=d[mxCellPath.create(g)],this.setTerminal(f,g,!1))}}finally{this.endUpdate()}};
+mxGraphModel.prototype.mergeChildrenImpl=function(a,b,c,d){this.beginUpdate();try{for(var e=a.getChildCount(),f=0;f<e;f++){var g=a.getChildAt(f);if("function"==typeof g.getId){var k=g.getId(),l=null==k||this.isEdge(g)&&c?null:this.getCell(k);if(null==l){var m=g.clone();m.setId(k);m.setTerminal(g.getTerminal(!0),!0);m.setTerminal(g.getTerminal(!1),!1);l=b.insert(m);this.cellAdded(l)}d[mxCellPath.create(g)]=l;this.mergeChildrenImpl(g,l,c,d)}}}finally{this.endUpdate()}};
+mxGraphModel.prototype.getParents=function(a){var b=[];if(null!=a)for(var c=new mxDictionary,d=0;d<a.length;d++){var e=this.getParent(a[d]);null==e||c.get(e)||(c.put(e,!0),b.push(e))}return b};mxGraphModel.prototype.cloneCell=function(a){return null!=a?this.cloneCells([a],!0)[0]:null};
+mxGraphModel.prototype.cloneCells=function(a,b,c){c=null!=c?c:{};for(var d=[],e=0;e<a.length;e++)null!=a[e]?d.push(this.cloneCellImpl(a[e],c,b)):d.push(null);for(e=0;e<d.length;e++)null!=d[e]&&this.restoreClone(d[e],a[e],c);return d};mxGraphModel.prototype.cloneCellImpl=function(a,b,c){var d=this.cellCloned(a);b[mxObjectIdentity.get(a)]=d;if(c){c=this.getChildCount(a);for(var e=0;e<c;e++){var f=this.cloneCellImpl(this.getChildAt(a,e),b,!0);d.insert(f)}}return d};
+mxGraphModel.prototype.cellCloned=function(a){return a.clone()};mxGraphModel.prototype.restoreClone=function(a,b,c){var d=this.getTerminal(b,!0);null!=d&&(d=c[mxObjectIdentity.get(d)],null!=d&&d.insertEdge(a,!0));d=this.getTerminal(b,!1);null!=d&&(d=c[mxObjectIdentity.get(d)],null!=d&&d.insertEdge(a,!1));for(var d=this.getChildCount(a),e=0;e<d;e++)this.restoreClone(this.getChildAt(a,e),this.getChildAt(b,e),c)};function mxRootChange(a,b){this.model=a;this.previous=this.root=b}
+mxRootChange.prototype.execute=function(){this.root=this.previous;this.previous=this.model.rootChanged(this.previous)};function mxChildChange(a,b,c,d){this.model=a;this.previous=this.parent=b;this.child=c;this.previousIndex=this.index=d}
+mxChildChange.prototype.execute=function(){if(null!=this.child){var a=this.model.getParent(this.child),b=null!=a?a.getIndex(this.child):0;null==this.previous&&this.connect(this.child,!1);a=this.model.parentForCellChanged(this.child,this.previous,this.previousIndex);null!=this.previous&&this.connect(this.child,!0);this.parent=this.previous;this.previous=a;this.index=this.previousIndex;this.previousIndex=b}};
+mxChildChange.prototype.connect=function(a,b){b=null!=b?b:!0;var c=a.getTerminal(!0),d=a.getTerminal(!1);null!=c&&(b?this.model.terminalForCellChanged(a,c,!0):this.model.terminalForCellChanged(a,null,!0));null!=d&&(b?this.model.terminalForCellChanged(a,d,!1):this.model.terminalForCellChanged(a,null,!1));a.setTerminal(c,!0);a.setTerminal(d,!1);c=this.model.getChildCount(a);for(d=0;d<c;d++)this.connect(this.model.getChildAt(a,d),b)};
+function mxTerminalChange(a,b,c,d){this.model=a;this.cell=b;this.previous=this.terminal=c;this.source=d}mxTerminalChange.prototype.execute=function(){null!=this.cell&&(this.terminal=this.previous,this.previous=this.model.terminalForCellChanged(this.cell,this.previous,this.source))};function mxValueChange(a,b,c){this.model=a;this.cell=b;this.previous=this.value=c}
+mxValueChange.prototype.execute=function(){null!=this.cell&&(this.value=this.previous,this.previous=this.model.valueForCellChanged(this.cell,this.previous))};function mxStyleChange(a,b,c){this.model=a;this.cell=b;this.previous=this.style=c}mxStyleChange.prototype.execute=function(){null!=this.cell&&(this.style=this.previous,this.previous=this.model.styleForCellChanged(this.cell,this.previous))};function mxGeometryChange(a,b,c){this.model=a;this.cell=b;this.previous=this.geometry=c}
+mxGeometryChange.prototype.execute=function(){null!=this.cell&&(this.geometry=this.previous,this.previous=this.model.geometryForCellChanged(this.cell,this.previous))};function mxCollapseChange(a,b,c){this.model=a;this.cell=b;this.previous=this.collapsed=c}mxCollapseChange.prototype.execute=function(){null!=this.cell&&(this.collapsed=this.previous,this.previous=this.model.collapsedStateForCellChanged(this.cell,this.previous))};
+function mxVisibleChange(a,b,c){this.model=a;this.cell=b;this.previous=this.visible=c}mxVisibleChange.prototype.execute=function(){null!=this.cell&&(this.visible=this.previous,this.previous=this.model.visibleStateForCellChanged(this.cell,this.previous))};function mxCellAttributeChange(a,b,c){this.cell=a;this.attribute=b;this.previous=this.value=c}
+mxCellAttributeChange.prototype.execute=function(){if(null!=this.cell){var a=this.cell.getAttribute(this.attribute);null==this.previous?this.cell.value.removeAttribute(this.attribute):this.cell.setAttribute(this.attribute,this.previous);this.previous=a}};function mxCell(a,b,c){this.value=a;this.setGeometry(b);this.setStyle(c);if(null!=this.onInit)this.onInit()}mxCell.prototype.id=null;mxCell.prototype.value=null;mxCell.prototype.geometry=null;mxCell.prototype.style=null;mxCell.prototype.vertex=!1;
+mxCell.prototype.edge=!1;mxCell.prototype.connectable=!0;mxCell.prototype.visible=!0;mxCell.prototype.collapsed=!1;mxCell.prototype.parent=null;mxCell.prototype.source=null;mxCell.prototype.target=null;mxCell.prototype.children=null;mxCell.prototype.edges=null;mxCell.prototype.mxTransient="id value parent source target children edges".split(" ");mxCell.prototype.getId=function(){return this.id};mxCell.prototype.setId=function(a){this.id=a};mxCell.prototype.getValue=function(){return this.value};
+mxCell.prototype.setValue=function(a){this.value=a};mxCell.prototype.valueChanged=function(a){var b=this.getValue();this.setValue(a);return b};mxCell.prototype.getGeometry=function(){return this.geometry};mxCell.prototype.setGeometry=function(a){this.geometry=a};mxCell.prototype.getStyle=function(){return this.style};mxCell.prototype.setStyle=function(a){this.style=a};mxCell.prototype.isVertex=function(){return 0!=this.vertex};mxCell.prototype.setVertex=function(a){this.vertex=a};
+mxCell.prototype.isEdge=function(){return 0!=this.edge};mxCell.prototype.setEdge=function(a){this.edge=a};mxCell.prototype.isConnectable=function(){return 0!=this.connectable};mxCell.prototype.setConnectable=function(a){this.connectable=a};mxCell.prototype.isVisible=function(){return 0!=this.visible};mxCell.prototype.setVisible=function(a){this.visible=a};mxCell.prototype.isCollapsed=function(){return 0!=this.collapsed};mxCell.prototype.setCollapsed=function(a){this.collapsed=a};
+mxCell.prototype.getParent=function(){return this.parent};mxCell.prototype.setParent=function(a){this.parent=a};mxCell.prototype.getTerminal=function(a){return a?this.source:this.target};mxCell.prototype.setTerminal=function(a,b){b?this.source=a:this.target=a;return a};mxCell.prototype.getChildCount=function(){return null==this.children?0:this.children.length};mxCell.prototype.getIndex=function(a){return mxUtils.indexOf(this.children,a)};
+mxCell.prototype.getChildAt=function(a){return null==this.children?null:this.children[a]};mxCell.prototype.insert=function(a,b){null!=a&&(null==b&&(b=this.getChildCount(),a.getParent()==this&&b--),a.removeFromParent(),a.setParent(this),null==this.children?(this.children=[],this.children.push(a)):this.children.splice(b,0,a));return a};mxCell.prototype.remove=function(a){var b=null;null!=this.children&&0<=a&&(b=this.getChildAt(a),null!=b&&(this.children.splice(a,1),b.setParent(null)));return b};
+mxCell.prototype.removeFromParent=function(){if(null!=this.parent){var a=this.parent.getIndex(this);this.parent.remove(a)}};mxCell.prototype.getEdgeCount=function(){return null==this.edges?0:this.edges.length};mxCell.prototype.getEdgeIndex=function(a){return mxUtils.indexOf(this.edges,a)};mxCell.prototype.getEdgeAt=function(a){return null==this.edges?null:this.edges[a]};
+mxCell.prototype.insertEdge=function(a,b){null!=a&&(a.removeFromTerminal(b),a.setTerminal(this,b),null==this.edges||a.getTerminal(!b)!=this||0>mxUtils.indexOf(this.edges,a))&&(null==this.edges&&(this.edges=[]),this.edges.push(a));return a};mxCell.prototype.removeEdge=function(a,b){if(null!=a){if(a.getTerminal(!b)!=this&&null!=this.edges){var c=this.getEdgeIndex(a);0<=c&&this.edges.splice(c,1)}a.setTerminal(null,b)}return a};
+mxCell.prototype.removeFromTerminal=function(a){var b=this.getTerminal(a);null!=b&&b.removeEdge(this,a)};mxCell.prototype.hasAttribute=function(a){var b=this.getValue();return null!=b&&b.nodeType==mxConstants.NODETYPE_ELEMENT&&b.hasAttribute?b.hasAttribute(a):null!=b.getAttribute(a)};mxCell.prototype.getAttribute=function(a,b){var c=this.getValue();return(null!=c&&c.nodeType==mxConstants.NODETYPE_ELEMENT?c.getAttribute(a):null)||b};
+mxCell.prototype.setAttribute=function(a,b){var c=this.getValue();null!=c&&c.nodeType==mxConstants.NODETYPE_ELEMENT&&c.setAttribute(a,b)};mxCell.prototype.clone=function(){var a=mxUtils.clone(this,this.mxTransient);a.setValue(this.cloneValue());return a};mxCell.prototype.cloneValue=function(){var a=this.getValue();null!=a&&("function"==typeof a.clone?a=a.clone():isNaN(a.nodeType)||(a=a.cloneNode(!0)));return a};function mxGeometry(a,b,c,d){mxRectangle.call(this,a,b,c,d)}mxGeometry.prototype=new mxRectangle;
+mxGeometry.prototype.constructor=mxGeometry;mxGeometry.prototype.TRANSLATE_CONTROL_POINTS=!0;mxGeometry.prototype.alternateBounds=null;mxGeometry.prototype.sourcePoint=null;mxGeometry.prototype.targetPoint=null;mxGeometry.prototype.points=null;mxGeometry.prototype.offset=null;mxGeometry.prototype.relative=!1;
+mxGeometry.prototype.swap=function(){if(null!=this.alternateBounds){var a=new mxRectangle(this.x,this.y,this.width,this.height);this.x=this.alternateBounds.x;this.y=this.alternateBounds.y;this.width=this.alternateBounds.width;this.height=this.alternateBounds.height;this.alternateBounds=a}};mxGeometry.prototype.getTerminalPoint=function(a){return a?this.sourcePoint:this.targetPoint};mxGeometry.prototype.setTerminalPoint=function(a,b){b?this.sourcePoint=a:this.targetPoint=a;return a};
+mxGeometry.prototype.rotate=function(a,b){var c=mxUtils.toRadians(a),d=Math.cos(c),c=Math.sin(c);if(!this.relative){var e=new mxPoint(this.getCenterX(),this.getCenterY()),e=mxUtils.getRotatedPoint(e,d,c,b);this.x=Math.round(e.x-this.width/2);this.y=Math.round(e.y-this.height/2)}null!=this.sourcePoint&&(e=mxUtils.getRotatedPoint(this.sourcePoint,d,c,b),this.sourcePoint.x=Math.round(e.x),this.sourcePoint.y=Math.round(e.y));null!=this.targetPoint&&(e=mxUtils.getRotatedPoint(this.targetPoint,d,c,b),this.targetPoint.x=
+Math.round(e.x),this.targetPoint.y=Math.round(e.y));if(null!=this.points)for(var f=0;f<this.points.length;f++)null!=this.points[f]&&(e=mxUtils.getRotatedPoint(this.points[f],d,c,b),this.points[f].x=Math.round(e.x),this.points[f].y=Math.round(e.y))};
+mxGeometry.prototype.translate=function(a,b){a=parseFloat(a);b=parseFloat(b);this.relative||(this.x=parseFloat(this.x)+a,this.y=parseFloat(this.y)+b);null!=this.sourcePoint&&(this.sourcePoint.x=parseFloat(this.sourcePoint.x)+a,this.sourcePoint.y=parseFloat(this.sourcePoint.y)+b);null!=this.targetPoint&&(this.targetPoint.x=parseFloat(this.targetPoint.x)+a,this.targetPoint.y=parseFloat(this.targetPoint.y)+b);if(this.TRANSLATE_CONTROL_POINTS&&null!=this.points)for(var c=0;c<this.points.length;c++)null!=
+this.points[c]&&(this.points[c].x=parseFloat(this.points[c].x)+a,this.points[c].y=parseFloat(this.points[c].y)+b)};
+mxGeometry.prototype.scale=function(a,b,c){a=parseFloat(a);b=parseFloat(b);null!=this.sourcePoint&&(this.sourcePoint.x=parseFloat(this.sourcePoint.x)*a,this.sourcePoint.y=parseFloat(this.sourcePoint.y)*b);null!=this.targetPoint&&(this.targetPoint.x=parseFloat(this.targetPoint.x)*a,this.targetPoint.y=parseFloat(this.targetPoint.y)*b);if(null!=this.points)for(var d=0;d<this.points.length;d++)null!=this.points[d]&&(this.points[d].x=parseFloat(this.points[d].x)*a,this.points[d].y=parseFloat(this.points[d].y)*
+b);this.relative||(this.x=parseFloat(this.x)*a,this.y=parseFloat(this.y)*b,c&&(b=a=Math.min(a,b)),this.width=parseFloat(this.width)*a,this.height=parseFloat(this.height)*b)};
+mxGeometry.prototype.equals=function(a){return mxRectangle.prototype.equals.apply(this,arguments)&&this.relative==a.relative&&(null==this.sourcePoint&&null==a.sourcePoint||null!=this.sourcePoint&&this.sourcePoint.equals(a.sourcePoint))&&(null==this.targetPoint&&null==a.targetPoint||null!=this.targetPoint&&this.targetPoint.equals(a.targetPoint))&&(null==this.points&&null==a.points||null!=this.points&&mxUtils.equalPoints(this.points,a.points))&&(null==this.alternateBounds&&null==a.alternateBounds||
+null!=this.alternateBounds&&this.alternateBounds.equals(a.alternateBounds))&&(null==this.offset&&null==a.offset||null!=this.offset&&this.offset.equals(a.offset))};
+var mxCellPath={PATH_SEPARATOR:".",create:function(a){var b="";if(null!=a)for(var c=a.getParent();null!=c;)b=c.getIndex(a)+mxCellPath.PATH_SEPARATOR+b,a=c,c=a.getParent();a=b.length;1<a&&(b=b.substring(0,a-1));return b},getParentPath:function(a){if(null!=a){var b=a.lastIndexOf(mxCellPath.PATH_SEPARATOR);if(0<=b)return a.substring(0,b);if(0<a.length)return""}return null},resolve:function(a,b){var c=a;if(null!=b)for(var d=b.split(mxCellPath.PATH_SEPARATOR),e=0;e<d.length;e++)c=c.getChildAt(parseInt(d[e]));
+return c},compare:function(a,b){for(var c=Math.min(a.length,b.length),d=0,e=0;e<c;e++)if(a[e]!=b[e]){0==a[e].length||0==b[e].length?d=a[e]==b[e]?0:a[e]>b[e]?1:-1:(c=parseInt(a[e]),e=parseInt(b[e]),d=c==e?0:c>e?1:-1);break}0==d&&(c=a.length,e=b.length,c!=e&&(d=c>e?1:-1));return d}},mxPerimeter={RectanglePerimeter:function(a,b,c,d){b=a.getCenterX();var e=a.getCenterY(),f=Math.atan2(c.y-e,c.x-b),g=new mxPoint(0,0),k=Math.PI,l=Math.PI/2-f,m=Math.atan2(a.height,a.width);f<-k+m||f>k-m?(g.x=a.x,g.y=e-a.width*
+Math.tan(f)/2):f<-m?(g.y=a.y,g.x=b-a.height*Math.tan(l)/2):f<m?(g.x=a.x+a.width,g.y=e+a.width*Math.tan(f)/2):(g.y=a.y+a.height,g.x=b+a.height*Math.tan(l)/2);d&&(c.x>=a.x&&c.x<=a.x+a.width?g.x=c.x:c.y>=a.y&&c.y<=a.y+a.height&&(g.y=c.y),c.x<a.x?g.x=a.x:c.x>a.x+a.width&&(g.x=a.x+a.width),c.y<a.y?g.y=a.y:c.y>a.y+a.height&&(g.y=a.y+a.height));return g},EllipsePerimeter:function(a,b,c,d){var e=a.x,f=a.y,g=a.width/2,k=a.height/2,l=e+g,m=f+k;b=c.x;c=c.y;var n=parseInt(b-l),p=parseInt(c-m);if(0==n&&0!=p)return new mxPoint(l,
+m+k*p/Math.abs(p));if(0==n&&0==p)return new mxPoint(b,c);if(d){if(c>=f&&c<=f+a.height)return a=c-m,a=Math.sqrt(g*g*(1-a*a/(k*k)))||0,b<=e&&(a=-a),new mxPoint(l+a,c);if(b>=e&&b<=e+a.width)return a=b-l,a=Math.sqrt(k*k*(1-a*a/(g*g)))||0,c<=f&&(a=-a),new mxPoint(b,m+a)}e=p/n;m-=e*l;f=g*g*e*e+k*k;a=-2*l*f;k=Math.sqrt(a*a-4*f*(g*g*e*e*l*l+k*k*l*l-g*g*k*k));g=(-a+k)/(2*f);l=(-a-k)/(2*f);k=e*g+m;m=e*l+m;Math.sqrt(Math.pow(g-b,2)+Math.pow(k-c,2))<Math.sqrt(Math.pow(l-b,2)+Math.pow(m-c,2))?(b=g,c=k):(b=l,c=
+m);return new mxPoint(b,c)},RhombusPerimeter:function(a,b,c,d){b=a.x;var e=a.y,f=a.width;a=a.height;var g=b+f/2,k=e+a/2,l=c.x;c=c.y;if(g==l)return k>c?new mxPoint(g,e):new mxPoint(g,e+a);if(k==c)return g>l?new mxPoint(b,k):new mxPoint(b+f,k);var m=g,n=k;d&&(l>=b&&l<=b+f?m=l:c>=e&&c<=e+a&&(n=c));return l<g?c<k?mxUtils.intersection(l,c,m,n,g,e,b,k):mxUtils.intersection(l,c,m,n,g,e+a,b,k):c<k?mxUtils.intersection(l,c,m,n,g,e,b+f,k):mxUtils.intersection(l,c,m,n,g,e+a,b+f,k)},TrianglePerimeter:function(a,
+b,c,d){b=null!=b?b.style[mxConstants.STYLE_DIRECTION]:null;var e=b==mxConstants.DIRECTION_NORTH||b==mxConstants.DIRECTION_SOUTH,f=a.x,g=a.y,k=a.width,l=a.height;a=f+k/2;var m=g+l/2,n=new mxPoint(f,g),p=new mxPoint(f+k,m),q=new mxPoint(f,g+l);b==mxConstants.DIRECTION_NORTH?(n=q,p=new mxPoint(a,g),q=new mxPoint(f+k,g+l)):b==mxConstants.DIRECTION_SOUTH?(p=new mxPoint(a,g+l),q=new mxPoint(f+k,g)):b==mxConstants.DIRECTION_WEST&&(n=new mxPoint(f+k,g),p=new mxPoint(f,m),q=new mxPoint(f+k,g+l));var r=c.x-
+a,t=c.y-m,r=e?Math.atan2(r,t):Math.atan2(t,r),t=e?Math.atan2(k,l):Math.atan2(l,k);(b==mxConstants.DIRECTION_NORTH||b==mxConstants.DIRECTION_WEST?r>-t&&r<t:r<-Math.PI+t||r>Math.PI-t)?c=d&&(e&&c.x>=n.x&&c.x<=q.x||!e&&c.y>=n.y&&c.y<=q.y)?e?new mxPoint(c.x,n.y):new mxPoint(n.x,c.y):b==mxConstants.DIRECTION_NORTH?new mxPoint(f+k/2+l*Math.tan(r)/2,g+l):b==mxConstants.DIRECTION_SOUTH?new mxPoint(f+k/2-l*Math.tan(r)/2,g):b==mxConstants.DIRECTION_WEST?new mxPoint(f+k,g+l/2+k*Math.tan(r)/2):new mxPoint(f,g+
+l/2-k*Math.tan(r)/2):(d&&(d=new mxPoint(a,m),c.y>=g&&c.y<=g+l?(d.x=e?a:b==mxConstants.DIRECTION_WEST?f+k:f,d.y=c.y):c.x>=f&&c.x<=f+k&&(d.x=c.x,d.y=e?b==mxConstants.DIRECTION_NORTH?g+l:g:m),a=d.x,m=d.y),c=e&&c.x<=f+k/2||!e&&c.y<=g+l/2?mxUtils.intersection(c.x,c.y,a,m,n.x,n.y,p.x,p.y):mxUtils.intersection(c.x,c.y,a,m,p.x,p.y,q.x,q.y));null==c&&(c=new mxPoint(a,m));return c},HexagonPerimeter:function(a,b,c,d){var e=a.x,f=a.y,g=a.width,k=a.height,l=a.getCenterX();a=a.getCenterY();var m=c.x,n=c.y,p=-Math.atan2(n-
+a,m-l),q=Math.PI,r=Math.PI/2;new mxPoint(l,a);b=null!=b?mxUtils.getValue(b.style,mxConstants.STYLE_DIRECTION,mxConstants.DIRECTION_EAST):mxConstants.DIRECTION_EAST;var t=b==mxConstants.DIRECTION_NORTH||b==mxConstants.DIRECTION_SOUTH;b=new mxPoint;var u=new mxPoint;if(m<e&&n<f||m<e&&n>f+k||m>e+g&&n<f||m>e+g&&n>f+k)d=!1;if(d){if(t){if(m==l){if(n<=f)return new mxPoint(l,f);if(n>=f+k)return new mxPoint(l,f+k)}else if(m<e){if(n==f+k/4)return new mxPoint(e,f+k/4);if(n==f+3*k/4)return new mxPoint(e,f+3*
+k/4)}else if(m>e+g){if(n==f+k/4)return new mxPoint(e+g,f+k/4);if(n==f+3*k/4)return new mxPoint(e+g,f+3*k/4)}else if(m==e){if(n<a)return new mxPoint(e,f+k/4);if(n>a)return new mxPoint(e,f+3*k/4)}else if(m==e+g){if(n<a)return new mxPoint(e+g,f+k/4);if(n>a)return new mxPoint(e+g,f+3*k/4)}if(n==f)return new mxPoint(l,f);if(n==f+k)return new mxPoint(l,f+k);m<l?n>f+k/4&&n<f+3*k/4?(b=new mxPoint(e,f),u=new mxPoint(e,f+k)):n<f+k/4?(b=new mxPoint(e-Math.floor(.5*g),f+Math.floor(.5*k)),u=new mxPoint(e+g,f-
+Math.floor(.25*k))):n>f+3*k/4&&(b=new mxPoint(e-Math.floor(.5*g),f+Math.floor(.5*k)),u=new mxPoint(e+g,f+Math.floor(1.25*k))):m>l&&(n>f+k/4&&n<f+3*k/4?(b=new mxPoint(e+g,f),u=new mxPoint(e+g,f+k)):n<f+k/4?(b=new mxPoint(e,f-Math.floor(.25*k)),u=new mxPoint(e+Math.floor(1.5*g),f+Math.floor(.5*k))):n>f+3*k/4&&(b=new mxPoint(e+Math.floor(1.5*g),f+Math.floor(.5*k)),u=new mxPoint(e,f+Math.floor(1.25*k))))}else{if(n==a){if(m<=e)return new mxPoint(e,f+k/2);if(m>=e+g)return new mxPoint(e+g,f+k/2)}else if(n<
+f){if(m==e+g/4)return new mxPoint(e+g/4,f);if(m==e+3*g/4)return new mxPoint(e+3*g/4,f)}else if(n>f+k){if(m==e+g/4)return new mxPoint(e+g/4,f+k);if(m==e+3*g/4)return new mxPoint(e+3*g/4,f+k)}else if(n==f){if(m<l)return new mxPoint(e+g/4,f);if(m>l)return new mxPoint(e+3*g/4,f)}else if(n==f+k){if(m<l)return new mxPoint(e+g/4,f+k);if(n>a)return new mxPoint(e+3*g/4,f+k)}if(m==e)return new mxPoint(e,a);if(m==e+g)return new mxPoint(e+g,a);n<a?m>e+g/4&&m<e+3*g/4?(b=new mxPoint(e,f),u=new mxPoint(e+g,f)):
+m<e+g/4?(b=new mxPoint(e-Math.floor(.25*g),f+k),u=new mxPoint(e+Math.floor(.5*g),f-Math.floor(.5*k))):m>e+3*g/4&&(b=new mxPoint(e+Math.floor(.5*g),f-Math.floor(.5*k)),u=new mxPoint(e+Math.floor(1.25*g),f+k)):n>a&&(m>e+g/4&&m<e+3*g/4?(b=new mxPoint(e,f+k),u=new mxPoint(e+g,f+k)):m<e+g/4?(b=new mxPoint(e-Math.floor(.25*g),f),u=new mxPoint(e+Math.floor(.5*g),f+Math.floor(1.5*k))):m>e+3*g/4&&(b=new mxPoint(e+Math.floor(.5*g),f+Math.floor(1.5*k)),u=new mxPoint(e+Math.floor(1.25*g),f)))}d=l;p=a;m>=e&&m<=
+e+g?(d=m,p=n<a?f+k:f):n>=f&&n<=f+k&&(p=n,d=m<l?e+g:e);c=mxUtils.intersection(d,p,c.x,c.y,b.x,b.y,u.x,u.y)}else{if(t){m=Math.atan2(k/4,g/2);if(p==m)return new mxPoint(e+g,f+Math.floor(.25*k));if(p==r)return new mxPoint(e+Math.floor(.5*g),f);if(p==q-m)return new mxPoint(e,f+Math.floor(.25*k));if(p==-m)return new mxPoint(e+g,f+Math.floor(.75*k));if(p==-r)return new mxPoint(e+Math.floor(.5*g),f+k);if(p==-q+m)return new mxPoint(e,f+Math.floor(.75*k));p<m&&p>-m?(b=new mxPoint(e+g,f),u=new mxPoint(e+g,f+
+k)):p>m&&p<r?(b=new mxPoint(e,f-Math.floor(.25*k)),u=new mxPoint(e+Math.floor(1.5*g),f+Math.floor(.5*k))):p>r&&p<q-m?(b=new mxPoint(e-Math.floor(.5*g),f+Math.floor(.5*k)),u=new mxPoint(e+g,f-Math.floor(.25*k))):p>q-m&&p<=q||p<-q+m&&p>=-q?(b=new mxPoint(e,f),u=new mxPoint(e,f+k)):p<-m&&p>-r?(b=new mxPoint(e+Math.floor(1.5*g),f+Math.floor(.5*k)),u=new mxPoint(e,f+Math.floor(1.25*k))):p<-r&&p>-q+m&&(b=new mxPoint(e-Math.floor(.5*g),f+Math.floor(.5*k)),u=new mxPoint(e+g,f+Math.floor(1.25*k)))}else{m=
+Math.atan2(k/2,g/4);if(p==m)return new mxPoint(e+Math.floor(.75*g),f);if(p==q-m)return new mxPoint(e+Math.floor(.25*g),f);if(p==q||p==-q)return new mxPoint(e,f+Math.floor(.5*k));if(0==p)return new mxPoint(e+g,f+Math.floor(.5*k));if(p==-m)return new mxPoint(e+Math.floor(.75*g),f+k);if(p==-q+m)return new mxPoint(e+Math.floor(.25*g),f+k);0<p&&p<m?(b=new mxPoint(e+Math.floor(.5*g),f-Math.floor(.5*k)),u=new mxPoint(e+Math.floor(1.25*g),f+k)):p>m&&p<q-m?(b=new mxPoint(e,f),u=new mxPoint(e+g,f)):p>q-m&&
+p<q?(b=new mxPoint(e-Math.floor(.25*g),f+k),u=new mxPoint(e+Math.floor(.5*g),f-Math.floor(.5*k))):0>p&&p>-m?(b=new mxPoint(e+Math.floor(.5*g),f+Math.floor(1.5*k)),u=new mxPoint(e+Math.floor(1.25*g),f)):p<-m&&p>-q+m?(b=new mxPoint(e,f+k),u=new mxPoint(e+g,f+k)):p<-q+m&&p>-q&&(b=new mxPoint(e-Math.floor(.25*g),f),u=new mxPoint(e+Math.floor(.5*g),f+Math.floor(1.5*k)))}c=mxUtils.intersection(l,a,c.x,c.y,b.x,b.y,u.x,u.y)}return null==c?new mxPoint(l,a):c}};
+function mxPrintPreview(a,b,c,d,e,f,g,k,l){this.graph=a;this.scale=null!=b?b:1/a.pageScale;this.border=null!=d?d:0;this.pageFormat=mxRectangle.fromRectangle(null!=c?c:a.pageFormat);this.title=null!=k?k:"Printer-friendly version";this.x0=null!=e?e:0;this.y0=null!=f?f:0;this.borderColor=g;this.pageSelector=null!=l?l:!0}mxPrintPreview.prototype.graph=null;mxPrintPreview.prototype.pageFormat=null;mxPrintPreview.prototype.scale=null;mxPrintPreview.prototype.border=0;
+mxPrintPreview.prototype.marginTop=0;mxPrintPreview.prototype.marginBottom=0;mxPrintPreview.prototype.x0=0;mxPrintPreview.prototype.y0=0;mxPrintPreview.prototype.autoOrigin=!0;mxPrintPreview.prototype.printOverlays=!1;mxPrintPreview.prototype.printControls=!1;mxPrintPreview.prototype.printBackgroundImage=!1;mxPrintPreview.prototype.backgroundColor="#ffffff";mxPrintPreview.prototype.borderColor=null;mxPrintPreview.prototype.title=null;mxPrintPreview.prototype.pageSelector=null;
+mxPrintPreview.prototype.wnd=null;mxPrintPreview.prototype.targetWindow=null;mxPrintPreview.prototype.pageCount=0;mxPrintPreview.prototype.clipping=!0;mxPrintPreview.prototype.getWindow=function(){return this.wnd};
+mxPrintPreview.prototype.getDoctype=function(){var a="";5==document.documentMode?a='<meta http-equiv="X-UA-Compatible" content="IE=5">':8==document.documentMode?a='<meta http-equiv="X-UA-Compatible" content="IE=8">':8<document.documentMode&&(a='\x3c!--[if IE]><meta http-equiv="X-UA-Compatible" content="IE=edge"><![endif]--\x3e');return a};mxPrintPreview.prototype.appendGraph=function(a,b,c,d,e,f){this.graph=a;this.scale=null!=b?b:1/a.pageScale;this.x0=c;this.y0=d;this.open(null,null,e,f)};
+mxPrintPreview.prototype.open=function(a,b,c,d){var e=this.graph.cellRenderer.initializeOverlay,f=null;try{this.printOverlays&&(this.graph.cellRenderer.initializeOverlay=function(a,b){b.init(a.view.getDrawPane())});this.printControls&&(this.graph.cellRenderer.initControl=function(a,b,c,d){b.dialect=a.view.graph.dialect;b.init(a.view.getDrawPane())});this.wnd=null!=b?b:this.wnd;var g=!1;null==this.wnd&&(g=!0,this.wnd=window.open());var k=this.wnd.document;if(g){var l=this.getDoctype();null!=l&&0<l.length&&
+k.writeln(l);mxClient.IS_VML?k.writeln('<html xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office">'):("CSS1Compat"===document.compatMode&&k.writeln("<!DOCTYPE html>"),k.writeln("<html>"));k.writeln("<head>");this.writeHead(k,a);k.writeln("</head>");k.writeln('<body class="mxPage">')}var m=this.graph.getGraphBounds().clone(),n=this.graph.getView().getScale(),p=n/this.scale,q=this.graph.getView().getTranslate();this.autoOrigin||(this.x0-=q.x*this.scale,this.y0-=
+q.y*this.scale,m.width+=m.x,m.height+=m.y,m.x=0,this.border=m.y=0);var r=this.pageFormat.width-2*this.border,t=this.pageFormat.height-2*this.border;this.pageFormat.height+=this.marginTop+this.marginBottom;m.width/=p;m.height/=p;var u=Math.max(1,Math.ceil((m.width+this.x0)/r)),x=Math.max(1,Math.ceil((m.height+this.y0)/t));this.pageCount=u*x;var y=mxUtils.bind(this,function(){if(this.pageSelector&&(1<x||1<u)){var a=this.createPageSelector(x,u);k.body.appendChild(a);if(mxClient.IS_IE&&null==k.documentMode||
+5==k.documentMode||8==k.documentMode||7==k.documentMode){a.style.position="absolute";var b=function(){a.style.top=(k.body.scrollTop||k.documentElement.scrollTop)+10+"px"};mxEvent.addListener(this.wnd,"scroll",function(a){b()});mxEvent.addListener(this.wnd,"resize",function(a){b()})}}}),A=mxUtils.bind(this,function(a,b){null!=this.borderColor&&(a.style.borderColor=this.borderColor,a.style.borderStyle="solid",a.style.borderWidth="1px");a.style.background=this.backgroundColor;if(c||b)a.style.pageBreakAfter=
+"always";g&&(mxClient.IS_IE||11<=document.documentMode||mxClient.IS_EDGE)?(k.writeln(a.outerHTML),a.parentNode.removeChild(a)):(a.parentNode.removeChild(a),k.body.appendChild(a));(c||b)&&this.addPageBreak(k)}),z=this.getCoverPages(this.pageFormat.width,this.pageFormat.height);if(null!=z)for(var v=0;v<z.length;v++)A(z[v],!0);for(var B=this.getAppendices(this.pageFormat.width,this.pageFormat.height),v=0;v<x;v++){var C=v*t/this.scale-this.y0/this.scale+(m.y-q.y*n)/n;for(a=0;a<u;a++){if(null==this.wnd)return null;
+var F=a*r/this.scale-this.x0/this.scale+(m.x-q.x*n)/n,D=v*u+a+1,I=new mxRectangle(F,C,r,t),f=this.renderPage(this.pageFormat.width,this.pageFormat.height,0,0,mxUtils.bind(this,function(a){this.addGraphFragment(-F,-C,this.scale,D,a,I);this.printBackgroundImage&&this.insertBackgroundImage(a,-F,-C)}),D);f.setAttribute("id","mxPage-"+D);A(f,null!=B||v<x-1||a<u-1)}}if(null!=B)for(v=0;v<B.length;v++)A(B[v],v<B.length-1);g&&!d&&(this.closeDocument(),y());this.wnd.focus()}catch(E){null!=f&&null!=f.parentNode&&
+f.parentNode.removeChild(f)}finally{this.graph.cellRenderer.initializeOverlay=e}return this.wnd};mxPrintPreview.prototype.addPageBreak=function(a){var b=a.createElement("hr");b.className="mxPageBreak";a.body.appendChild(b)};mxPrintPreview.prototype.closeDocument=function(){if(null!=this.wnd&&null!=this.wnd.document){var a=this.wnd.document;this.writePostfix(a);a.writeln("</body>");a.writeln("</html>");a.close();mxEvent.release(a.body)}};
+mxPrintPreview.prototype.writeHead=function(a,b){null!=this.title&&a.writeln("<title>"+this.title+"</title>");mxClient.IS_VML&&a.writeln('<style type="text/css">v\\:*{behavior:url(#default#VML)}o\\:*{behavior:url(#default#VML)}</style>');mxClient.link("stylesheet",mxClient.basePath+"/css/common.css",a);a.writeln('<style type="text/css">');a.writeln("@media print {");a.writeln(" table.mxPageSelector { display: none; }");a.writeln(" hr.mxPageBreak { display: none; }");a.writeln("}");a.writeln("@media screen {");
+a.writeln(" table.mxPageSelector { position: fixed; right: 10px; top: 10px;font-family: Arial; font-size:10pt; border: solid 1px darkgray;background: white; border-collapse:collapse; }");a.writeln(" table.mxPageSelector td { border: solid 1px gray; padding:4px; }");a.writeln(" body.mxPage { background: gray; }");a.writeln("}");null!=b&&a.writeln(b);a.writeln("</style>")};mxPrintPreview.prototype.writePostfix=function(a){};
+mxPrintPreview.prototype.createPageSelector=function(a,b){var c=this.wnd.document,d=c.createElement("table");d.className="mxPageSelector";d.setAttribute("border","0");for(var e=c.createElement("tbody"),f=0;f<a;f++){for(var g=c.createElement("tr"),k=0;k<b;k++){var l=f*b+k+1,m=c.createElement("td"),n=c.createElement("a");n.setAttribute("href","#mxPage-"+l);!mxClient.IS_NS||mxClient.IS_SF||mxClient.IS_GC||n.setAttribute("onclick","var page = document.getElementById('mxPage-"+l+"');page.scrollIntoView(true);event.preventDefault();");
+mxUtils.write(n,l,c);m.appendChild(n);g.appendChild(m)}e.appendChild(g)}d.appendChild(e);return d};
+mxPrintPreview.prototype.renderPage=function(a,b,c,d,e,f){f=this.wnd.document;var g=document.createElement("div"),k=null;try{if(0!=c||0!=d){g.style.position="relative";g.style.width=a+"px";g.style.height=b+"px";g.style.pageBreakInside="avoid";var l=document.createElement("div");l.style.position="relative";l.style.top=this.border+"px";l.style.left=this.border+"px";l.style.width=a-2*this.border+"px";l.style.height=b-2*this.border+"px";l.style.overflow="hidden";var m=document.createElement("div");m.style.position=
+"relative";m.style.marginLeft=c+"px";m.style.marginTop=d+"px";8==f.documentMode&&(l.style.position="absolute",m.style.position="absolute");10==f.documentMode&&(m.style.width="100%",m.style.height="100%");l.appendChild(m);g.appendChild(l);document.body.appendChild(g);k=m}else g.style.width=a+"px",g.style.height=b+"px",g.style.overflow="hidden",g.style.pageBreakInside="avoid",8==f.documentMode&&(g.style.position="relative"),l=document.createElement("div"),l.style.width=a-2*this.border+"px",l.style.height=
+b-2*this.border+"px",l.style.overflow="hidden",!mxClient.IS_IE||null!=f.documentMode&&5!=f.documentMode&&8!=f.documentMode&&7!=f.documentMode?(l.style.top=this.border+"px",l.style.left=this.border+"px"):(l.style.marginTop=this.border+"px",l.style.marginLeft=this.border+"px"),this.graph.dialect==mxConstants.DIALECT_VML&&(l.style.position="absolute"),g.appendChild(l),document.body.appendChild(g),k=l}catch(n){throw g.parentNode.removeChild(g),n;}e(k);return g};
+mxPrintPreview.prototype.getRoot=function(){var a=this.graph.view.currentRoot;null==a&&(a=this.graph.getModel().getRoot());return a};
+mxPrintPreview.prototype.addGraphFragment=function(a,b,c,d,e,f){var g=this.graph.getView();d=this.graph.container;this.graph.container=e;var k=g.getCanvas(),l=g.getBackgroundPane(),m=g.getDrawPane(),n=g.getOverlayPane();this.graph.dialect==mxConstants.DIALECT_SVG?g.createSvg():this.graph.dialect==mxConstants.DIALECT_VML?g.createVml():g.createHtml();var p=g.isEventsEnabled();g.setEventsEnabled(!1);var q=this.graph.isEnabled();this.graph.setEnabled(!1);var r=g.getTranslate();g.translate=new mxPoint(a,
+b);var t=this.graph.cellRenderer.redraw,u=g.states;a=g.scale;if(this.clipping){var x=new mxRectangle((f.x+r.x)*a,(f.y+r.y)*a,f.width*a/c,f.height*a/c);this.graph.cellRenderer.redraw=function(a,b,c){if(null!=a){var d=u.get(a.cell);if(null!=d&&(d=g.getBoundingBox(d,!1),null!=d&&!mxUtils.intersects(x,d)))return}t.apply(this,arguments)}}a=null;try{var y=[this.getRoot()];a=new mxTemporaryCellStates(g,c,y,null,mxUtils.bind(this,function(a){return this.getLinkForCellState(a)}))}finally{if(mxClient.IS_IE)g.overlayPane.innerHTML=
+"",g.canvas.style.overflow="hidden",g.canvas.style.position="relative",g.canvas.style.top=this.marginTop+"px",g.canvas.style.width=f.width+"px",g.canvas.style.height=f.height+"px";else for(c=e.firstChild;null!=c;)y=c.nextSibling,b=c.nodeName.toLowerCase(),"svg"==b?(c.style.overflow="hidden",c.style.position="relative",c.style.top=this.marginTop+"px",c.setAttribute("width",f.width),c.setAttribute("height",f.height),c.style.width="",c.style.height=""):"default"!=c.style.cursor&&"div"!=b&&c.parentNode.removeChild(c),
+c=y;this.printBackgroundImage&&(e=e.getElementsByTagName("svg"),0<e.length&&(e[0].style.position="absolute"));g.overlayPane.parentNode.removeChild(g.overlayPane);this.graph.setEnabled(q);this.graph.container=d;this.graph.cellRenderer.redraw=t;g.canvas=k;g.backgroundPane=l;g.drawPane=m;g.overlayPane=n;g.translate=r;a.destroy();g.setEventsEnabled(p)}};mxPrintPreview.prototype.getLinkForCellState=function(a){return this.graph.getLinkForCell(a.cell)};
+mxPrintPreview.prototype.insertBackgroundImage=function(a,b,c){var d=this.graph.backgroundImage;if(null!=d){var e=document.createElement("img");e.style.position="absolute";e.style.marginLeft=Math.round(b*this.scale)+"px";e.style.marginTop=Math.round(c*this.scale)+"px";e.setAttribute("width",Math.round(this.scale*d.width));e.setAttribute("height",Math.round(this.scale*d.height));e.src=d.src;a.insertBefore(e,a.firstChild)}};mxPrintPreview.prototype.getCoverPages=function(){return null};
+mxPrintPreview.prototype.getAppendices=function(){return null};mxPrintPreview.prototype.print=function(a){a=this.open(a);null!=a&&a.print()};mxPrintPreview.prototype.close=function(){null!=this.wnd&&(this.wnd.close(),this.wnd=null)};function mxStylesheet(){this.styles={};this.putDefaultVertexStyle(this.createDefaultVertexStyle());this.putDefaultEdgeStyle(this.createDefaultEdgeStyle())}
+mxStylesheet.prototype.createDefaultVertexStyle=function(){var a={};a[mxConstants.STYLE_SHAPE]=mxConstants.SHAPE_RECTANGLE;a[mxConstants.STYLE_PERIMETER]=mxPerimeter.RectanglePerimeter;a[mxConstants.STYLE_VERTICAL_ALIGN]=mxConstants.ALIGN_MIDDLE;a[mxConstants.STYLE_ALIGN]=mxConstants.ALIGN_CENTER;a[mxConstants.STYLE_FILLCOLOR]="#C3D9FF";a[mxConstants.STYLE_STROKECOLOR]="#6482B9";a[mxConstants.STYLE_FONTCOLOR]="#774400";return a};
+mxStylesheet.prototype.createDefaultEdgeStyle=function(){var a={};a[mxConstants.STYLE_SHAPE]=mxConstants.SHAPE_CONNECTOR;a[mxConstants.STYLE_ENDARROW]=mxConstants.ARROW_CLASSIC;a[mxConstants.STYLE_VERTICAL_ALIGN]=mxConstants.ALIGN_MIDDLE;a[mxConstants.STYLE_ALIGN]=mxConstants.ALIGN_CENTER;a[mxConstants.STYLE_STROKECOLOR]="#6482B9";a[mxConstants.STYLE_FONTCOLOR]="#446299";return a};mxStylesheet.prototype.putDefaultVertexStyle=function(a){this.putCellStyle("defaultVertex",a)};
+mxStylesheet.prototype.putDefaultEdgeStyle=function(a){this.putCellStyle("defaultEdge",a)};mxStylesheet.prototype.getDefaultVertexStyle=function(){return this.styles.defaultVertex};mxStylesheet.prototype.getDefaultEdgeStyle=function(){return this.styles.defaultEdge};mxStylesheet.prototype.putCellStyle=function(a,b){this.styles[a]=b};
+mxStylesheet.prototype.getCellStyle=function(a,b){var c=b;if(null!=a&&0<a.length)for(var d=a.split(";"),c=null!=c&&";"!=a.charAt(0)?mxUtils.clone(c):{},e=0;e<d.length;e++){var f=d[e],g=f.indexOf("=");if(0<=g){var k=f.substring(0,g),f=f.substring(g+1);f==mxConstants.NONE?delete c[k]:mxUtils.isNumeric(f)?c[k]=parseFloat(f):c[k]=f}else if(f=this.styles[f],null!=f)for(k in f)c[k]=f[k]}return c};
+function mxCellState(a,b,c){this.view=a;this.cell=b;this.style=c;this.origin=new mxPoint;this.absoluteOffset=new mxPoint}mxCellState.prototype=new mxRectangle;mxCellState.prototype.constructor=mxCellState;mxCellState.prototype.view=null;mxCellState.prototype.cell=null;mxCellState.prototype.style=null;mxCellState.prototype.invalid=!0;mxCellState.prototype.origin=null;mxCellState.prototype.absolutePoints=null;mxCellState.prototype.absoluteOffset=null;mxCellState.prototype.visibleSourceState=null;
+mxCellState.prototype.visibleTargetState=null;mxCellState.prototype.terminalDistance=0;mxCellState.prototype.length=0;mxCellState.prototype.segments=null;mxCellState.prototype.shape=null;mxCellState.prototype.text=null;mxCellState.prototype.unscaledWidth=null;
+mxCellState.prototype.getPerimeterBounds=function(a,b){a=a||0;b=null!=b?b:new mxRectangle(this.x,this.y,this.width,this.height);if(null!=this.shape&&null!=this.shape.stencil&&"fixed"==this.shape.stencil.aspect){var c=this.shape.stencil.computeAspect(this.style,b.x,b.y,b.width,b.height);b.x=c.x;b.y=c.y;b.width=this.shape.stencil.w0*c.width;b.height=this.shape.stencil.h0*c.height}0!=a&&b.grow(a);return b};
+mxCellState.prototype.setAbsoluteTerminalPoint=function(a,b){b?(null==this.absolutePoints&&(this.absolutePoints=[]),0==this.absolutePoints.length?this.absolutePoints.push(a):this.absolutePoints[0]=a):null==this.absolutePoints?(this.absolutePoints=[],this.absolutePoints.push(null),this.absolutePoints.push(a)):1==this.absolutePoints.length?this.absolutePoints.push(a):this.absolutePoints[this.absolutePoints.length-1]=a};
+mxCellState.prototype.setCursor=function(a){null!=this.shape&&this.shape.setCursor(a);null!=this.text&&this.text.setCursor(a)};mxCellState.prototype.getVisibleTerminal=function(a){a=this.getVisibleTerminalState(a);return null!=a?a.cell:null};mxCellState.prototype.getVisibleTerminalState=function(a){return a?this.visibleSourceState:this.visibleTargetState};mxCellState.prototype.setVisibleTerminalState=function(a,b){b?this.visibleSourceState=a:this.visibleTargetState=a};
+mxCellState.prototype.getCellBounds=function(){return this.cellBounds};mxCellState.prototype.getPaintBounds=function(){return this.paintBounds};mxCellState.prototype.updateCachedBounds=function(){var a=this.view.translate,b=this.view.scale;this.cellBounds=new mxRectangle(this.x/b-a.x,this.y/b-a.y,this.width/b,this.height/b);this.paintBounds=mxRectangle.fromRectangle(this.cellBounds);null!=this.shape&&this.shape.isPaintBoundsInverted()&&this.paintBounds.rotate90()};
+mxCellState.prototype.setState=function(a){this.view=a.view;this.cell=a.cell;this.style=a.style;this.absolutePoints=a.absolutePoints;this.origin=a.origin;this.absoluteOffset=a.absoluteOffset;this.boundingBox=a.boundingBox;this.terminalDistance=a.terminalDistance;this.segments=a.segments;this.length=a.length;this.x=a.x;this.y=a.y;this.width=a.width;this.height=a.height;this.unscaledWidth=a.unscaledWidth};
+mxCellState.prototype.clone=function(){var a=new mxCellState(this.view,this.cell,this.style);if(null!=this.absolutePoints){a.absolutePoints=[];for(var b=0;b<this.absolutePoints.length;b++)a.absolutePoints[b]=this.absolutePoints[b].clone()}null!=this.origin&&(a.origin=this.origin.clone());null!=this.absoluteOffset&&(a.absoluteOffset=this.absoluteOffset.clone());null!=this.boundingBox&&(a.boundingBox=this.boundingBox.clone());a.terminalDistance=this.terminalDistance;a.segments=this.segments;a.length=
+this.length;a.x=this.x;a.y=this.y;a.width=this.width;a.height=this.height;a.unscaledWidth=this.unscaledWidth;return a};mxCellState.prototype.destroy=function(){this.view.graph.cellRenderer.destroy(this)};function mxGraphSelectionModel(a){this.graph=a;this.cells=[]}mxGraphSelectionModel.prototype=new mxEventSource;mxGraphSelectionModel.prototype.constructor=mxGraphSelectionModel;mxGraphSelectionModel.prototype.doneResource="none"!=mxClient.language?"done":"";
+mxGraphSelectionModel.prototype.updatingSelectionResource="none"!=mxClient.language?"updatingSelection":"";mxGraphSelectionModel.prototype.graph=null;mxGraphSelectionModel.prototype.singleSelection=!1;mxGraphSelectionModel.prototype.isSingleSelection=function(){return this.singleSelection};mxGraphSelectionModel.prototype.setSingleSelection=function(a){this.singleSelection=a};mxGraphSelectionModel.prototype.isSelected=function(a){return null!=a?0<=mxUtils.indexOf(this.cells,a):!1};
+mxGraphSelectionModel.prototype.isEmpty=function(){return 0==this.cells.length};mxGraphSelectionModel.prototype.clear=function(){this.changeSelection(null,this.cells)};mxGraphSelectionModel.prototype.setCell=function(a){null!=a&&this.setCells([a])};mxGraphSelectionModel.prototype.setCells=function(a){if(null!=a){this.singleSelection&&(a=[this.getFirstSelectableCell(a)]);for(var b=[],c=0;c<a.length;c++)this.graph.isCellSelectable(a[c])&&b.push(a[c]);this.changeSelection(b,this.cells)}};
+mxGraphSelectionModel.prototype.getFirstSelectableCell=function(a){if(null!=a)for(var b=0;b<a.length;b++)if(this.graph.isCellSelectable(a[b]))return a[b];return null};mxGraphSelectionModel.prototype.addCell=function(a){null!=a&&this.addCells([a])};
+mxGraphSelectionModel.prototype.addCells=function(a){if(null!=a){var b=null;this.singleSelection&&(b=this.cells,a=[this.getFirstSelectableCell(a)]);for(var c=[],d=0;d<a.length;d++)!this.isSelected(a[d])&&this.graph.isCellSelectable(a[d])&&c.push(a[d]);this.changeSelection(c,b)}};mxGraphSelectionModel.prototype.removeCell=function(a){null!=a&&this.removeCells([a])};
+mxGraphSelectionModel.prototype.removeCells=function(a){if(null!=a){for(var b=[],c=0;c<a.length;c++)this.isSelected(a[c])&&b.push(a[c]);this.changeSelection(null,b)}};mxGraphSelectionModel.prototype.changeSelection=function(a,b){if(null!=a&&0<a.length&&null!=a[0]||null!=b&&0<b.length&&null!=b[0]){var c=new mxSelectionChange(this,a,b);c.execute();var d=new mxUndoableEdit(this,!1);d.add(c);this.fireEvent(new mxEventObject(mxEvent.UNDO,"edit",d))}};
+mxGraphSelectionModel.prototype.cellAdded=function(a){null==a||this.isSelected(a)||this.cells.push(a)};mxGraphSelectionModel.prototype.cellRemoved=function(a){null!=a&&(a=mxUtils.indexOf(this.cells,a),0<=a&&this.cells.splice(a,1))};function mxSelectionChange(a,b,c){this.selectionModel=a;this.added=null!=b?b.slice():null;this.removed=null!=c?c.slice():null}
+mxSelectionChange.prototype.execute=function(){var a=mxLog.enter("mxSelectionChange.execute");window.status=mxResources.get(this.selectionModel.updatingSelectionResource)||this.selectionModel.updatingSelectionResource;if(null!=this.removed)for(var b=0;b<this.removed.length;b++)this.selectionModel.cellRemoved(this.removed[b]);if(null!=this.added)for(b=0;b<this.added.length;b++)this.selectionModel.cellAdded(this.added[b]);b=this.added;this.added=this.removed;this.removed=b;window.status=mxResources.get(this.selectionModel.doneResource)||
+this.selectionModel.doneResource;mxLog.leave("mxSelectionChange.execute",a);this.selectionModel.fireEvent(new mxEventObject(mxEvent.CHANGE,"added",this.added,"removed",this.removed))};
+function mxCellEditor(a){this.graph=a;this.zoomHandler=mxUtils.bind(this,function(){this.graph.isEditing()&&this.resize()});this.graph.view.addListener(mxEvent.SCALE,this.zoomHandler);this.graph.view.addListener(mxEvent.SCALE_AND_TRANSLATE,this.zoomHandler);this.changeHandler=mxUtils.bind(this,function(a){null!=this.editingCell&&null==this.graph.getView().getState(this.editingCell)&&this.stopEditing(!0)});this.graph.getModel().addListener(mxEvent.CHANGE,this.changeHandler)}
+mxCellEditor.prototype.graph=null;mxCellEditor.prototype.textarea=null;mxCellEditor.prototype.editingCell=null;mxCellEditor.prototype.trigger=null;mxCellEditor.prototype.modified=!1;mxCellEditor.prototype.autoSize=!0;mxCellEditor.prototype.selectText=!0;mxCellEditor.prototype.emptyLabelText=mxClient.IS_FF?"<br>":"";mxCellEditor.prototype.escapeCancelsEditing=!0;mxCellEditor.prototype.textNode="";mxCellEditor.prototype.zIndex=5;mxCellEditor.prototype.minResize=new mxRectangle(0,20);
+mxCellEditor.prototype.wordWrapPadding=mxClient.IS_QUIRKS?2:mxClient.IS_IE11?0:1;mxCellEditor.prototype.blurEnabled=!1;mxCellEditor.prototype.initialValue=null;mxCellEditor.prototype.init=function(){this.textarea=document.createElement("div");this.textarea.className="mxCellEditor mxPlainTextEditor";this.textarea.contentEditable=!0;mxClient.IS_GC&&(this.textarea.style.minHeight="1em");this.textarea.style.position=this.isLegacyEditor()?"absolute":"relative";this.installListeners(this.textarea)};
+mxCellEditor.prototype.applyValue=function(a,b){this.graph.labelChanged(a.cell,b,this.trigger)};mxCellEditor.prototype.getInitialValue=function(a,b){var c=mxUtils.htmlEntities(this.graph.getEditingValue(a.cell,b),!1);mxClient.IS_QUIRKS||8==document.documentMode||9==document.documentMode||10==document.documentMode||(c=mxUtils.replaceTrailingNewlines(c,"<div><br></div>"));return c.replace(/\n/g,"<br>")};mxCellEditor.prototype.getCurrentValue=function(a){return mxUtils.extractTextWithWhitespace(this.textarea.childNodes)};
+mxCellEditor.prototype.isCancelEditingKeyEvent=function(a){return this.escapeCancelsEditing||mxEvent.isShiftDown(a)||mxEvent.isControlDown(a)||mxEvent.isMetaDown(a)};
+mxCellEditor.prototype.installListeners=function(a){mxEvent.addListener(a,"blur",mxUtils.bind(this,function(a){this.blurEnabled&&this.focusLost(a)}));mxEvent.addListener(a,"keydown",mxUtils.bind(this,function(a){mxEvent.isConsumed(a)||(this.isStopEditingEvent(a)?(this.graph.stopEditing(!1),mxEvent.consume(a)):27==a.keyCode&&(this.graph.stopEditing(this.isCancelEditingKeyEvent(a)),mxEvent.consume(a)))}));var b=mxUtils.bind(this,function(b){null!=this.editingCell&&this.clearOnChange&&a.innerHTML==this.getEmptyLabelText()&&
+(!mxClient.IS_FF||8!=b.keyCode&&46!=b.keyCode)&&(this.clearOnChange=!1,a.innerHTML="")});mxEvent.addListener(a,"keypress",b);mxEvent.addListener(a,"paste",b);b=mxUtils.bind(this,function(a){null!=this.editingCell&&(0==this.textarea.innerHTML.length||"<br>"==this.textarea.innerHTML?(this.textarea.innerHTML=this.getEmptyLabelText(),this.clearOnChange=0<this.textarea.innerHTML.length):this.clearOnChange=!1)});mxEvent.addListener(a,mxClient.IS_IE11||mxClient.IS_IE?"keyup":"input",b);mxEvent.addListener(a,
+"cut",b);mxEvent.addListener(a,"paste",b);var b=mxClient.IS_IE11||mxClient.IS_IE?"keydown":"input",c=mxUtils.bind(this,function(a){null!=this.editingCell&&this.autoSize&&!mxEvent.isConsumed(a)&&(null!=this.resizeThread&&window.clearTimeout(this.resizeThread),this.resizeThread=window.setTimeout(mxUtils.bind(this,function(){this.resizeThread=null;this.resize()}),0))});mxEvent.addListener(a,b,c);9<=document.documentMode?(mxEvent.addListener(a,"DOMNodeRemoved",c),mxEvent.addListener(a,"DOMNodeInserted",
+c)):(mxEvent.addListener(a,"cut",c),mxEvent.addListener(a,"paste",c))};mxCellEditor.prototype.isStopEditingEvent=function(a){return 113==a.keyCode||this.graph.isEnterStopsCellEditing()&&13==a.keyCode&&!mxEvent.isControlDown(a)&&!mxEvent.isShiftDown(a)};mxCellEditor.prototype.isEventSource=function(a){return mxEvent.getSource(a)==this.textarea};
+mxCellEditor.prototype.resize=function(){var a=this.graph.getView().getState(this.editingCell);if(null==a)this.stopEditing(!0);else if(null!=this.textarea){var b=this.graph.getModel().isEdge(a.cell),c=this.graph.getView().scale,d=null;if(this.autoSize&&"fill"!=a.style[mxConstants.STYLE_OVERFLOW]){var e=mxUtils.getValue(a.style,mxConstants.STYLE_LABEL_WIDTH,null),d=null!=a.text?a.text.margin:null;null==d&&(d=mxUtils.getAlignmentAsPoint(mxUtils.getValue(a.style,mxConstants.STYLE_ALIGN,mxConstants.ALIGN_CENTER),
+mxUtils.getValue(a.style,mxConstants.STYLE_VERTICAL_ALIGN,mxConstants.ALIGN_MIDDLE)));if(b)this.bounds=new mxRectangle(a.absoluteOffset.x,a.absoluteOffset.y,0,0),null!=e&&(e=(parseFloat(e)+2)*c,this.bounds.width=e,this.bounds.x+=d.x*e);else{var b=mxRectangle.fromRectangle(a),f=mxUtils.getValue(a.style,mxConstants.STYLE_LABEL_POSITION,mxConstants.ALIGN_CENTER),g=mxUtils.getValue(a.style,mxConstants.STYLE_VERTICAL_LABEL_POSITION,mxConstants.ALIGN_MIDDLE),b=null!=a.shape&&f==mxConstants.ALIGN_CENTER&&
+g==mxConstants.ALIGN_MIDDLE?a.shape.getLabelBounds(b):b;null!=e&&(b.width=parseFloat(e)*c);if(!a.view.graph.cellRenderer.legacySpacing||"width"!=a.style[mxConstants.STYLE_OVERFLOW])var f=parseInt(a.style[mxConstants.STYLE_SPACING]||2)*c,k=(parseInt(a.style[mxConstants.STYLE_SPACING_TOP]||0)+mxText.prototype.baseSpacingTop)*c+f,l=(parseInt(a.style[mxConstants.STYLE_SPACING_RIGHT]||0)+mxText.prototype.baseSpacingRight)*c+f,m=(parseInt(a.style[mxConstants.STYLE_SPACING_BOTTOM]||0)+mxText.prototype.baseSpacingBottom)*
+c+f,n=(parseInt(a.style[mxConstants.STYLE_SPACING_LEFT]||0)+mxText.prototype.baseSpacingLeft)*c+f,f=mxUtils.getValue(a.style,mxConstants.STYLE_LABEL_POSITION,mxConstants.ALIGN_CENTER),g=mxUtils.getValue(a.style,mxConstants.STYLE_VERTICAL_LABEL_POSITION,mxConstants.ALIGN_MIDDLE),b=new mxRectangle(b.x+n,b.y+k,b.width-(f==mxConstants.ALIGN_CENTER&&null==e?n+l:0),b.height-(g==mxConstants.ALIGN_MIDDLE?k+m:0));this.bounds=new mxRectangle(b.x+a.absoluteOffset.x,b.y+a.absoluteOffset.y,b.width,b.height)}this.graph.isWrapping(a.cell)&&
+(2<=this.bounds.width||2<=this.bounds.height)&&this.textarea.innerHTML!=this.getEmptyLabelText()?(this.textarea.style.wordWrap=mxConstants.WORD_WRAP,this.textarea.style.whiteSpace="normal",e=Math.round(this.bounds.width/c)+this.wordWrapPadding,"relative"!=this.textarea.style.position?(this.textarea.style.width=e+"px",this.textarea.scrollWidth>e&&(this.textarea.style.width=this.textarea.scrollWidth+"px")):this.textarea.style.maxWidth=e+"px"):(this.textarea.style.whiteSpace="nowrap",this.textarea.style.width=
+"");8==document.documentMode&&(this.textarea.style.zoom="1",this.textarea.style.height="auto");a=this.textarea.scrollWidth;e=this.textarea.scrollHeight;8==document.documentMode?(this.textarea.style.left=Math.max(0,Math.ceil((this.bounds.x-d.x*(this.bounds.width-(a+1)*c)+a*(c-1)*0+2*(d.x+.5))/c))+"px",this.textarea.style.top=Math.max(0,Math.ceil((this.bounds.y-d.y*(this.bounds.height-(e+.5)*c)+e*(c-1)*0+1*Math.abs(d.y+.5))/c))+"px",this.textarea.style.width=Math.round(a*c)+"px",this.textarea.style.height=
+Math.round(e*c)+"px"):mxClient.IS_QUIRKS?(this.textarea.style.left=Math.max(0,Math.ceil(this.bounds.x-d.x*(this.bounds.width-(a+1)*c)+a*(c-1)*0+2*(d.x+.5)))+"px",this.textarea.style.top=Math.max(0,Math.ceil(this.bounds.y-d.y*(this.bounds.height-(e+.5)*c)+e*(c-1)*0+1*Math.abs(d.y+.5)))+"px"):(this.textarea.style.left=Math.max(0,Math.round(this.bounds.x-d.x*(this.bounds.width-2))+1)+"px",this.textarea.style.top=Math.max(0,Math.round(this.bounds.y-d.y*(this.bounds.height-4)+(-1==d.y?3:0))+1)+"px")}else this.bounds=
+this.getEditorBounds(a),this.textarea.style.width=Math.round(this.bounds.width/c)+"px",this.textarea.style.height=Math.round(this.bounds.height/c)+"px",8==document.documentMode||mxClient.IS_QUIRKS?(this.textarea.style.left=Math.round(this.bounds.x)+"px",this.textarea.style.top=Math.round(this.bounds.y)+"px"):(this.textarea.style.left=Math.max(0,Math.round(this.bounds.x+1))+"px",this.textarea.style.top=Math.max(0,Math.round(this.bounds.y+1))+"px"),this.graph.isWrapping(a.cell)&&(2<=this.bounds.width||
+2<=this.bounds.height)&&this.textarea.innerHTML!=this.getEmptyLabelText()?(this.textarea.style.wordWrap=mxConstants.WORD_WRAP,this.textarea.style.whiteSpace="normal","fill"!=a.style[mxConstants.STYLE_OVERFLOW]&&(this.textarea.style.width=Math.round(this.bounds.width/c)+this.wordWrapPadding+"px")):(this.textarea.style.whiteSpace="nowrap","fill"!=a.style[mxConstants.STYLE_OVERFLOW]&&(this.textarea.style.width=""));mxClient.IS_VML?this.textarea.style.zoom=c:(mxUtils.setPrefixedStyle(this.textarea.style,
+"transformOrigin","0px 0px"),mxUtils.setPrefixedStyle(this.textarea.style,"transform","scale("+c+","+c+")"+(null==d?"":" translate("+100*d.x+"%,"+100*d.y+"%)")))}};mxCellEditor.prototype.focusLost=function(){this.stopEditing(!this.graph.isInvokesStopCellEditing())};mxCellEditor.prototype.getBackgroundColor=function(a){return null};
+mxCellEditor.prototype.isLegacyEditor=function(){if(mxClient.IS_VML)return!0;var a=!1;if(mxClient.IS_SVG){var b=this.graph.view.getDrawPane().ownerSVGElement;null!=b&&(a="absolute"==mxUtils.getCurrentStyle(b).position)}return!a};
+mxCellEditor.prototype.startEditing=function(a,b){this.stopEditing(!0);null==this.textarea&&this.init();null!=this.graph.tooltipHandler&&this.graph.tooltipHandler.hideTooltip();var c=this.graph.getView().getState(a);if(null!=c){this.graph.getView();var d=mxUtils.getValue(c.style,mxConstants.STYLE_FONTSIZE,mxConstants.DEFAULT_FONTSIZE),e=mxUtils.getValue(c.style,mxConstants.STYLE_FONTFAMILY,mxConstants.DEFAULT_FONTFAMILY),f=mxUtils.getValue(c.style,mxConstants.STYLE_FONTCOLOR,"black"),g=mxUtils.getValue(c.style,
+mxConstants.STYLE_ALIGN,mxConstants.ALIGN_LEFT),k=(mxUtils.getValue(c.style,mxConstants.STYLE_FONTSTYLE,0)&mxConstants.FONT_BOLD)==mxConstants.FONT_BOLD,l=(mxUtils.getValue(c.style,mxConstants.STYLE_FONTSTYLE,0)&mxConstants.FONT_ITALIC)==mxConstants.FONT_ITALIC,m=(mxUtils.getValue(c.style,mxConstants.STYLE_FONTSTYLE,0)&mxConstants.FONT_UNDERLINE)==mxConstants.FONT_UNDERLINE;this.textarea.style.lineHeight=mxConstants.ABSOLUTE_LINE_HEIGHT?Math.round(d*mxConstants.LINE_HEIGHT)+"px":mxConstants.LINE_HEIGHT;
+this.textarea.style.backgroundColor=this.getBackgroundColor(c);this.textarea.style.textDecoration=m?"underline":"";this.textarea.style.fontWeight=k?"bold":"normal";this.textarea.style.fontStyle=l?"italic":"";this.textarea.style.fontSize=Math.round(d)+"px";this.textarea.style.zIndex=this.zIndex;this.textarea.style.fontFamily=e;this.textarea.style.textAlign=g;this.textarea.style.outline="none";this.textarea.style.color=f;d=this.textDirection=mxUtils.getValue(c.style,mxConstants.STYLE_TEXT_DIRECTION,
+mxConstants.DEFAULT_TEXT_DIRECTION);d==mxConstants.TEXT_DIRECTION_AUTO&&(null==c||null==c.text||c.text.dialect==mxConstants.DIALECT_STRICTHTML||mxUtils.isNode(c.text.value)||(d=c.text.getAutoDirection()));d==mxConstants.TEXT_DIRECTION_LTR||d==mxConstants.TEXT_DIRECTION_RTL?this.textarea.setAttribute("dir",d):this.textarea.removeAttribute("dir");this.textarea.innerHTML=this.getInitialValue(c,b)||"";this.initialValue=this.textarea.innerHTML;0==this.textarea.innerHTML.length||"<br>"==this.textarea.innerHTML?
+(this.textarea.innerHTML=this.getEmptyLabelText(),this.clearOnChange=!0):this.clearOnChange=this.textarea.innerHTML==this.getEmptyLabelText();this.graph.container.appendChild(this.textarea);this.editingCell=a;this.trigger=b;this.textNode=null;null!=c.text&&this.isHideLabel(c)&&(this.textNode=c.text.node,this.textNode.style.visibility="hidden");this.autoSize&&(this.graph.model.isEdge(c.cell)||"fill"!=c.style[mxConstants.STYLE_OVERFLOW])&&window.setTimeout(mxUtils.bind(this,function(){this.resize()}),
+0);this.resize();try{this.textarea.focus(),this.isSelectText()&&0<this.textarea.innerHTML.length&&(this.textarea.innerHTML!=this.getEmptyLabelText()||!this.clearOnChange)&&document.execCommand("selectAll",!1,null)}catch(n){}}};mxCellEditor.prototype.isSelectText=function(){return this.selectText};mxCellEditor.prototype.clearSelection=function(){var a=null;window.getSelection?a=window.getSelection():document.selection&&(a=document.selection);null!=a&&(a.empty?a.empty():a.removeAllRanges&&a.removeAllRanges())};
+mxCellEditor.prototype.stopEditing=function(a){if(null!=this.editingCell){null!=this.textNode&&(this.textNode.style.visibility="visible",this.textNode=null);a=a?null:this.graph.view.getState(this.editingCell);var b=this.initialValue;this.bounds=this.trigger=this.editingCell=this.initialValue=null;this.textarea.blur();this.clearSelection();null!=this.textarea.parentNode&&this.textarea.parentNode.removeChild(this.textarea);this.clearOnChange&&this.textarea.innerHTML==this.getEmptyLabelText()&&(this.textarea.innerHTML=
+"",this.clearOnChange=!1);null!=a&&this.textarea.innerHTML!=b&&(this.prepareTextarea(),b=this.getCurrentValue(a),null!=b&&this.applyValue(a,b));mxEvent.release(this.textarea);this.textarea=null}};mxCellEditor.prototype.prepareTextarea=function(){mxClient.IS_FF&&null!=this.textarea.lastChild&&"BR"==this.textarea.lastChild.nodeName&&this.textarea.removeChild(this.textarea.lastChild)};mxCellEditor.prototype.isHideLabel=function(a){return!0};
+mxCellEditor.prototype.getMinimumSize=function(a){var b=this.graph.getView().scale;return new mxRectangle(0,0,null==a.text?30:a.text.size*b+20,"left"==this.textarea.style.textAlign?120:40)};
+mxCellEditor.prototype.getEditorBounds=function(a){var b=this.graph.getModel().isEdge(a.cell),c=this.graph.getView().scale,d=this.getMinimumSize(a),e=d.width,d=d.height;if(!b&&a.view.graph.cellRenderer.legacySpacing&&"fill"==a.style[mxConstants.STYLE_OVERFLOW])c=a.shape.getLabelBounds(mxRectangle.fromRectangle(a));else{var f=parseInt(a.style[mxConstants.STYLE_SPACING]||0)*c,g=(parseInt(a.style[mxConstants.STYLE_SPACING_TOP]||0)+mxText.prototype.baseSpacingTop)*c+f,k=(parseInt(a.style[mxConstants.STYLE_SPACING_RIGHT]||
+0)+mxText.prototype.baseSpacingRight)*c+f,l=(parseInt(a.style[mxConstants.STYLE_SPACING_BOTTOM]||0)+mxText.prototype.baseSpacingBottom)*c+f,f=(parseInt(a.style[mxConstants.STYLE_SPACING_LEFT]||0)+mxText.prototype.baseSpacingLeft)*c+f,c=new mxRectangle(a.x,a.y,Math.max(e,a.width-f-k),Math.max(d,a.height-g-l)),k=mxUtils.getValue(a.style,mxConstants.STYLE_LABEL_POSITION,mxConstants.ALIGN_CENTER),l=mxUtils.getValue(a.style,mxConstants.STYLE_VERTICAL_LABEL_POSITION,mxConstants.ALIGN_MIDDLE),c=null!=a.shape&&
+k==mxConstants.ALIGN_CENTER&&l==mxConstants.ALIGN_MIDDLE?a.shape.getLabelBounds(c):c;b?(c.x=a.absoluteOffset.x,c.y=a.absoluteOffset.y,null!=a.text&&null!=a.text.boundingBox&&(0<a.text.boundingBox.x&&(c.x=a.text.boundingBox.x),0<a.text.boundingBox.y&&(c.y=a.text.boundingBox.y))):null!=a.text&&null!=a.text.boundingBox&&(c.x=Math.min(c.x,a.text.boundingBox.x),c.y=Math.min(c.y,a.text.boundingBox.y));c.x+=f;c.y+=g;null!=a.text&&null!=a.text.boundingBox&&(b?(c.width=Math.max(e,a.text.boundingBox.width),
+c.height=Math.max(d,a.text.boundingBox.height)):(c.width=Math.max(c.width,a.text.boundingBox.width),c.height=Math.max(c.height,a.text.boundingBox.height)));this.graph.getModel().isVertex(a.cell)&&(b=mxUtils.getValue(a.style,mxConstants.STYLE_LABEL_POSITION,mxConstants.ALIGN_CENTER),b==mxConstants.ALIGN_LEFT?c.x-=a.width:b==mxConstants.ALIGN_RIGHT&&(c.x+=a.width),b=mxUtils.getValue(a.style,mxConstants.STYLE_VERTICAL_LABEL_POSITION,mxConstants.ALIGN_MIDDLE),b==mxConstants.ALIGN_TOP?c.y-=a.height:b==
+mxConstants.ALIGN_BOTTOM&&(c.y+=a.height))}return new mxRectangle(Math.round(c.x),Math.round(c.y),Math.round(c.width),Math.round(c.height))};mxCellEditor.prototype.getEmptyLabelText=function(a){return this.emptyLabelText};mxCellEditor.prototype.getEditingCell=function(){return this.editingCell};
+mxCellEditor.prototype.destroy=function(){null!=this.textarea&&(mxEvent.release(this.textarea),null!=this.textarea.parentNode&&this.textarea.parentNode.removeChild(this.textarea),this.textarea=null);null!=this.changeHandler&&(this.graph.getModel().removeListener(this.changeHandler),this.changeHandler=null);this.zoomHandler&&(this.graph.view.removeListener(this.zoomHandler),this.zoomHandler=null)};function mxCellRenderer(){}mxCellRenderer.defaultShapes={};
+mxCellRenderer.prototype.defaultEdgeShape=mxConnector;mxCellRenderer.prototype.defaultVertexShape=mxRectangleShape;mxCellRenderer.prototype.defaultTextShape=mxText;mxCellRenderer.prototype.legacyControlPosition=!0;mxCellRenderer.prototype.legacySpacing=!0;mxCellRenderer.prototype.antiAlias=!0;mxCellRenderer.prototype.minSvgStrokeWidth=1;mxCellRenderer.prototype.forceControlClickHandler=!1;mxCellRenderer.registerShape=function(a,b){mxCellRenderer.defaultShapes[a]=b};
+mxCellRenderer.registerShape(mxConstants.SHAPE_RECTANGLE,mxRectangleShape);mxCellRenderer.registerShape(mxConstants.SHAPE_ELLIPSE,mxEllipse);mxCellRenderer.registerShape(mxConstants.SHAPE_RHOMBUS,mxRhombus);mxCellRenderer.registerShape(mxConstants.SHAPE_CYLINDER,mxCylinder);mxCellRenderer.registerShape(mxConstants.SHAPE_CONNECTOR,mxConnector);mxCellRenderer.registerShape(mxConstants.SHAPE_ACTOR,mxActor);mxCellRenderer.registerShape(mxConstants.SHAPE_TRIANGLE,mxTriangle);
+mxCellRenderer.registerShape(mxConstants.SHAPE_HEXAGON,mxHexagon);mxCellRenderer.registerShape(mxConstants.SHAPE_CLOUD,mxCloud);mxCellRenderer.registerShape(mxConstants.SHAPE_LINE,mxLine);mxCellRenderer.registerShape(mxConstants.SHAPE_ARROW,mxArrow);mxCellRenderer.registerShape(mxConstants.SHAPE_ARROW_CONNECTOR,mxArrowConnector);mxCellRenderer.registerShape(mxConstants.SHAPE_DOUBLE_ELLIPSE,mxDoubleEllipse);mxCellRenderer.registerShape(mxConstants.SHAPE_SWIMLANE,mxSwimlane);
+mxCellRenderer.registerShape(mxConstants.SHAPE_IMAGE,mxImageShape);mxCellRenderer.registerShape(mxConstants.SHAPE_LABEL,mxLabel);mxCellRenderer.prototype.initializeShape=function(a){a.shape.dialect=a.view.graph.dialect;this.configureShape(a);a.shape.init(a.view.getDrawPane())};mxCellRenderer.prototype.createShape=function(a){var b=null;null!=a.style&&(b=mxStencilRegistry.getStencil(a.style[mxConstants.STYLE_SHAPE]),b=null!=b?new mxShape(b):new (this.getShapeConstructor(a)));return b};
+mxCellRenderer.prototype.createIndicatorShape=function(a){a.shape.indicatorShape=this.getShape(a.view.graph.getIndicatorShape(a))};mxCellRenderer.prototype.getShape=function(a){return null!=a?mxCellRenderer.defaultShapes[a]:null};mxCellRenderer.prototype.getShapeConstructor=function(a){var b=this.getShape(a.style[mxConstants.STYLE_SHAPE]);null==b&&(b=a.view.graph.getModel().isEdge(a.cell)?this.defaultEdgeShape:this.defaultVertexShape);return b};
+mxCellRenderer.prototype.configureShape=function(a){a.shape.apply(a);a.shape.image=a.view.graph.getImage(a);a.shape.indicatorColor=a.view.graph.getIndicatorColor(a);a.shape.indicatorStrokeColor=a.style[mxConstants.STYLE_INDICATOR_STROKECOLOR];a.shape.indicatorGradientColor=a.view.graph.getIndicatorGradientColor(a);a.shape.indicatorDirection=a.style[mxConstants.STYLE_INDICATOR_DIRECTION];a.shape.indicatorImage=a.view.graph.getIndicatorImage(a);this.postConfigureShape(a)};
+mxCellRenderer.prototype.postConfigureShape=function(a){null!=a.shape&&(this.resolveColor(a,"indicatorColor",mxConstants.STYLE_FILLCOLOR),this.resolveColor(a,"indicatorGradientColor",mxConstants.STYLE_GRADIENTCOLOR),this.resolveColor(a,"fill",mxConstants.STYLE_FILLCOLOR),this.resolveColor(a,"stroke",mxConstants.STYLE_STROKECOLOR),this.resolveColor(a,"gradient",mxConstants.STYLE_GRADIENTCOLOR))};
+mxCellRenderer.prototype.checkPlaceholderStyles=function(a){if(null!=a.style)for(var b=["inherit","swimlane","indicated"],c=[mxConstants.STYLE_FILLCOLOR,mxConstants.STYLE_STROKECOLOR,mxConstants.STYLE_GRADIENTCOLOR],d=0;d<c.length;d++)if(0<=mxUtils.indexOf(b,a.style[c[d]]))return!0;return!1};
+mxCellRenderer.prototype.resolveColor=function(a,b,c){var d=a.shape[b],e=a.view.graph,f=null;"inherit"==d?f=e.model.getParent(a.cell):"swimlane"==d?(a.shape[b]=c==mxConstants.STYLE_STROKECOLOR?"#000000":"#ffffff",f=null!=e.model.getTerminal(a.cell,!1)?e.model.getTerminal(a.cell,!1):a.cell,f=e.getSwimlane(f),c=e.swimlaneIndicatorColorAttribute):"indicated"==d&&(a.shape[b]=a.shape.indicatorColor);null!=f&&(d=e.getView().getState(f),a.shape[b]=null,null!=d&&(a.shape[b]=null!=d.shape&&"indicatorColor"!=
+b?d.shape[b]:d.style[c]))};mxCellRenderer.prototype.getLabelValue=function(a){return a.view.graph.getLabel(a.cell)};
+mxCellRenderer.prototype.createLabel=function(a,b){var c=a.view.graph;c.getModel().isEdge(a.cell);if(0<a.style[mxConstants.STYLE_FONTSIZE]||null==a.style[mxConstants.STYLE_FONTSIZE]){var d=c.isHtmlLabel(a.cell)||null!=b&&mxUtils.isNode(b);a.text=new this.defaultTextShape(b,new mxRectangle,a.style[mxConstants.STYLE_ALIGN]||mxConstants.ALIGN_CENTER,c.getVerticalAlign(a),a.style[mxConstants.STYLE_FONTCOLOR],a.style[mxConstants.STYLE_FONTFAMILY],a.style[mxConstants.STYLE_FONTSIZE],a.style[mxConstants.STYLE_FONTSTYLE],
+a.style[mxConstants.STYLE_SPACING],a.style[mxConstants.STYLE_SPACING_TOP],a.style[mxConstants.STYLE_SPACING_RIGHT],a.style[mxConstants.STYLE_SPACING_BOTTOM],a.style[mxConstants.STYLE_SPACING_LEFT],a.style[mxConstants.STYLE_HORIZONTAL],a.style[mxConstants.STYLE_LABEL_BACKGROUNDCOLOR],a.style[mxConstants.STYLE_LABEL_BORDERCOLOR],c.isWrapping(a.cell)&&c.isHtmlLabel(a.cell),c.isLabelClipped(a.cell),a.style[mxConstants.STYLE_OVERFLOW],a.style[mxConstants.STYLE_LABEL_PADDING],mxUtils.getValue(a.style,mxConstants.STYLE_TEXT_DIRECTION,
+mxConstants.DEFAULT_TEXT_DIRECTION));a.text.opacity=mxUtils.getValue(a.style,mxConstants.STYLE_TEXT_OPACITY,100);a.text.dialect=d?mxConstants.DIALECT_STRICTHTML:a.view.graph.dialect;a.text.style=a.style;a.text.state=a;this.initializeLabel(a,a.text);var e=!1,f=function(b){var d=a;if(mxClient.IS_TOUCH||e)d=mxEvent.getClientX(b),b=mxEvent.getClientY(b),b=mxUtils.convertPoint(c.container,d,b),d=c.view.getState(c.getCellAt(b.x,b.y));return d};mxEvent.addGestureListeners(a.text.node,mxUtils.bind(this,function(b){this.isLabelEvent(a,
+b)&&(c.fireMouseEvent(mxEvent.MOUSE_DOWN,new mxMouseEvent(b,a)),e=c.dialect!=mxConstants.DIALECT_SVG&&"IMG"==mxEvent.getSource(b).nodeName)}),mxUtils.bind(this,function(b){this.isLabelEvent(a,b)&&c.fireMouseEvent(mxEvent.MOUSE_MOVE,new mxMouseEvent(b,f(b)))}),mxUtils.bind(this,function(b){this.isLabelEvent(a,b)&&(c.fireMouseEvent(mxEvent.MOUSE_UP,new mxMouseEvent(b,f(b))),e=!1)}));c.nativeDblClickEnabled&&mxEvent.addListener(a.text.node,"dblclick",mxUtils.bind(this,function(b){this.isLabelEvent(a,
+b)&&(c.dblClick(b,a.cell),mxEvent.consume(b))}))}};mxCellRenderer.prototype.initializeLabel=function(a,b){mxClient.IS_SVG&&mxClient.NO_FO&&b.dialect!=mxConstants.DIALECT_SVG?b.init(a.view.graph.container):b.init(a.view.getDrawPane())};
+mxCellRenderer.prototype.createCellOverlays=function(a){var b=a.view.graph.getCellOverlays(a.cell),c=null;if(null!=b)for(var c=new mxDictionary,d=0;d<b.length;d++){var e=null!=a.overlays?a.overlays.remove(b[d]):null;null==e&&(e=new mxImageShape(new mxRectangle,b[d].image.src),e.dialect=a.view.graph.dialect,e.preserveImageAspect=!1,e.overlay=b[d],this.initializeOverlay(a,e),this.installCellOverlayListeners(a,b[d],e),null!=b[d].cursor&&(e.node.style.cursor=b[d].cursor));c.put(b[d],e)}null!=a.overlays&&
+a.overlays.visit(function(a,b){b.destroy()});a.overlays=c};mxCellRenderer.prototype.initializeOverlay=function(a,b){b.init(a.view.getOverlayPane())};
+mxCellRenderer.prototype.installCellOverlayListeners=function(a,b,c){var d=a.view.graph;mxEvent.addListener(c.node,"click",function(c){d.isEditing()&&d.stopEditing(!d.isInvokesStopCellEditing());b.fireEvent(new mxEventObject(mxEvent.CLICK,"event",c,"cell",a.cell))});mxEvent.addGestureListeners(c.node,function(a){mxEvent.consume(a)},function(b){d.fireMouseEvent(mxEvent.MOUSE_MOVE,new mxMouseEvent(b,a))});mxClient.IS_TOUCH&&mxEvent.addListener(c.node,"touchend",function(c){b.fireEvent(new mxEventObject(mxEvent.CLICK,
+"event",c,"cell",a.cell))})};mxCellRenderer.prototype.createControl=function(a){var b=a.view.graph,c=b.getFoldingImage(a);if(b.foldingEnabled&&null!=c){if(null==a.control){var d=new mxRectangle(0,0,c.width,c.height);a.control=new mxImageShape(d,c.src);a.control.preserveImageAspect=!1;a.control.dialect=b.dialect;this.initControl(a,a.control,!0,this.createControlClickHandler(a))}}else null!=a.control&&(a.control.destroy(),a.control=null)};
+mxCellRenderer.prototype.createControlClickHandler=function(a){var b=a.view.graph;return mxUtils.bind(this,function(c){if(this.forceControlClickHandler||b.isEnabled()){var d=!b.isCellCollapsed(a.cell);b.foldCells(d,!1,[a.cell],null,c);mxEvent.consume(c)}})};
+mxCellRenderer.prototype.initControl=function(a,b,c,d){var e=a.view.graph;e.isHtmlLabel(a.cell)&&mxClient.NO_FO&&e.dialect==mxConstants.DIALECT_SVG?(b.dialect=mxConstants.DIALECT_PREFERHTML,b.init(e.container),b.node.style.zIndex=1):b.init(a.view.getOverlayPane());b=b.innerNode||b.node;null==d||mxClient.IS_IOS||(e.isEnabled()&&(b.style.cursor="pointer"),mxEvent.addListener(b,"click",d));if(c){var f=null;mxEvent.addGestureListeners(b,function(b){f=new mxPoint(mxEvent.getClientX(b),mxEvent.getClientY(b));
+e.fireMouseEvent(mxEvent.MOUSE_DOWN,new mxMouseEvent(b,a));mxEvent.consume(b)},function(b){e.fireMouseEvent(mxEvent.MOUSE_MOVE,new mxMouseEvent(b,a))},function(b){e.fireMouseEvent(mxEvent.MOUSE_UP,new mxMouseEvent(b,a));mxEvent.consume(b)});null!=d&&mxClient.IS_IOS&&b.addEventListener("touchend",function(a){if(null!=f){var b=e.tolerance;Math.abs(f.x-mxEvent.getClientX(a))<b&&Math.abs(f.y-mxEvent.getClientY(a))<b&&(d.call(d,a),mxEvent.consume(a))}},!0)}return b};
+mxCellRenderer.prototype.isShapeEvent=function(a,b){return!0};mxCellRenderer.prototype.isLabelEvent=function(a,b){return!0};
+mxCellRenderer.prototype.installListeners=function(a){var b=a.view.graph,c=function(c){var d=a;if(b.dialect!=mxConstants.DIALECT_SVG&&"IMG"==mxEvent.getSource(c).nodeName||mxClient.IS_TOUCH)d=mxEvent.getClientX(c),c=mxEvent.getClientY(c),c=mxUtils.convertPoint(b.container,d,c),d=b.view.getState(b.getCellAt(c.x,c.y));return d};mxEvent.addGestureListeners(a.shape.node,mxUtils.bind(this,function(c){this.isShapeEvent(a,c)&&b.fireMouseEvent(mxEvent.MOUSE_DOWN,new mxMouseEvent(c,a))}),mxUtils.bind(this,
+function(d){this.isShapeEvent(a,d)&&b.fireMouseEvent(mxEvent.MOUSE_MOVE,new mxMouseEvent(d,c(d)))}),mxUtils.bind(this,function(d){this.isShapeEvent(a,d)&&b.fireMouseEvent(mxEvent.MOUSE_UP,new mxMouseEvent(d,c(d)))}));b.nativeDblClickEnabled&&mxEvent.addListener(a.shape.node,"dblclick",mxUtils.bind(this,function(c){this.isShapeEvent(a,c)&&(b.dblClick(c,a.cell),mxEvent.consume(c))}))};
+mxCellRenderer.prototype.redrawLabel=function(a,b){var c=a.view.graph,d=this.getLabelValue(a),e=c.isWrapping(a.cell),f=c.isLabelClipped(a.cell),g=a.view.graph.isHtmlLabel(a.cell)||null!=d&&mxUtils.isNode(d)?mxConstants.DIALECT_STRICTHTML:a.view.graph.dialect,k=a.style[mxConstants.STYLE_OVERFLOW]||"visible";null==a.text||a.text.wrap==e&&a.text.clipped==f&&a.text.overflow==k&&a.text.dialect==g||(a.text.destroy(),a.text=null);null==a.text&&null!=d&&(mxUtils.isNode(d)||0<d.length)?this.createLabel(a,
+d):null==a.text||null!=d&&0!=d.length||(a.text.destroy(),a.text=null);if(null!=a.text){b&&(null!=a.text.lastValue&&this.isTextShapeInvalid(a,a.text)&&(a.text.lastValue=null),a.text.resetStyles(),a.text.apply(a),a.text.valign=c.getVerticalAlign(a));var c=this.getLabelBounds(a),l=this.getTextScale(a);if(b||a.text.value!=d||a.text.isWrapping!=e||a.text.overflow!=k||a.text.isClipping!=f||a.text.scale!=l||a.text.dialect!=g||!a.text.bounds.equals(c))0!=a.text.bounds.width&&null!=a.unscaledWidth&&0!=Math.round(a.text.bounds.width/
+a.text.scale*l-c.width)&&(a.unscaledWidth=null),a.text.dialect=g,a.text.value=d,a.text.bounds=c,a.text.scale=l,a.text.wrap=e,a.text.clipped=f,a.text.overflow=k,d=a.text.node.style.visibility,this.redrawLabelShape(a.text),a.text.node.style.visibility=d}};
+mxCellRenderer.prototype.isTextShapeInvalid=function(a,b){function c(c,e,f){return result="spacingTop"==e||"spacingRight"==e||"spacingBottom"==e||"spacingLeft"==e?parseFloat(b[c])-parseFloat(b.spacing)!=(a.style[e]||f):b[c]!=(a.style[e]||f)}return c("fontStyle",mxConstants.STYLE_FONTSTYLE,mxConstants.DEFAULT_FONTSTYLE)||c("family",mxConstants.STYLE_FONTFAMILY,mxConstants.DEFAULT_FONTFAMILY)||c("size",mxConstants.STYLE_FONTSIZE,mxConstants.DEFAULT_FONTSIZE)||c("color",mxConstants.STYLE_FONTCOLOR,"black")||
+c("align",mxConstants.STYLE_ALIGN,"")||c("valign",mxConstants.STYLE_VERTICAL_ALIGN,"")||c("spacing",mxConstants.STYLE_SPACING,2)||c("spacingTop",mxConstants.STYLE_SPACING_TOP,0)||c("spacingRight",mxConstants.STYLE_SPACING_RIGHT,0)||c("spacingBottom",mxConstants.STYLE_SPACING_BOTTOM,0)||c("spacingLeft",mxConstants.STYLE_SPACING_LEFT,0)||c("horizontal",mxConstants.STYLE_HORIZONTAL,!0)||c("background",mxConstants.STYLE_LABEL_BACKGROUNDCOLOR)||c("border",mxConstants.STYLE_LABEL_BORDERCOLOR)||c("opacity",
+mxConstants.STYLE_TEXT_OPACITY,100)||c("textDirection",mxConstants.STYLE_TEXT_DIRECTION,mxConstants.DEFAULT_TEXT_DIRECTION)};mxCellRenderer.prototype.redrawLabelShape=function(a){a.redraw()};mxCellRenderer.prototype.getTextScale=function(a){return a.view.scale};
+mxCellRenderer.prototype.getLabelBounds=function(a){var b=a.view.graph,c=a.view.scale,d=b.getModel().isEdge(a.cell),e=new mxRectangle(a.absoluteOffset.x,a.absoluteOffset.y);if(d){var f=a.text.getSpacing();e.x+=f.x*c;e.y+=f.y*c;b=b.getCellGeometry(a.cell);null!=b&&(e.width=Math.max(0,b.width*c),e.height=Math.max(0,b.height*c))}else a.text.isPaintBoundsInverted()&&(b=e.x,e.x=e.y,e.y=b),e.x+=a.x,e.y+=a.y,e.width=Math.max(1,a.width),e.height=Math.max(1,a.height),b=mxUtils.getValue(a.style,mxConstants.STYLE_STROKECOLOR,
+mxConstants.NONE),b!=mxConstants.NONE&&""!=b&&(f=parseFloat(mxUtils.getValue(a.style,mxConstants.STYLE_STROKEWIDTH,1))*c,b=1+Math.floor((f-1)/2),f=Math.floor(f+1),e.x+=b,e.y+=b,e.width-=f,e.height-=f);a.text.isPaintBoundsInverted()&&(b=(a.width-a.height)/2,e.x+=b,e.y-=b,b=e.width,e.width=e.height,e.height=b);null!=a.shape&&(b=mxUtils.getValue(a.style,mxConstants.STYLE_LABEL_POSITION,mxConstants.ALIGN_CENTER),f=mxUtils.getValue(a.style,mxConstants.STYLE_VERTICAL_LABEL_POSITION,mxConstants.ALIGN_MIDDLE),
+b==mxConstants.ALIGN_CENTER&&f==mxConstants.ALIGN_MIDDLE&&(e=a.shape.getLabelBounds(e)));b=mxUtils.getValue(a.style,mxConstants.STYLE_LABEL_WIDTH,null);null!=b&&(e.width=parseFloat(b)*c);d||this.rotateLabelBounds(a,e);return e};
+mxCellRenderer.prototype.rotateLabelBounds=function(a,b){b.y-=a.text.margin.y*b.height;b.x-=a.text.margin.x*b.width;if(!this.legacySpacing||"fill"!=a.style[mxConstants.STYLE_OVERFLOW]&&"width"!=a.style[mxConstants.STYLE_OVERFLOW]){var c=a.view.scale,d=a.text.getSpacing();b.x+=d.x*c;b.y+=d.y*c;var d=mxUtils.getValue(a.style,mxConstants.STYLE_LABEL_POSITION,mxConstants.ALIGN_CENTER),e=mxUtils.getValue(a.style,mxConstants.STYLE_VERTICAL_LABEL_POSITION,mxConstants.ALIGN_MIDDLE),f=mxUtils.getValue(a.style,
+mxConstants.STYLE_LABEL_WIDTH,null);b.width=Math.max(0,b.width-(d==mxConstants.ALIGN_CENTER&&null==f?a.text.spacingLeft*c+a.text.spacingRight*c:0));b.height=Math.max(0,b.height-(e==mxConstants.ALIGN_MIDDLE?a.text.spacingTop*c+a.text.spacingBottom*c:0))}e=a.text.getTextRotation();0!=e&&null!=a&&a.view.graph.model.isVertex(a.cell)&&(c=a.getCenterX(),d=a.getCenterY(),b.x!=c||b.y!=d)&&(e*=Math.PI/180,pt=mxUtils.getRotatedPoint(new mxPoint(b.x,b.y),Math.cos(e),Math.sin(e),new mxPoint(c,d)),b.x=pt.x,b.y=
+pt.y)};
+mxCellRenderer.prototype.redrawCellOverlays=function(a,b){this.createCellOverlays(a);if(null!=a.overlays){var c=mxUtils.mod(mxUtils.getValue(a.style,mxConstants.STYLE_ROTATION,0),90),d=mxUtils.toRadians(c),e=Math.cos(d),f=Math.sin(d);a.overlays.visit(function(d,k){var g=k.overlay.getBounds(a);if(!a.view.graph.getModel().isEdge(a.cell)&&null!=a.shape&&0!=c){var m=g.getCenterX(),n=g.getCenterY(),n=mxUtils.getRotatedPoint(new mxPoint(m,n),e,f,new mxPoint(a.getCenterX(),a.getCenterY())),m=n.x,n=n.y;g.x=
+Math.round(m-g.width/2);g.y=Math.round(n-g.height/2)}if(b||null==k.bounds||k.scale!=a.view.scale||!k.bounds.equals(g))k.bounds=g,k.scale=a.view.scale,k.redraw()})}};
+mxCellRenderer.prototype.redrawControl=function(a,b){var c=a.view.graph.getFoldingImage(a);if(null!=a.control&&null!=c){var c=this.getControlBounds(a,c.width,c.height),d=this.legacyControlPosition?mxUtils.getValue(a.style,mxConstants.STYLE_ROTATION,0):a.shape.getTextRotation(),e=a.view.scale;if(b||a.control.scale!=e||!a.control.bounds.equals(c)||a.control.rotation!=d)a.control.rotation=d,a.control.bounds=c,a.control.scale=e,a.control.redraw()}};
+mxCellRenderer.prototype.getControlBounds=function(a,b,c){if(null!=a.control){var d=a.view.scale,e=a.getCenterX(),f=a.getCenterY();if(!a.view.graph.getModel().isEdge(a.cell)&&(e=a.x+b*d,f=a.y+c*d,null!=a.shape)){var g=a.shape.getShapeRotation();if(this.legacyControlPosition)g=mxUtils.getValue(a.style,mxConstants.STYLE_ROTATION,0);else if(a.shape.isPaintBoundsInverted())var k=(a.width-a.height)/2,e=e+k,f=f-k;0!=g&&(k=mxUtils.toRadians(g),g=Math.cos(k),k=Math.sin(k),f=mxUtils.getRotatedPoint(new mxPoint(e,
+f),g,k,new mxPoint(a.getCenterX(),a.getCenterY())),e=f.x,f=f.y)}return a.view.graph.getModel().isEdge(a.cell),new mxRectangle(Math.round(e-b/2*d),Math.round(f-c/2*d),Math.round(b*d),Math.round(c*d))}return null};
+mxCellRenderer.prototype.insertStateAfter=function(a,b,c){for(var d=this.getShapesForState(a),e=0;e<d.length;e++)if(null!=d[e]&&null!=d[e].node){var f=d[e].node.parentNode!=a.view.getDrawPane()&&d[e].node.parentNode!=a.view.getOverlayPane(),g=f?c:b;if(null!=g&&g.nextSibling!=d[e].node)null==g.nextSibling?g.parentNode.appendChild(d[e].node):g.parentNode.insertBefore(d[e].node,g.nextSibling);else if(null==g)if(d[e].node.parentNode==a.view.graph.container){for(g=a.view.canvas;null!=g&&g.parentNode!=
+a.view.graph.container;)g=g.parentNode;null!=g&&null!=g.nextSibling?g.nextSibling!=d[e].node&&d[e].node.parentNode.insertBefore(d[e].node,g.nextSibling):d[e].node.parentNode.appendChild(d[e].node)}else null!=d[e].node.parentNode.firstChild&&d[e].node.parentNode.firstChild!=d[e].node&&d[e].node.parentNode.insertBefore(d[e].node,d[e].node.parentNode.firstChild);f?c=d[e].node:b=d[e].node}return[b,c]};mxCellRenderer.prototype.getShapesForState=function(a){return[a.shape,a.text,a.control]};
+mxCellRenderer.prototype.redraw=function(a,b,c){b=this.redrawShape(a,b,c);null==a.shape||null!=c&&!c||(this.redrawLabel(a,b),this.redrawCellOverlays(a,b),this.redrawControl(a,b))};
+mxCellRenderer.prototype.redrawShape=function(a,b,c){var d=a.view.graph.model,e=!1;null!=a.shape&&null!=a.shape.style&&null!=a.style&&a.shape.style[mxConstants.STYLE_SHAPE]!=a.style[mxConstants.STYLE_SHAPE]&&(a.shape.destroy(),a.shape=null);null==a.shape&&null!=a.view.graph.container&&a.cell!=a.view.currentRoot&&(d.isVertex(a.cell)||d.isEdge(a.cell))?(a.shape=this.createShape(a),null!=a.shape&&(a.shape.minSvgStrokeWidth=this.minSvgStrokeWidth,a.shape.antiAlias=this.antiAlias,this.createIndicatorShape(a),
+this.initializeShape(a),this.createCellOverlays(a),this.installListeners(a),a.view.graph.selectionCellsHandler.updateHandler(a))):b||null==a.shape||mxUtils.equalEntries(a.shape.style,a.style)&&!this.checkPlaceholderStyles(a)||(a.shape.resetStyles(),this.configureShape(a),a.view.graph.selectionCellsHandler.updateHandler(a),b=!0);null!=a.shape&&(this.createControl(a),b||this.isShapeInvalid(a,a.shape))&&(null!=a.absolutePoints?(a.shape.points=a.absolutePoints.slice(),a.shape.bounds=null):(a.shape.points=
+null,a.shape.bounds=new mxRectangle(a.x,a.y,a.width,a.height)),a.shape.scale=a.view.scale,null==c||c?this.doRedrawShape(a):a.shape.updateBoundingBox(),e=!0);return e};mxCellRenderer.prototype.doRedrawShape=function(a){a.shape.redraw()};mxCellRenderer.prototype.isShapeInvalid=function(a,b){return null==b.bounds||b.scale!=a.view.scale||null==a.absolutePoints&&!b.bounds.equals(a)||null!=a.absolutePoints&&!mxUtils.equalPoints(b.points,a.absolutePoints)};
+mxCellRenderer.prototype.destroy=function(a){null!=a.shape&&(null!=a.text&&(a.text.destroy(),a.text=null),null!=a.overlays&&(a.overlays.visit(function(a,c){c.destroy()}),a.overlays=null),null!=a.control&&(a.control.destroy(),a.control=null),a.shape.destroy(),a.shape=null)};
+var mxEdgeStyle={EntityRelation:function(a,b,c,d,e){var f=a.view,g=f.graph;d=mxUtils.getValue(a.style,mxConstants.STYLE_SEGMENT,mxConstants.ENTITY_SEGMENT)*f.scale;var k=a.absolutePoints,l=k[0],m=k[k.length-1],k=!1;if(null!=l)b=new mxCellState,b.x=l.x,b.y=l.y;else if(null!=b){var n=mxUtils.getPortConstraints(b,a,!0,mxConstants.DIRECTION_MASK_NONE);n!=mxConstants.DIRECTION_MASK_NONE&&n!=mxConstants.DIRECTION_MASK_WEST+mxConstants.DIRECTION_MASK_EAST?k=n==mxConstants.DIRECTION_MASK_WEST:(l=g.getCellGeometry(b.cell),
+l.relative?k=.5>=l.x:null!=c&&(k=c.x+c.width<b.x))}else return;l=!0;null!=m?(c=new mxCellState,c.x=m.x,c.y=m.y):null!=c&&(n=mxUtils.getPortConstraints(c,a,!1,mxConstants.DIRECTION_MASK_NONE),n!=mxConstants.DIRECTION_MASK_NONE&&n!=mxConstants.DIRECTION_MASK_WEST+mxConstants.DIRECTION_MASK_EAST?l=n==mxConstants.DIRECTION_MASK_WEST:(a=g.getCellGeometry(c.cell),a.relative?l=.5>=a.x:null!=b&&(l=b.x+b.width<c.x)));null!=b&&null!=c&&(a=k?b.x:b.x+b.width,b=f.getRoutingCenterY(b),g=l?c.x:c.x+c.width,c=f.getRoutingCenterY(c),
+f=new mxPoint(a+(k?-d:d),b),m=new mxPoint(g+(l?-d:d),c),k==l?(d=k?Math.min(a,g)-d:Math.max(a,g)+d,e.push(new mxPoint(d,b)),e.push(new mxPoint(d,c))):(f.x<m.x==k?(d=b+(c-b)/2,e.push(f),e.push(new mxPoint(f.x,d)),e.push(new mxPoint(m.x,d))):e.push(f),e.push(m)))},Loop:function(a,b,c,d,e){c=a.absolutePoints;var f=c[c.length-1];if(null!=c[0]&&null!=f){if(null!=d&&0<d.length)for(b=0;b<d.length;b++)c=d[b],c=a.view.transformControlPoint(a,c),e.push(new mxPoint(c.x,c.y))}else if(null!=b){var f=a.view,g=f.graph;
+c=null!=d&&0<d.length?d[0]:null;null!=c&&(c=f.transformControlPoint(a,c),mxUtils.contains(b,c.x,c.y)&&(c=null));var k=d=0,l=0,m=0,g=mxUtils.getValue(a.style,mxConstants.STYLE_SEGMENT,g.gridSize)*f.scale;a=mxUtils.getValue(a.style,mxConstants.STYLE_DIRECTION,mxConstants.DIRECTION_WEST);a==mxConstants.DIRECTION_NORTH||a==mxConstants.DIRECTION_SOUTH?(d=f.getRoutingCenterX(b),k=g):(l=f.getRoutingCenterY(b),m=g);null==c||c.x<b.x||c.x>b.x+b.width?null!=c?(d=c.x,m=Math.max(Math.abs(l-c.y),m)):a==mxConstants.DIRECTION_NORTH?
+l=b.y-2*k:a==mxConstants.DIRECTION_SOUTH?l=b.y+b.height+2*k:d=a==mxConstants.DIRECTION_EAST?b.x-2*m:b.x+b.width+2*m:null!=c&&(d=f.getRoutingCenterX(b),k=Math.max(Math.abs(d-c.x),m),l=c.y,m=0);e.push(new mxPoint(d-k,l-m));e.push(new mxPoint(d+k,l+m))}},ElbowConnector:function(a,b,c,d,e){var f=null!=d&&0<d.length?d[0]:null,g=!1,k=!1;if(null!=b&&null!=c)if(null!=f)var l=Math.min(b.x,c.x),m=Math.max(b.x+b.width,c.x+c.width),k=Math.min(b.y,c.y),n=Math.max(b.y+b.height,c.y+c.height),f=a.view.transformControlPoint(a,
+f),g=f.y<k||f.y>n,k=f.x<l||f.x>m;else l=Math.max(b.x,c.x),m=Math.min(b.x+b.width,c.x+c.width),g=l==m,g||(k=Math.max(b.y,c.y),n=Math.min(b.y+b.height,c.y+c.height),k=k==n);k||!g&&a.style[mxConstants.STYLE_ELBOW]!=mxConstants.ELBOW_VERTICAL?mxEdgeStyle.SideToSide(a,b,c,d,e):mxEdgeStyle.TopToBottom(a,b,c,d,e)},SideToSide:function(a,b,c,d,e){var f=a.view;d=null!=d&&0<d.length?d[0]:null;var g=a.absolutePoints,k=g[0],g=g[g.length-1];null!=d&&(d=f.transformControlPoint(a,d));null!=k&&(b=new mxCellState,
+b.x=k.x,b.y=k.y);null!=g&&(c=new mxCellState,c.x=g.x,c.y=g.y);null!=b&&null!=c&&(a=Math.max(b.x,c.x),k=Math.min(b.x+b.width,c.x+c.width),a=null!=d?d.x:Math.round(k+(a-k)/2),k=f.getRoutingCenterY(b),f=f.getRoutingCenterY(c),null!=d&&(d.y>=b.y&&d.y<=b.y+b.height&&(k=d.y),d.y>=c.y&&d.y<=c.y+c.height&&(f=d.y)),mxUtils.contains(c,a,k)||mxUtils.contains(b,a,k)||e.push(new mxPoint(a,k)),mxUtils.contains(c,a,f)||mxUtils.contains(b,a,f)||e.push(new mxPoint(a,f)),1==e.length&&(null!=d?mxUtils.contains(c,a,
+d.y)||mxUtils.contains(b,a,d.y)||e.push(new mxPoint(a,d.y)):(f=Math.max(b.y,c.y),e.push(new mxPoint(a,f+(Math.min(b.y+b.height,c.y+c.height)-f)/2)))))},TopToBottom:function(a,b,c,d,e){var f=a.view;d=null!=d&&0<d.length?d[0]:null;var g=a.absolutePoints,k=g[0],g=g[g.length-1];null!=d&&(d=f.transformControlPoint(a,d));null!=k&&(b=new mxCellState,b.x=k.x,b.y=k.y);null!=g&&(c=new mxCellState,c.x=g.x,c.y=g.y);null!=b&&null!=c&&(k=Math.max(b.y,c.y),g=Math.min(b.y+b.height,c.y+c.height),a=f.getRoutingCenterX(b),
+null!=d&&d.x>=b.x&&d.x<=b.x+b.width&&(a=d.x),k=null!=d?d.y:Math.round(g+(k-g)/2),mxUtils.contains(c,a,k)||mxUtils.contains(b,a,k)||e.push(new mxPoint(a,k)),a=null!=d&&d.x>=c.x&&d.x<=c.x+c.width?d.x:f.getRoutingCenterX(c),mxUtils.contains(c,a,k)||mxUtils.contains(b,a,k)||e.push(new mxPoint(a,k)),1==e.length&&(null!=d&&1==e.length?mxUtils.contains(c,d.x,k)||mxUtils.contains(b,d.x,k)||e.push(new mxPoint(d.x,k)):(f=Math.max(b.x,c.x),e.push(new mxPoint(f+(Math.min(b.x+b.width,c.x+c.width)-f)/2,k)))))},
+SegmentConnector:function(a,b,c,d,e){function f(a){if(null==l||Math.abs(l.x-a.x)>=k||Math.abs(l.y-a.y)>=k)e.push(a),l=a;return l}var g=a.absolutePoints,k=Math.max(1,a.view.scale),l=0<e.length?e[0]:null,m=!0,n=null,p=g[0];null==p&&null!=b?p=new mxPoint(a.view.getRoutingCenterX(b),a.view.getRoutingCenterY(b)):null!=p&&(p=p.clone());p.x=Math.round(p.x);p.y=Math.round(p.y);var q=g.length-1;if(null!=d&&0<d.length){for(var n=[],r=0;r<d.length;r++){var t=a.view.transformControlPoint(a,d[r]);null!=t&&(t.x=
+Math.round(t.x),t.y=Math.round(t.y),n.push(t))}if(0==n.length)return;d=n;null!=p&&null!=d[0]&&(Math.abs(d[0].x-p.x)<k&&(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/dashboard/src/templates/resource/steps/define_hardware.html b/dashboard/src/templates/resource/steps/define_hardware.html
new file mode 100644
index 0000000..57078e9
--- /dev/null
+++ b/dashboard/src/templates/resource/steps/define_hardware.html
@@ -0,0 +1,24 @@
+{% 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://google.com">here</a>
+ for more details</h4>
+<form id="define_hardware_form" action="/wf/workflow/" method="post">
+ {% csrf_token %}
+ {{form.filter_field|default:"<p>No Form</p>"}}
+</form>
+{% endblock content %}
+{% block onleave %}
+multi_filter_widget.finish();
+var formData = $("#define_hardware_form").serialize();
+req = new XMLHttpRequest();
+req.open('POST', '/wf/workflow/', false);
+req.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
+req.send(formData);
+{% endblock %}
diff --git a/dashboard/src/templates/resource/steps/host_info.html b/dashboard/src/templates/resource/steps/host_info.html
new file mode 100644
index 0000000..bbbafdc
--- /dev/null
+++ b/dashboard/src/templates/resource/steps/host_info.html
@@ -0,0 +1,43 @@
+{% extends "workflow/viewport-element.html" %}
+{% load staticfiles %}
+
+{% load bootstrap4 %}
+
+{% block content %}
+
+{% if error %}
+<p>{{error}}</p>
+{% else %}
+
+
+<form id="host_meta_form" method="post" action="/wf/workflow/">
+ {% 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 %}
+
+{% block onleave %}
+var formData = $("#host_meta_form").serialize();
+var req = new XMLHttpRequest();
+req.open("POST", "/wf/workflow/", false);
+req.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
+req.onerror = function() { alert("There was a problem submitting the form"); }
+req.send(formData);
+{% endblock %}
diff --git a/dashboard/src/templates/resource/steps/meta_info.html b/dashboard/src/templates/resource/steps/meta_info.html
new file mode 100644
index 0000000..cebd343
--- /dev/null
+++ b/dashboard/src/templates/resource/steps/meta_info.html
@@ -0,0 +1,46 @@
+{% extends "workflow/viewport-element.html" %}
+{% load staticfiles %}
+
+{% load bootstrap4 %}
+
+{% block content %}
+
+<style>
+#resource_meta_form {
+ padding: 80px;
+ display: grid;
+}
+
+#resource_meta_form td > * {
+ width: 100%;
+ margin-bottom: 20px;
+ margin-top: 20px;
+}
+
+#resource_meta_form > table > tbody > tr {
+ border-bottom: 1px solid #cccccc;
+}
+
+#resource_meta_form > table > tbody > tr:last-child {
+ border-bottom: none;
+}
+
+</style>
+
+<form id="resource_meta_form" method="post" action="/wf/workflow/">
+ {% csrf_token %}
+ <table>
+ {{form}}
+ </table>
+</form>
+{% endblock content %}
+
+{% block onleave %}
+var ajaxForm = $("#resource_meta_form");
+var formData = ajaxForm.serialize();
+req = new XMLHttpRequest();
+req.open("POST", "/wf/workflow/", false);
+req.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
+req.onerror = function() { alert("problem submitting form"); }
+req.send(formData);
+{% endblock %}
diff --git a/dashboard/src/templates/resource/steps/pod_definition.html b/dashboard/src/templates/resource/steps/pod_definition.html
new file mode 100644
index 0000000..5826ccb
--- /dev/null
+++ b/dashboard/src/templates/resource/steps/pod_definition.html
@@ -0,0 +1,140 @@
+{% extends "workflow/viewport-element.html" %}
+{% block extrahead %}
+<link href="/static/css/graph_common.css" rel="stylesheet">
+<title>Pod Definition Prototype</title>
+
+<!-- Loads and initializes the library -->
+<script>
+ var mxLoadStylesheets = false;
+</script>
+<script type="text/javascript" src="/static/js/mxClient.min.js" ></script>
+<script type="text/javascript" src="/static/js/dashboard.js" ></script>
+{% endblock extrahead %}
+
+<!-- Calls the main function after the page has loaded. Container is dynamically created. -->
+{% block content %}
+ <div id="graphParent"
+ style="position:absolute;overflow:hidden;top:0px;bottom:0px;width:75%;left:0px;">
+ <div id="graphContainer"
+ style="position:relative;overflow:hidden;top:36px;bottom:0px;left:0px;right:0px;background-image:url('/static/img/mxgraph/grid.gif');cursor:default;">
+ </div>
+
+
+ <!-- Creates a container for the sidebar -->
+ <div id="toolbarContainer"
+ style="position:absolute;white-space:nowrap;overflow:hidden;top:0px;left:0px;right:0px;padding:6px;">
+ </div>
+
+ <!-- Creates a container for the outline -->
+ <div id="outlineContainer"
+ style="position:absolute;overflow:hidden;top:36px;right:0px;width:200px;height:140px;background:transparent;border-style:solid;border-color:black;">
+ </div>
+ </div>
+
+ <style>
+ p {
+ word-break: normal;
+ white-space: normal;
+ }
+ #network_select {
+ background: inherit;
+ padding: 0px;
+ padding-top: 0px;
+ }
+ #toolbarContainer {
+ background: #DDDDDD;
+ height: 36px;
+ }
+ #toolbar_extension {
+ height: 36px;
+ background: #DDDDDD;
+ }
+ #btn_add_network {
+ width: 100%;
+ }
+ #vlan_notice {
+ margin: 20px;
+ }
+ #network_list li {
+ border-radius: 2px;
+ margin: 5px;
+ padding: 5px;
+ vertical-align: middle;
+ background: #DDDDDD;
+ }
+ #network_list {
+ list-style-type: none;
+ padding: 0;
+ }
+ .colorblob {
+ width: 20px;
+ height: 20px;
+ border-radius: 50%;
+ display: inline-block;
+ vertical-align: middle;
+ }
+ .network_innertext {
+ display: inline-block;
+ padding-left: 10px;
+ vertical-align: middle;
+ padding-bottom: 0px;
+ margin-bottom: 2px;
+ }
+ .mxWindow {
+ background: #FFFFFF;
+ }
+ </style>
+
+ <div id="network_select" style="position:absolute;top:0px;bottom:0px;width:25%;right:0px;left:auto;">
+ <div id="toolbar_extension">
+ <button id="btn_add_network" type="button" class="btn btn-primary" onclick="network_step.newNetworkWindow();">Add Network</button>
+ </div>
+ <ul id="network_list">
+ </ul>
+ <button type="button" style="display: none" onclick="network_step.submitForm();">Submit</button>
+ </div>
+ <form id="xml_form" method="post" action="/wf/workflow/">
+ {% csrf_token %}
+ <input type="hidden" id="hidden_xml_input" name="xml" />
+ </form>
+
+<script>
+ //gather context data
+ let debug = false;
+ {% if debug %}
+ debug = true;
+ {% endif %}
+
+ let xml = '';
+ {% if xml %}
+ xml = '{{xml|safe}}';
+ {% endif %}
+
+ let hosts = [];
+ {% for host in hosts %}
+ hosts.push({{host|safe}});
+ {% endfor %}
+
+ let added_hosts = [];
+ {% for host in added_hosts %}
+ added_hosts.push({{host|safe}});
+ {% endfor %}
+
+ let removed_host_ids = {{removed_hosts|safe}};
+
+ network_step = new NetworkStep(
+ debug,
+ xml,
+ hosts,
+ added_hosts,
+ removed_host_ids,
+ document.getElementById('graphContainer'),
+ document.getElementById('outlineContainer'),
+ document.getElementById('toolbarContainer'),
+ document.getElementById('sidebarContainer')
+ );
+</script>
+{% endblock content %}
+{% block onleave %}
+network_step.submitForm();
+{% endblock %}
diff --git a/dashboard/src/templates/resource/uncommon.css b/dashboard/src/templates/resource/uncommon.css
new file mode 100644
index 0000000..1ea9f35
--- /dev/null
+++ b/dashboard/src/templates/resource/uncommon.css
@@ -0,0 +1,162 @@
+div.mxRubberband {
+ position: absolute;
+ overflow: hidden;
+ border-style: solid;
+ border-width: 1px;
+ border-color: #0000FF;
+ background: #0077FF;
+}
+.mxCellEditor {
+ background: url(data:image/gif;base64,R0lGODlhMAAwAIAAAP///wAAACH5BAEAAAAALAAAAAAwADAAAAIxhI+py+0Po5y02ouz3rz7D4biSJbmiabqyrbuC8fyTNf2jef6zvf+DwwKh8Si8egpAAA7);
+ _background: url('static/img/mxgraph/transparent.gif');
+ border-color: transparent;
+ border-style: solid;
+ display: inline-block;
+ position: absolute;
+ overflow: visible;
+ word-wrap: normal;
+ border-width: 0;
+ min-width: 1px;
+ resize: none;
+ padding: 0px;
+ margin: 0px;
+}
+.mxPlainTextEditor * {
+ padding: 0px;
+ margin: 0px;
+}
+div.mxWindow {
+ -webkit-box-shadow: 3px 3px 12px #C0C0C0;
+ -moz-box-shadow: 3px 3px 12px #C0C0C0;
+ box-shadow: 3px 3px 12px #C0C0C0;
+ background: url('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/dashboard/src/templates/snapshot_workflow/steps/meta.html b/dashboard/src/templates/snapshot_workflow/steps/meta.html
new file mode 100644
index 0000000..bea475d
--- /dev/null
+++ b/dashboard/src/templates/snapshot_workflow/steps/meta.html
@@ -0,0 +1,32 @@
+{% extends "workflow/viewport-element.html" %}
+{% load staticfiles %}
+
+{% load bootstrap4 %}
+
+{% block content %}
+<style>
+.meta_container {
+ padding: 50px;
+}
+</style>
+{% bootstrap_form_errors form type='non_fields' %}
+<div class="meta_container">
+ <form id="meta_form" action="/wf/workflow/" method="POST" class="form">
+ {% csrf_token %}
+ <div class="form-group">
+ {% bootstrap_field form.name %}
+ {% bootstrap_field form.description %}
+ </div>
+ </form>
+</div>
+{% endblock content %}
+
+{% block onleave %}
+var ajaxForm = $("#meta_form");
+var formData = ajaxForm.serialize();
+req = new XMLHttpRequest();
+req.open("POST", "/wf/workflow/", false);
+req.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
+req.onerror = function() { alert("problem submitting form"); }
+req.send(formData);
+{% endblock %}
diff --git a/dashboard/src/templates/snapshot_workflow/steps/select_host.html b/dashboard/src/templates/snapshot_workflow/steps/select_host.html
new file mode 100644
index 0000000..f438bac
--- /dev/null
+++ b/dashboard/src/templates/snapshot_workflow/steps/select_host.html
@@ -0,0 +1,128 @@
+{% extends "workflow/viewport-element.html" %}
+{% load staticfiles %}
+
+{% load bootstrap4 %}
+
+{% block content %}
+
+<style>
+ .booking {
+ border-style: none;
+ border-color: black;
+ border: 2px;
+ border-radius: 5px;
+ margin: 20px;
+ padding-left: 25px;
+ padding-right: 25px;
+ padding-bottom: 25px;
+ box-shadow: 0px 0px 7px 0px rgba(0,0,0,0.75);
+ transition-property: box-shadow;
+ transition-duration: 0.1s;
+ float: left;
+ }
+ .booking:hover {
+ box-shadow: 0px 0px 10px 0px rgba(0,0,0,0.75);
+ transition-property: box-shadow;
+ transition-duration: 0.1s;
+ }
+ .host {
+ cursor: pointer;
+ border-style: solid;
+ border-color: black;
+ border-width: 1px;
+ border-radius: 5px;
+ margin: 5px;
+ padding: 5px;
+ text-align: center;
+ box-shadow: 0px 0px 2px 0px rgba(0,0,0,0.75);
+ transition-property: box-shadow;
+ transition-duration: 0.1s;
+ }
+ .host:hover {
+ box-shadow: 0px 0px 4px 0px rgba(0,0,0,0.75);
+ transition-property: box-shadow;
+ transition-duration: 0.1s;
+ background-color: rgba(144,238,144,0.3);
+ }
+ .selected {
+ background-color: lightgreen !important;
+ }
+ .booking_container {
+ overflow: auto;
+ padding: 30px;
+ }
+</style>
+{% bootstrap_form_errors form type='non_fields' %}
+<form id="host_select_form" action="/wf/workflow/" method="POST" class="form">
+{% csrf_token %}
+<input type="hidden" id="hidden_json_input", name="host"/>
+</form>
+<div id="host_select_container" class="booking_container">
+</div>
+<script>
+var selected_host = null;
+var initial = {{chosen|safe|default:'null'}};
+
+function select(booking_id, host_name){
+ 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){
+ selected_host.classList.remove("selected");
+ }
+ selected_host = document.getElementById("booking_" + booking_id + "_host_" + host_name);
+ selected_host.classList.add("selected");
+}
+
+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){
+ var booking = document.createElement("DIV");
+ var heading = document.createElement("H3");
+ heading.appendChild(document.createTextNode("Booking " + booking_id));
+ booking.appendChild(heading);
+ booking.appendChild(document.createTextNode("start: " + booking_hosts[booking_id].start));
+ booking.appendChild(document.createElement("BR"));
+ booking.appendChild(document.createTextNode("end: " + booking_hosts[booking_id].end));
+ booking.appendChild(document.createElement("BR"));
+ booking.appendChild(document.createTextNode("purpose: " + booking_hosts[booking_id].purpose));
+ booking.appendChild(document.createElement("BR"));
+ booking.appendChild(document.createTextNode("hosts:"));
+ booking.id = "booking_" + booking_id;
+ booking.className = "booking";
+ var hosts = booking_hosts[booking_id].hosts;
+ for(var i=0; i<hosts.length; i++){
+ var host = document.createElement("DIV");
+ host.id = "booking_" + booking_id + "_host_" + hosts[i].name;
+ host.classList.add("host");
+ host.appendChild(document.createTextNode(hosts[i].name));
+ var hostname = hosts[i].name;
+ host.booking = booking_id;
+ host.hostname = hostname;
+ host.onclick = function() {
+ select(this.booking, this.hostname);
+ }
+ booking.appendChild(host);
+ }
+ bookings.push(booking);
+ container.appendChild(booking);
+ }
+}
+draw_bookings();
+if(initial){
+ select(initial.booking_id, initial.hostname);
+}
+</script>
+{% endblock content %}
+
+{% block onleave %}
+var ajaxForm = $("#host_select_form");
+var formData = ajaxForm.serialize();
+req = new XMLHttpRequest();
+req.open("POST", "/wf/workflow/", false);
+req.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
+req.onerror = function() { alert("problem submitting form"); }
+req.send(formData);
+{% endblock %}
diff --git a/dashboard/src/templates/workflow/confirm.html b/dashboard/src/templates/workflow/confirm.html
new file mode 100644
index 0000000..c1f3440
--- /dev/null
+++ b/dashboard/src/templates/workflow/confirm.html
@@ -0,0 +1,163 @@
+{% extends "workflow/viewport-element.html" %}
+{% load staticfiles %}
+
+{% load bootstrap4 %}
+
+{% block content %}
+
+<style>
+ #form_div {
+ width: 100%;
+ padding: 5%;
+ text-align: center;
+ }
+ #text_display {
+ text-align: left;
+ display: inline-block;
+ }
+ #text_wrapper {
+ text-align: center; /*centers child div*/
+ }
+ p {
+ margin:0;
+ padding:0;
+ }
+</style>
+
+<div style="text-align:center;">
+ <h3>Confirm Session</h3>
+</div>
+<div id="vlan_warning"></div>
+<form id="vlan_form" action="/wf/workflow/" method="post">
+ {% csrf_token %}
+ <input id="vlan_input" name="vlan_input" type="hidden"/>
+</form>
+<div id="text_wrapper">
+ <div id="text_display">
+ <pre>{{confirmation_info|escape}}</pre>
+ </div>
+</div>
+<div id="form_div">
+<form id="confirmation_form" action="/wf/workflow/" method="post">
+ {% csrf_token %}
+ <div style="display: none;">
+ {{form|default:"<p> No Form Loaded</p>"}}
+ </div>
+</form>
+<div class="cform_buttons">
+ <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 style="display: none;">
+<form id="manager_delete_form" action="/wf/workflow/finish/" method="post">
+ {% csrf_token %}
+</form>
+</div>
+
+<script>
+ var select = document.getElementById("id_confirm");
+
+ function processResponseText(json)
+ {
+ var dict = JSON.parse(json);
+
+ if( !dict["redir_url"] ) {
+ window.top.refresh_iframe();
+ } else {
+ top.window.location.href = dict["redir_url"];
+ }
+ }
+
+ function delete_manager()
+ {
+ var form = $("#manager_delete_form");
+ var formData = form.serialize();
+ var req = new XMLHttpRequest();
+ req.open("POST", "/wf/workflow/finish/", false);
+ req.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
+ req.onerror = function() { alert("problem with cleaning up session"); }
+ req.onreadystatechange = function() { if(req.readyState === 4 ) {
+ processResponseText(req.responseText);
+ }}
+ req.send(formData);
+ }
+
+ function submitForm()
+ {
+ var form = $("#confirmation_form");
+ var formData = form.serialize();
+ var req = new XMLHttpRequest();
+ req.open("POST", "/wf/workflow/", false);
+ req.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
+ req.onerror = function() { alert("problem submitting confirmation"); }
+ req.onreadystatechange = function() { if(req.readyState === 4 ) { delete_manager(); } }
+ req.send(formData);
+ }
+
+
+ function formconfirm()
+ {
+ select.value = "True";
+ submitForm();
+ }
+ function formcancel()
+ {
+ select.value = "False";
+ submitForm();
+ }
+
+ var confirmed = {{bypassed|default:"false"}};
+ if( confirmed )
+ {
+ delete_manager();
+ }
+</script>
+<script>
+
+function fixVlans() {
+ document.getElementById("vlan_input").value = "True";
+ var form = $("#vlan_form");
+ var formData = form.serialize();
+ var req = new XMLHttpRequest();
+ req.open("POST", "/wf/workflow/", false);
+ req.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
+ req.onerror = function() { alert("problem submitting form"); }
+ req.onreadystatechange = function() { //replaces current page with response
+ if(req.readyState === 4 ) {
+ var d = document.getElementById("vlan_warning").innerHTML = "";
+ document.getElementById("confirm_button").disabled = false;
+ document.getElementById("cancel_button").disabled = false;
+ }
+ }
+ req.send(formData);
+}
+var problem = {{vlan_warning|default:'false'}};
+if(problem){
+ var d = document.getElementById("vlan_warning");
+ var h3 = document.createElement("h3");
+ h3.innerHTML = "WARNING: Vlans not available";
+ var h4 = document.createElement("h4");
+ h4.innerHTML = "The vlans you selected are not currently available. Would you like to automatically change them?";
+ var button1 = document.createElement("button");
+ button1.innerHTML = "Correct Vlans For Me";
+ button1.onclick = function() { fixVlans(); }
+
+ var button2 = document.createElement("button");
+ button2.innerHTML = "Cancel. I will change my vlans";
+ button2.onclick = function() { formcancel(); }
+ d.appendChild(h3);
+ d.appendChild(h4);
+ d.appendChild(button1);
+ d.appendChild(button2);
+ document.getElementById("confirm_button").disabled = true;
+ document.getElementById("cancel_button").disabled = true;
+}
+</script>
+</div>
+{% block element_messages %}
+
+{% endblock element_messages %}
+{% endblock content %}
+{% block onleave %}
+{% endblock %}
diff --git a/dashboard/src/templates/workflow/no_workflow.html b/dashboard/src/templates/workflow/no_workflow.html
new file mode 100644
index 0000000..0ac6549
--- /dev/null
+++ b/dashboard/src/templates/workflow/no_workflow.html
@@ -0,0 +1,3 @@
+<script>
+ top.window.location.href='/';
+</script>
diff --git a/dashboard/src/templates/workflow/resource_select.html b/dashboard/src/templates/workflow/resource_select.html
new file mode 100644
index 0000000..cd04137
--- /dev/null
+++ b/dashboard/src/templates/workflow/resource_select.html
@@ -0,0 +1,50 @@
+{% extends "workflow/viewport-element.html" %}
+{% load staticfiles %}
+
+{% load bootstrap4 %}
+
+{% block content %}
+
+<p>resource selection template</p>
+
+<style>
+ .db_pane_wrapper{
+ display: grid;
+ grid-template-columns: 49% 2% 49%;
+ }
+ .divider{
+ border-style: solid;
+ height: 100vh;
+ }
+
+ .hidden_form{
+ display: none;
+ }
+</style>
+
+<div class="db_pane_wrapper">
+ <div class="pane_one">
+ <p>Select Resource</p>
+
+ </div>
+ <div class="divider">
+
+ </div>
+ <div class="pane_two">
+ <button>Create New Resource</button>
+ </div>
+</div>
+
+<div class="hidden_form" id="form_div">
+ <form method="post" action="" class="form" id="resource_selection_form">
+ {% csrf_token %}
+ {% bootstrap_field form.resourcebundle %}
+ {% buttons %}
+ <button type="submit" class="btn btn btn-success">
+ Confirm Edit
+ </button>
+ {% endbuttons %}
+ </form>
+</div>
+
+{% endblock content %} \ No newline at end of file
diff --git a/dashboard/src/templates/workflow/viewport-base.html b/dashboard/src/templates/workflow/viewport-base.html
new file mode 100644
index 0000000..aa01d7e
--- /dev/null
+++ b/dashboard/src/templates/workflow/viewport-base.html
@@ -0,0 +1,486 @@
+{% extends "base.html" %}
+{% load staticfiles %}
+
+{% load bootstrap4 %}
+
+{% block content %}
+
+<style>
+ .go_btn {
+
+ position: absolute;
+ width: 100px;
+ top: 170px;
+ height: calc(100% - 170px);
+
+ }
+
+ .go_btn_disabled {
+ background-color: #ffffff;
+ }
+
+ .go_forward {
+ right: 0px;
+ border-left: none;
+ }
+
+ .go_back {
+ left: 251px;
+ border-right: none;
+ }
+
+
+ .btn_wrapper {
+ text-align: center;
+ margin-bottom: 5px;
+
+ }
+
+ {% if DEBUG %}
+ .add_btn_wrapper {
+ right: 130px;
+ top: 10px;
+ position: absolute;
+ }
+ {% endif %}
+
+ #breadcrumbs {
+ margin-bottom: 0;
+ }
+
+ .btn_wrapper {
+ margin: 0;
+ }
+
+ .step {
+ display: inline;
+ padding: 7px;
+ margin: 1px;
+ font-size: 14pt;
+ cursor: default;
+ }
+
+ .step:active {
+ -webkit-box-shadow: inherit;
+ box-shadow: inherit;
+ }
+
+ .step_active:active {
+ -webkit-box-shadow: inherit;
+ box-shadow: inherit;
+ }
+
+ .step_active {
+ display: inline;
+ padding: 7px;
+ margin: 1px;
+ cursor: default;
+ font-size: 14pt;
+ padding-bottom: 4px !important;
+ border-bottom: 4px solid #41ba78 !important;
+ }
+
+ .step_hidden {
+ background: #EFEFEF;
+ color: #999999;
+ }
+
+ .step_invalid::after {
+ content: " \2612";
+ color: #CC3300;
+ }
+
+ .step_valid::after {
+ content: " \2611";
+ color: #41ba78;
+ }
+
+ .step_untouched::after {
+ content: " \2610";
+ }
+
+ .iframe_div {
+ width: calc(100% - 450px);
+ margin-left: 70px;
+ height: calc(100vh - 155px);
+ position: absolute;
+ border: none;
+ }
+
+ .iframe_elem {
+ width: 100%;
+ height: calc(100vh - 155px);
+ border: none;
+ }
+
+ #breadcrumbs {
+ background-color: inherit;
+ }
+
+ #breadcrumbs.breadcrumb>li {
+ border: 1px solid #cccccc;
+ border-left: none;
+ }
+
+ #breadcrumbs.breadcrumb>li:first-child {
+ border-left: 1px solid #cccccc;
+ }
+
+ #breadcrumbs.breadcrumb>li+li:before {
+ content: "";
+ width: 0;
+ margin: 0;
+ padding: 0;
+ }
+
+ #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;
+ }
+</style>
+<!-- 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">
+ <a class="page-link" href="#" id="gob" onclick="go('prev')">
+ <i class="fas fa-backward"></i> Back
+ </a>
+ </li>
+ <li class="page-item flex-grow-1 active">
+ <a class="page-link disabled" href="#">
+ Select <i class="far fa-check-square"></i>
+ </a>
+ </li>
+ <li class="page-item flex-grow-1">
+ <a class="page-link disabled" href="#">
+ Configure <i class="far fa-square"></i>
+ </a>
+ </li>
+ <li class="page-item flex-grow-1">
+ <a class="page-link disabled" href="#">
+ Information <i class="far fa-square"></i>
+ </a>
+ </li>
+ <li class="page-item flex-grow-1">
+ <a class="page-link disabled" href="#">
+ OPNFV <i class="far fa-square"></i>
+ </a>
+ </li>
+ <li class="page-item flex-grow-1">
+ <a class="page-link disabled" href="#">
+ Confirm <i class="far fa-square"></i>
+ </a>
+ </li>
+ <li class="page-item flex-shrink-1 page-control">
+ <a class="page-link text-right" href="#" id="gof" onclick="go('next')">
+ Next <i class="fas fa-forward"></i>
+ </a>
+ </li>
+ </ul>
+ </nav>
+ </div>
+</div>
+<!-- Top header -->
+<div class="row px-4">
+ <div class="col">
+ <div id="iframe_header" class="row view-header">
+ <div class="col-lg-12 step_header">
+ <h1 class="step_title d-inline-block" id="view_title"></h1>
+ <span class="description text-muted" id="view_desc"></span>
+ <p class="step_message" id="view_message"></p>
+ </div>
+ <script>
+ function update_description(title, desc) {
+ document.getElementById("view_title").innerText = title;
+ document.getElementById("view_desc").innerText = desc;
+ }
+
+ function update_message(message, stepstatus) {
+ document.getElementById("view_message").innerText = message;
+ document.getElementById("view_message").className = "step_message";
+ document.getElementById("view_message").classList.add("message_" + stepstatus);
+ }
+ </script>
+ <!-- /.col-lg-12 -->
+ </div>
+ </div>
+ <div class="col-auto align-self-center d-flex">
+ <button id="cancel_btn" class="btn btn-danger ml-auto" onclick="cancel_wf()">Cancel</button>
+ </div>
+</div>
+<!-- Content here -->
+<div class="row d-flex flex-column flex-grow-1">
+ <div class="container-fluid d-flex flex-column h-100">
+ <div class="row d-flex flex-grow-1 p-4">
+ <!-- iframe workflow -->
+ <div class="col-12 d-flex border flex-grow-1">
+ <!-- This was where the iframe went -->
+ <iframe src="/wf/workflow" class="w-100 h-100" scrolling="yes" id="viewport-iframe"
+ frameBorder="0"></iframe>
+ </div>
+ </div>
+ </div>
+</div>
+<div class="btn_wrapper">
+</div>
+{% csrf_token %}
+
+<script type="text/javascript">
+ update_context();
+ var step = 0;
+ var page_count = 0;
+ var context_data = false;
+
+ function go(to) {
+ step_on_leave();
+ request_leave(to);
+ }
+
+ function request_leave(to) {
+ $.ajax({
+ type: "GET",
+ url: "/wf/manager/",
+ beforeSend: function (request) {
+ request.setRequestHeader("X-CSRFToken",
+ $('input[name="csrfmiddlewaretoken"]').val());
+ },
+ success: function (data) {
+ confirm_permission(to, data);
+ update_page(data);
+ }
+ });
+ }
+
+ function confirm_permission(to, data) {
+ if (errors_exist(data)) {
+ if (to != "prev") {
+ return;
+ }
+ }
+
+ var problem = function () {
+ alert("There was a problem");
+ }
+ //makes an asynch request
+ req = new XMLHttpRequest();
+ url = "/wf/workflow/?step=" + to;
+ req.open("GET", url, true);
+ req.onload = function (e) {
+ if (req.readyState === 4) {
+ if (req.status < 300) {
+ document.getElementById("viewport-iframe").srcdoc = this.responseText;
+ } else {
+ problem();
+ }
+ } else {
+ problem();
+ }
+ }
+ req.onerror = problem;
+ req.send();
+ }
+
+ function step_on_leave() {
+ document.getElementById("viewport-iframe").contentWindow.step_on_leave();
+ }
+
+ function errors_exist(data) {
+ var stat = data['steps'][data['active']]['valid'];
+ if (stat >= 100 && stat < 200) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ function update_context() {
+ $.ajax({
+ type: "GET",
+ url: "/wf/manager/",
+ beforeSend: function (request) {
+ request.setRequestHeader("X-CSRFToken",
+ $('input[name="csrfmiddlewaretoken"]').val());
+ },
+ success: function (data) {
+ update_page(data);
+ }
+ });
+ }
+
+ function update_page(data) {
+ context_data = data;
+ update_breadcrumbs(data);
+ if (data["workflow_count"] == 1) {
+ document.getElementById("cancel_btn").innerText = "Exit Workflow";
+ } else {
+ document.getElementById("cancel_btn").innerText = "Return to Parent";
+ }
+ }
+
+ function update_breadcrumbs(meta_json) {
+ step = meta_json['active'];
+ page_count = meta_json['steps'].length;
+ if (step == 0) {
+ var btn = document.getElementById("gob");
+ btn.classList.add("invisible");
+ btn.disabled = true;
+ } else {
+ var btn = document.getElementById("gob");
+ btn.classList.remove("invisible");
+ btn.disabled = false;
+ }
+ if (step == page_count - 1) {
+ var btn = document.getElementById("gof");
+ btn.classList.add("invisible");
+ btn.disabled = true;
+ } else {
+ var btn = document.getElementById("gof");
+ btn.classList.remove("invisible");
+ btn.disabled = false;
+ }
+ //remove all children of breadcrumbs so we can redraw
+ $("#topPagination").children().not(".page-control").remove();
+ draw_steps(meta_json);
+ }
+
+ function draw_steps(meta_json) {
+ for (var i = 0; i < meta_json["steps"].length; i++) {
+ meta_json["steps"][i]["index"] = i;
+ var step_btn = create_step(meta_json["steps"][i], i == meta_json["active"]);
+ $("#topPagination li:last-child").before(step_btn);
+ }
+ }
+
+ function create_step(step_json, active) {
+ var step_dom = document.createElement("li");
+ // First create the dom object depending on active or not
+ if (active) {
+ step_dom.className = "topcrumb active";
+ } else {
+ step_dom.className = "topcrumb";
+ }
+ $(step_dom).html(`<span class="d-flex align-items-center justify-content-center text-capitalize w-100">${step_json['title']}</span>`)
+ var code = step_json['valid'];
+ stat = "";
+ 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);
+ }
+
+ var step_number = step_json['index'];
+ return step_dom;
+ }
+
+ function cancel_wf() {
+ var form = $("#workflow_pop_form");
+ var formData = form.serialize();
+ var req = new XMLHttpRequest();
+ req.open("POST", "/wf/workflow/finish/", 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) {
+ refresh_iframe();
+ }
+ };
+ req.send(formData);
+ }
+
+ function refresh_iframe() {
+ req = new XMLHttpRequest();
+ url = "/wf/workflow/";
+ req.open("GET", url, true);
+ req.onload = function (e) {
+ var doc = document.getElementById("viewport-iframe").contentWindow.document;
+ doc.open();
+ doc.write(this.responseText);
+ doc.close();
+ }
+ req.send();
+ }
+
+ function write_iframe(contents) {
+ document.getElementById("viewport-iframe").contentWindow.document.innerHTML = contents;
+ }
+
+ function redirect_root() {
+ window.location.replace('/wf/');
+ }
+
+ function add_wf(type) {
+ add_wf_internal(type, false);
+ }
+
+ function add_edit_wf(type, target) {
+ add_wf_internal(type, target);
+ }
+
+ function add_wf_internal(type, itemid) {
+ data = {
+ "add": type
+ };
+ if (itemid) {
+ data['target'] = itemid;
+ }
+ $.ajax({
+ type: "POST",
+ url: "/wf/manager/",
+ data: data,
+ beforeSend: function (request) {
+ request.setRequestHeader("X-CSRFToken",
+ $('input[name="csrfmiddlewaretoken"]').val()
+ );
+ },
+ success: refresh_wf_iframe()
+ });
+ }
+
+ function refresh_wf_iframe() {
+ window.location = window.location;
+ }
+</script>
+<div style="display: none;" id="workflow_pop_form_div">
+ <form id="workflow_pop_form" action="/wf/workflow/finish/" method="post">
+ {% csrf_token %}
+ </form>
+</div>
+{% endblock content %} \ No newline at end of file
diff --git a/dashboard/src/templates/workflow/viewport-element.html b/dashboard/src/templates/workflow/viewport-element.html
new file mode 100644
index 0000000..7a7165a
--- /dev/null
+++ b/dashboard/src/templates/workflow/viewport-element.html
@@ -0,0 +1,69 @@
+{% extends "layout.html" %}
+{% load bootstrap4 %}
+{% load staticfiles %}
+
+{% block basecontent %}
+
+ <div id="wrapper">
+ <!-- Page Content -->
+ <div id="page-wrapper">
+
+ {% block content %}
+
+ {% endblock content %}
+ </div>
+ <!-- /#page-wrapper -->
+ </div>
+ {% block vport_comm %}
+ <script type="text/javascript">
+ var step_count = {{ step_number|default:0 }};
+ var active_step = {{ active_step|default:0 }};
+ var render_correct = {{ render_correct|default:"false" }};
+ var title = "{{ step_title|default:"Workflow Step" }}";
+ var description = "{{ description|default:"Contact the admins, because this field should have something else filled in here" }}";
+ if(render_correct){
+ parent.update_context();
+ }
+ parent.update_description(title, description);
+ </script>
+
+ {% endblock vport_comm %}
+ {% block validate_step %}
+ <script>
+
+ function step_is_valid()
+ {
+ valid = confirm("Is this form valid?");
+ if( valid )
+ {
+ return true;
+ }
+ else{
+ return false;
+ }
+ }
+
+ function onError()
+ {
+ alert("Error: something!");
+ }
+ </script>
+
+ {% endblock validate_step %}
+
+ <script>
+ function step_on_leave() {
+ {% block onleave %}
+ alert("override onleave");
+ {% endblock %}
+ }
+ </script>
+
+ <div class="messages">
+ {% block element_messages %}
+ {% bootstrap_messages %}
+ {% endblock %}
+ </div>
+
+ <!-- /#wrapper -->
+{% endblock basecontent %}
diff --git a/dashboard/src/workflow/__init__.py b/dashboard/src/workflow/__init__.py
new file mode 100644
index 0000000..e0408fa
--- /dev/null
+++ b/dashboard/src/workflow/__init__.py
@@ -0,0 +1,8 @@
+##############################################################################
+# Copyright (c) 2018 Parker Berberian, Sawyer Bergeron, and others.
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+##############################################################################
diff --git a/dashboard/src/workflow/apps.py b/dashboard/src/workflow/apps.py
new file mode 100644
index 0000000..adc2738
--- /dev/null
+++ b/dashboard/src/workflow/apps.py
@@ -0,0 +1,15 @@
+##############################################################################
+# 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/dashboard/src/workflow/booking_workflow.py b/dashboard/src/workflow/booking_workflow.py
new file mode 100644
index 0000000..42372ce
--- /dev/null
+++ b/dashboard/src/workflow/booking_workflow.py
@@ -0,0 +1,220 @@
+##############################################################################
+# 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 messages
+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, SWConfigSelectorForm, BookingMetaForm, OPNFVSelectForm
+from resource_inventory.models import GenericResourceBundle, ConfigBundle, OPNFVConfig
+
+
+"""
+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_GRESOURCE_BUNDLE
+ self.confirm_key = self.workflow_type
+
+ def alert_bundle_missing(self):
+ self.set_invalid("Please select a valid resource bundle")
+
+ def get_form_queryset(self):
+ user = self.repo_get(self.repo.SESSION_USER)
+ qs = GenericResourceBundle.objects.filter(owner=user)
+ return qs
+
+ def get_page_context(self):
+ return {
+ 'select_type': 'resource',
+ 'select_type_title': 'Resource Bundle',
+ '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 SWConfig_Select(AbstractSelectOrCreate):
+ title = "Select Software Configuration"
+ description = "Choose the software and related configurations you want to have used for your deployment"
+ short_title = "pod config"
+ form = SWConfigSelectorForm
+
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+ self.select_repo_key = self.repo.SELECTED_CONFIG_BUNDLE
+ self.confirm_key = "booking"
+
+ def alert_bundle_missing(self):
+ self.set_invalid("Please select a valid pod config")
+
+ def get_form_queryset(self):
+ user = self.repo_get(self.repo.SESSION_USER)
+ grb = self.repo_get(self.repo.SELECTED_GRESOURCE_BUNDLE)
+ qs = ConfigBundle.objects.filter(owner=user).filter(bundle=grb)
+ return qs
+
+ 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]["Software Configuration"] = 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 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_render(self, request):
+ form = BookingMetaForm(data=request.POST, owner=request.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)
+ messages.add_message(request, messages.SUCCESS, 'Form Validated', fail_silently=True)
+ self.set_valid("Step Completed")
+ else:
+ messages.add_message(request, messages.ERROR, "Form didn't validate", fail_silently=True)
+ self.set_invalid("Please complete the fields highlighted in red to continue")
+ return self.render(request)
diff --git a/dashboard/src/workflow/forms.py b/dashboard/src/workflow/forms.py
new file mode 100644
index 0000000..ee44ecd
--- /dev/null
+++ b/dashboard/src/workflow/forms.py
@@ -0,0 +1,448 @@
+##############################################################################
+# 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
+##############################################################################
+
+
+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
+
+from account.models import Lab
+from account.models import UserProfile
+from resource_inventory.models import (
+ OPNFVRole,
+ Installer,
+ Scenario,
+)
+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 []
+ data_as_list = json.loads(data)
+ 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):
+
+ 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):
+ return json.loads(value)
+
+
+class FormUtils:
+ @staticmethod
+ def getLabData(multiple_hosts=False):
+ """
+ Gets all labs and thier host profiles and returns a serialized version the form can understand.
+ Should 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 = {}
+ hosts = {}
+ 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': false,
+ 'multiple': false,
+ 'class': 'lab'
+ }
+ if multiple_hosts:
+ # "follow" this lab node to discover more hosts if allowed
+ lab_node['follow'] = true
+ items[lab_node['id']] = lab_node
+ neighbors[lab_node['id']] = []
+ labs[lab_node['id']] = lab_node
+
+ for host in lab.hostprofiles.all():
+ host_node = {
+ 'form': {"name": "host_name", "type": "text", "placeholder": "hostname"},
+ 'id': "host_" + str(host.id),
+ 'model_id': host.id,
+ 'name': host.name,
+ 'description': host.description,
+ 'selected': false,
+ 'selectable': true,
+ 'follow': false,
+ 'multiple': multiple_hosts,
+ 'class': 'host'
+ }
+ if multiple_hosts:
+ host_node['values'] = [] # place to store multiple values
+ items[host_node['id']] = host_node
+ neighbors[lab_node['id']].append(host_node['id'])
+ if host_node['id'] not in neighbors:
+ neighbors[host_node['id']] = []
+ neighbors[host_node['id']].append(lab_node['id'])
+ hosts[host_node['id']] = host_node
+
+ display_objects = [("lab", labs.values()), ("host", hosts.values())]
+
+ context = {
+ 'display_objects': display_objects,
+ 'neighbors': neighbors,
+ 'filter_items': items
+ }
+ return context
+
+
+class HardwareDefinitionForm(forms.Form):
+
+ def __init__(self, *args, **kwargs):
+ super(HardwareDefinitionForm, self).__init__(*args, **kwargs)
+ attrs = FormUtils.getLabData(multiple_hosts=True)
+ 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)
+
+
+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):
+
+ host_name = forms.CharField(max_length=200, disabled=True, required=False)
+ 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=(
+ (True, "Confirm"),
+ (False, "Cancel")
+ )
+ )
+
+
+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/dashboard/src/workflow/models.py b/dashboard/src/workflow/models.py
new file mode 100644
index 0000000..6c6bd9a
--- /dev/null
+++ b/dashboard/src/workflow/models.py
@@ -0,0 +1,732 @@
+##############################################################################
+# Copyright (c) 2018 Sawyer Bergeron, Parker Berberian, and others.
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+##############################################################################
+
+
+from django.shortcuts import render
+from django.contrib import messages
+from django.http import HttpResponse
+from django.utils import timezone
+
+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, GenericInterface, OPNFVConfig, HostOPNFVConfig, 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():
+ 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):
+ """
+ will return the PTL in the INFO file on success, or None
+ """
+ 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):
+ """
+ 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.getHosts()) < 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):
+ 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):
+ self.context = self.get_context()
+ return render(request, self.template, self.context)
+
+ def post_render(self, request):
+ return self.render(request)
+
+ def test_render(self, request):
+ if request.method == "POST":
+ return self.post_render(request)
+ return self.render(request)
+
+ 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_render(self, request):
+ context = self.get_context()
+ form = self.form(request.POST, queryset=self.get_form_queryset())
+ if form.is_valid():
+ bundle = form.get_validated_bundle()
+ if not bundle:
+ self.alert_bundle_missing()
+ return render(request, self.template, context)
+ self.repo_put(self.select_repo_key, bundle)
+ self.put_confirm_info(bundle)
+ self.set_valid("Step Completed")
+ else:
+ self.alert_bundle_missing()
+ messages.add_message(request, messages.ERROR, "Form Didn't Validate", fail_silently=True)
+
+ return self.render(request)
+
+ 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()
+ context['confirmation_info'] = yaml.dump(
+ self.repo_get(self.repo.CONFIRMATION),
+ default_flow_style=False
+ ).strip()
+
+ return context
+
+ def flush_to_db(self):
+ errors = self.repo.make_models()
+ if errors:
+ return errors
+
+ def post_render(self, request):
+ form = ConfirmationForm(request.POST)
+ if form.is_valid():
+ data = form.cleaned_data['confirm']
+ context = self.get_context()
+ if data == "True":
+ context["bypassed"] = "true"
+ errors = self.flush_to_db()
+ if errors:
+ messages.add_message(request, messages.ERROR, "ERROR OCCURRED: " + errors)
+ else:
+ messages.add_message(request, messages.SUCCESS, "Confirmed")
+
+ return HttpResponse('')
+ elif data == "False":
+ context["bypassed"] = "true"
+ messages.add_message(request, messages.SUCCESS, "Canceled")
+ return render(request, self.template, context)
+ else:
+ pass
+
+ else:
+ pass
+
+
+class Repository():
+
+ EDIT = "editing"
+ MODELS = "models"
+ RESOURCE_SELECT = "resource_select"
+ CONFIRMATION = "confirmation"
+ SELECTED_GRESOURCE_BUNDLE = "selected generic bundle pk"
+ SELECTED_CONFIG_BUNDLE = "selected config bundle pk"
+ SELECTED_OPNFV_CONFIG = "selected opnfv deployment config"
+ GRESOURCE_BUNDLE_MODELS = "generic_resource_bundle_models"
+ GRESOURCE_BUNDLE_INFO = "generic_resource_bundle_info"
+ BOOKING = "booking"
+ LAB = "lab"
+ GRB_LAST_HOSTLIST = "grb_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"
+
+ # 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_GRESOURCE_BUNDLE, 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 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.GRESOURCE_BUNDLE_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_GRESOURCE_BUNDLE
+ return
+
+ if self.CONFIG_MODELS in self.el:
+ errors = self.make_software_config_bundle()
+ if errors:
+ return errors
+ else:
+ self.el[self.HAS_RESULT] = True
+ self.el[self.RESULT_KEY] = self.SELECTED_CONFIG_BUNDLE
+ 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.GRESOURCE_BUNDLE_MODELS in self.el:
+ models = self.el[self.GRESOURCE_BUNDLE_MODELS]
+ if 'hosts' in models:
+ hosts = models['hosts']
+ else:
+ return "GRB has no hosts. CODE:0x0002"
+ if 'bundle' in models:
+ bundle = models['bundle']
+ else:
+ return "GRB, no bundle in models. CODE:0x0003"
+
+ try:
+ bundle.owner = owner
+ bundle.save()
+ except Exception as e:
+ return "GRB, saving bundle generated exception: " + str(e) + " CODE:0x0004"
+ try:
+ for host in hosts:
+ genericresource = host.resource
+ genericresource.bundle = bundle
+ genericresource.save()
+ host.resource = genericresource
+ host.save()
+ except Exception as e:
+ return "GRB, saving hosts generated exception: " + str(e) + " CODE:0x0005"
+
+ if 'networks' in models:
+ for net in models['networks'].values():
+ net.bundle = bundle
+ net.save()
+
+ if 'interfaces' in models:
+ for interface_set in models['interfaces'].values():
+ for interface in interface_set:
+ try:
+ interface.host = interface.host
+ interface.save()
+ except Exception:
+ return "GRB, saving interface " + str(interface) + " failed. CODE:0x0019"
+ else:
+ return "GRB, no interface set provided. CODE:0x001a"
+
+ if 'connections' in models:
+ for resource_name, mapping in models['connections'].items():
+ for profile_name, connection_set in mapping.items():
+ interface = GenericInterface.objects.get(
+ profile__name=profile_name,
+ host__resource__name=resource_name,
+ host__resource__bundle=models['bundle']
+ )
+ for connection in connection_set:
+ try:
+ connection.network = connection.network
+ connection.save()
+ interface.connections.add(connection)
+ except Exception as e:
+ return "GRB, saving vlan " + str(connection) + " failed. Exception: " + str(e) + ". CODE:0x0017"
+ else:
+ return "GRB, no vlan set provided. CODE:0x0018"
+
+ else:
+ return "GRB no models given. CODE:0x0001"
+
+ self.el[self.RESULT] = bundle
+ self.el[self.HAS_RESULT] = True
+ return False
+
+ 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_GRESOURCE_BUNDLE]
+ 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.bundle = host_config.bundle
+ host_config.host = host_config.host
+ 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
+
+ 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_GRESOURCE_BUNDLE in self.el:
+ selected_grb = self.el[self.SELECTED_GRESOURCE_BUNDLE]
+ else:
+ return "BOOK, no selected resource. CODE:0x000e"
+
+ if self.SELECTED_CONFIG_BUNDLE not in self.el:
+ return "BOOK, no selected config bundle. CODE:0x001f"
+
+ booking.config_bundle = self.el[self.SELECTED_CONFIG_BUNDLE]
+
+ 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:
+ resource_bundle = ResourceManager.getInstance().convertResourceBundle(selected_grb, config=booking.config_bundle)
+ 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']
+ )
+ HostOPNFVConfig.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/dashboard/src/workflow/opnfv_workflow.py b/dashboard/src/workflow/opnfv_workflow.py
new file mode 100644
index 0000000..7d499ec
--- /dev/null
+++ b/dashboard/src/workflow/opnfv_workflow.py
@@ -0,0 +1,299 @@
+##############################################################################
+# 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 ConfigBundle, 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 = ConfigBundle.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_render(self, request):
+ form = OPNFVSelectionForm(request.POST)
+ 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")
+
+ return self.render(request)
+
+
+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_render(self, request):
+ 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=request.POST)
+ 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")
+ return self.render(request)
+
+
+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.getHosts())
+ 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_render(self, request):
+ formset = self.create_host_role_formset(data=request.POST)
+
+ 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")
+
+ return self.render(request)
+
+
+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_render(self, request):
+ models = self.repo_get(self.repo.OPNFV_MODELS, {})
+ info = models.get("meta", {})
+
+ form = BasicMetaForm(request.POST)
+ 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)
+ return self.render(request)
diff --git a/dashboard/src/workflow/resource_bundle_workflow.py b/dashboard/src/workflow/resource_bundle_workflow.py
new file mode 100644
index 0000000..06737d2
--- /dev/null
+++ b/dashboard/src/workflow/resource_bundle_workflow.py
@@ -0,0 +1,463 @@
+##############################################################################
+# 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 render
+from django.forms import formset_factory
+from django.conf import settings
+
+import json
+import re
+from xml.dom import minidom
+
+from workflow.models import WorkflowStep
+from account.models import Lab
+from workflow.forms import (
+ HardwareDefinitionForm,
+ NetworkDefinitionForm,
+ ResourceMetaForm,
+ GenericHostMetaForm
+)
+from resource_inventory.models import (
+ GenericResourceBundle,
+ GenericInterface,
+ GenericHost,
+ GenericResource,
+ HostProfile,
+ Network,
+ NetworkConnection
+)
+from dashboard.exceptions import (
+ InvalidVlanConfigurationException,
+ NetworkExistsException,
+ InvalidHostnameException,
+ NonUniqueHostnameException,
+ 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()
+ context['form'] = self.form or HardwareDefinitionForm()
+ return context
+
+ def update_models(self, data):
+ data = data['filter_field']
+ models = self.repo_get(self.repo.GRESOURCE_BUNDLE_MODELS, {})
+ models['hosts'] = [] # This will always clear existing data when this step changes
+ models['interfaces'] = {}
+ if "bundle" not in models:
+ models['bundle'] = GenericResourceBundle(owner=self.repo_get(self.repo.SESSION_USER))
+ host_data = data['host']
+ names = {}
+ for host_profile_dict in host_data.values():
+ id = host_profile_dict['id']
+ profile = HostProfile.objects.get(id=id)
+ # instantiate genericHost and store in repo
+ for name in host_profile_dict['values'].values():
+ if not re.match(r"(?=^.{1,253}$)(^([A-Za-z0-9-_]{1,62}\.)*[A-Za-z0-9-_]{1,63})", name):
+ raise InvalidHostnameException("Invalid hostname: '" + name + "'")
+ if name in names:
+ raise NonUniqueHostnameException("All hosts must have unique names")
+ names[name] = True
+ genericResource = GenericResource(bundle=models['bundle'], name=name)
+ genericHost = GenericHost(profile=profile, resource=genericResource)
+ models['hosts'].append(genericHost)
+ for interface_profile in profile.interfaceprofile.all():
+ genericInterface = GenericInterface(profile=interface_profile, host=genericHost)
+ if genericHost.resource.name not in models['interfaces']:
+ models['interfaces'][genericHost.resource.name] = []
+ models['interfaces'][genericHost.resource.name].append(genericInterface)
+
+ # add selected lab to models
+ for lab_dict in data['lab'].values():
+ if lab_dict['selected']:
+ models['bundle'].lab = Lab.objects.get(lab_user__id=lab_dict['id'])
+ break # if somehow we get two 'true' labs, we only use one
+
+ # return to repo
+ self.repo_put(self.repo.GRESOURCE_BUNDLE_MODELS, models)
+
+ def update_confirmation(self):
+ confirm = self.repo_get(self.repo.CONFIRMATION, {})
+ if "resource" not in confirm:
+ confirm['resource'] = {}
+ confirm['resource']['hosts'] = []
+ models = self.repo_get(self.repo.GRESOURCE_BUNDLE_MODELS, {"hosts": []})
+ for host in models['hosts']:
+ host_dict = {"name": host.resource.name, "profile": host.profile.name}
+ confirm['resource']['hosts'].append(host_dict)
+ if "lab" in models:
+ confirm['resource']['lab'] = models['lab'].lab_user.username
+ self.repo_put(self.repo.CONFIRMATION, confirm)
+
+ def post_render(self, request):
+ try:
+ self.form = HardwareDefinitionForm(request.POST)
+ 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:
+ self.set_invalid(str(e))
+ self.context = self.get_context()
+ return render(request, self.template, self.context)
+
+
+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.GRESOURCE_BUNDLE_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_vlan(count=lab.vlan_manager.block_size)
+ self.repo_put(self.repo.VLANS, vlans)
+ return vlans
+ except Exception:
+ return None
+
+ def make_mx_host_dict(self, generic_host):
+ host = {
+ 'id': generic_host.resource.name,
+ 'interfaces': [],
+ 'value': {
+ "name": generic_host.resource.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
+
+ def get_context(self):
+ context = super(Define_Nets, self).get_context()
+ context.update({
+ 'form': NetworkDefinitionForm(),
+ 'debug': settings.DEBUG,
+ 'hosts': [],
+ 'added_hosts': [],
+ 'removed_hosts': []
+ })
+ vlans = self.get_vlans()
+ if vlans:
+ context['vlans'] = vlans
+ try:
+ models = self.repo_get(self.repo.GRESOURCE_BUNDLE_MODELS, {})
+ hosts = models.get("hosts", [])
+ # calculate if the selected hosts have changed
+ added_hosts = set()
+ host_set = set(self.repo_get(self.repo.GRB_LAST_HOSTLIST, []))
+ if len(host_set):
+ new_host_set = set([h.resource.name + "*" + h.profile.name for h in models['hosts']])
+ context['removed_hosts'] = [h.split("*")[0] for h in (host_set - new_host_set)]
+ added_hosts.update([h.split("*")[0] for h in (new_host_set - host_set)])
+
+ # add all host info to context
+ for generic_host in hosts:
+ host = self.make_mx_host_dict(generic_host)
+ host_serialized = json.dumps(host)
+ context['hosts'].append(host_serialized)
+ if host['id'] in added_hosts:
+ context['added_hosts'].append(host_serialized)
+ bundle = models.get("bundle", False)
+ if bundle:
+ context['xml'] = bundle.xml or False
+
+ except Exception:
+ pass
+
+ return context
+
+ def post_render(self, request):
+ models = self.repo_get(self.repo.GRESOURCE_BUNDLE_MODELS, {})
+ if 'hosts' in models:
+ host_set = set([h.resource.name + "*" + h.profile.name for h in models['hosts']])
+ self.repo_put(self.repo.GRB_LAST_HOSTLIST, host_set)
+ try:
+ xmlData = request.POST.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:
+ self.set_invalid("An error occurred when applying networks: " + str(e))
+ return self.render(request)
+
+ def updateModels(self, xmlData):
+ models = self.repo_get(self.repo.GRESOURCE_BUNDLE_MODELS, {})
+ models["connections"] = {}
+ models['networks'] = {}
+ given_hosts, interfaces, networks = self.parseXml(xmlData)
+ existing_host_list = models.get("hosts", [])
+ existing_hosts = {} # maps id to host
+ for host in existing_host_list:
+ existing_hosts[host.resource.name] = host
+
+ bundle = models.get("bundle", GenericResourceBundle(owner=self.repo_get(self.repo.SESSION_USER)))
+
+ for net_id, net in networks.items():
+ network = Network()
+ network.name = net['name']
+ network.bundle = bundle
+ network.is_public = net['public']
+ models['networks'][net_id] = network
+
+ for hostid, given_host in given_hosts.items():
+ existing_host = existing_hosts[hostid[5:]]
+
+ for ifaceId in given_host['interfaces']:
+ iface = interfaces[ifaceId]
+ if existing_host.resource.name not in models['connections']:
+ models['connections'][existing_host.resource.name] = {}
+ models['connections'][existing_host.resource.name][iface['profile_name']] = []
+ for connection in iface['connections']:
+ network_id = connection['network']
+ net = models['networks'][network_id]
+ connection = NetworkConnection(vlan_is_tagged=connection['tagged'], network=net)
+ models['connections'][existing_host.resource.name][iface['profile_name']].append(connection)
+ bundle.xml = xmlData
+ self.repo_put(self.repo.GRESOURCE_BUNDLE_MODELS, models)
+
+ def decomposeXml(self, xmlString):
+ """
+ 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, "profile_name": 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 = {"name": cellId, "connections": [], "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['name'] in untagged_ifaces:
+ raise InvalidVlanConfigurationException("More than one untagged vlan on an interface")
+ untagged_ifaces.add(interface['name'])
+
+ # 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 get_context(self):
+ context = super(Resource_Meta_Info, self).get_context()
+ name = ""
+ desc = ""
+ bundle = self.repo_get(self.repo.GRESOURCE_BUNDLE_MODELS, {}).get("bundle", False)
+ if bundle and bundle.name:
+ name = bundle.name
+ desc = bundle.description
+ context['form'] = ResourceMetaForm(initial={"bundle_name": name, "bundle_description": desc})
+ return context
+
+ def post_render(self, request):
+ form = ResourceMetaForm(request.POST)
+ if form.is_valid():
+ models = self.repo_get(self.repo.GRESOURCE_BUNDLE_MODELS, {})
+ name = form.cleaned_data['bundle_name']
+ desc = form.cleaned_data['bundle_description']
+ bundle = models.get("bundle", GenericResourceBundle(owner=self.repo_get(self.repo.SESSION_USER)))
+ bundle.name = name
+ bundle.description = desc
+ models['bundle'] = bundle
+ self.repo_put(self.repo.GRESOURCE_BUNDLE_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 correct the fields highlighted in red to continue")
+ pass
+ return self.render(request)
+
+
+class Host_Meta_Info(WorkflowStep):
+ template = "resource/steps/host_info.html"
+ title = "Host Info"
+ description = "We need a little bit of information about your chosen machines"
+ short_title = "host info"
+
+ def __init__(self, *args, **kwargs):
+ super(Host_Meta_Info, self).__init__(*args, **kwargs)
+ self.formset = formset_factory(GenericHostMetaForm, extra=0)
+
+ def get_context(self):
+ context = super(Host_Meta_Info, self).get_context()
+ GenericHostFormset = self.formset
+ models = self.repo_get(self.repo.GRESOURCE_BUNDLE_MODELS, {})
+ initial_data = []
+ if "hosts" not in models:
+ context['error'] = "Please go back and select your hosts"
+ else:
+ for host in models['hosts']:
+ profile = host.profile.name
+ name = host.resource.name
+ if not name:
+ name = ""
+ initial_data.append({"host_profile": profile, "host_name": name})
+ context['formset'] = GenericHostFormset(initial=initial_data)
+ return context
+
+ def post_render(self, request):
+ models = self.repo_get(self.repo.GRESOURCE_BUNDLE_MODELS, {})
+ if 'hosts' not in models:
+ models['hosts'] = []
+ hosts = models['hosts']
+ i = 0
+ confirm_hosts = []
+ GenericHostFormset = self.formset
+ formset = GenericHostFormset(request.POST)
+ if formset.is_valid():
+ for form in formset:
+ host = hosts[i]
+ host.resource.name = form.cleaned_data['host_name']
+ i += 1
+ confirm_hosts.append({"name": host.resource.name, "profile": host.profile.name})
+ models['hosts'] = hosts
+ self.repo_put(self.repo.GRESOURCE_BUNDLE_MODELS, models)
+ confirm = self.repo_get(self.repo.CONFIRMATION, {})
+ if "resource" not in confirm:
+ confirm['resource'] = {}
+ confirm['resource']['hosts'] = confirm_hosts
+ self.repo_put(self.repo.CONFIRMATION, confirm)
+ else:
+ pass
+ return self.render(request)
diff --git a/dashboard/src/workflow/snapshot_workflow.py b/dashboard/src/workflow/snapshot_workflow.py
new file mode 100644
index 0000000..5414784
--- /dev/null
+++ b/dashboard/src/workflow/snapshot_workflow.py
@@ -0,0 +1,119 @@
+##############################################################################
+# 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 Host, 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.getHosts():
+ 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_render(self, request):
+ host_data = request.POST.get("host")
+ if not host_data:
+ self.set_invalid("Please select a host")
+ return self.render(request)
+ host = json.loads(host_data)
+ if 'name' not in host or 'booking' not in host:
+ self.set_invalid("Invalid host selected")
+ return self.render(request)
+ name = host['name']
+ booking_id = host['booking']
+ booking = Booking.objects.get(pk=booking_id)
+ host = Host.objects.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")
+ return self.render(request)
+
+
+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_render(self, request):
+ form = BasicMetaForm(request.POST)
+ 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")
+
+ return self.render(request)
diff --git a/dashboard/src/workflow/sw_bundle_workflow.py b/dashboard/src/workflow/sw_bundle_workflow.py
new file mode 100644
index 0000000..0c558fc
--- /dev/null
+++ b/dashboard/src/workflow/sw_bundle_workflow.py
@@ -0,0 +1,198 @@
+##############################################################################
+# 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.forms import formset_factory
+
+from workflow.models import WorkflowStep
+from workflow.forms import BasicMetaForm, HostSoftwareDefinitionForm
+from workflow.booking_workflow import Abstract_Resource_Select
+from resource_inventory.models import Image, GenericHost, ConfigBundle, HostConfiguration
+
+
+class SWConf_Resource_Select(Abstract_Resource_Select):
+ workflow_type = "configuration"
+
+
+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):
+ """
+ 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.SELECTED_GRESOURCE_BUNDLE).lab
+ for i, host_data in enumerate(hosts_data):
+ host = GenericHost.objects.get(pk=host_data['host_id'])
+ wrong_owner = Image.objects.exclude(owner=user).exclude(public=True)
+ wrong_host = Image.objects.exclude(host_type=host.profile)
+ 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 = []
+ host_configs = self.repo_get(self.repo.CONFIG_MODELS, {}).get("host_configs", False)
+ if host_configs:
+ for config in host_configs:
+ hosts_initial.append({
+ 'host_id': config.host.id,
+ 'host_name': config.host.resource.name,
+ 'headnode': config.is_head_node,
+ 'image': config.image
+ })
+ else:
+ for host in hostlist:
+ hosts_initial.append({
+ 'host_id': host.id,
+ 'host_name': host.resource.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):
+ if grb is None:
+ grb = self.repo_get(self.repo.SELECTED_GRESOURCE_BUNDLE, False)
+ if not grb:
+ return []
+ if grb.id:
+ return GenericHost.objects.filter(resource__bundle=grb)
+ generic_hosts = self.repo_get(self.repo.GRESOURCE_BUNDLE_MODELS, {}).get("hosts", [])
+ return generic_hosts
+
+ def get_context(self):
+ context = super(Define_Software, self).get_context()
+
+ grb = self.repo_get(self.repo.SELECTED_GRESOURCE_BUNDLE, False)
+
+ if grb:
+ context["grb"] = grb
+ formset = self.create_hostformset(self.get_host_list(grb))
+ context["formset"] = formset
+ context['headnode'] = self.repo_get(self.repo.CONFIG_MODELS, {}).get("headnode_index", 1)
+ else:
+ context["error"] = "Please select a resource first"
+ self.set_invalid("Step requires information that is not yet provided by previous step")
+
+ return context
+
+ def post_render(self, request):
+ models = self.repo_get(self.repo.CONFIG_MODELS, {})
+ if "bundle" not in models:
+ models['bundle'] = ConfigBundle(owner=self.repo_get(self.repo.SESSION_USER))
+
+ confirm = self.repo_get(self.repo.CONFIRMATION, {})
+
+ hosts = self.get_host_list()
+ models['headnode_index'] = request.POST.get("headnode", 1)
+ formset = self.create_hostformset(hosts, data=request.POST)
+ has_headnode = False
+ if formset.is_valid():
+ models['host_configs'] = []
+ confirm_hosts = []
+ for i, form in enumerate(formset):
+ host = hosts[i]
+ image = form.cleaned_data['image']
+ headnode = form.cleaned_data['headnode']
+ if headnode:
+ has_headnode = True
+ bundle = models['bundle']
+ hostConfig = HostConfiguration(
+ host=host,
+ image=image,
+ bundle=bundle,
+ is_head_node=headnode
+ )
+ models['host_configs'].append(hostConfig)
+ confirm_hosts.append({
+ "name": host.resource.name,
+ "image": image.name,
+ "headnode": headnode
+ })
+
+ if not has_headnode:
+ self.set_invalid('Must have one "Headnode" per POD')
+ return self.render(request)
+
+ self.repo_put(self.repo.CONFIG_MODELS, models)
+ if "configuration" not in confirm:
+ confirm['configuration'] = {}
+ confirm['configuration']['hosts'] = confirm_hosts
+ self.repo_put(self.repo.CONFIRMATION, confirm)
+ self.set_valid("Completed")
+ else:
+ self.set_invalid("Please complete all fields")
+
+ return self.render(request)
+
+
+class Config_Software(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(Config_Software, self).get_context()
+
+ initial = {}
+ models = self.repo_get(self.repo.CONFIG_MODELS, {})
+ bundle = models.get("bundle", False)
+ if bundle:
+ initial['name'] = bundle.name
+ initial['description'] = bundle.description
+ context["form"] = BasicMetaForm(initial=initial)
+ return context
+
+ def post_render(self, request):
+ models = self.repo_get(self.repo.CONFIG_MODELS, {})
+ if "bundle" not in models:
+ models['bundle'] = ConfigBundle(owner=self.repo_get(self.repo.SESSION_USER))
+
+ confirm = self.repo_get(self.repo.CONFIRMATION, {})
+ if "configuration" not in confirm:
+ confirm['configuration'] = {}
+
+ form = BasicMetaForm(request.POST)
+ if form.is_valid():
+ models['bundle'].name = form.cleaned_data['name']
+ models['bundle'].description = form.cleaned_data['description']
+
+ confirm['configuration']['name'] = form.cleaned_data['name']
+ confirm['configuration']['description'] = form.cleaned_data['description']
+ self.set_valid("Complete")
+ else:
+ self.set_invalid("Please correct the errors shown below")
+
+ self.repo_put(self.repo.CONFIG_MODELS, models)
+ self.repo_put(self.repo.CONFIRMATION, confirm)
+
+ return self.render(request)
diff --git a/dashboard/dashboard_notification/__init__.py b/dashboard/src/workflow/tests/__init__.py
index b5914ce..4f0437d 100644
--- a/dashboard/dashboard_notification/__init__.py
+++ b/dashboard/src/workflow/tests/__init__.py
@@ -1,10 +1,8 @@
##############################################################################
-# Copyright (c) 2016 Max Breitenfeldt and others.
+# Copyright (c) 2018 Parker Berberian, Sawyer Bergeron and others.
#
# All rights reserved. This program and the accompanying materials
# are made available under the terms of the Apache License, Version 2.0
# which accompanies this distribution, and is available at
# http://www.apache.org/licenses/LICENSE-2.0
##############################################################################
-
-
diff --git a/dashboard/src/workflow/tests/constants.py b/dashboard/src/workflow/tests/constants.py
new file mode 100644
index 0000000..f94a949
--- /dev/null
+++ b/dashboard/src/workflow/tests/constants.py
@@ -0,0 +1,198 @@
+##############################################################################
+# 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/dashboard/src/workflow/tests/test_steps.py b/dashboard/src/workflow/tests/test_steps.py
new file mode 100644
index 0000000..380102a
--- /dev/null
+++ b/dashboard/src/workflow/tests/test_steps.py
@@ -0,0 +1,281 @@
+##############################################################################
+# 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 dashboard.populate_db import Populator
+from workflow.tests import constants
+from workflow.workflow_factory import WorkflowFactory
+from workflow.models import Repository
+from workflow.resource_bundle_workflow import Define_Hardware, Define_Nets, Resource_Meta_Info, Host_Meta_Info
+from workflow.sw_bundle_workflow import SWConf_Resource_Select, Define_Software, Config_Software
+from workflow.booking_workflow import Booking_Resource_Select, SWConfig_Select, Booking_Meta
+from django.http import QueryDict, HttpRequest
+from django.contrib.auth.models import User
+from resource_inventory.models import (
+ Scenario,
+ Installer,
+ OPNFVRole,
+ Image,
+ GenericResourceBundle,
+ GenericHost,
+ HostProfile,
+ GenericResource,
+ ConfigBundle
+)
+
+
+class BaseStepTestCase(TestCase):
+
+ @classmethod
+ def setUpTestData(cls):
+ Populator().populate()
+
+ def makeRepo(self):
+ repo = Repository()
+ repo.el[repo.SESSION_USER] = User.objects.filter(username="user 1").first()
+ return repo
+
+ def step_test(self, step_type, data):
+ step = WorkflowFactory().make_step(step_type, self.makeRepo())
+ formData = QueryDict(mutable=True)
+ formData.update(data)
+ request = HttpRequest()
+ request.POST = formData
+ response = step.post_render(request)
+ context = step.get_context()
+ return response, context
+
+
+class BookingResourceSelectTestCase(BaseStepTestCase):
+
+ def test_step_with_good_data(self):
+ grb_model = GenericResourceBundle.objects.filter(owner__username="user 1").first()
+ grb = [{"small_name": grb_model.name, "expanded_name": "user 1", "id": grb_model.id, "string": ""}]
+ grb = str(grb).replace("'", '"')
+ data = {"generic_resource_bundle": grb}
+ response, context = self.step_test(Booking_Resource_Select, data)
+ self.assertTrue(True)
+
+ def test_step_with_bad_data(self): # TODO
+ data = {}
+ response, context = self.step_test(Booking_Resource_Select, data)
+
+ def test_step_with_empty_data(self):
+ data = {}
+ response, context = self.step_test(SWConfig_Select, data)
+
+
+class SoftwareConfigSelectTestCase(BaseStepTestCase):
+
+ def test_step_with_good_data(self):
+ config_model = ConfigBundle.objects.filter(owner__username="user 1").first()
+ config = [{"expanded_name": "user 1", "small_name": config_model.name, "id": config_model.id, "string": ""}]
+ config = str(config).replace("'", '"')
+ data = {"software_bundle": config}
+ response, context = self.step_test(SWConfig_Select, data)
+
+ def test_step_with_bad_data(self): # TODO
+ data = {}
+ response, context = self.step_test(SWConfig_Select, data)
+
+ def test_step_with_empty_data(self):
+ data = {}
+ response, context = self.step_test(SWConfig_Select, data)
+
+
+class BookingMetaTestCase(BaseStepTestCase):
+
+ def test_step_with_good_data(self):
+ data = {"length": 7, "project": "LaaS", "purpose": "testing"}
+ user2 = User.objects.get(username="user 2")
+ john = User.objects.get(username="johnsmith")
+ users = [
+ {"expanded_name": "", "id": user2.id, "small_name": user2.username, "string": user2.email},
+ {"expanded_name": "", "id": john.id, "small_name": john.username, "string": john.email}
+ ]
+ users = str(users).replace("'", '"')
+ data['users'] = users
+ response, context = self.step_test(Booking_Meta, data)
+
+ def test_step_with_bad_data(self): # TODO
+ data = {}
+ response, context = self.step_test(Booking_Meta, data)
+
+ def test_step_with_empty_data(self):
+ data = {}
+ response, context = self.step_test(Booking_Meta, data)
+
+
+class DefineHardwareTestCase(BaseStepTestCase):
+
+ def test_step_with_good_data(self):
+ hosts = {"host_4": 1, "host_1": 1}
+ labs = {"lab_1": "true"}
+ data = {"hosts": hosts, "labs": labs}
+ response, context = self.step_test(Define_Hardware, data)
+
+ def test_step_with_bad_data(self): # TODO
+ data = {}
+ response, context = self.step_test(Define_Hardware, data)
+
+ def test_step_with_empty_data(self):
+ data = {}
+ response, context = self.step_test(Define_Hardware, data)
+
+
+class HostMetaInfoTestCase(BaseStepTestCase):
+
+ def makeRepo(self):
+ """
+ override to provide step with needed host info
+ """
+ repo = super(HostMetaInfoTestCase, self).makeRepo()
+ # get models
+ models = {}
+ models['bundle'] = GenericResourceBundle()
+ # make generic hosts
+ gres1 = GenericResource(bundle=models['bundle'])
+ prof1 = HostProfile.objects.get(name="Test profile 0")
+ ghost1 = GenericHost(profile=prof1, resource=gres1)
+
+ gres2 = GenericResource(bundle=models['bundle'])
+ prof2 = HostProfile.objects.get(name="Test profile 3")
+ ghost2 = GenericHost(profile=prof2, resource=gres2)
+ models['hosts'] = [ghost1, ghost2]
+ repo.el[repo.GRESOURCE_BUNDLE_MODELS] = models
+ return repo
+
+ def test_step_with_good_data(self):
+ data = {"form-INITIAL_FORMS": 2, "form-MAX_NUM_FORMS": 1000}
+ data["form-MIN_NUM_FORMS"] = 0
+ data["form-TOTAL_FORMS"] = 2
+ data['form-0-host_name'] = "first host"
+ data['form-1-host_name'] = "second host"
+ response, context = self.step_test(Host_Meta_Info, data)
+
+ def test_step_with_bad_data(self): # TODO
+ data = {"form-INITIAL_FORMS": 0, "form-MAX_NUM_FORMS": 1000}
+ data["form-MIN_NUM_FORMS"] = 0
+ data["form-TOTAL_FORMS"] = 0
+ response, context = self.step_test(Host_Meta_Info, data)
+
+ def test_step_with_empty_data(self):
+ data = {"form-INITIAL_FORMS": 0, "form-MAX_NUM_FORMS": 1000}
+ data["form-MIN_NUM_FORMS"] = 0
+ data["form-TOTAL_FORMS"] = 0
+ response, context = self.step_test(Host_Meta_Info, data)
+
+
+class DefineNetsTestCase(BaseStepTestCase):
+
+ def test_step_with_good_data(self):
+ xml = constants.POD_XML
+ data = {"xml": xml}
+ response, context = self.step_test(Define_Nets, data)
+
+ def test_step_with_bad_data(self): # TODO
+ data = {}
+ response, context = self.step_test(Define_Nets, data)
+
+ def test_step_with_empty_data(self):
+ data = {}
+ response, context = self.step_test(Define_Nets, data)
+
+
+class ResourceMetaInfoTestCase(BaseStepTestCase):
+
+ def test_step_with_good_data(self):
+ data = {"bundle_description": "description", "bundle_name": "my testing bundle"}
+ response, context = self.step_test(Resource_Meta_Info, data)
+
+ def test_step_with_bad_data(self): # TODO
+ data = {}
+ response, context = self.step_test(Resource_Meta_Info, data)
+
+ def test_step_with_empty_data(self):
+ data = {}
+ response, context = self.step_test(Resource_Meta_Info, data)
+
+
+class SWConfResourceSelectTestCase(BaseStepTestCase):
+
+ def test_step_with_good_data(self):
+ grb_model = GenericResourceBundle.objects.filter(owner__username="user 1").first()
+ grb = [{"small_name": grb_model.name, "expanded_name": "user 1", "id": grb_model.id, "string": ""}]
+ grb = str(grb).replace("'", '"')
+ data = {"generic_resource_bundle": grb}
+ response, context = self.step_test(SWConf_Resource_Select, data)
+
+ def test_step_with_bad_data(self): # TODO
+ data = {}
+ response, context = self.step_test(SWConf_Resource_Select, data)
+
+ def test_step_with_empty_data(self):
+ data = {}
+ response, context = self.step_test(SWConf_Resource_Select, data)
+
+
+class DefineSoftwareTestCase(BaseStepTestCase):
+
+ def makeRepo(self):
+ """
+ put selected grb in repo for step
+ """
+ repo = super(DefineSoftwareTestCase, self).makeRepo()
+ grb = GenericResourceBundle.objects.filter(owner__username="user 1").first()
+ repo.el[repo.SWCONF_SELECTED_GRB] = grb
+ return repo
+
+ def test_step_with_good_data(self):
+ data = {"form-INITIAL_FORMS": 3, "form-MAX_NUM_FORMS": 1000}
+ data["form-MIN_NUM_FORMS"] = 0
+ data["form-TOTAL_FORMS"] = 3
+ an_image_id = Image.objects.get(name="a host image").id
+ another_image_id = Image.objects.get(name="another host image").id
+ control = OPNFVRole.objects.get(name="Controller")
+ compute = OPNFVRole.objects.get(name="Compute")
+ jumphost = OPNFVRole.objects.get(name="Jumphost")
+ data['form-0-image'] = an_image_id
+ data['form-1-image'] = an_image_id
+ data['form-2-image'] = another_image_id
+ data['form-0-role'] = compute.id
+ data['form-1-role'] = control.id
+ data['form-2-role'] = jumphost.id
+ response, context = self.step_test(Define_Software, data)
+
+ def test_step_with_bad_data(self): # TODO
+ data = {"form-INITIAL_FORMS": 0, "form-MAX_NUM_FORMS": 1000}
+ data["form-MIN_NUM_FORMS"] = 0
+ data["form-TOTAL_FORMS"] = 0
+ response, context = self.step_test(Define_Software, data)
+
+ def test_step_with_empty_data(self):
+ data = {"form-INITIAL_FORMS": 0, "form-MAX_NUM_FORMS": 1000}
+ data["form-MIN_NUM_FORMS"] = 0
+ data["form-TOTAL_FORMS"] = 0
+ response, context = self.step_test(Define_Software, data)
+
+
+class ConfigSoftwareTestCase(BaseStepTestCase):
+
+ def test_step_with_good_data(self):
+ data = {"description": "description", "name": "namey"}
+ installer = Installer.objects.get(name="Fuel")
+ scenario = Scenario.objects.get(name="os-nosdn-nofeature-noha")
+ data['installer'] = installer.id
+ data['scenario'] = scenario.id
+ response, context = self.step_test(Config_Software, data)
+
+ def test_step_with_bad_data(self): # TODO
+ data = {}
+ response, context = self.step_test(Config_Software, data)
+
+ def test_step_with_empty_data(self):
+ data = {}
+ response, context = self.step_test(Config_Software, data)
diff --git a/dashboard/src/workflow/tests/test_steps_render.py b/dashboard/src/workflow/tests/test_steps_render.py
new file mode 100644
index 0000000..f3df8f2
--- /dev/null
+++ b/dashboard/src/workflow/tests/test_steps_render.py
@@ -0,0 +1,43 @@
+##############################################################################
+# 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, Client
+
+
+class SuperViewTestCase(TestCase):
+ url = "/"
+ client = Client()
+
+ def test_get(self):
+ response = self.client.get(self.url)
+ self.assertLess(response.status_code, 300)
+
+
+class DefineHardwareViewTestCase(SuperViewTestCase):
+ url = "/wf/workflow/step/define_hardware"
+
+
+class DefineNetworkViewTestCase(SuperViewTestCase):
+ url = "/wf/workflow/step/define_net"
+
+
+class ResourceMetaViewTestCase(SuperViewTestCase):
+ url = "/wf/workflow/step/resource_meta"
+
+
+class BookingMetaViewTestCase(SuperViewTestCase):
+ url = "/wf/workflow/step/booking_meta"
+
+
+class SoftwareSelectViewTestCase(SuperViewTestCase):
+ url = "/wf/workflow/step/software_select"
+
+
+class ResourceSelectViewTestCase(SuperViewTestCase):
+ url = "/wf/workflow/step/resource_select"
diff --git a/dashboard/src/workflow/tests/test_workflows.py b/dashboard/src/workflow/tests/test_workflows.py
new file mode 100644
index 0000000..7a53521
--- /dev/null
+++ b/dashboard/src/workflow/tests/test_workflows.py
@@ -0,0 +1,100 @@
+##############################################################################
+# 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 workflow.workflow_factory import WorkflowFactory
+from dashboard.populate_db import Populator
+
+
+"""
+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 setUpTestData(cls):
+ Populator().populate()
+
+ 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):
+ """
+ retrieves 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/dashboard/src/workflow/urls.py b/dashboard/src/workflow/urls.py
new file mode 100644
index 0000000..5a97904
--- /dev/null
+++ b/dashboard/src/workflow/urls.py
@@ -0,0 +1,34 @@
+##############################################################################
+# 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 django.conf import settings
+
+from workflow.views import step_view, delete_session, manager_view, viewport_view
+from workflow.models import Repository
+from workflow.resource_bundle_workflow import Define_Hardware, Define_Nets, Resource_Meta_Info
+from workflow.booking_workflow import SWConfig_Select, Booking_Resource_Select, Booking_Meta
+
+app_name = 'workflow'
+urlpatterns = [
+
+ url(r'^workflow/$', step_view, name='workflow'),
+ url(r'^workflow/finish/$', delete_session, name='delete_session'),
+ url(r'^manager/$', manager_view, name='manager'),
+ url(r'^$', viewport_view, name='viewport')
+]
+
+if settings.TESTING:
+ urlpatterns.append(url(r'^workflow/step/define_hardware$', Define_Hardware("", Repository()).test_render))
+ urlpatterns.append(url(r'^workflow/step/define_net$', Define_Nets("", Repository()).test_render))
+ urlpatterns.append(url(r'^workflow/step/resource_meta$', Resource_Meta_Info("", Repository()).test_render))
+ urlpatterns.append(url(r'^workflow/step/booking_meta$', Booking_Meta("", Repository()).test_render))
+ urlpatterns.append(url(r'^workflow/step/software_select$', SWConfig_Select("", Repository()).test_render))
+ urlpatterns.append(url(r'^workflow/step/resource_select$', Booking_Resource_Select("", Repository()).test_render))
diff --git a/dashboard/src/workflow/views.py b/dashboard/src/workflow/views.py
new file mode 100644
index 0000000..7ed9031
--- /dev/null
+++ b/dashboard/src/workflow/views.py
@@ -0,0 +1,139 @@
+##############################################################################
+# 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 HttpResponseGone, JsonResponse
+from django.shortcuts import render
+from django.urls import reverse
+
+import uuid
+
+from workflow.workflow_manager import ManagerTracker, SessionManager
+from booking.models import Booking
+
+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 get_redirect_response(result):
+ if not result:
+ return {}
+
+ # need to get type of result, and switch on the type
+ # since has_result, result must be populated with a valid object
+ if isinstance(result, Booking):
+ return {
+ 'redir_url': reverse('booking:booking_detail', kwargs={'booking_id': result.id})
+ }
+ else:
+ return {}
+
+
+def delete_session(request):
+ manager = attempt_auth(request)
+
+ if not manager:
+ return HttpResponseGone("No session found that relates to current request")
+
+ not_last_workflow, result = manager.pop_workflow()
+
+ if not_last_workflow: # this was not the last workflow, so don't redirect away
+ return JsonResponse({})
+ else:
+ del ManagerTracker.managers[request.session['manager_session']]
+ return JsonResponse(get_redirect_response(result))
+
+
+def step_view(request):
+ manager = attempt_auth(request)
+ if not manager:
+ # no manager found, redirect to "lost" page
+ return no_workflow(request)
+ if request.GET.get('step') is not None:
+ if request.GET.get('step') == 'next':
+ manager.go_next()
+ elif request.GET.get('step') == 'prev':
+ manager.go_prev()
+ else:
+ raise Exception("requested action for new step had malformed contents: " + request.GET.get('step'))
+ return manager.render(request)
+
+
+def manager_view(request):
+ manager = attempt_auth(request)
+
+ if not manager:
+ return HttpResponseGone("No session found that relates to current request")
+
+ if request.method == 'GET':
+ # no need for this statement if only intercepting post requests
+
+ # return general context for viewport page
+ return manager.status(request)
+
+ if request.method == 'POST':
+ if request.POST.get('add') is not None:
+ logger.debug("add found")
+ target_id = None
+ if 'target' in request.POST:
+ target_id = int(request.POST.get('target'))
+ manager.add_workflow(workflow_type=int(request.POST.get('add')), target_id=target_id)
+ elif request.POST.get('edit') is not None and request.POST.get('edit_id') is not None:
+ logger.debug("edit found")
+ manager.add_workflow(workflow_type=request.POST.get('edit'), edit_object=int(request.POST.get('edit_id')))
+ elif request.POST.get('cancel') is not None:
+ if not manager.pop_workflow():
+ del ManagerTracker.managers[request.session['manager_session']]
+
+ return manager.status(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 render(request, 'workflow/viewport-base.html')
+ else:
+ pass
+
+
+def create_session(wf_type, request):
+ wf = int(wf_type)
+ smgr = SessionManager(request=request)
+ smgr.add_workflow(workflow_type=wf, target_id=request.POST.get("target"))
+ manager_uuid = uuid.uuid4().hex
+ ManagerTracker.getInstance().managers[manager_uuid] = smgr
+
+ return manager_uuid
+
+
+def no_workflow(request):
+
+ logger.debug("There is no active workflow")
+
+ return render(request, 'workflow/no_workflow.html', {'title': "Not Found"})
+
+
+def login(request):
+ return render(request, "dashboard/login.html", {'title': 'Authentication Required'})
diff --git a/dashboard/src/workflow/workflow_factory.py b/dashboard/src/workflow/workflow_factory.py
new file mode 100644
index 0000000..03c8126
--- /dev/null
+++ b/dashboard/src/workflow/workflow_factory.py
@@ -0,0 +1,134 @@
+##############################################################################
+# 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 workflow.booking_workflow import Booking_Resource_Select, SWConfig_Select, Booking_Meta, OPNFV_Select
+from workflow.resource_bundle_workflow import Define_Hardware, Define_Nets, Resource_Meta_Info
+from workflow.sw_bundle_workflow import Config_Software, Define_Software, SWConf_Resource_Select
+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,
+ SWConfig_Select,
+ Booking_Meta,
+ OPNFV_Select,
+ ]
+
+ resource_steps = [
+ Define_Hardware,
+ Define_Nets,
+ Resource_Meta_Info,
+ ]
+
+ config_steps = [
+ SWConf_Resource_Select,
+ Define_Software,
+ Config_Software,
+ ]
+
+ 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.config_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/dashboard/src/workflow/workflow_manager.py b/dashboard/src/workflow/workflow_manager.py
new file mode 100644
index 0000000..80b8a67
--- /dev/null
+++ b/dashboard/src/workflow/workflow_manager.py
@@ -0,0 +1,243 @@
+##############################################################################
+# 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 booking.models import Booking
+from workflow.workflow_factory import WorkflowFactory
+from workflow.models import Repository
+from resource_inventory.models import (
+ GenericResourceBundle,
+ ConfigBundle,
+ HostConfiguration,
+ OPNFVConfig
+)
+
+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()
+
+ 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, target_id=None, **kwargs):
+ if target_id is not None:
+ self.prefill_repo(target_id, workflow_type)
+
+ 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 pop_workflow(self):
+ 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
+ self.workflows.pop()
+ current_repo = self.workflows[-1].repository
+ return (multiple_wfs, current_repo.el[current_repo.RESULT])
+
+ def status(self, request):
+ try:
+ meta_json = []
+ for step in self.active_workflow().steps:
+ meta_json.append(step.to_json())
+ responsejson = {}
+ responsejson["steps"] = meta_json
+ responsejson["active"] = self.active_workflow().repository.el['active_step']
+ responsejson["workflow_count"] = len(self.workflows)
+ return JsonResponse(responsejson, safe=False)
+ except Exception:
+ pass
+
+ def render(self, request, **kwargs):
+ # filter out when a step needs to handle post/form data
+ # if 'workflow' in post data, this post request was meant for me, not step
+ if request.method == 'POST' and request.POST.get('workflow', None) is None:
+ return self.active_workflow().steps[self.active_workflow().active_index].post_render(request)
+ return self.active_workflow().steps[self.active_workflow().active_index].render(request)
+
+ 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 = GenericResourceBundle.objects.get(pk=target_id)
+ self.prefill_resource(edit_object)
+ elif workflow_type == 2:
+ edit_object = ConfigBundle.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.GRESOURCE_BUNDLE_MODELS] = self.make_grb_models(booking.resource.template)
+ self.active_workflow().repository.el[self.active_workflow().repository.SELECTED_GRESOURCE_BUNDLE] = 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.GRESOURCE_BUNDLE_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.GRESOURCE_BUNDLE_MODELS] = grb_models
+
+ def make_grb_models(self, resource):
+ models = self.active_workflow().repository.el.get(self.active_workflow().repository.GRESOURCE_BUNDLE_MODELS, {})
+ models['hosts'] = []
+ models['bundle'] = resource
+ models['interfaces'] = {}
+ models['vlans'] = {}
+ for host in resource.getHosts():
+ 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.getHosts():
+ 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 HostConfiguration.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 HostConfiguration.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
diff --git a/dashboard/test.sh b/dashboard/test.sh
new file mode 100755
index 0000000..0fbfd0e
--- /dev/null
+++ b/dashboard/test.sh
@@ -0,0 +1,16 @@
+#!/bin/bash
+##############################################################################
+# 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
+##############################################################################
+
+# first, basic lint with flake8
+find . -type f -name "*.py" -not -name "manage.py" | xargs flake8 --count --ignore E501
+
+
+# this file should be executed from the dir it is in
+docker exec -it dg01 python manage.py test
diff --git a/dashboard/tox.ini b/dashboard/tox.ini
new file mode 100644
index 0000000..3d01e10
--- /dev/null
+++ b/dashboard/tox.ini
@@ -0,0 +1,21 @@
+[tox]
+envlist = flake8
+skipsdist = True
+
+[testenv]
+deps = -rrequirements.txt
+basepython = python3
+
+# Store flake8 config here intead of .flake8
+[flake8]
+ignore =
+ # Ignore 'line-too-long' warnings
+ E501
+exclude =
+ src/manage.py
+
+[testenv:flake8]
+deps =
+ -rrequirements.txt
+ flake8
+commands = flake8 src/
diff --git a/dashboard/web/Dockerfile b/dashboard/web/Dockerfile
index 228b0b0..a153192 100644
--- a/dashboard/web/Dockerfile
+++ b/dashboard/web/Dockerfile
@@ -1,7 +1,26 @@
+##############################################################################
+# Copyright (c) 2018 Trevor Bramwell 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 node as static
+RUN npm install -g bower
+ADD src/static/ /static
+WORKDIR /static/
+RUN bower install --allow-root
+
FROM python:3.5
ENV PYTHONUNBUFFERED 1
-RUN mkdir /config
-ADD ./requirements.txt /config/
-RUN pip install -r /config/requirements.txt
-RUN mkdir -p /pharos_dashboard/src
-WORKDIR /pharos_dashboard/src
+
+ADD requirements.txt /requirements.txt
+RUN pip install -r /requirements.txt
+
+ADD web/init.sh /init.sh
+ADD src/ /pharos_dashboard/
+COPY --from=static /static/ pharos_dashboard/static/
+
+WORKDIR /pharos_dashboard/
+CMD ["/init.sh"]
diff --git a/dashboard/web/init.sh b/dashboard/web/init.sh
new file mode 100755
index 0000000..736acc1
--- /dev/null
+++ b/dashboard/web/init.sh
@@ -0,0 +1,12 @@
+#!/bin/bash -e
+##############################################################################
+# Copyright (c) 2018 Trevor Bramwell 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
+##############################################################################
+python manage.py migrate && \
+python manage.py collectstatic --no-input && \
+gunicorn pharos_dashboard.wsgi -b 0.0.0.0:8000
diff --git a/dashboard/worker/Dockerfile b/dashboard/worker/Dockerfile
index c1e8aff..cd0f797 100644
--- a/dashboard/worker/Dockerfile
+++ b/dashboard/worker/Dockerfile
@@ -1,8 +1,22 @@
+##############################################################################
+# Copyright (c) 2018 Trevor Bramwell 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 python:3.5
ENV PYTHONUNBUFFERED 1
-RUN mkdir /config
-ADD ./requirements.txt /config/
-RUN pip install -r /config/requirements.txt
+
+ADD requirements.txt /requirements.txt
+RUN pip install -r /requirements.txt
+
+ADD worker/init.sh /init.sh
+ADD src/ /pharos_dashboard/
+
RUN useradd -ms /bin/bash celery
USER celery
-WORKDIR /pharos_dashboard/src
+
+WORKDIR /pharos_dashboard/
+CMD ["/init.sh"]
diff --git a/dashboard/worker/init.sh b/dashboard/worker/init.sh
new file mode 100755
index 0000000..ecec2a1
--- /dev/null
+++ b/dashboard/worker/init.sh
@@ -0,0 +1,10 @@
+#!/bin/bash -e
+##############################################################################
+# Copyright (c) 2018 Trevor Bramwell 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
+##############################################################################
+celery -A pharos_dashboard worker -l info -B --schedule=~/celerybeat-schedule
diff --git a/dashboard/worker/requirements.txt b/dashboard/worker/requirements.txt
deleted file mode 100644
index 0d864d3..0000000
--- a/dashboard/worker/requirements.txt
+++ /dev/null
@@ -1,18 +0,0 @@
-celery==3.1.23
-cryptography==1.4
-Django==1.10
-django-bootstrap3==7.0.1
-django-crispy-forms==1.6.0
-django-filter==0.14.0
-django-registration==2.1.2
-djangorestframework==3.4.6
-gunicorn==19.6.0
-jira==1.0.7
-jsonpickle==0.9.3
-oauth2==1.9.0.post1
-oauthlib==1.1.2
-pika==0.10.0
-psycopg2==2.6.2
-PyJWT==1.4.2
-requests==2.11.0
-django-fernet-fields==0.5