From 639bff363c83e0feb22c3b83f56d0eb8d30ea37a Mon Sep 17 00:00:00 2001 From: exodus4d Date: Sat, 6 Jun 2015 16:47:04 +0200 Subject: [PATCH] landing page pricing table, map bug fixes, signature module improvement --- .idea/dataSources.ids | 5366 +++-------------- .idea/dataSources.xml | 2 +- .idea/dictionaries/exodus4d.xml | 1 + .idea/sqldialects.xml | 6 + .idea/workspace.xml | 1497 +++-- app/config.cfg | 10 + app/lib/db/cortex.php | 388 +- app/main/controller/AccessController.php | 30 +- app/main/controller/Controller.php | 16 +- app/main/controller/MapController.php | 5 + app/main/controller/api/Access.php | 75 + app/main/controller/api/Map.php | 363 +- app/main/controller/api/Routes.php | 338 ++ app/main/controller/api/System.php | 15 +- app/main/model/BasicModel.php | 16 + app/main/model/MapModel.php | 239 +- app/main/model/SystemModel.php | 37 +- app/main/model/SystemSignatureModel.php | 1 + app/main/model/UserModel.php | 22 +- app/routes.cfg | 1 + js/app.js | 13 +- js/app/init.js | 6 +- js/app/landingpage.js | 40 +- js/app/main.js | 89 +- js/app/map/map.js | 592 +- js/app/module_map.js | 159 +- js/app/notification.js | 1 - js/app/page.js | 14 +- js/app/ui/dialog/map_info.js | 4 +- js/app/ui/dialog/map_settings.js | 163 +- js/app/ui/dialog/settings.js | 102 +- js/app/ui/form_element.js | 155 +- js/app/ui/system_signature.js | 498 +- js/app/util.js | 720 ++- .../responsive/dataTables.responsive.js | 873 +++ js/lib/dom.jsPlumb-1.7.5-min.js | 6 + js/lib/jquery.dragToSelect.js | 8 +- js/lib/select2.min.js | 4 +- public/css/pathfinder.css | 8 +- public/templates/dialog/map.html | 109 +- public/templates/dialog/settings.html | 21 +- .../system_dialog.html => dialog/system.html} | 2 +- public/templates/form/character_panel.html | 5 + public/templates/form/map_settings.html | 5 + public/templates/view/parts/landingpage.html | 253 +- sass/_colors.scss | 13 +- sass/_variables.scss | 26 +- sass/bootstrap/_badges.scss | 2 +- sass/bootstrap/_buttons.scss | 3 +- sass/bootstrap/_modals.scss | 2 +- sass/bootstrap/_wells.scss | 2 +- sass/layout/_all.scss | 3 +- sass/layout/_dialogs.scss | 14 +- sass/layout/_forms.scss | 2 +- sass/layout/_landing.scss | 85 +- sass/layout/_logo.scss | 8 +- sass/layout/_main.scss | 69 +- sass/layout/_map.scss | 4 +- sass/layout/_ribbon.scss | 58 + sass/layout/_system-info.scss | 25 +- .../blue-imp-gallery/_blueimp-gallery.scss | 1 + .../data-tables/_dataTables-fontAwesome.scss | 2 +- .../data-tables/_dataTables-responsive.scss | 106 + sass/library/data-tables/_dataTables.scss | 16 +- .../library/drag-to-select/_dragToSelect.scss | 13 +- sass/library/select2/_core.scss | 14 +- sass/library/select2/_dropdown.scss | 22 +- sass/library/select2/_multiple.scss | 6 + sass/library/select2/_single.scss | 4 +- .../select2/theme/default/_layout.scss | 98 - .../select2/theme/default/_multiple.scss | 88 - .../select2/theme/default/_single.scss | 85 - .../{classic => pathfinder}/_defaults.scss | 17 +- .../{classic => pathfinder}/_layout.scss | 9 +- .../{classic => pathfinder}/_multiple.scss | 14 +- .../{classic => pathfinder}/_single.scss | 29 +- .../x-editable/_bootstrap-editable.scss | 2 +- sass/pathfinder.scss | 6 +- sass/smartadmin/_main-colorpallet.scss | 4 +- sass/smartadmin/_main.scss | 37 +- 80 files changed, 6168 insertions(+), 6999 deletions(-) create mode 100644 .idea/sqldialects.xml create mode 100644 app/main/controller/api/Access.php create mode 100644 app/main/controller/api/Routes.php create mode 100644 js/lib/datatables/extensions/responsive/dataTables.responsive.js create mode 100644 js/lib/dom.jsPlumb-1.7.5-min.js rename public/templates/{modules/system_dialog.html => dialog/system.html} (96%) create mode 100644 sass/layout/_ribbon.scss create mode 100644 sass/library/data-tables/_dataTables-responsive.scss delete mode 100644 sass/library/select2/theme/default/_layout.scss delete mode 100644 sass/library/select2/theme/default/_multiple.scss delete mode 100644 sass/library/select2/theme/default/_single.scss rename sass/library/select2/theme/{classic => pathfinder}/_defaults.scss (60%) rename sass/library/select2/theme/{classic => pathfinder}/_layout.scss (83%) rename sass/library/select2/theme/{classic => pathfinder}/_multiple.scss (94%) rename sass/library/select2/theme/{classic => pathfinder}/_single.scss (79%) diff --git a/.idea/dataSources.ids b/.idea/dataSources.ids index f04fc18d..5e6f973c 100644 --- a/.idea/dataSources.ids +++ b/.idea/dataSources.ids @@ -1,2220 +1,525 @@ - + #@ ` - - - - - - - - - - - - - - - -
- - - - - - - - - - - - -
- - - - -
- - - - - -
- - - - - - -
- - - - -
- - - - - - -
- - - - - - - - - - - - - -
- - - - - - - - -
- - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - -
- - - - - - - -
- - - - - -
- - - - - -
- - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - -
- - - - - - -
- - - - - -
- - - - - - - - - - - -
- - - - - -
- - - - - - - - -
- - - - - - - - - -
- - - - - -
- - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - -
- - - - - -
- - - - - -
- - - - - - -
- - - - - - -
- - - - - - - - -
- - - - - - - - -
- - - - - - - -
- - - - - - - -
- - - - -
- - - - - - - - - - - - - - - - - -
- - - - - - - -
- - - - - - - - - -
- - - - -
- - - - - - - - -
- - - - - - -
- - - - - - - - - - - - - - - -
- - - - - - - - - - -
- - - - - - - - -
- - - - - - -
- - - - - -
- - - - -
- - - - - - - - - -
- - - - - - -
- - - - - -
- - - - - - -
- - - - - - - - - - - - - - - - -
- - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - -
- - - - - - -
- - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - -
- - - - - - - - - - -
- - - - -
- - - - -
- - - - -
- - - - - - - - - - - - - - - - -
- - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - -
- - - - - -
- - - - -
- - - - - - -
- - - - - - - -
- - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - -
- - - - - - - -
- - - - - - - -
- - - - - - - - - - - -
- - - - - -
- - - - - - - - -
- - - - - - - - - - - - - - - - -
- - - - -
- - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - -
- - - - - - - -
- - - - - - - -
- - - - - -
- - - - - - -
- - - - - - - -
- - - - -
- - - - - - - - - - - - -
- - - - -
- - - - - -
- - - - - - -
- - - - -
- - - - - - -
- - - - - - - - - - - - - -
- - - - - - - - -
- - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - -
- - - - - - - -
- - - - - -
- - - - - -
- - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - -
- - - - - - -
- - - - - -
- - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - -
- - - - - -
- - - - - -
- - - - - - -
- - - - - - -
- - - - - - - - -
- - - - - - - - -
- - - - - - - -
- - - - - - - -
- - - - -
- - - - - - - -
- - - - - - - - - -
- - - - -
- - - - - - - - -
- - - - - - -
- - - - - - - - - - - - - - - -
- - - - - - - - - - -
- - - - - - - - -
- - - - - - -
- - - - - -
- - - - -
- - - - - - - - - -
- - - - - - -
- - - - - -
- - - - - - -
- - - - - - - - - - - - - - - - -
- - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - -
- - - - - - -
- - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - -
- - - - - - - - - - -
- - - - -
- - - - -
- - - - -
- - - - - - - - - - - - - - - - -
- - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - -
- - - - - -
- - - - -
- - - - - - -
- - - - - - - -
- - - - - - - - - - - -
- - - - - - - -
- - - - - - - -
- - - - - - - - - - - -
- - - - - -
- - - - - - - - - - - - - - - - -
- - - - -
- - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - -
- - - - - - - -
- - - - - - - -
- - - - - -
- - - - - - -
- - - - - - - - + + + + + + + +
- - + +
- - + +
- - - - + + + +
- - - + + +
- - - - - + + + + +
- - - - - - - - - - - + + + + + + + + + + +
- - - - - - + + + + + +
- - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + +
- - - - - - - - - - - + + + + + + + + + + +
- - - - - + + + + +
- - - + + +
- - - + + +
- - + +
- - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + +
- - + +
- - - - + + + +
- - - + + +
- - - - - - - - - - - + + + + + + + + + + +
- - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + +
- - - - + + + +
- - - + + +
- - - + + +
- - - - + + + +
- - - + + +
- - - - - + + + + +
- - - - + + + +
- - - - + + + +
- - - - + + + +
- - - - + + + +
- - + +
- - - - - + + + + +
- - - - - - + + + + + +
- - + +
- - - - - - + + + + + +
- - - - + + + +
- - - - - - - - - - - - + + + + + + + + + + + +
- - - - - - + + + + + +
- - - - - - + + + + + +
- - - - + + + +
- - - + + +
- - + +
- - - - - - - + + + + + + +
- - - - - + + + + +
- - - + + +
- - - - + + + +
- - - - - - - - - - - - - + + + + + + + + + + + + +
- - - + + +
- - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + +
- - - - + + + +
- - - - - - - - - - - - - - + + + + + + + + + + + + + +
- - - - - - - - - - - - - - - + + + + + + + + + + + + + + + @@ -2225,228 +530,228 @@
- - + +
- - - - - - - - + + + + + + + +
- - + +
- - + +
- - + +
- - - - - - - - - - - - - + + + + + + + + + + + + +
- - - - - - + + + + + +
- - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + +
- - - - - - - - - - - - + + + + + + + + + + + +
- - - + + +
- - + +
- - - - + + + +
- - - - - + + + + +
- - - - - - - + + + + + + +
- - - - - + + + + +
- - - - - + + + + +
- - - - - - - - - + + + + + + + + +
- - - + + +
- - - - - - - - - - - - - - + + + + + + + + + + + + + +
- - + +
- - - + + +
- - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + @@ -2456,1133 +761,156 @@
- - - - - - - - - - - + + + + + + + + + + +
- - - - - + + + + +
- - - - - + + + + +
- - - + + +
- - - - + + + +
- - - - - + + + + +
- - + +
- - - - - -
- - - - - - - -
- - - -
- - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - -
- - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- - - -
- - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - -
- - - - - - - -
- - - - - - - -
- - - - - - - - - -
- - - - - - - - - -
- - - - - - - -
- - -
- - - -
- - -
- - -
- - - - - - - -
- - - - - - - -
- - - - - - - - - - - -
- - - - - -
- - - - - - - - - - - - - - - - - - -
- - - - - - - -
- - - -
- - - - -
- - - - - - -
- - - - - -
- - - - - - - - -
- - - - - - - - - -
- - - - - - - - -
- - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - -
- - - - - -
- - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - -
- - - - - - - - - -
- - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - -
- - - - - - -
- - - -
- - - -
- - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - -
- - - - - - - -
- - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - -
- - - - - -
- - - - - - - - - - - -
- - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - -
- - - - - - - -
- - - - - - - -
- - - - - -
- - - - -
- - - - - - - - - -
- - - - - - - - - - -
- - - - - - - - -
- - - - - - - - - - - - -
- - - - -
- - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - -
- - - - - - - - - - -
- - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - -
- - - - - - - - - - - - - - -
- - - - - - - - - - - - - -
- - - - - - - - - - - -
- - - - -
- - - - -
- - - - -
- - - - - -
- - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
+ + + + + + + + + + + +
+ + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
- - - - - - - - - + + + + + + + + + @@ -3593,30 +921,71 @@
- - - - - - + + + + + +
+ + + + + + + + + + + + +
+ + + + + + + + + + + + + + + +
+ + + + + + + + + +
- - - - - - - - + + + + + + + + + + @@ -3624,24 +993,24 @@
- - - - - - + + + + + +
- - - - - - - - + + + + + + + + @@ -3649,42 +1018,48 @@
- - - - - - + + + + + +
- - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -3696,49 +1071,178 @@
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + +
- - - - - - - + + + + + + + @@ -3746,11 +1250,11 @@
- - - - - + + + + + @@ -3758,28 +1262,68 @@
- - - - - - - + + + + + + + + + + +
+ + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
- - - - - - + + + + + + @@ -3790,1018 +1334,12 @@
- - - - + + + +
- - - - - -
- - - -
- - - - - - - - - - - -
- - - - - - - - - - - -
- - - - - - - - - - - -
- - - - - - - - - -
- - - - - - - - -
- - - - - - - - -
- - - - - - - - -
- - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - -
- - - - - - - - -
- - - - - - - - -
- - - - - - - - -
- - - - - - - - -
- - - - - - - -
- - - - -
- - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - -
- - - - -
- - - - - - - - - -
- - - - - -
- - - - - -
- - - - - -
- - - - - -
- - - - -
- - - -
- - - - -
- - - - - - -
- - - -
- - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - -
- - - - -
- - - - - - - -
- - - - - - - - - - - -
- - - - - - - - -
- - - - - - - - - -
- - - - - - -
- - - - -
- - - - - - - - - -
- - - - - - - -
- - - - - -
- - - - - - - -
- - - - - - - - - - - - -
- - - - - -
- - - - -
\ No newline at end of file diff --git a/.idea/dataSources.xml b/.idea/dataSources.xml index 0bd12d01..1a8a2903 100644 --- a/.idea/dataSources.xml +++ b/.idea/dataSources.xml @@ -1,6 +1,6 @@ - + mysql true diff --git a/.idea/dictionaries/exodus4d.xml b/.idea/dictionaries/exodus4d.xml index 73cee183..04f573b2 100644 --- a/.idea/dictionaries/exodus4d.xml +++ b/.idea/dictionaries/exodus4d.xml @@ -39,6 +39,7 @@ tbody textarea timelimit + viewport waypoint diff --git a/.idea/sqldialects.xml b/.idea/sqldialects.xml new file mode 100644 index 00000000..1f5dad14 --- /dev/null +++ b/.idea/sqldialects.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/workspace.xml b/.idea/workspace.xml index 0215ef1b..93eed40d 100644 --- a/.idea/workspace.xml +++ b/.idea/workspace.xml @@ -5,24 +5,17 @@ - - - - - - - - - - - - - - + + + + + + + @@ -43,18 +36,17 @@ - - - - - + + + + @@ -62,9 +54,12 @@ + + + @@ -79,6 +74,7 @@ + @@ -89,6 +85,7 @@ + @@ -99,6 +96,7 @@ + @@ -111,12 +109,14 @@ - + + + @@ -131,98 +131,101 @@ + + - - + + + + - - - - - - - - + - - - - - - - + + + - - + + + - + + + + + + + + - + + - + + + - - + + + + - - - - - + - - - - - + + + + - - - + + + - + - - + - - + + + + + @@ -257,38 +260,6 @@ @@ -303,27 +274,25 @@ - + - - + + - - + + - - + + - - - - - + + + @@ -331,38 +300,38 @@ - - + + - - + + - - + + - - + + - - + + - - + + - - + + @@ -371,49 +340,98 @@ - - + + - - + - - + + - - + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - - + + - - - - - - - - - - - - + + @@ -422,28 +440,30 @@ - - + + + + + + + + + + + + + + - - + + - - - - - - - - - - - - + + @@ -452,18 +472,8 @@ - - - - - - - - - - - - + + @@ -472,33 +482,76 @@ - - + + - - + + - - + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -506,8 +559,50 @@ - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -516,30 +611,35 @@ - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -548,58 +648,18 @@ - - + + - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + @@ -627,57 +687,57 @@ @@ -690,10 +750,10 @@ - @@ -884,6 +944,24 @@ - + @@ -1381,11 +1389,11 @@ + - - - - + + + @@ -1403,15 +1411,15 @@ - - - + + + @@ -1419,6 +1427,9 @@ + + + @@ -1428,9 +1439,6 @@ - - - @@ -1442,7 +1450,7 @@ - C:\Users\exodus4d\AppData\Roaming\Subversion @@ -1610,7 +1618,13 @@ @@ -1634,31 +1648,31 @@ - + - - + + - - + + - + - + - - + + @@ -1679,9 +1693,9 @@ - - + + @@ -1717,7 +1731,8 @@ - @@ -1726,450 +1741,412 @@ - + - - - - - - - - - - - - - - - - + + - - - - - - - - - - - - - - - - + + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - + - - - - - - - - - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - + - - + + + - + - + + + + + + + + - + - - + + + + + + + + + + + - - - + + - + - - - - - - - - - + + - + - - + + - + - - + + - + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + + - + - - - - - - - - - + + - + - - + + + + + + + + + + - + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - DATABASE - da3cf616-be39-4091-94bd-76d70f41765d.table:pathfinder.map - - da3cf616-be39-4091-94bd-76d70f41765d.table:pathfinder.map_type - da3cf616-be39-4091-94bd-76d70f41765d.table:pathfinder.map_scope - da3cf616-be39-4091-94bd-76d70f41765d.table:pathfinder.map - - - - - - - Columns - Key columns - - All - - - - - - - DATABASE - da3cf616-be39-4091-94bd-76d70f41765d.schema:pathfinder - - da3cf616-be39-4091-94bd-76d70f41765d.table:pathfinder.user - da3cf616-be39-4091-94bd-76d70f41765d.table:pathfinder.map - da3cf616-be39-4091-94bd-76d70f41765d.table:pathfinder.system_jumps - da3cf616-be39-4091-94bd-76d70f41765d.table:pathfinder.system - da3cf616-be39-4091-94bd-76d70f41765d.table:pathfinder.map_type - da3cf616-be39-4091-94bd-76d70f41765d.table:pathfinder.map_scope - da3cf616-be39-4091-94bd-76d70f41765d.table:pathfinder.system_type - da3cf616-be39-4091-94bd-76d70f41765d.table:pathfinder.system_status - da3cf616-be39-4091-94bd-76d70f41765d.table:pathfinder.sessions - da3cf616-be39-4091-94bd-76d70f41765d.table:pathfinder.user_map - da3cf616-be39-4091-94bd-76d70f41765d.table:pathfinder.wh_statics - da3cf616-be39-4091-94bd-76d70f41765d.table:pathfinder.connection_scope - da3cf616-be39-4091-94bd-76d70f41765d.table:pathfinder.connection - - - - - - - Columns - Key columns - - All - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/app/config.cfg b/app/config.cfg index f0d5129b..774fc730 100644 --- a/app/config.cfg +++ b/app/config.cfg @@ -56,6 +56,16 @@ NAME = "PATHFINDER" VERSION = "v0.10" CONTACT = "https://github.com/exodus4d" +; Max number of maps an entity can create +MAX_MAPS_PRIVATE = 3 +MAX_MAPS_CORPORATION = 3 +MAX_MAPS_ALLIANCE = 3 + +; Max number of shared entities per map +MAX_SHARED_USER = 10 +MAX_SHARED_CORPORATION = 3 +MAX_SHARED_ALLIANCE = 2 + ; ====================================================================================================== [api_path] diff --git a/app/lib/db/cortex.php b/app/lib/db/cortex.php index fbb8bda1..113e6d38 100644 --- a/app/lib/db/cortex.php +++ b/app/lib/db/cortex.php @@ -18,7 +18,7 @@ * https://github.com/ikkez/F3-Sugar/ * * @package DB - * @version 1.3.0 + * @version 1.3.1-dev * @since 24.04.2012 * @date 19.01.2015 */ @@ -44,7 +44,7 @@ class Cortex extends Cursor { $dbsType, // mapper engine type [jig, sql, mongo] $fieldsCache, // relation field cache $saveCsd, // mm rel save cascade - $collectionID, // collection set identifier + $collection, // collection $relFilter, // filter for loading related models $hasCond, // IDs of records the next find should have $whitelist, // restrict to these fields @@ -107,7 +107,9 @@ class Cortex extends Cursor { $this->dbsType = 'mongo'; if ($table) $this->table = $table; - if (!$this->primary || $this->dbsType != 'sql') + if ($this->dbsType != 'sql') + $this->primary = '_id'; + elseif (!$this->primary) $this->primary = 'id'; if (!$this->table && !$this->fluid) trigger_error(self::E_NO_TABLE); @@ -149,8 +151,10 @@ class Cortex extends Cursor { $this->standardiseID = $f3->exists('CORTEX.standardiseID') ? $f3->get('CORTEX.standardiseID') : TRUE; if(!empty($this->fieldConf)) - foreach($this->fieldConf as &$conf) + foreach($this->fieldConf as &$conf) { $conf=static::resolveRelationConf($conf); + unset($conf); + } } /** @@ -236,8 +240,22 @@ class Cortex extends Cursor { return $conf; } - public function addToCollection($cID) { - $this->collectionID = $cID; + /** + * give this model a reference to the collection it is part of + * @param CortexCollection $cx + */ + public function addToCollection($cx) { + $this->collection = $cx; + } + + /** + * returns the collection where this model lives in + * @return CortexCollection + */ + protected function getCollection() + { + return ($this->collection && $this->smartLoading) + ? $this->collection : false; } /** @@ -303,8 +321,8 @@ class Cortex extends Cursor { $rel['fieldConf'][$relConf[1]]['has-many']); if (!in_array($mmTable,$schema->getTables())) { $mmt = $schema->createTable($mmTable); - $mmt->addColumn($relConf[1])->type_int(); - $mmt->addColumn($key)->type_int(); + $mmt->addColumn($relConf[1])->type($relConf['relFieldType']); + $mmt->addColumn($key)->type($field['type']); $index = array($relConf[1],$key); sort($index); $mmt->addIndex($index); @@ -321,7 +339,9 @@ class Cortex extends Cursor { if (in_array($field['type'], array(self::DT_JSON, self::DT_SERIALIZED))) $field['type']=$schema::DT_TEXT; // defaults values - if (!array_key_exists('nullable', $field)) $field['nullable'] = true; + if (!array_key_exists('nullable', $field)) + $field['nullable'] = true; + unset($field); } if (!in_array($table, $schema->getTables())) { // create table @@ -435,6 +455,26 @@ class Cortex extends Cursor { return $return; } + /** + * get mm table name from config + * @param array $conf own relation config + * @param string $key relation field + * @param null|array $fConf optional foreign config + * @return string + */ + protected function mmTable($conf, $key, $fConf=null) + { + if (!isset($conf['refTable'])) { + // compute mm table name + $mmTable = isset($conf[2]) ? $conf[2] : + static::getMMTableName($conf['relTable'], + $conf['relField'], $this->getTable(), $key, $fConf); + $this->fieldConf[$key]['has-many']['refTable'] = $mmTable; + } else + $mmTable = $conf['refTable']; + return $mmTable; + } + /** * resolve relation field types * @param $field @@ -448,7 +488,7 @@ class Cortex extends Cursor { $relConf = array($relConf, '_id'); // set field type if ($relConf[1] == '_id') - $field['type'] = Schema::DT_INT8; + $field['type'] = Schema::DT_INT4; else { // find foreign field type $fc = $relConf[0]::resolveConfiguration(); @@ -465,6 +505,8 @@ class Cortex extends Cursor { } elseif (array_key_exists('has-many', $field)){ $field['relType'] = 'has-many'; + if (!isset($field['type'])) + $field['type'] = Schema::DT_INT; $relConf = $field['has-many']; if(!is_array($relConf)) return $field; @@ -473,7 +515,9 @@ class Cortex extends Cursor { $field['has-many']['hasRel'] = 'has-many'; $field['has-many']['relTable'] = $rel['table']; $field['has-many']['relField'] = $relConf[1]; - $field['has-many']['relPK'] = $rel['primary']; + $field['has-many']['relFieldType'] = isset($rel['fieldConf'][$relConf[1]]['type']) ? + $rel['fieldConf'][$relConf[1]]['type'] : Schema::DT_INT; + $field['has-many']['relPK'] = isset($relConf[3])?$relConf[3]:$rel['primary']; } else { $field['has-many']['hasRel'] = 'belongs-to-one'; $toConf=$rel['fieldConf'][$relConf[1]]['belongs-to-one']; @@ -543,8 +587,9 @@ class Cortex extends Cursor { foreach($result as &$mapper) { $cr=$mapper->get($counter); $mapper->virtual('count_'.$counter,$cr?count($cr):null); + unset($mapper); } - $cc = new \DB\CortexCollection(); + $cc = new CortexCollection(); $cc->setModels($result); if($sort) { $cc->orderBy($options['order']); @@ -604,6 +649,10 @@ class Cortex extends Cursor { if ($this->hasCond) { foreach($this->hasCond as $key => $hasCond) { $addToFilter = null; + if ($deep = is_int(strpos($key,'.'))) { + $key = rtrim($key,'.'); + $hasCond = array(null,null); + } list($has_filter,$has_options) = $hasCond; $type = $this->fieldConf[$key]['relType']; $fromConf = $this->fieldConf[$key][$type]; @@ -618,7 +667,7 @@ class Cortex extends Cursor { $id=$fromConf['relField']; // many-to-many if ($type == 'has-many' && $fromConf['hasRel'] == 'has-many') { - if ($this->dbsType == 'sql' + if (!$deep && $this->dbsType == 'sql' && !isset($has_options['limit']) && !isset($has_options['offset'])) { $hasJoin = array_merge($hasJoin, $this->_hasJoinMM_sql($key,$hasCond,$filter,$options)); @@ -639,7 +688,7 @@ class Cortex extends Cursor { break; // one-to-* case 'belongs-to-one': - if ($this->dbsType == 'sql' + if (!$deep && $this->dbsType == 'sql' && !isset($has_options['limit']) && !isset($has_options['offset'])) { if (!is_array($fromConf)) $fromConf = array($fromConf, '_id'); @@ -660,7 +709,10 @@ class Cortex extends Cursor { $filter = array(''); if (!empty($filter[0])) $filter[0] .= ' and '; - $filter[0] .= '('.array_shift($addToFilter).')'; + $cond = array_shift($addToFilter); + if ($this->dbsType=='sql') + $cond = $this->_sql_quoteCondition($cond,$this->db->quotekey($this->getTable())); + $filter[0] .= '('.$cond.')'; $filter = array_merge($filter, $addToFilter); } } @@ -726,6 +778,8 @@ class Cortex extends Cursor { // factory new mappers $mapper = clone($this->mapper); $mapper->reset(); + // TODO: refactor this. Reflection can be removed for F3 >= v3.4.1 + $mapper->query= array($record); $m_adhoc = empty($adhoc) ? array() : $m_refl_adhoc; foreach ($record as $key=>$val) if (isset($m_refl_adhoc[$key])) @@ -787,13 +841,20 @@ class Cortex extends Cursor { * @return $this */ public function has($key, $filter, $options = null) { - if (!isset($this->fieldConf[$key])) - trigger_error(sprintf(self::E_UNKNOWN_FIELD,$key,get_called_class())); - if (!isset($this->fieldConf[$key]['relType'])) - trigger_error(self::E_HAS_COND); if (is_string($filter)) $filter=array($filter); - $this->hasCond[$key] = array($filter,$options); + if (is_int(strpos($key,'.'))) { + list($key,$fkey) = explode('.',$key,2); + if (!isset($this->hasCond[$key.'.'])) + $this->hasCond[$key.'.'] = array(); + $this->hasCond[$key.'.'][$fkey] = array($filter,$options); + } else { + if (!isset($this->fieldConf[$key])) + trigger_error(sprintf(self::E_UNKNOWN_FIELD,$key,get_called_class())); + if (!isset($this->fieldConf[$key]['relType'])) + trigger_error(self::E_HAS_COND); + $this->hasCond[$key] = array($filter,$options); + } return $this; } @@ -809,12 +870,8 @@ class Cortex extends Cursor { { $type = $this->fieldConf[$key]['relType']; $fieldConf = $this->fieldConf[$key][$type]; - if (!is_array($fieldConf)) - // one-to-many shortcut - $fieldConf = array($fieldConf, '_id'); - $rel = $this->getRelInstance($fieldConf[0],null,$key); - if($this->dbsType=='sql' && $fieldConf[1] == '_id') - $fieldConf[1] = $rel->primary; + // one-to-many shortcut + $rel = $this->getRelFromConf($fieldConf,$key); $hasSet = $rel->find($filter, $options, $ttl); if (!$hasSet) return false; @@ -833,18 +890,12 @@ class Cortex extends Cursor { protected function _hasRefsInMM($key, $filter, $options, $ttl=0) { $fieldConf = $this->fieldConf[$key]['has-many']; - $rel = $this->getRelInstance($fieldConf[0],null,$key); + $rel = $this->getRelInstance($fieldConf[0],null,$key,true); $hasSet = $rel->find($filter,$options,$ttl); $result = false; if ($hasSet) { $hasIDs = $hasSet->getAll('_id',true); - if (!array_key_exists('refTable', $fieldConf)) { - $mmTable = isset($fieldConf[2]) ? $fieldConf[2] : - static::getMMTableName($fieldConf['relTable'], - $fieldConf['relField'], $this->getTable(), $key); - $this->fieldConf[$key]['has-many']['refTable'] = $mmTable; - } else - $mmTable = $fieldConf['refTable']; + $mmTable = $this->mmTable($fieldConf,$key); $pivot = $this->getRelInstance(null,array('db'=>$this->db,'table'=>$mmTable)); $pivotSet = $pivot->find(array($key.' IN ?',$hasIDs),null,$ttl); if ($pivotSet) @@ -860,14 +911,7 @@ class Cortex extends Cursor { { $fieldConf = $this->fieldConf[$key]['has-many']; $hasJoin = array(); - if (!array_key_exists('refTable', $fieldConf)) { - // compute mm table name - $mmTable = isset($fieldConf[2]) ? $fieldConf[2] : - static::getMMTableName($fieldConf['relTable'], - $fieldConf['relField'], $this->getTable(), $key); - $this->fieldConf[$key]['has-many']['refTable'] = $mmTable; - } else - $mmTable = $fieldConf['refTable']; + $mmTable = $this->mmTable($fieldConf,$key); $hasJoin[] = $this->_sql_left_join($this->primary,$this->table,$fieldConf['relField'],$mmTable); $hasJoin[] = $this->_sql_left_join($key,$mmTable,$fieldConf['relPK'],$fieldConf['relTable']); $this->_sql_mergeRelCondition($hasCond,$fieldConf['relTable'],$filter,$options); @@ -948,7 +992,13 @@ class Cortex extends Cursor { */ public function filter($key,$filter=null,$option=null) { - $this->relFilter[$key] = array($filter,$option); + if (is_int(strpos($key,'.'))) { + list($key,$fkey) = explode('.',$key,2); + if (!isset($this->relFilter[$key.'.'])) + $this->relFilter[$key.'.'] = array(); + $this->relFilter[$key.'.'][$fkey] = array($filter,$option); + } else + $this->relFilter[$key] = array($filter,$option); return $this; } @@ -1002,7 +1052,7 @@ class Cortex extends Cursor { public function erase($filter = null) { $filter = $this->queryParser->prepareFilter($filter, $this->dbsType); - if ((!$filter && $this->emit('beforeerase')!==false) || $filter) { + if (!$filter && $this->emit('beforeerase')!==false) { if ($this->fieldConf) { foreach($this->fieldConf as $field => $conf) if (isset($conf['has-many']) && @@ -1010,10 +1060,10 @@ class Cortex extends Cursor { $this->set($field,null); $this->save(); } + $this->mapper->erase(); + $this->emit('aftererase'); + } elseif($filter) $this->mapper->erase($filter); - if (!$filter) - $this->emit('aftererase'); - } } /** @@ -1031,22 +1081,22 @@ class Cortex extends Cursor { return false; $result=$this->update(); } + // update changed collections + $fields = $this->fieldConf; + if ($fields) + foreach($fields as $key=>$conf) + if (!empty($this->fieldsCache[$key]) && $this->fieldsCache[$key] instanceof CortexCollection + && $this->fieldsCache[$key]->hasChanged()) + $this->set($key,$this->fieldsCache[$key]->getAll('_id',true)); + // m:m save cascade if (!empty($this->saveCsd)) { - $fields = $this->fieldConf; foreach($this->saveCsd as $key => $val) { if($fields[$key]['relType'] == 'has-many') { $relConf = $fields[$key]['has-many']; - if (!isset($relConf['refTable'])) { - // compute mm table name - $mmTable = isset($relConf[2]) ? $relConf[2] : - static::getMMTableName($relConf['relTable'], - $relConf['relField'], $this->getTable(), $key); - $this->fieldConf[$key]['has-many']['refTable'] = $mmTable; - } else - $mmTable = $relConf['refTable']; + $mmTable = $this->mmTable($relConf,$key); $rel = $this->getRelInstance(null, array('db'=>$this->db, 'table'=>$mmTable)); - $id = $this->get('_id',true); + $id = $this->get($relConf['relPK'],true); // delete all refs if (is_null($val)) $rel->erase(array($relConf['relField'].' = ?', $id)); @@ -1065,6 +1115,7 @@ class Cortex extends Cursor { $val->save(); } } + $this->saveCsd = array(); } $this->emit($new?'afterinsert':'afterupdate'); return $result; @@ -1116,14 +1167,7 @@ class Cortex extends Cursor { if ($relConf['hasRel']=='has-many') { // many-to-many if ($this->dbsType == 'sql') { - if (!isset($relConf['refTable'])) { - // compute mm table name - $mmTable = isset($relConf[2]) ? $relConf[2] : - static::getMMTableName($relConf['relTable'], - $relConf['relField'], $this->getTable(), $key); - $this->fieldConf[$key]['has-many']['refTable'] = $mmTable; - } else - $mmTable = $relConf['refTable']; + $mmTable = $this->mmTable($relConf,$key); $filter = array($this->db->quotekey($mmTable).'.'.$this->db->quotekey($relConf['relField']) .' = '.$this->db->quotekey($this->getTable()).'.'.$this->db->quotekey($this->primary)); $from=$mmTable; @@ -1187,6 +1231,23 @@ class Cortex extends Cursor { $this->grp_stack['finalize'].=$opt['finalize']; } + /** + * update a given date or time field with the current time + * @param string $key + */ + public function touch($key) { + if (isset($this->fieldConf[$key]) + && isset($this->fieldConf[$key]['type'])) { + $type = $this->fieldConf[$key]['type']; + $date = ($this->dbsType=='sql' && preg_match('/mssql|sybase|dblib|odbc|sqlsrv/', + $this->db->driver())) ? 'Ymd' : 'Y-m-d'; + if ($type == Schema::DT_DATETIME || Schema::DT_TIMESTAMP) + $this->set($key,date($date.' H:i:s')); + elseif ($type == Schema::DT_DATE) + $this->set($key,date($date)); + } + } + /** * Bind value to key * @return mixed @@ -1283,9 +1344,11 @@ class Cortex extends Cursor { $schema = new Schema($this->db); $table = $schema->alterTable($this->table); // add missing field - if(!in_array($key,$table->getCols())) { + if (!in_array($key,$table->getCols())) { // determine data type - if (is_int($val)) $type = $schema::DT_INT; + if (isset($this->fieldConf[$key]) && isset($this->fieldConf[$key]['type'])) + $type = $this->fieldConf[$key]['type']; + elseif (is_int($val)) $type = $schema::DT_INT; elseif (is_double($val)) $type = $schema::DT_DOUBLE; elseif (is_float($val)) $type = $schema::DT_FLOAT; elseif (is_bool($val)) $type = $schema::DT_BOOLEAN; @@ -1377,7 +1440,7 @@ class Cortex extends Cursor { return $out; } $fields = $this->fieldConf; - $id = ($this->dbsType == 'sql') ? $this->primary : '_id'; + $id = $this->primary; if ($key == '_id' && $this->dbsType == 'sql') $key = $id; if ($this->whitelist && !in_array($key,$this->whitelist)) { @@ -1398,15 +1461,10 @@ class Cortex extends Cursor { else { // get config for this field $relConf = $fields[$key]['belongs-to-one']; - if (!is_array($relConf)) - $relConf = array($relConf, '_id'); // fetch related model - $rel = $this->getRelInstance($relConf[0],null,$key); - if ($this->dbsType == 'sql' && $relConf[1] == '_id') - $relConf[1] = $rel->primary; + $rel = $this->getRelFromConf($relConf,$key); // am i part of a result collection? - if ($this->collectionID && $this->smartLoading) { - $cx = CortexCollection::instance($this->collectionID); + if ($cx = $this->getCollection()) { // does the collection has cached results for this key? if (!$cx->hasRelSet($key)) { // build the cache, find all values of current key @@ -1435,7 +1493,7 @@ class Cortex extends Cursor { $fromConf = $fields[$key][$type]; if (!is_array($fromConf)) trigger_error(sprintf(self::E_REL_CONF_INC, $key)); - $rel = $this->getRelInstance($fromConf[0],null,$key); + $rel = $this->getRelInstance($fromConf[0],null,$key,true); $relFieldConf = $rel->getFieldConfiguration(); $relType = key($relFieldConf[$fromConf[1]]); // one-to-*, bidirectional, inverse way @@ -1446,9 +1504,8 @@ class Cortex extends Cursor { if ($toConf[1] != $id && (!$this->exists($toConf[1]) || is_null($this->mapper->get($toConf[1])))) $this->fieldsCache[$key] = null; - elseif($this->collectionID && $this->smartLoading) { + elseif($cx = $this->getCollection()) { // part of a result set - $cx = CortexCollection::instance($this->collectionID); if(!$cx->hasRelSet($key)) { // emit eager loading $relKeys = $cx->getAll($toConf[1],true); @@ -1459,7 +1516,7 @@ class Cortex extends Cursor { } $result = $cx->getSubset($key, array($this->get($toConf[1]))); $this->fieldsCache[$key] = $result ? (($type == 'has-one') - ? $result[0][0] : $result[0]) : NULL; + ? $result[0][0] : CortexCollection::factory($result[0])) : NULL; } else { $crit = array($fromConf[1].' = ?', $this->get($toConf[1],true)); $crit = $this->mergeWithRelFilter($key, $crit); @@ -1471,19 +1528,16 @@ class Cortex extends Cursor { } // many-to-many, bidirectional elseif ($relType == 'has-many') { - if (!array_key_exists('refTable', $fromConf)) { - // compute mm table name - $toConf = $relFieldConf[$fromConf[1]]['has-many']; - $mmTable = isset($fromConf[2]) ? $fromConf[2] : - static::getMMTableName($fromConf['relTable'], - $fromConf['relField'], $this->getTable(), $key, $toConf); - $this->fieldConf[$key]['has-many']['refTable'] = $mmTable; - } else - $mmTable = $fromConf['refTable']; + $toConf = $relFieldConf[$fromConf[1]]['has-many']; + $mmTable = $this->mmTable($fromConf,$key,$toConf); // create mm table mapper + if (!$this->get($id,true)) { + $this->fieldsCache[$key] = null; + return $this->fieldsCache[$key]; + } + $id = $toConf['relPK']; $rel = $this->getRelInstance(null,array('db'=>$this->db,'table'=>$mmTable)); - if ($this->collectionID && $this->smartLoading) { - $cx = CortexCollection::instance($this->collectionID); + if ($cx = $this->getCollection()) { if (!$cx->hasRelSet($key)) { // get IDs of all results $relKeys = $cx->getAll($id,true); @@ -1503,8 +1557,8 @@ class Cortex extends Cursor { $cx->setRelSet($key.'_pivot', $pivotRel); // preload all rels $pivotKeys = array_unique($pivotKeys); - $fRel = $this->getRelInstance($fromConf[0],null,$key); - $crit = array(($fRel->primary!='id' ? $fRel->primary : '_id').' IN ?', $pivotKeys); + $fRel = $this->getRelInstance($fromConf[0],null,$key,true); + $crit = array($toConf['relPK'].' IN ?', $pivotKeys); $relSet = $fRel->find($this->mergeWithRelFilter($key, $crit), $this->getRelFilterOption($key),$this->_ttl); $cx->setRelSet($key, $relSet ? $relSet->getBy($id) : NULL); @@ -1513,21 +1567,22 @@ class Cortex extends Cursor { } // fetch subset from preloaded rels using cached pivot keys $fkeys = $cx->getSubset($key.'_pivot', array($this->get($id))); - $this->fieldsCache[$key] = $fkeys ? $cx->getSubset($key, $fkeys[0]) : NULL; + $this->fieldsCache[$key] = $fkeys ? + CortexCollection::factory($cx->getSubset($key, $fkeys[0])) : NULL; } // no collection else { // find foreign keys $results = $rel->find( - array($fromConf['relField'].' = ?', $this->get($id,true)),null,$this->_ttl); + array($fromConf['relField'].' = ?', $this->get($fromConf['relPK'],true)),null,$this->_ttl); if(!$results) $this->fieldsCache[$key] = NULL; else { $fkeys = $results->getAll($key,true); // create foreign table mapper unset($rel); - $rel = $this->getRelInstance($fromConf[0],null,$key); + $rel = $this->getRelInstance($fromConf[0],null,$key,true); // load foreign models - $filter = array(($rel->primary!='id' ? $rel->primary : '_id').' IN ?', $fkeys); + $filter = array($toConf['relPK'].' IN ?', $fkeys); $filter = $this->mergeWithRelFilter($key, $filter); $this->fieldsCache[$key] = $rel->find($filter, $this->getRelFilterOption($key),$this->_ttl); @@ -1546,17 +1601,12 @@ class Cortex extends Cursor { else { // create foreign table mapper $relConf = $fields[$key]['belongs-to-many']; - if (!is_array($relConf)) - $relConf = array($relConf, '_id'); - $rel = $this->getRelInstance($relConf[0],null,$key); - if ($this->dbsType == 'sql' && $relConf[1] == '_id') - $relConf[1] = $rel->primary; + $rel = $this->getRelFromConf($relConf,$key); $fkeys = array(); foreach ($result as $el) $fkeys[] = is_int($el)||ctype_digit($el)?(int)$el:(string)$el; // if part of a result set - if ($this->collectionID && $this->smartLoading) { - $cx = CortexCollection::instance($this->collectionID); + if ($cx = $this->getCollection()) { if (!$cx->hasRelSet($key)) { // find all keys $relKeys = ($cx->getAll($key,true)); @@ -1576,7 +1626,7 @@ class Cortex extends Cursor { $cx->setRelSet($key, $relSet ? $relSet->getBy($relConf[1]) : NULL); } // get a subset of the preloaded set - $this->fieldsCache[$key] = $cx->getSubset($key, $fkeys); + $this->fieldsCache[$key] = CortexCollection::factory($cx->getSubset($key, $fkeys)); } else { // load foreign models $filter = array($relConf[1].' IN ?', $fkeys); @@ -1664,14 +1714,16 @@ class Cortex extends Cursor { * @param string $model * @param array $relConf * @param string $key + * @param bool $pushFilter * @return Cortex */ - protected function getRelInstance($model=null,$relConf=null,$key='') + protected function getRelInstance($model=null,$relConf=null,$key='',$pushFilter=false) { if (!$model && !$relConf) trigger_error(self::E_MISSING_REL_CONF); $relConf = $model ? $model::resolveConfiguration() : $relConf; - $relName = ($model?:'Cortex').'\\'.$relConf['db']->uuid().'\\'.$relConf['table']; + $relName = ($model?:'Cortex').'\\'.$relConf['db']->uuid(). + '\\'.$relConf['table'].'\\'.$key; if (\Registry::exists($relName)) { $rel = \Registry::get($relName); $rel->reset(); @@ -1688,14 +1740,53 @@ class Cortex extends Cursor { if (isset($this->relWhitelist[$key][1])) $rel->fields($this->relWhitelist[$key][1],true); } + if ($pushFilter && !empty($key)) { + if (isset($this->relFilter[$key.'.'])) { + foreach($this->relFilter[$key.'.'] as $fkey=>$conf) + $rel->filter($fkey,$conf[0],$conf[1]); + } + if (isset($this->hasCond[$key.'.'])) { + foreach($this->hasCond[$key.'.'] as $fkey=>$conf) + $rel->has($fkey,$conf[0],$conf[1]); + } + } return $rel; } + /** + * get relation model from config + * @param $fieldConf + * @param $key + * @return Cortex + */ + protected function getRelFromConf(&$fieldConf, $key) { + if (!is_array($fieldConf)) + $fieldConf = array($fieldConf, '_id'); + $rel = $this->getRelInstance($fieldConf[0],null,$key,true); + if($this->dbsType=='sql' && $fieldConf[1] == '_id') + $fieldConf[1] = $rel->primary; + return $rel; + } + + /** + * returns a clean/dry model from a relation + * @param string $key + * @return Cortex + */ + public function rel($key) + { + $rt = $this->fieldConf[$key]['relType']; + $rc = $this->fieldConf[$key][$rt]; + if (!is_array($rc)) + $rc = array($rc,'_id'); + return $this->getRelInstance($rc[0],null,$key); + } + /** * Return fields of mapper object as an associative array * @return array * @param bool|Cortex $obj - * @param bool|int $rel_depths depths to resolve relations + * @param int|array $rel_depths depths to resolve relations */ public function cast($obj = NULL, $rel_depths = 1) { @@ -1704,7 +1795,9 @@ class Cortex extends Cursor { foreach(array_keys($this->vFields) as $key) $fields[$key]=$this->get($key); if (is_int($rel_depths)) - $rel_depths--; + $rel_depths = array('*'=>$rel_depths-1); + elseif (is_array($rel_depths)) + $rel_depths['*'] = isset($rel_depths['*'])?--$rel_depths['*']:-1; if (!empty($this->fieldConf)) { $fields += array_fill_keys(array_keys($this->fieldConf),NULL); if($this->whitelist) @@ -1714,36 +1807,29 @@ class Cortex extends Cursor { // post process configured fields if (isset($this->fieldConf[$key]) && is_array($this->fieldConf[$key])) { // handle relations - if (($rel_depths === TRUE || (is_int($rel_depths) && $rel_depths >= 0)) - && $type=preg_grep('/[belongs|has]-(to-)*[one|many]/', + $rd = isset($rel_depths[$key]) ? $rel_depths[$key] : $rel_depths['*']; + if ((is_array($rd) || $rd >= 0) && $type=preg_grep('/[belongs|has]-(to-)*[one|many]/', array_keys($this->fieldConf[$key]))) { $relType=$type[0]; // cast relations $val = (($relType == 'belongs-to-one' || $relType == 'belongs-to-many') && !$mp->exists($key)) ? NULL : $mp->get($key); - if (is_array($val) || is_object($val)) { - if ($relType == 'belongs-to-one' || $relType == 'has-one') - // single object - $val = $val->cast(null, $rel_depths); - elseif ($relType == 'belongs-to-many' || $relType == 'has-many') - // multiple objects - foreach ($val as $k => $item) - $val[$k] = is_object($item) ? $item->cast(null, $rel_depths) : null; - } - if ($val instanceof CortexCollection) - $val = $val->expose(); + if ($val instanceof Cortex) + $val = $val->cast(null, $rd); + elseif ($val instanceof CortexCollection) + $val = $val->castAll($rd); } - // decode array fields + // extract array fields elseif (isset($this->fieldConf[$key]['type'])) { if ($this->dbsType == 'sql') { if ($this->fieldConf[$key]['type'] == self::DT_SERIALIZED) - $val=unserialize($this->mapper->{$key}); + $val=unserialize($mp->mapper->{$key}); elseif ($this->fieldConf[$key]['type'] == self::DT_JSON) - $val=json_decode($this->mapper->{$key}, true); + $val=json_decode($mp->mapper->{$key}, true); } if ($this->exists($key) && preg_match('/BOOL/i',$this->fieldConf[$key]['type'])) { - $val = (bool) $this->mapper->{$key}; + $val = (bool) $mp->mapper->{$key}; } } } @@ -2043,6 +2129,7 @@ class CortexQueryParser extends \Prefab { } else $ncond[] = $val; } + unset($part); } array_unshift($ncond, implode(' ', $parts)); break; @@ -2089,6 +2176,7 @@ class CortexQueryParser extends \Prefab { $params[] = $args[$match[0]]; } elseif (is_int(strpos($part, '?'))) $params[] = $args[$pos++]; + unset($part); } return array($parts, $params); } @@ -2140,6 +2228,7 @@ class CortexQueryParser extends \Prefab { $chks[] = 'isset('.$field.')'; $part = '('.implode(' && ',$chks).' && ('.$part.'))'; } + unset($part); } array_unshift($ncond, implode(' ', $parts)); return $ncond; @@ -2302,7 +2391,8 @@ class CortexQueryParser extends \Prefab { } if (array_key_exists('group', $options) && is_string($options['group'])) { $keys = explode(',',$options['group']); - $options['group']=array('keys'=>array(),'initial'=>array(),'reduce'=>'function (obj, prev) {}','finalize'=>''); + $options['group']=array('keys'=>array(),'initial'=>array(), + 'reduce'=>'function (obj, prev) {}','finalize'=>''); $keys = array_combine($keys,array_fill(0,count($keys),1)); $options['group']['keys']=$keys; $options['group']['initial']=$keys; @@ -2318,6 +2408,7 @@ class CortexCollection extends \ArrayIterator { protected $relSets = array(), $pointer = 0, + $changed = false, $cid; const @@ -2326,15 +2417,9 @@ class CortexCollection extends \ArrayIterator { public function __construct() { $this->cid = uniqid('cortex_collection_'); - \Registry::set($this->cid,$this); parent::__construct(); } - public function __destruct() { - // free embedded relation cache from memory - \Registry::clear($this->cid); - } - //! Prohibit cloning to ensure an existing relation cache private function __clone() { } @@ -2342,20 +2427,30 @@ class CortexCollection extends \ArrayIterator { * set a collection of models * @param $models */ - function setModels($models) { + function setModels($models,$init=true) { array_map(array($this,'add'),$models); + if ($init) + $this->changed = false; } /** * add single model to collection * @param $model */ - function add(Cortex $model) - { - $model->addToCollection($this->cid); + function add(Cortex $model) { + $model->addToCollection($this); $this->append($model); } + public function offsetSet($i, $val) { + $this->changed=true; + parent::offsetSet($i,$val); + } + + public function hasChanged() { + return $this->changed; + } + /** * get a related collection * @param $key @@ -2400,9 +2495,11 @@ class CortexCollection extends \ArrayIterator { trigger_error(sprintf(self::E_SubsetKeysValue,gettype($keys))); if (!$this->hasRelSet($prop) || !($relSet = $this->getRelSet($prop))) return null; - foreach ($keys as &$key) + foreach ($keys as &$key) { if ($key instanceof \MongoId) $key = (string) $key; + unset($key); + } return array_values(array_intersect_key($relSet, array_flip($keys))); } @@ -2415,18 +2512,19 @@ class CortexCollection extends \ArrayIterator { public function getAll($prop, $raw = false) { $out = array(); - foreach ($this->getArrayCopy() as $model) + foreach ($this->getArrayCopy() as $model) { if ($model->exists($prop,true)) { $val = $model->get($prop, $raw); if (!empty($val)) $out[] = $val; } + } return $out; } /** * cast all contained mappers to a nested array - * @param int $rel_depths depths to resolve relations + * @param int|array $rel_depths depths to resolve relations * @return array */ public function castAll($rel_depths=1) { @@ -2496,14 +2594,10 @@ class CortexCollection extends \ArrayIterator { unset($this[$ii]); } - /** - * @param $cid - * @return CortexCollection - */ - static public function instance($cid) { - if (!\Registry::exists($cid)) - trigger_error(sprintf(self::E_UnknownCID, $cid)); - return \Registry::get($cid); + static public function factory($records) { + $cc = new self(); + $cc->setModels($records); + return $cc; } } \ No newline at end of file diff --git a/app/main/controller/AccessController.php b/app/main/controller/AccessController.php index f7932454..394204c7 100644 --- a/app/main/controller/AccessController.php +++ b/app/main/controller/AccessController.php @@ -22,25 +22,6 @@ class AccessController extends Controller { $accessRoute = $this->_isLoggedIn(); - /* - $userName = 'user_exodus'; - $password = '1234567'; - - // try to verify user - $accessRoute = $this->_verifyUser($userName, $password); - - if(!$accessRoute){ - // add new User - try{ - $this->_registerUser($userName, $password); - }catch(\Exception\ValidationException $e){ - // registration failed - $this->f3->error($e->getCode(), $e->getMessage()); - } - } - */ - - if( !$this->f3->get('AJAX') && !$accessRoute @@ -108,6 +89,17 @@ class AccessController extends Controller { return $loggedIn; } + /** + * get error object is a user is not found/logged of + * @return object + */ + protected function getUserLoggedOffError(){ + $userError = (object) []; + $userError->type = 'error'; + $userError->message = 'User not found'; + + return $userError; + } diff --git a/app/main/controller/Controller.php b/app/main/controller/Controller.php index a88fb70b..060469c6 100644 --- a/app/main/controller/Controller.php +++ b/app/main/controller/Controller.php @@ -58,7 +58,7 @@ class Controller { * set/change DB connection * @param $type */ - protected function setDB($type){ + protected function setDB($type = ''){ if($type === 'CCP'){ // CCP DB @@ -84,14 +84,20 @@ class Controller { /** * get current user model * @return bool|null + * @throws \Exception */ protected function _getUser(){ - $user = Model\BasicModel::getNew('UserModel', 5); - $user->getById($this->f3->get('SESSION.user.id')); + $user = false; + $userId = $this->f3->get('SESSION.user.id'); - if($user->dry()){ - $user = false; + if($userId > 0){ + $userModel = Model\BasicModel::getNew('UserModel', 5); + $userModel->getById($userId); + + if( !$userModel->dry() ){ + $user = $userModel; + } } return $user; diff --git a/app/main/controller/MapController.php b/app/main/controller/MapController.php index fcd3ccfb..babe5c05 100644 --- a/app/main/controller/MapController.php +++ b/app/main/controller/MapController.php @@ -52,6 +52,11 @@ class MapController extends \Controller\AccessController { 'text' => $f3->get('ERROR.text') ]; + // append stack trace for greater debug level + if( $f3->get('DEBUG') === 3){ + $errorData['trace'] = $f3->get('ERROR.trace'); + } + echo json_encode($errorData); }else{ echo $f3->get('ERROR.text'); diff --git a/app/main/controller/api/Access.php b/app/main/controller/api/Access.php new file mode 100644 index 00000000..99321092 --- /dev/null +++ b/app/main/controller/api/Access.php @@ -0,0 +1,75 @@ +find( array( + "LOWER(name) LIKE :token AND active = 1", + ':token' => '%' . $searchToken . '%' + )); + + if($accessList){ + foreach($accessList as $accessObject){ + $accessData[] = $accessObject->getData(); + } + } + } + + } + + echo json_encode($accessData); + } + + + + +} \ No newline at end of file diff --git a/app/main/controller/api/Map.php b/app/main/controller/api/Map.php index eb03f9ea..83f0a01a 100644 --- a/app/main/controller/api/Map.php +++ b/app/main/controller/api/Map.php @@ -125,6 +125,14 @@ class Map extends \Controller\AccessController { } $initData['characterStatus'] = $characterStatusData; + // get max number of shared entities per map -------------------------- + $maxSharedCount = [ + 'user' => $f3->get('PATHFINDER.MAX_SHARED_USER'), + 'corporation' => $f3->get('PATHFINDER.MAX_SHARED_CORPORATION'), + 'alliance' => $f3->get('PATHFINDER.MAX_SHARED_ALLIANCE'), + ]; + $initData['maxSharedCount'] = $maxSharedCount; + echo json_encode($initData); } @@ -133,45 +141,138 @@ class Map extends \Controller\AccessController { * @param $f3 */ public function save($f3){ - $mapData = (array)$f3->get('POST.mapData'); + $formData = (array)$f3->get('POST.formData'); - $user = $this->_getUser(); + $return = (object) []; + $return->error = []; - $map = Model\BasicModel::getNew('MapModel'); - $map->getById($mapData['id']); - $map->setData($mapData); - $map->save(); + if(array_key_exists( 'id', $formData)){ - if($map->isPrivate()){ - $map->clearAccess(['corporation', 'alliance']); - $map->setAccess($user); - }elseif($map->isCorporation()){ - $activeCharacter = $user->getActiveUserCharacter(); + $user = $this->_getUser(); - if($activeCharacter){ - $corporation = $activeCharacter->getCharacter()->getCorporation(); + $map = Model\BasicModel::getNew('MapModel'); + $map->getById($formData['id']); - if($corporation){ - $map->clearAccess(['user', 'alliance']); - $map->setAccess($corporation); - } + // check if the user has access to this map + $mapAccess = true; + if(! $map->dry() ){ + $mapAccess = $map->hasAccess($user); } - }elseif($map->isAlliance()){ - $activeCharacter = $user->getActiveUserCharacter(); - if($activeCharacter){ - $alliance = $activeCharacter->getCharacter()->getAlliance(); + if($mapAccess){ + $map->setData($formData); + $map = $map->save(); - if($alliance){ - $map->clearAccess(['user', 'corporation']); - $map->setAccess($alliance); + // save global map access. Depends on map "type" + if($map->isPrivate()){ + + // share map between users -> set access + if(array_key_exists('mapUsers', $formData)){ + // clear map access. In case something has removed from access list + $map->clearAccess(); + + $tempUser = Model\BasicModel::getNew('UserModel'); + + foreach((array)$formData['mapUsers'] as $userId){ + $tempUser->getById( $userId ); + + if( !$tempUser->dry() ){ + $map->setAccess($tempUser); + } + + $tempUser->reset(); + } + } + + // the current user itself should always have access + // just in case he removed himself :) + $map->setAccess($user); + }elseif($map->isCorporation()){ + $activeCharacter = $user->getActiveUserCharacter(); + + if($activeCharacter){ + $corporation = $activeCharacter->getCharacter()->getCorporation(); + + if($corporation){ + // the current user has to have a corporation when + // working on corporation maps! + + // share map between corporations -> set access + if(array_key_exists('mapCorporations', $formData)){ + // clear map access. In case something has removed from access list + $map->clearAccess(); + + $tempCorporation = Model\BasicModel::getNew('CorporationModel'); + + foreach((array)$formData['mapCorporations'] as $corporationId){ + $tempCorporation->getById( $corporationId ); + + if( !$tempCorporation->dry() ){ + $map->setAccess($tempCorporation); + } + + $tempCorporation->reset(); + } + } + + // the corporation of the current user should always have access + $map->setAccess($corporation); + } + } + }elseif($map->isAlliance()){ + $activeCharacter = $user->getActiveUserCharacter(); + + if($activeCharacter){ + $alliance = $activeCharacter->getCharacter()->getAlliance(); + + if($alliance){ + // the current user has to have a alliance when + // working on alliance maps! + + // share map between alliances -> set access + if(array_key_exists('mapAlliances', $formData)){ + // clear map access. In case something has removed from access list + $map->clearAccess(); + + $tempAlliance = Model\BasicModel::getNew('AllianceModel'); + + foreach((array)$formData['mapAlliances'] as $allianceId){ + $tempAlliance->getById( $allianceId ); + + if( !$tempAlliance->dry() ){ + $map->setAccess($tempAlliance); + } + + $tempAlliance->reset(); + } + + } + + // the alliance of the current user should always have access + $map->setAccess($alliance); + } + } } + + $return->mapData = $map->getData(); + }else{ + // map access denied + $captchaError = new \stdClass(); + $captchaError->type = 'error'; + $captchaError->message = 'Access denied'; + $return->error[] = $captchaError; } + }else{ + // map id field missing + $idError = new \stdClass(); + $idError->type = 'error'; + $idError->message = 'Map id missing'; + $return->error[] = $idError; } - $mapData = $map->getData(); - echo json_encode($mapData); + + echo json_encode($return); } /** @@ -202,92 +303,102 @@ class Map extends \Controller\AccessController { $responseTTL = 3; $user = $this->_getUser(); - $cacheKey = 'user_map_data_' . $user->id; - if($f3->exists($cacheKey) === false){ + $return = (object) []; + $return->error = []; - $mapData = (array)$f3->get('POST.mapData'); + if($user){ + $cacheKey = 'user_map_data_' . $user->id; + if($f3->exists($cacheKey) === false){ + + $mapData = (array)$f3->get('POST.mapData'); - $map = Model\BasicModel::getNew('MapModel'); - $system = Model\BasicModel::getNew('SystemModel'); - $connection = Model\BasicModel::getNew('ConnectionModel'); + $map = Model\BasicModel::getNew('MapModel'); + $system = Model\BasicModel::getNew('SystemModel'); + $connection = Model\BasicModel::getNew('ConnectionModel'); - $activeCharacter = $user->getActiveUserCharacter(); + $activeCharacter = $user->getActiveUserCharacter(); - foreach($mapData as $data){ + foreach($mapData as $data){ - $config = $data['config']; - $systems = []; - $connections = []; + $config = $data['config']; + $systems = []; + $connections = []; - // check whether system data and/or connection data is send - // empty arrays are not included in ajax requests - if(array_key_exists('data', $data)){ - if(array_key_exists('systems', $data['data'])){ - $systems = $data['data']['systems']; + // check whether system data and/or connection data is send + // empty arrays are not included in ajax requests + if(array_key_exists('data', $data)){ + if(array_key_exists('systems', $data['data'])){ + $systems = $data['data']['systems']; + } + if(array_key_exists('connections', $data['data'])){ + $connections = $data['data']['connections']; + } } - if(array_key_exists('connections', $data['data'])){ - $connections = $data['data']['connections']; + + // update map data --------------------------------------------- + $map->getById($config['id']); + + if(!$map->dry()){ + // update map on change + if( (int)$config['updated'] > strtotime($map->updated)){ + $map->setData($config); + $map->save(); + } } + + // get system data ----------------------------------------------- + foreach($systems as $systemData){ + $system->getById($systemData['id']); + + if( + (int)$systemData['updated']['updated'] === 0 && + !$system->dry() + ){ + $system->setData($systemData); + $system->mapId = $map; + $system->updatedCharacterId = $activeCharacter->characterId; + $system->save(); + } + + $system->reset(); + } + + // get connection data ------------------------------------------- + foreach($connections as $connectionData){ + $connection->getById($connectionData['id']); + + if( + (int)$connectionData['updated'] === 0 && + !$connection->dry() + ){ + $connectionData['mapId'] = $map; + $connection->setData($connectionData); + $connection->save($user); + } + + $connection->reset(); + } + + $map->reset(); } - // update map data --------------------------------------------- - $map->getById($config['id']); + // get map data ====================================================== + $activeMaps = $user->getMaps(); - if(!$map->dry()){ - // update map on change - if( (int)$config['updated'] > strtotime($map->updated)){ - $map->setData($config); - $map->save(); - } - } + // format map Data for return + $newData = self::getFormattedMapData($activeMaps); - // get system data ----------------------------------------------- - foreach($systems as $systemData){ - $system->getById($systemData['id']); - - if( - (int)$systemData['updated']['updated'] === 0 && - !$system->dry() - ){ - $system->setData($systemData); - $system->mapId = $map; - $system->updatedCharacterId = $activeCharacter->characterId; - $system->save(); - } - - $system->reset(); - } - - // get connection data ------------------------------------------- - foreach($connections as $connectionData){ - $connection->getById($connectionData['id']); - - if( - (int)$connectionData['updated'] === 0 && - !$connection->dry() - ){ - $connectionData['mapId'] = $map; - $connection->setData($connectionData); - $connection->save($user); - } - - $connection->reset(); - } - - $map->reset(); + $f3->set($cacheKey, $newData, $responseTTL); } - // get map data ====================================================== - $activeMaps = $user->getMaps(); - - // format map Data for return - $newData = self::getFormattedMapData($activeMaps); - - $f3->set($cacheKey, $newData, $responseTTL); + $return = $f3->get($cacheKey); + }else{ + // user logged of + $return->error[] = $this->getUserLoggedOffError(); } - echo json_encode( $f3->get($cacheKey) ); + echo json_encode( $return ); } /** @@ -322,45 +433,55 @@ class Map extends \Controller\AccessController { $responseTTL = 2; $user = $this->_getUser(); - $cacheKey = 'user_data_' . $user->id; - if($f3->exists($cacheKey) === false){ + $return = (object) []; + $return->error = []; - // check if data for specific system is requested - $systemData = (array)$f3->get('POST.systemData'); + if($user){ + $cacheKey = 'user_data_' . $user->id; + if($f3->exists($cacheKey) === false){ - // update current location (IGB data) - $user->updateCharacterLog(); + // check if data for specific system is requested + $systemData = (array)$f3->get('POST.systemData'); - $userData = (object) []; - // data for the current user - $userData->userData = $user->getData(); + // update current location (IGB data) + $user->updateCharacterLog(); - // get user Data for each map ======================================== - $activeMaps = $user->getMaps(); + // data for the current user + $return->userData = $user->getData(); - foreach($activeMaps as $mapModel){ - $userData->mapUserData[] = $mapModel->getUserData(); + // get user Data for each map ======================================== + $activeMaps = $user->getMaps(); - // request signature data for a system if user has map access - if( - !empty($systemData) && - $systemData['mapId'] == $mapModel->id - ){ - $system = $mapModel->getSystem( (int)$systemData['systemData']['id']); + foreach($activeMaps as $mapModel){ + $return->mapUserData[] = $mapModel->getUserData(); + + // request signature data for a system if user has map access + if( + !empty($systemData) && + $systemData['mapId'] == $mapModel->id + ){ + $system = $mapModel->getSystem( (int)$systemData['systemData']['id']); + + if(! is_null($system)){ + // data for the current selected system + $return->system = $system->getData(); + $return->system->signatures = $system->getSignaturesData(); + } - if(! is_null($system)){ - // data for the current selected system - $userData->system = $system->getData(); - $userData->system->signatures = $system->getSignaturesData(); } - } + + $f3->set($cacheKey, $return, $responseTTL); } - $f3->set($cacheKey, $userData, $responseTTL); + $return = $f3->get($cacheKey); + }else{ + // user logged of + $return->error[] = $this->getUserLoggedOffError(); } - echo json_encode( $f3->get($cacheKey) ); + + echo json_encode( $return ); } } diff --git a/app/main/controller/api/Routes.php b/app/main/controller/api/Routes.php new file mode 100644 index 00000000..de2aff60 --- /dev/null +++ b/app/main/controller/api/Routes.php @@ -0,0 +1,338 @@ + systemId matching + * @var array + */ + private $idArray = []; + + + function __construct() { + parent::__construct(); + + // set cache time for static jump data + $this->jumpDataCacheTime = 60 * 60 * 24; + } + + /** + * set static system jump data for this instance + * the data is fixed and should not change + */ + private function setSystemJumpData(){ + $query = "SELECT * FROM system_neighbour"; + + $rows = $this->f3->get('DB')->exec($query, null, $this->jumpDataCacheTime); + + + foreach($rows as $row){ + $regionId = trim($row['regionId']); + $constId = trim($row['constellationId']); + $systemName = strtoupper(trim($row['systemName'])); + $systemId = trim($row['systemId']); + $secStatus = trim($row['trueSec']); + + $this->nameArray[$systemId][0] = $systemName; + $this->nameArray[$systemId][1] = $regionId; + $this->nameArray[$systemId][2] = $constId; + $this->nameArray[$systemId][3] = $secStatus; + + $this->idArray[strtoupper($systemName)] = $systemId; + + $this->jumpArray[$systemName]= explode(":", strtoupper($row['jumpNodes'])); + array_push($this->jumpArray[$systemName],$systemId); + } + } + + /** + * get system data by systemId and dataName + * @param $systemId + * @param $option + * @return null + */ + private function getSystemInfoBySystemId($systemId, $option){ + + $info = null; + + switch($option){ + case 'systemName': + $info = $this->nameArray[ $systemId ][0]; + break; + case 'regionId': + $info = $this->nameArray[ $systemId ][1]; + break; + case 'constellationId': + $info = $this->nameArray[ $systemId ][2]; + break; + case 'trueSec': + $info = $this->nameArray[ $systemId ][3]; + break; + } + + return $info; + } + + /** + * find a route between two systems + * @param $f3 + * @return array + */ + public function findRoute($f3){ + $parameter = $f3->get('GET'); + + $routeData = [ + 'routePossible' => false, + 'routeJumps' => 0, + 'route' => [] + ]; + + if( + array_key_exists('from', $parameter) && + array_key_exists('to', $parameter) + ){ + $from = strtoupper( $parameter['from'] ); + $to = strtoupper( $parameter['to'] ); + + // set static system jump data + $this->setSystemJumpData(); + + // jump counter + $jumpNum = 0; + + // check if the system we are looking for is a direct neighbour + foreach( $this->jumpArray[$from] as $n ) { + + if ($n == $to) { + $jumpNum = 2; + + $jumpNode = [ + 'system' => $n, + 'security' => $this->getSystemInfoBySystemId($this->idArray[$n], 'trueSec') + ]; + + $routeData['route'][] = $jumpNode; + break; + } + } + + // system is not a direct neighbour -> search recursive its neighbours + if ($jumpNum == 0) { + foreach( $this->graph_find_path( $this->jumpArray, $from, $to ) as $n ) { + if ($jumpNum > 0) { + + $jumpNode = [ + 'system' => $n, + 'security' => $this->getSystemInfoBySystemId($this->idArray[$n], 'trueSec') + ]; + + $routeData['route'][] = $jumpNode; + } + $jumpNum++; + } + } + + if ($jumpNum > 0) { + // route found + $routeData['routePossible'] = true; + + $jumpNode = [ + 'system' => $from, + 'security' => $this->getSystemInfoBySystemId($this->idArray[$from], 'trueSec') + ]; + + // insert "from" system on top + array_unshift($routeData['route'], $jumpNode); + } else { + // route not found + $routeData['routePossible'] = true; + } + + // route jumps + $routeData['routeJumps'] = $jumpNum - 1; + } + + return $routeData; + } + + /** + * recursive search function within a undirected graph + * @param $G + * @param $A + * @param $B + * @param int $M + * @return array + */ + private function graph_find_path(&$G, $A, $B, $M = 50000){ + // $P will hold the result path at the end. + // Remains empty if no path was found. + $P = array(); + + // For each Node ID create a "visit information", + // initially set as 0 (meaning not yet visited) + // as soon as we visit a node we will tag it with the "source" + // so we can track the path when we reach the search target + + $V = array(); + + // We are going to keep a list of nodes that are "within reach", + // initially this list will only contain the start node, + // then gradually expand (almost like a flood fill) + $R = array(trim($A)); + + $A = trim($A); + $B = trim($B); + + while(count($R) > 0 && $M > 0){ + $M--; + + $X = trim(array_shift($R)); + + if( array_key_exists($X, $G) ){ + foreach($G[$X] as $Y){ + $Y = trim($Y); + // See if we got a solution + if($Y == $B){ + // We did? Construct a result path then + array_push($P, $B); + array_push($P, $X); + while($V[$X] != $A){ + array_push($P, trim($V[$X])); + $X = $V[$X]; + } + array_push($P, $A); + return array_reverse($P); + } + // First time we visit this node? + if(!array_key_exists($Y, $V)){ + // Store the path so we can track it back, + $V[$Y] = $X; + // and add it to the "within reach" list + array_push($R, $Y); + } + } + } + } + + return $P; + } + + /** + * this funcion is just for setting up the cache table 'system_neighbour' which is used + * for system jump calculation. Call this function manually if CCP adds Systems/Stargates + */ + private function setupSystemJumpTable(){ + + // switch DB + $this->setDB('CCP'); + + $query = "SELECT + map_sys.solarSystemID system_id, + map_sys.regionID region_id, + map_sys.constellationID constellation_id, + map_sys.solarSystemName system_name, + ROUND( map_sys.security, 2) system_security, + ( + SELECT + GROUP_CONCAT( NULLIF(map_sys_inner.solarSystemName, NULL) SEPARATOR ':') + FROM + mapsolarsystemjumps map_jump INNER JOIN + mapsolarsystems map_sys_inner ON + map_sys_inner.solarSystemID = map_jump.toSolarSystemID + WHERE + map_jump.fromSolarSystemID = map_sys.solarSystemID + ) system_neighbours + FROM + mapsolarsystems map_sys + HAVING + -- skip systems without neighbors (e.g. WHs) + system_neighbours IS NOT NULL + "; + + $rows = $this->f3->get('DB')->exec($query); + + if(count($rows) > 0){ + // switch DB back to pathfinder DB + $this->setDB(); + + // clear cache table + $query = "TRUNCATE system_neighbour"; + $this->f3->get('DB')->exec($query); + + foreach($rows as $row){ + $this->f3->get('DB')->exec(" + INSERT INTO + system_neighbour( + regionId, + constellationId, + systemName, + systemId, + jumpNodes, + trueSec + ) + VALUES( + :regionId, + :constellationId, + :systemName, + :systemId, + :jumpNodes, + :trueSec + )", + [ + ':regionId' => $row['region_id'], + ':constellationId' => $row['constellation_id'], + ':systemName' => $row['system_name'], + ':systemId' => $row['system_id'], + ':jumpNodes' => $row['system_neighbours'], + ':trueSec' => $row['system_security'] + ]); + } + } + } + +} + + + + + + + + + + + + + diff --git a/app/main/controller/api/System.php b/app/main/controller/api/System.php index 81514b25..77b50ce9 100644 --- a/app/main/controller/api/System.php +++ b/app/main/controller/api/System.php @@ -135,14 +135,12 @@ class System extends \Controller\AccessController { $rows = $this->f3->get('DB')->exec($query, null, 60 * 60 * 24); - // format result $mapper = new Mapper\CcpSystemsMapper($rows); return $mapper->getData(); } - /** * search systems by name * @param $f3 @@ -244,14 +242,17 @@ class System extends \Controller\AccessController { $systemIds = $f3->get('POST.systemIds'); $user = $this->_getUser(); - $system = Model\BasicModel::getNew('SystemModel'); - foreach($systemIds as $systemId){ + if($user){ + $system = Model\BasicModel::getNew('SystemModel'); - $system->getById($systemId); - $system->delete($user); + foreach((array)$systemIds as $systemId){ - $system->reset(); + $system->getById($systemId); + $system->delete($user); + + $system->reset(); + } } echo json_encode([]); diff --git a/app/main/model/BasicModel.php b/app/main/model/BasicModel.php index ec54a6a0..be2f119c 100644 --- a/app/main/model/BasicModel.php +++ b/app/main/model/BasicModel.php @@ -119,6 +119,22 @@ class BasicModel extends \DB\Cortex{ } + /** + * set "updated" field to current timestamp + * this is useful to mark a row as "changed" + */ + protected function setUpdated(){ + if($this->_id > 0){ + $f3 = \Base::instance(); + $f3->get('DB')->exec( + ["UPDATE " . $this->table . " SET updated=NOW() WHERE id=:id"], + [ + [':id' => $this->_id] + ] + ); + } + } + /** * get single dataSet by id * @param $id diff --git a/app/main/model/MapModel.php b/app/main/model/MapModel.php index 3a937af7..c133ede7 100644 --- a/app/main/model/MapModel.php +++ b/app/main/model/MapModel.php @@ -9,7 +9,7 @@ namespace Model; -class MapModel extends BasicModel{ +class MapModel extends BasicModel { protected $table = 'map'; protected $ttl = 5; @@ -31,8 +31,8 @@ class MapModel extends BasicModel{ 'mapCorporations' => array( 'has-many' => array('Model\CorporationMapModel', 'mapId') ), - 'mapAlliances' => array( - 'has-many' => array('Model\AllianceMapModel', 'mapId') + 'mapAlliances' => array('has-many' => array( + 'Model\AllianceMapModel', 'mapId') ) ); @@ -52,7 +52,7 @@ class MapModel extends BasicModel{ ], 'typeId' => [ 'regex' => '/^[1-9]+$/' - ], + ] ]; /** @@ -67,11 +67,11 @@ class MapModel extends BasicModel{ if($this->exists($key)){ $this->$key = $value; } - }else{ + } else{ // special array data if($key == 'scope'){ $this->scopeId = $value['id']; - }elseif($key == 'type'){ + } elseif($key == 'type'){ $this->typeId = $value['id']; } } @@ -85,24 +85,34 @@ class MapModel extends BasicModel{ */ public function getData(){ - $mapData = [ - 'id' => $this->id, - 'name' => $this->name, - 'scope' => [ - 'id' => $this->scopeId->id, - 'name' => $this->scopeId->name, - 'label' => $this->scopeId->label - ], - 'type' => [ - 'id' => $this->typeId->id, - 'name' => $this->typeId->name, - 'classTab' => $this->typeId->classTab - ], - 'icon' => $this->icon, - 'updated' => strtotime($this->updated) + $mapData = ['id' => $this->id, 'name' => $this->name, 'scope' => ['id' => $this->scopeId->id, 'name' => $this->scopeId->name, 'label' => $this->scopeId->label], 'type' => ['id' => $this->typeId->id, 'name' => $this->typeId->name, 'classTab' => $this->typeId->classTab], 'icon' => $this->icon, 'updated' => strtotime($this->updated), 'access' => ['user' => [], 'corporation' => [], 'alliance' => []]]; + // get access object data ------------- + if($this->isPrivate()){ + $users = $this->getUsers(); + $userData = []; - ]; + foreach($users as $user){ + $userData[] = $user->getSimpleData(); + } + $mapData['access']['user'] = $userData; + } elseif($this->isCorporation()){ + $corporations = $this->getCorporations(); + $corporationData = []; + + foreach($corporations as $corporation){ + $corporationData[] = $corporation->getData(); + } + $mapData['access']['corporation'] = $corporationData; + } elseif($this->isAlliance()){ + $alliances = $this->getAlliances(); + $allianceData = []; + + foreach($alliances as $alliance){ + $allianceData[] = $alliance->getData(); + } + $mapData['access']['alliance'] = $allianceData; + } return $mapData; } @@ -127,7 +137,7 @@ class MapModel extends BasicModel{ * @param $systemId * @return null */ - public function getSystem( $systemId ){ + public function getSystem($systemId){ $systems = $this->getSystems(); $searchSystem = null; foreach($systems as $system){ @@ -180,48 +190,68 @@ class MapModel extends BasicModel{ */ public function setAccess($obj){ + $newAccessGranted = false; + if($obj instanceof UserModel){ // private map - // get all userModels who have map access - $userMaps = $this->getRelatedModels('UserMapModel', 'mapId'); - $userFound = false; - if($userMaps){ - foreach($userMaps as $userMap){ - if($userMap->userId->id !== $obj->id){ - // remove map access - $userMap->erase(); - }else{ - $userFound = true; - } - } - } + // check whether the user already has map access + $this->has('mapUsers', array('active = 1 AND userId = :userId', ':userId' => $obj->id)); + $result = $this->findone(array('id = :id', ':id' => $this->id)); - if(!$userFound){ - // set user who has access to this map + if($result === false){ + // grant access for the user $userMap = self::getNew('UserMapModel'); $userMap->userId = $obj; $userMap->mapId = $this; $userMap->save(); + + $newAccessGranted = true; + } + } elseif($obj instanceof CorporationModel){ + + // check whether the corporation already has map access + $this->has('mapCorporations', array('active = 1 AND corporationId = :corporationId', ':corporationId' => $obj->id)); + $result = $this->findone(array('id = :id', ':id' => $this->id)); + + if($result === false){ + // grant access for this corporation + $corporationMap = self::getNew('CorporationMapModel'); + $corporationMap->corporationId = $obj; + $corporationMap->mapId = $this; + $corporationMap->save(); + + $newAccessGranted = true; + } + } elseif($obj instanceof AllianceModel){ + + // check whether the corporation already has map access + $this->has('mapAlliances', array('active = 1 AND allianceId = :allianceId', ':allianceId' => $obj->id)); + $result = $this->findone(array('id = :id', ':id' => $this->id)); + + if($result === false){ + $allianceMap = self::getNew('AllianceMapModel'); + $allianceMap->allianceId = $obj; + $allianceMap->mapId = $this; + $allianceMap->save(); + + $newAccessGranted = true; } - }elseif($obj instanceof CorporationModel){ - $corporationMap = self::getNew('CorporationMapModel'); - $corporationMap->corporationId = $obj; - $corporationMap->mapId = $this; - $corporationMap->save(); - }elseif($obj instanceof AllianceModel){ - $allianceMap = self::getNew('AllianceMapModel'); - $allianceMap->allianceId = $obj; - $allianceMap->mapId = $this; - $allianceMap->save(); } + + + if($newAccessGranted){ + // mark this map as updated + $this->setUpdated(); + } + } /** * clear access for a given type of objects * @param $clearKeys */ - public function clearAccess($clearKeys){ + public function clearAccess($clearKeys = ['user', 'corporation', 'alliance']){ foreach($clearKeys as $key){ switch($key){ @@ -245,22 +275,23 @@ class MapModel extends BasicModel{ } /** - * checks weather an object (user or alliance) has - * @param $accessObject + * checks weather a user has access to this map or not + * @param $user * @return bool */ - public function hasAccess($accessObject){ + public function hasAccess($user){ $hasAccess = false; - if($accessObject instanceof UserModel){ - // get all userModels who have map access - $userMaps = $this->getRelatedModels('UserMapModel', 'mapId'); - if($userMaps){ - foreach($userMaps as $userMap){ - if($userMap->userId->id === $accessObject->id){ - $hasAccess = true; - break; - } + if($user instanceof UserModel){ + + // get all maps the user has access to + // this includes corporation and alliance maps + $maps = $user->getMaps(); + + foreach($maps as $map){ + if($map->id === $this->id){ + $hasAccess = true; + break; } } } @@ -269,22 +300,66 @@ class MapModel extends BasicModel{ } /** - * get all user models with access to this map model + * get all user models that have access to this map * @return array */ public function getUsers(){ - $this->filter('mapUsers', array('active = ?', 1)); - $users = []; - if($this->mapUsers){ - foreach($this->mapUsers as $mapUser){ - $users[] = $mapUser->userId; + + if($this->isPrivate()){ + $this->filter('mapUsers', array('active = ?', 1)); + + if($this->mapUsers){ + foreach($this->mapUsers as $mapUser){ + $users[] = $mapUser->userId; + } } } return $users; } + /** + * get all corporations that have access to this map + * @return array + */ + public function getCorporations(){ + $corporations = []; + + if($this->isCorporation()){ + $this->filter('mapCorporations', array('active = ?', 1)); + + if($this->mapCorporations){ + foreach($this->mapCorporations as $mapCorporation){ + $corporations[] = $mapCorporation->corporationId; + } + } + } + + return $corporations; + } + + /** + * get all alliances that have access to this map + * @return array + */ + public function getAlliances(){ + $alliances = []; + + if($this->isAlliance()){ + $this->filter('mapAlliances', array('active = ?', 1)); + + if($this->mapAlliances){ + foreach($this->mapAlliances as $mapAlliance){ + $alliances[] = $mapAlliance->allianceId; + } + } + } + + return $alliances; + } + + /** * delete this map and all dependencies */ @@ -387,13 +462,13 @@ class MapModel extends BasicModel{ } } - $mapUserData = (object) []; - $mapUserData->config = (object) []; + $mapUserData = (object)[]; + $mapUserData->config = (object)[]; $mapUserData->config->id = $this->id; - $mapUserData->data = (object) []; + $mapUserData->data = (object)[]; $mapUserData->data->systems = []; foreach($systems as $system){ - $systemUserData = (object) []; + $systemUserData = (object)[]; $systemUserData->id = $system->id; $systemUserData->user = []; @@ -417,6 +492,26 @@ class MapModel extends BasicModel{ return $mapUserData; } + /** + * save a map + * @return mixed + */ + public function save(){ + $mapModel = parent::save(); -} \ No newline at end of file + // check if map type has changed and clear access objects + if( !$mapModel->dry() ){ + if( $mapModel->isPrivate() ){ + $mapModel->clearAccess(['corporation', 'alliance']); + }elseif( $mapModel->isCorporation() ){ + $mapModel->clearAccess(['user', 'alliance']); + }elseif( $mapModel->isAlliance() ){ + $mapModel->clearAccess(['user', 'corporation']); + } + } + + return $mapModel; + } + +} diff --git a/app/main/model/SystemModel.php b/app/main/model/SystemModel.php index 73cc01a3..0eccd5f3 100644 --- a/app/main/model/SystemModel.php +++ b/app/main/model/SystemModel.php @@ -132,7 +132,7 @@ class SystemModel extends BasicModel { /** * delete a system from a map - * hint: signatures will be deleted on cascade + * hint: signatures and connections will be deleted on cascade * @param $accessObject */ public function delete($accessObject){ @@ -140,46 +140,11 @@ class SystemModel extends BasicModel { if(! $this->dry()){ // check if user has access if($this->hasAccess($accessObject)){ - // delete all system connections - $connections = $this->getConnections(); - - if(is_object($connections)){ - foreach($connections as $connection){ - $connection->erase(); - } - } $this->erase(); } } } - /** - * get all connections for this system - * @return array - */ - public function getConnections(){ - $connections = false; - - // connections where system is source - $sourceConnections = $this->getRelatedModels('ConnectionModel', 'source'); - $targetConnections = $this->getRelatedModels('ConnectionModel', 'target'); - - if(is_object($sourceConnections)){ - $connections = $sourceConnections; - } - - if(is_object($targetConnections)){ - if(is_object($connections)){ - $connections->append($targetConnections); - }else{ - $connections = $targetConnections; - - } - } - - return $connections; - } - /** * get all signatures of this system * @return array diff --git a/app/main/model/SystemSignatureModel.php b/app/main/model/SystemSignatureModel.php index c1e5ffcb..1bca7efb 100644 --- a/app/main/model/SystemSignatureModel.php +++ b/app/main/model/SystemSignatureModel.php @@ -62,6 +62,7 @@ class SystemSignatureModel extends BasicModel { 'groupId' => $this->groupId, 'typeId' => $this->typeId, 'name' => $this->name, + 'description' => $this->description, 'created' => [ 'character' => $this->createdCharacterId->getData(), 'created' => strtotime($this->created) diff --git a/app/main/model/UserModel.php b/app/main/model/UserModel.php index 9032ea76..50674a7f 100644 --- a/app/main/model/UserModel.php +++ b/app/main/model/UserModel.php @@ -45,13 +45,16 @@ class UserModel extends BasicModel { /** * get all data for this user + * ! caution ! this function returns sensitive data! + * -> user getSimpleData() for faster performance and public user data * @return object */ public function getData(){ - $userData = (object) []; - $userData->id = $this->id; - $userData->name = $this->name; + // get public user data for this user + $userData = $this->getSimpleData(); + + // add sensitive user data $userData->email = $this->email; // api data @@ -76,6 +79,19 @@ class UserModel extends BasicModel { return $userData; } + /** + * get public user data + * - check out getData() for all user data + * @return object + */ + public function getSimpleData(){ + $userData = (object) []; + $userData->id = $this->id; + $userData->name = $this->name; + + return $userData; + } + /** * validate and set a email address for this user * @param $email diff --git a/app/routes.cfg b/app/routes.cfg index 60e60d03..ca771123 100644 --- a/app/routes.cfg +++ b/app/routes.cfg @@ -7,6 +7,7 @@ GET|POST @map: /map= Controller\MapController->showMap, 86400 ; APIs GET|POST /api/@controller/@action = Controller\Api\@controller->@action, 0, 512 GET|POST /api/@controller/@action/@arg1 = Controller\Api\@controller->@action, 0, 512 +GET|POST /api/@controller/@action/@arg1/@arg2 = Controller\Api\@controller->@action, 0, 512 ; cronjob APIs GET /cron/@controller/@action = Controller\Cron\@controller->@action diff --git a/js/app.js b/js/app.js index 3890a40a..b15b4884 100644 --- a/js/app.js +++ b/js/app.js @@ -13,11 +13,13 @@ requirejs.config({ velocityUI: 'lib/velocity.ui.min', // v5.0.3 plugin for velocity - http://julian.com/research/velocity/#uiPack templates: '../public/templates', // template dir slidebars: 'lib/slidebars', // v0.10 Slidebars - side menu plugin http://plugins.adchsm.me/slidebars/ - jsPlumb: 'lib/dom.jsPlumb-1.7.2-min', // v1.7.2 jsPlumb (Vanilla)- main map draw plugin http://www.jsplumb.org/ + jsPlumb: 'lib/dom.jsPlumb-1.7.5-min', // v1.7.5 jsPlumb (Vanilla)- main map draw plugin http://www.jsplumb.org/ customScrollbar: 'lib/jquery.mCustomScrollbar.concat.min', // v3.1.11 Custom scroll bars - http://manos.malihu.gr/ datatables: 'lib/datatables/jquery.dataTables.min', // v1.10.7 DataTables - https://datatables.net/ datatablesBootstrap: 'lib/datatables/dataTables.bootstrap', // DataTables - not used (bootstrap style) - datatablesTableTools: 'lib/datatables/extensions/TableTools/js/dataTables.tableTools', // v2.2.3 TableTools (PlugIn) - https://datatables.net/extensions/tabletools/ + datatablesResponsive: 'lib/datatables/extensions/responsive/dataTables.responsive', // v1.0.6 TableTools (PlugIn) - https://datatables.net/extensions/responsive/ + + datatablesTableTools: 'lib/datatables/extensions/tabletools/js/dataTables.tableTools', // v2.2.3 TableTools (PlugIn) - https://datatables.net/extensions/tabletools/ xEditable: 'lib/bootstrap-editable.min', // v1.5.1 X-editable - in placed editing morris: 'lib/morris.min', // v0.5.1 Morris.js - graphs and charts raphael: 'lib/raphael-min', // v2.1.2 Raphaƫl - required for morris (dependency) @@ -68,10 +70,13 @@ requirejs.config({ datatables: { deps: ['jquery'] }, - datatablesTableTools: { + datatablesBootstrap: { deps: ['datatables'] }, - datatablesBootstrap: { + datatablesResponsive: { + deps: ['datatables'] + }, + datatablesTableTools: { deps: ['datatables'] }, xEditable: { diff --git a/js/app/init.js b/js/app/init.js index 44ff82db..8c9f6f28 100644 --- a/js/app/init.js +++ b/js/app/init.js @@ -8,6 +8,7 @@ define(['jquery'], function($) { var Config = { timer: { + dblClickTimer: 250, // ms: double click timer programStatusVisible: 5000, // ms: timer for status change visibility in head mapUpdate: { delay: 3000, // ms: delay between ping calls @@ -23,11 +24,14 @@ define(['jquery'], function($) { }, path: { img: 'public/img/', // path for images - // map init and trigger + // user API getCaptcha: 'api/user/getCaptcha', // ajax URL - get captcha image logIn: 'api/user/logIn', // ajax URL - login logOut: 'api/user/logOut', // ajax URL - logout saveUserConfig: 'api/user/saveConfig', // ajax URL - saves custom configuration + // access API + searchAccess: 'api/access/search', // ajax URL - search corporation by name + // main config/map ping API initMap: 'api/map/init', // ajax URL - get static data updateMapData: 'api/map/updateData', // ajax URL - main map update trigger updateUserData: 'api/map/updateUserData', // ajax URL - main map user data trigger diff --git a/js/app/landingpage.js b/js/app/landingpage.js index a77db398..5f777a6e 100644 --- a/js/app/landingpage.js +++ b/js/app/landingpage.js @@ -34,7 +34,10 @@ define([ // gallery galleryId: 'pf-gallery', // id for gallery container - galleryThumbContainerId: 'pf-landing-gallery-thumb-container' // id for gallery thumb images + galleryThumbContainerId: 'pf-landing-gallery-thumb-container', // id for gallery thumb images + + // animation + animateElementClass: 'pf-animate-on-visible' // class for elements that will be animated to show }; @@ -164,7 +167,7 @@ define([ var showGallery = function(){ // thumb links var thumbLinks = $('#' + config.galleryThumbContainerId + ' a'); - +/* // show thumbs thumbLinks.velocity('transition.slideRightBigIn', { duration: 1200, @@ -173,6 +176,7 @@ define([ visibility: 'visible' }); + */ }; /** @@ -180,12 +184,36 @@ define([ */ var initScrollspy = function(){ // init scrollspy - $('body').scrollspy({ target: '#' + config.navigationElementId }); - $('#' + config.navigationElementId).on('activate.bs.scrollspy', function (e) { + $( window ).scroll(function() { - var ancorTag = $(e.target).find('a').attr('href'); - console.log(ancorTag); + // find all elements that should be animated + var visibleElements = $('.' + config.animateElementClass).isInViewport(); + + $(visibleElements).removeClass( config.animateElementClass ); + + $(visibleElements).velocity('transition.flipXIn', { + duration: 600, + stagger: 60, + delay: 500, + complete: function(element){ + $(element).find('.fade').addClass('in'); + }, + visibility: 'visible' + }); + + }); + + // event listener for navigation links + $('.page-scroll').on('click', function(){ + // get element to scroll + var anchorTag = $(this).attr('data-anchor'); + + // scroll to container + $(anchorTag).velocity('scroll', { + duration: 300, + easing: 'swing' + }); }); }; diff --git a/js/app/main.js b/js/app/main.js index 361aeef2..4e93fc00 100644 --- a/js/app/main.js +++ b/js/app/main.js @@ -16,11 +16,6 @@ define([ 'use strict'; - var config = { - mapModuleId: 'pf-map-module' - }; - - $(function(){ // load page $('body').loadPageStructure(); @@ -29,7 +24,7 @@ define([ Logging.init(); // page initialized event ============================================================== - $('#' + config.mapModuleId).on('pf:initModule', function(){ + $('#' + Util.config.mapModuleId).on('pf:initModule', function(){ if(! CCP.isTrusted()){ // show trust message @@ -50,6 +45,7 @@ define([ Init.systemStatus = initData.systemStatus; Init.systemType = initData.systemType; Init.characterStatus = initData.characterStatus; + Init.maxSharedCount = initData.maxSharedCount; // init map module mapModule.initMapModule(); @@ -110,25 +106,35 @@ define([ dataType: 'json' }).done(function(mapData){ - $(document).setProgramStatus('online'); - - if(mapData.length === 0){ - // no map data available -> show "new map" dialog - $(document).trigger('pf:menuShowMapSettings'); + if( + mapData.error && + mapData.error.length > 0 + ){ + // anny error in the main trigger functions result in a user log-off + $(document).trigger('pf:menuLogout'); }else{ - // map data found - // load map module - mapModule.updateMapModule(mapData); + $(document).setProgramStatus('online'); - // log execution time - var duration = Util.timeStop(mapUpdateKey); - Util.log(mapUpdateKey, {duration: duration, description: 'updateMapModule'}); + if(mapData.length === 0){ + // no map data available -> show "new map" dialog + $(document).trigger('pf:menuShowMapSettings', {tab: 'new'}); + }else{ + // map data found + + // load map module + mapModule.updateMapModule(mapData); + + // log execution time + var duration = Util.timeStop(mapUpdateKey); + Util.log(mapUpdateKey, {duration: duration, description: 'updateMapModule'}); + } + // init new trigger + setTimeout(function(){ + triggerMapUpdatePing(); + }, mapUpdateDelay); } - // init new trigger - setTimeout(function(){ - triggerMapUpdatePing(); - }, mapUpdateDelay); + }).fail(function( jqXHR, status, error) { var reason = status + ' ' + jqXHR.status + ': ' + error; @@ -155,31 +161,36 @@ define([ dataType: 'json' }).done(function(data){ - $(document).setProgramStatus('online'); + if(data.error.length > 0){ + // anny error in the main trigger functions result in a user log-off + $(document).trigger('pf:menuLogout'); + }else{ - if(data.userData !== undefined){ - // store current user data global (cache) - var userData = Util.setCurrentUserData(data.userData); + $(document).setProgramStatus('online'); - if(userData.character === undefined){ - // no active character found -> show settings dialog - $(document).triggerMenuEvent('ShowSettingsDialog'); - }else{ - // active character data found + if(data.userData !== undefined){ + // store current user data global (cache) + var userData = Util.setCurrentUserData(data.userData); - mapModule.updateMapModuleData(data); - var duration = Util.timeStop(mapUserUpdateKey); + if(userData.character === undefined){ + // no active character found -> show settings dialog + $(document).triggerMenuEvent('ShowSettingsDialog'); + }else{ + // active character data found - // log execution time - Util.log(mapUserUpdateKey, {duration: duration, description:'updateMapModuleData'}); + mapModule.updateMapModuleData(data); + var duration = Util.timeStop(mapUserUpdateKey); + + // log execution time + Util.log(mapUserUpdateKey, {duration: duration, description:'updateMapModuleData'}); - // init new trigger - setTimeout(function(){ - triggerUserUpdatePing(); - }, mapUserUpdateDelay); + // init new trigger + setTimeout(function(){ + triggerUserUpdatePing(); + }, mapUserUpdateDelay); + } } - } }).fail(function( jqXHR, status, error) { diff --git a/js/app/map/map.js b/js/app/map/map.js index 95c54532..ee61d57d 100644 --- a/js/app/map/map.js +++ b/js/app/map/map.js @@ -619,166 +619,170 @@ define([ var mapContainer = mapConfig.map.getContainer(); - // prevent jsPlumb from re-painting during main-map update -> performance boost :) - mapConfig.map.doWhileSuspended(function() { - if(mapContainer === undefined){ - // add new map + if(mapContainer === undefined){ + // add new map - // create map wrapper - var mapWrapper = $('
', { - class: config.mapWrapperClass - }); + // create map wrapper + var mapWrapper = $('
', { + class: config.mapWrapperClass + }); - // create new map container - mapContainer = $('
', { - id: config.mapIdPrefix + mapConfig.config.id, - class: [config.mapClass].join(' ') - }); + // create new map container + mapContainer = $('
', { + id: config.mapIdPrefix + mapConfig.config.id, + class: [config.mapClass].join(' ') + }); - // add additional information - mapContainer.data('id', mapConfig.config.id); + // add additional information + mapContainer.data('id', mapConfig.config.id); - mapWrapper.append(mapContainer); + mapWrapper.append(mapContainer); - // append mapWrapper to parent element (at the top) - $(parentElement).prepend(mapWrapper); + // append mapWrapper to parent element (at the top) + $(parentElement).prepend(mapWrapper); - // set main Container for current map -> the container exists now in DOM !! very important - mapConfig.map.setContainer($('#' + config.mapIdPrefix + mapConfig.config.id)); + // set main Container for current map -> the container exists now in DOM !! very important + mapConfig.map.setContainer($('#' + config.mapIdPrefix + mapConfig.config.id)); - // set map observer - setMapObserver(mapConfig.map); + // set map observer + setMapObserver(mapConfig.map); + } + + mapContainer = $(mapContainer); + + // add additional information for this map + if(mapContainer.data('updated') !== mapConfig.config.updated){ + mapContainer.data('name', mapConfig.config.name); + mapContainer.data('scopeId', mapConfig.config.scope.id); + mapContainer.data('typeId', mapConfig.config.type.id); + mapContainer.data('icon', mapConfig.config.icon); + mapContainer.data('updated', mapConfig.config.updated); + } + + + // get map data + var mapData = mapContainer.getMapDataFromClient(); + + if(mapData !== false){ + // map data available -> map not locked by update counter :) + var currentSystemData = mapData.data.systems; + var currentConnectionData = mapData.data.connections; + + // update systems =========================================================== + + for(var i = 0; i < mapConfig.data.systems.length; i++){ + var systemData = mapConfig.data.systems[i]; + + // add system + var addNewSystem = true; + + for(var k = 0; k < currentSystemData.length; k++){ + if(currentSystemData[k].id === systemData.id){ + if( currentSystemData[k].updated.updated < systemData.updated.updated ){ + // system changed -> update + mapContainer.getSystem(mapConfig.map, systemData); + } + + addNewSystem = false; + break; + } + } + + if( addNewSystem === true){ + drawSystem(mapConfig.map, systemData); + } } - mapContainer = $(mapContainer); + // check for systems that are gone -> delete system + for(var a = 0; a < currentSystemData.length; a++){ - // add additional information for this map - if(mapContainer.data('updated') !== mapConfig.config.updated){ - mapContainer.data('name', mapConfig.config.name); - mapContainer.data('scopeId', mapConfig.config.scope.id); - mapContainer.data('typeId', mapConfig.config.type.id); - mapContainer.data('icon', mapConfig.config.icon); - mapContainer.data('updated', mapConfig.config.updated); + var deleteThisSystem = true; + + for(var b = 0; b < mapConfig.data.systems.length; b++){ + var deleteSystemData = mapConfig.data.systems[b]; + + if(deleteSystemData.id === currentSystemData[a].id){ + deleteThisSystem = false; + break; + } + } + + if(deleteThisSystem === true){ + var deleteSystem = $('#' + config.systemIdPrefix + mapContainer.data('id') + '-' + currentSystemData[a].id); + + // system not found -> delete system + removeSystem(mapConfig.map, deleteSystem); + } } + // update connections ========================================================= - // get map data - var mapData = mapContainer.getMapData(); + // set up default connections + for(var j = 0; j < mapConfig.data.connections.length; j++){ + var connectionData = mapConfig.data.connections[j]; - if(mapData !== false){ - // map data available -> map not locked by update counter :) - var currentSystemData = mapData.data.systems; - var currentConnectionData = mapData.data.connections; - - // update systems =========================================================== - - for(var i = 0; i < mapConfig.data.systems.length; i++){ - var systemData = mapConfig.data.systems[i]; - - // add system - var addNewSystem = true; - - for(var k = 0; k < currentSystemData.length; k++){ - if(currentSystemData[k].id === systemData.id){ - if( currentSystemData[k].updated.updated < systemData.updated.updated ){ - // system changed -> update - mapContainer.getSystem(mapConfig.map, systemData); - } - - addNewSystem = false; - break; - } - } - - if( addNewSystem === true){ - drawSystem(mapConfig.map, systemData); - } - } - - // check for systems that are gone -> delete system - for(var a = 0; a < currentSystemData.length; a++){ - - var deleteThisSystem = true; - - for(var b = 0; b < mapConfig.data.systems.length; b++){ - var deleteSystemData = mapConfig.data.systems[b]; - - if(deleteSystemData.id === currentSystemData[a].id){ - deleteThisSystem = false; - break; - } - } - - if(deleteThisSystem === true){ - var deleteSystem = $('#' + config.systemIdPrefix + mapContainer.data('id') + '-' + currentSystemData[a].id); - - // system not found -> delete system - removeSystem(mapConfig.map, deleteSystem); - } - } - - // update connections ========================================================= - - // set up default connections - for(var j = 0; j < mapConfig.data.connections.length; j++){ - var connectionData = mapConfig.data.connections[j]; - - // add connection - var addNewConnection= true; - - for(var c = 0; c < currentConnectionData.length; c++){ - if( - currentConnectionData[c].id === connectionData.id - ){ - // connection already exists -> check for updates - if( - currentConnectionData[c].updated < connectionData.updated - ){ - // connection changed -> update - var tempConnection = ativeConnections[mapData.config.id][connectionData.id]; - updateConnection(tempConnection, currentConnectionData[c], connectionData); - } - - addNewConnection = false; - break; - } - } - - if(addNewConnection === true){ - drawConnection(mapConfig.map, connectionData); - } - } - - // check for connections that are gone -> delete connection - for(var d = 0; d < currentConnectionData.length; d++){ - - var deleteThisConnection = true; - - for(var e = 0; e < mapConfig.data.connections.length;e++){ - var deleteConnectionData = mapConfig.data.connections[e]; - - if(deleteConnectionData.id === currentConnectionData[d].id){ - deleteThisConnection = false; - break; - } - } + // add connection + var addNewConnection= true; + for(var c = 0; c < currentConnectionData.length; c++){ if( - deleteThisConnection === true && - ativeConnections[mapData.config.id][currentConnectionData[d].id] !== undefined + currentConnectionData[c].id === connectionData.id + ){ + // connection already exists -> check for updates + if( + currentConnectionData[c].updated < connectionData.updated + ){ + // connection changed -> update + var tempConnection = ativeConnections[mapData.config.id][connectionData.id]; + updateConnection(tempConnection, currentConnectionData[c], connectionData); + } + + addNewConnection = false; + break; + } + } + + if(addNewConnection === true){ + drawConnection(mapConfig.map, connectionData); + } + } + + // check for connections that are gone -> delete connection + for(var d = 0; d < currentConnectionData.length; d++){ + + var deleteThisConnection = true; + + for(var e = 0; e < mapConfig.data.connections.length;e++){ + var deleteConnectionData = mapConfig.data.connections[e]; + + if(deleteConnectionData.id === currentConnectionData[d].id){ + deleteThisConnection = false; + break; + } + } + + if( + deleteThisConnection === true && + ativeConnections[mapData.config.id][currentConnectionData[d].id] !== undefined + ){ + // connection not found -> delete connection + var deleteConnection = ativeConnections[mapData.config.id][currentConnectionData[d].id]; + + // check if "source" and "target" still exist before remove + // this is NOT the case if the system was removed previous + if( + deleteConnection.source && + deleteConnection.target ){ - // connection not found -> delete connection - var deleteConnection = ativeConnections[mapData.config.id][currentConnectionData[d].id]; mapConfig.map.detach(deleteConnection, {fireEvent: false}); } - } - - // repaint all connections because of some strange visual bugs -_- - mapConfig.map.repaintEverything(); } - }); + + // repaint all connections because of some strange visual bugs -_- + mapConfig.map.repaintEverything(); + } return mapContainer; @@ -804,6 +808,7 @@ define([ // no visual effects in IGB (glitches) if( systemElements.length === 0 || + endpointElements.length === 0 || CCP.isInGameBrowser() === true ){ callback(); @@ -827,20 +832,17 @@ define([ // show connections endpointElements.velocity('transition.fadeIn', { stagger: 50, - drag: true, - duration: 50, + duration: 50 }); - connectorElements.velocity('transition.flipBounceXIn', { + connectorElements.velocity('transition.fadeIn', { stagger: 50, - drag: true, - duration: 1000, + duration: 180 }); overlayElements.delay(500).velocity('transition.fadeIn', { stagger: 50, - drag: true, - duration: 200, + duration: 180, display: 'auto', complete: function(){ callback(); @@ -854,15 +856,15 @@ define([ $('.mCSB_container').velocity('callout.shake', { stagger: 0, drag: false, - duration: 200, - display: 'auto', + duration: 180, + display: 'auto' }); overlayElements.velocity('transition.fadeOut', { stagger: 50, drag: true, - duration: 200, - display: 'auto', + duration: 180, + display: 'auto' }); endpointElements.velocity('transition.fadeOut', { @@ -876,13 +878,13 @@ define([ stagger: 0, drag: true, duration: 20, - display: 'block', + display: 'block' }); systemElements.delay(100).velocity('transition.slideUpOut', { stagger: 50, drag: true, - duration: 200, + duration: 180, display: 'block', complete: function(){ callback(); @@ -999,6 +1001,8 @@ define([ var mapContainer = $( map.getContainer() ); + mapContainer.getMapOverlay().startMapUpdateCounter(); + var systemIds = []; // systemIds for delete request for(var i = 0; i < systems.length; i++){ @@ -1024,7 +1028,7 @@ define([ for(var i = 0; i < systems.length; i++){ var system = $(systems[i]); triggerData.systemIds.push( system.data('id') ); - removeSystem(map, system); + removeSystem(map, system ); } // trigger "system deleted" on Tab Content Element @@ -1048,11 +1052,8 @@ define([ var removeSystem = function(map, system){ system = $(system); - // detach all connections - map.detachAllConnections(system); - - // delete all endpoints - map.removeAllEndpoints(system); + // remove endpoints and their connections + map.removeAllEndpoints (system); // hide tooltip system.toggleSystemTooltip('hide', {}); @@ -1061,7 +1062,7 @@ define([ system.velocity('transition.whirlOut', { duration: Init.animationSpeed.mapDeleteSystem, complete: function(){ - $(this).remove() ; + map.remove(this); } }); @@ -1069,26 +1070,26 @@ define([ /** - * make a system name editable by x-editable + * make a system name/alias editable by x-editable * @param system */ var makeEditable = function(system){ - + system = $(system); var headElement = $(system).find('.' + config.systemHeadNameClass); - $(headElement).editable({ + headElement.editable({ mode: 'popup', type: 'text', - title: 'system name', + title: 'System alias', placement: 'top', onblur: 'submit', - toggle: 'dblclick', + toggle: 'manual', // is triggered manually on dblclick showbuttons: false }); - // update z-index for system, editable field should be on top - $(headElement).on('shown', function(e, editable) { - updateZIndex(system); + headElement.on('save', function(e, params) { + // system alias changed -> mark system as updated + system.markAsChanged(); }); }; @@ -1161,29 +1162,29 @@ define([ var seconds = config.logTimerCount; var fadeEffectDuration = 200; - // get counter interval (in case there is an active one) - var interval = counterChart.data('interval'); - - if(interval){ - clearInterval(interval); - } - - mapOverlay.velocity('stop').velocity('transition.whirlIn', { duration: fadeEffectDuration }); - var counterChartLabel = counterChart.find('span'); var percentPerCount = 100 / seconds; + // update counter + var updateChart = function(tempSeconds){ + var pieChart = counterChart.data('easyPieChart'); + + if(pieChart !== undefined){ + counterChart.data('easyPieChart').update( percentPerCount * tempSeconds); + } + counterChartLabel.text(tempSeconds); + }; + + // main timer function is called on any counter update var timer = function(){ seconds--; - var pieChart = counterChart.data('easyPieChart'); - // check if chart is available - if(pieChart !== undefined){ - counterChart.data('easyPieChart').update( percentPerCount * seconds); - } - counterChartLabel.text(seconds); - if(seconds <= 0){ + if(seconds >= 0){ + // update counter + updateChart(seconds); + }else{ + // hide counter and reset clearInterval(mapUpdateCounter); mapOverlay.velocity('transition.whirlOut', { @@ -1195,17 +1196,26 @@ define([ } }; - // start timer - var mapUpdateCounter = setInterval(timer, 1000); - var pieChart = counterChart.data('easyPieChart'); - if(pieChart !== undefined){ - pieChart.update( percentPerCount * seconds); - } - counterChartLabel.text(seconds); + // get counter interval (in case there is an active one) --------------------------- + var interval = counterChart.data('interval'); - // store counter + if(interval){ + clearInterval(interval); + } + + // start timer --------------------------------------------------------------------- + var mapUpdateCounter = setInterval(timer, 1000); + updateChart(seconds); + + // store counter ------------------------------------------------------------------- counterChart.data('interval', mapUpdateCounter); + // show overlay ------------------------------------------------------------------- + if(mapOverlay.is(':hidden')){ + mapOverlay.velocity('stop').velocity('transition.whirlIn', { duration: fadeEffectDuration }); + + } + }; /** @@ -1321,6 +1331,10 @@ define([ */ var saveConnection = function(connection){ + var map = connection._jsPlumb.instance; + var mapContainer = $( map.getContainer() ); + mapContainer.getMapOverlay().startMapUpdateCounter(); + var connectionData = getDataByConnection(connection); var requestData = { @@ -1358,45 +1372,68 @@ define([ /** * Programmatically delete a connection and all related data * @param connections + * @param deleteOnServer */ - var deleteConnections = function(connections){ + var deleteConnections = function(connections, deleteOnServer){ - var connectionIds = []; - // systemIds for delete request - for(var i = 0; i < connections.length; i++){ - var connectionId = connections[i].getParameter('connectionId'); - // drag&drop a new connection does not have an id yet, if connection is not established correct - if(connectionId !== undefined){ - connectionIds[i] = connections[i].getParameter('connectionId'); + if(connections.length > 0){ + + // remove connections from map + var removeConnections = function(tempConnections){ + for(var i = 0; i < tempConnections.length; i++){ + // if a connection is manually (drag&drop) detached, the jsPlumb instance does not exist any more + // connection is already deleted! + if(tempConnections[i]._jsPlumb){ + tempConnections[i]._jsPlumb.instance.detach(tempConnections[i], {fireEvent: false}); + } + } + }; + + if(deleteOnServer === true){ + // prepare delete request + + var map = connections[0]._jsPlumb.instance; + var mapContainer = $( map.getContainer() ); + mapContainer.getMapOverlay().startMapUpdateCounter(); + + + var connectionIds = []; + // systemIds for delete request + for(var i = 0; i < connections.length; i++){ + var connectionId = connections[i].getParameter('connectionId'); + // drag&drop a new connection does not have an id yet, if connection is not established correct + if(connectionId !== undefined){ + connectionIds[i] = connections[i].getParameter('connectionId'); + } + } + + if(connectionIds.length > 0){ + var requestData = { + connectionIds: connectionIds + }; + + $.ajax({ + type: 'POST', + url: Init.path.deleteConnection, + data: requestData, + dataType: 'json' + }).done(function(data){ + + // remove connections from map + removeConnections(connections); + + }).fail(function( jqXHR, status, error) { + var reason = status + ' ' + error; + Util.showNotify({title: jqXHR.status + ': deleteSystem', text: reason, type: 'warning'}); + $(document).setProgramStatus('problem'); + }); + } + }else{ + // remove connections from map (no request) + removeConnections(connections); } } - if(connectionIds.length > 0){ - var requestData = { - connectionIds: connectionIds - }; - - $.ajax({ - type: 'POST', - url: Init.path.deleteConnection, - data: requestData, - dataType: 'json' - }).done(function(data){ - - // remove systems from map - for(var i = 0; i < connections.length; i++){ - // if a connection is manually (drag&drop) detached, the jsPlumb instance does not exist any more - // connection is already deleted! - if(connections[i]._jsPlumb){ - connections[i]._jsPlumb.instance.detach(connections[i], {fireEvent: false}); - } - } - }).fail(function( jqXHR, status, error) { - var reason = status + ' ' + error; - Util.showNotify({title: jqXHR.status + ': deleteSystem', text: reason, type: 'warning'}); - $(document).setProgramStatus('problem'); - }); - } }; @@ -1644,19 +1681,24 @@ define([ }; /** - * set up context menu for all Systems within a given map - * @param endpoints + * set up all actions that can be preformed on a system + * @param map + * @param system */ var setSystemObserver = function(map, system){ system = $(system); + // get map container var mapContainer = $( map.getContainer() ); - var systemHeadExpand = $( system.find('.' + config.systemHeadExpandClass) ); var systemBody = $( system.find('.' + config.systemBodyClass) ); + // map overlay will be set on "drag" start + var mapOverlay = null; + + // make system draggable map.draggable(system, { containment: 'parent', @@ -1666,8 +1708,10 @@ define([ start: function(params){ var dragSystem = $(params.el); + mapOverlay = dragSystem.getMapOverlay(); + // start map update timer - dragSystem.getMapOverlay().startMapUpdateCounter(); + mapOverlay.startMapUpdateCounter(); // check if grid-snap is enable if(config.mapSnapToGrid){ @@ -1688,16 +1732,18 @@ define([ $(selectedSystems).toggleSystemTooltip('hide', {}); }, drag: function(){ + // start map update timer + mapOverlay.startMapUpdateCounter(); }, stop: function(params){ var dragSystem = $(params.el); // start map update timer - dragSystem.getMapOverlay().startMapUpdateCounter(); + mapOverlay.startMapUpdateCounter(); setTimeout(function(){ dragSystem.removeClass('no-click'); - }, 200); + }, Init.timer.dblClickTimer + 50); // render tooltip dragSystem.toggleSystemTooltip('show', {show: true}); @@ -1736,7 +1782,7 @@ define([ map.setDraggable(system, false); } - // init system tooltips ================================================================ + // init system tooltips ============================================================================= var systemTooltipOptions = { toggle: 'tooltip', placement: 'right', @@ -1745,7 +1791,7 @@ define([ system.find('.fa').tooltip(systemTooltipOptions); - // init system body expand ============================================================ + // init system body expand ========================================================================== systemHeadExpand.hoverIntent(function(e){ // hover in var hoverSystem = $(this).parents('.' + config.systemClass); @@ -1804,7 +1850,7 @@ define([ }); - // context menu ================================================================== + // context menu ===================================================================================== // trigger context menu system.on('contextmenu', function(e){ @@ -1940,22 +1986,47 @@ define([ } }); - // load system data ================================================================================= - system.on('click', function(e){ + // system click events ============================================================================== + var double = function(e){ var system = $(this); + var headElement = $(system).find('.' + config.systemHeadNameClass); - // left mouse button - if(e.which === 1){ - if(! system.hasClass('no-click')){ - if(e.ctrlKey === true){ - // select system - system.toggleSelectSystem(map); - }else{ - system.showSystemInfo(map); + // update z-index for system, editable field should be on top + updateZIndex(system); + + // show "set alias" input (x-editable) + headElement.editable('show'); + }; + + var single = function(e){ + + // check if click was performed on "popover" (x-editable + var popoverClick = false; + if( $(e.target).parents('.popover').length ){ + popoverClick = true; + } + + // continue if click was *not* on a popover dialog of a system + if( !popoverClick ){ + var system = $(this); + + // left mouse button + if(e.which === 1){ + if(! system.hasClass('no-click')){ + if(e.ctrlKey === true){ + // select system + system.toggleSelectSystem(map); + }else{ + system.showSystemInfo(map); + } } } } - }); + + }; + + system.singleDoubleClick(single, double); + }; /** @@ -2507,7 +2578,7 @@ define([ // confirm dialog bootbox.confirm('Is this connection really gone?', function(result) { if(result){ - deleteConnections([params.component]); + deleteConnections([params.component], true); } }); break; @@ -2635,7 +2706,7 @@ define([ selectClass: config.systemDialogSelectClass }; - requirejs(['text!templates/modules/system_dialog.html', 'mustache'], function(template, Mustache) { + requirejs(['text!templates/dialog/system.html', 'mustache'], function(template, Mustache) { var content = Mustache.render(template, data); @@ -2673,6 +2744,8 @@ define([ return false; } + mapContainer.getMapOverlay().startMapUpdateCounter(); + // calculate new system position ----------------------------------------------- var currentX = 0; var currentY = 0; @@ -2750,7 +2823,7 @@ define([ var modalContent = $('#' + config.systemDialogId); - // init system select live search + // init system select live search var selectElement = modalContent.find('.' + config.systemDialogSelectClass); selectElement.initSystemSelect({key: 'systemId'}); }); @@ -2862,10 +2935,11 @@ define([ }; /** - * collect all data for export/save for a map + * collect all map data for export/save for a map + * this function returns the "client" data NOT the "server" data for a map * @returns {*} */ - $.fn.getMapData = function(forceData){ + $.fn.getMapDataFromClient = function(forceData){ var mapElement = $(this); @@ -2971,7 +3045,7 @@ define([ }; systemData.locked = system.data('locked') ? 1 : 0; systemData.rally = system.data('rally') ? 1 : 0; - systemData.currentUser = system.data('currentUser'); + systemData.currentUser = system.data('currentUser'); // if user is currently in this system systemData.updated = { updated: parseInt( system.data('updated') ) }; @@ -3049,10 +3123,9 @@ define([ setConnectionObserver(newJsPlumbInstance, info.connection); }); - newJsPlumbInstance.bind('connectionDetached', function(info, e){ - // a connection is manually (drag&drop) detached! otherwise this event should not be send! - var connection = info.connection; - deleteConnections([connection]); + // event after connection moved + newJsPlumbInstance.bind('connectionMoved', function(info, e) { + }); // event after DragStop a connection or new connection @@ -3084,12 +3157,19 @@ define([ // event before Detach connection newJsPlumbInstance.bind('beforeDetach', function(info) { + return true; }); + newJsPlumbInstance.bind('connectionDetached', function(info, e){ + // a connection is manually (drag&drop) detached! otherwise this event should not be send! + var connection = info.connection; + deleteConnections([connection], true); + }); + activeInstances[mapId] = newJsPlumbInstance; - console.log('new jsPlumbInstance: ' + mapId); + //console.log('new jsPlumbInstance: ' + mapId); } return activeInstances[mapId]; @@ -3164,7 +3244,7 @@ define([ // reset map update timer - mapOverlay.setMapUpdateCounter(100, '5'); + mapOverlay.setMapUpdateCounter(100, config.logTimerCount); }; /** diff --git a/js/app/module_map.js b/js/app/module_map.js index ead89123..8f2eb72d 100644 --- a/js/app/module_map.js +++ b/js/app/module_map.js @@ -15,8 +15,6 @@ define([ 'use strict'; - var currentMapData = []; // current map data - var config = { dynamicElementWrapperId: 'pf-dialog-wrapper', // parent Element for dynamic content (dialogs,..) mapTabElementId: 'pf-map-tab-element', // id for map tab element (tabs + content) @@ -24,6 +22,8 @@ define([ mapTabIdPrefix: 'pf-map-tab-', // id prefix for a map tab mapTabClass: 'pf-map-tab', // class for a map tab mapTabLinkTextClass: 'nav-tabs-link', // class for span elements in a tab + mapTabIconClass: 'pf-map-tab-icon', // class for map icon + mapTabSharedIconClass: 'pf-map-tab-shared-icon', // class for map shared icon mapTabContentClass: 'pf-map-tab-content', // class for tab content container mapTabContentSystemInfoClass: 'pf-map-tab-content-system', mapWrapperClass: 'pf-map-wrapper', // scrollable @@ -101,11 +101,6 @@ define([ drawSystemModules($( e.target )); }); - $(this).on('pf:deleteSystemData', function(e, systemData){ - console.log(systemData); - }); - - }); }; @@ -302,39 +297,63 @@ define([ $.fn.updateTabData = function(options){ var tabElement = $(this); - // set main data + // set "main" data tabElement.data('map-id', options.id).data('updated', options.updated); - // change tab link + // change "tab" link tabElement.attr('href', '#' + config.mapTabIdPrefix + options.id); - // change map icon - tabElement.find('i').removeClass().addClass(['fa', 'fa-fw', options.icon].join(' ')); + // change "map" icon + var mapIconElement = tabElement.find('.' + config.mapTabIconClass); + mapIconElement.removeClass().addClass([config.mapTabIconClass, 'fa', 'fa-fw', options.icon].join(' ')); + + // change "shared" icon + var mapSharedIconElement = tabElement.find('.' + config.mapTabSharedIconClass); + mapSharedIconElement.hide(); + + // check if the map is a "shared" map + if(options.access){ + if( + options.access.user.length > 1 || + options.access.corporation.length > 1 || + options.access.alliance.length > 1 + ){ + mapSharedIconElement.show(); + } + } // change map name label - tabElement.find('.' + config.mapTabLinkTextClass).text(options.name); + var tabLinkTextElement = tabElement.find('.' + config.mapTabLinkTextClass); + tabLinkTextElement.text(options.name); // change tabClass var listElement = tabElement.parent(); - listElement.removeClass().addClass([config.mapTabClass, options.type.classTab ].join(' ')); + // new tab classes + var tabClasses = [config.mapTabClass, options.type.classTab ]; + + // check if tab was "active" before + if( listElement.hasClass('active') ){ + tabClasses.push('active'); + } + + listElement.removeClass().addClass( tabClasses.join(' ') ); // set title for tooltip - if(options.type.name !== undefined){ console.log(options.type.name) + if(options.type.name !== undefined){ - listElement.tooltip('destroy'); - - var mapTooltipOptions = { - placement: 'bottom', - container: 'body', - title: options.type.name + ' map', - trigger: 'hover', - delay: 200 - }; - - $(listElement).tooltip(mapTooltipOptions); + tabLinkTextElement.attr('title', options.type.name + ' map'); } + var mapTooltipOptions = { + placement: 'bottom', + container: 'body', + trigger: 'hover', + delay: 150 + }; + + listElement.find('[title]').tooltip(mapTooltipOptions).tooltip('fixTitle'); + if(options.right === true){ listElement.addClass('pull-right'); } @@ -358,19 +377,27 @@ define([ listElement.addClass('pull-right'); } - // link element ------- + // link element var linkElement = $('').attr('role', 'tab'); - // icon element ------ - var iconElement = $(''); + // map icon element + var mapIconElement = $('', { + class: config.mapTabIconClass + }); - // text element ----- + // map shared icon element + var mapSharedIconElement = $('', { + class: [config.mapTabSharedIconClass, 'fa', 'fa-fw', 'fa-share-alt'].join(' '), + title: 'shared map' + }); + + // text element var textElement = $('', { class: config.mapTabLinkTextClass }); var newListElement = listElement.append( - linkElement.append(iconElement).append(textElement) + linkElement.append(mapIconElement).append(textElement).append(mapSharedIconElement) ); tabBar.append( newListElement ); @@ -380,9 +407,9 @@ define([ // tabs content ==================================== var contentElement = $('
', { - id: config.mapTabIdPrefix + options.id, - class: options.contentClasses.join(' ') - }); + id: config.mapTabIdPrefix + parseInt( options.id ), + class: [config.mapTabContentClass].join(' ') + }) contentElement.addClass('tab-pane'); @@ -473,25 +500,6 @@ define([ return deletedTabName; }; - /** - * get current map data for a map id - * @param mapId - * @returns {boolean} - */ - var getMapDataById = function(mapId){ - - var mapData = false; - - for(var i = 0; i < currentMapData.length; i++){ - if(currentMapData[i].config.id === mapId){ - mapData = currentMapData[i]; - break; - } - } - - return mapData; - }; - /** * load/update map module into element (all maps) * @param mapData @@ -503,12 +511,9 @@ define([ return true; } - - // update current map data - currentMapData = mapData; - + // store current map data global (cache) // temp store current map data to prevent data-change while function execution! - var tempMapData = currentMapData; + var tempMapData = Util.setCurrentMapData(mapData); var mapModuleElement = $(this); @@ -531,7 +536,7 @@ define([ var mapId = tabElement.data('map-id'); if(mapId > 0){ - var tabMapData = getMapDataById(mapId); + var tabMapData = Util.getCurrentMapData(mapId); if(tabMapData !== false){ // map data available -> @@ -561,18 +566,7 @@ define([ if( activeMapIds.indexOf( data.config.id ) === -1 ){ // add new map tab - var tabOptions = { - id: parseInt( data.config.id ), - type: data.config.type, - contentClasses: [config.mapTabContentClass], - active: false, - icon: data.config.icon, - name: data.config.name, - right: false, - updated: data.config.updated - }; - - var newTabElements = tabMapElement.addTab(tabOptions); + var newTabElements = tabMapElement.addTab(data.config); // set observer for manually triggered map events newTabElements.contentElement.setTabContentObserver(); @@ -589,7 +583,7 @@ define([ // get current active map var activeMapId = Util.getMapModule().getActiveMap().data('id'); - var activeMapData = getMapDataById(activeMapId); + var activeMapData = Util.getCurrentMapData(activeMapId); if(activeMapData !== false){ // update active map with new mapData @@ -611,19 +605,7 @@ define([ for(var j = 0; j < tempMapData.length; j++){ var data = tempMapData[j]; - - var tabOptions = { - id: parseInt( data.config.id ), - type: data.config.type, - contentClasses: [config.mapTabContentClass], - icon: data.config.icon, - name: data.config.name, - right: false, - updated: data.config.updated - }; - - tabMapElement.addTab(tabOptions); - + tabMapElement.addTab(data.config); } // add "add" button @@ -632,7 +614,6 @@ define([ type: { classTab: Util.getInfoForMap( 'standard', 'classTab') }, - contentClasses: [config.mapTabContentClass], icon: 'fa-plus', name: 'add', right: true @@ -676,15 +657,15 @@ define([ if(mapId === 0){ // add new Tab selected - $(document).trigger('pf:menuShowMapSettings'); + $(document).trigger('pf:menuShowMapSettings', {tab: 'new'}); e.preventDefault(); } }); // load new map right after tab-change - allTabElements.on('shown.bs.tab', function (e) { + allTabElements.on('shown.bs.tab', function (e) { console.log('switch') var mapId = $(e.target).data('map-id'); - var tabMapData = getMapDataById(mapId); + var tabMapData = Util.getCurrentMapData(mapId); if(tabMapData !== false){ // load map @@ -733,7 +714,7 @@ define([ var data = []; for(var i = 0; i < mapElements.length; i++){ - var mapData = $(mapElements[i]).getMapData(false); + var mapData = $(mapElements[i]).getMapDataFromClient(false); if(mapData !== false){ data.push(mapData); diff --git a/js/app/notification.js b/js/app/notification.js index 860baadd..8b667de9 100644 --- a/js/app/notification.js +++ b/js/app/notification.js @@ -168,7 +168,6 @@ define([ var timeoutId; var blink = function(){ document.title = document.title === blinkTitle ? currentTitle : blinkTitle; - console.log(document.title) }; var clear = function() { diff --git a/js/app/page.js b/js/app/page.js index 388c6500..93c5e0ba 100644 --- a/js/app/page.js +++ b/js/app/page.js @@ -252,7 +252,7 @@ define([ class: 'fa fa-gears fa-fw' }) ).on('click', function(){ - $(document).triggerMenuEvent('ShowMapSettings', {newMap: false}); + $(document).triggerMenuEvent('ShowMapSettings', {tab: 'settings'}); }) ).append( $('', { @@ -454,17 +454,17 @@ define([ return false; }); - $(document).on('pf:menuShowMapSettings', function(e){ + $(document).on('pf:menuShowMapSettings', function(e, data){ // show map edit dialog or edit map var mapData = false; var activeMap = Util.getMapModule().getActiveMap(); if(activeMap){ - mapData = activeMap.getMapData(true); + mapData = Util.getCurrentMapData( activeMap.data('id') ); } - $.fn.showMapSettingsDialog(mapData); + $.fn.showMapSettingsDialog(mapData, data); return false; }); @@ -475,7 +475,7 @@ define([ var activeMap = Util.getMapModule().getActiveMap(); if(activeMap){ - mapData = activeMap.getMapData(true); + mapData = activeMap.getMapDataFromClient(true); } $.fn.showDeleteMapDialog(mapData); @@ -652,7 +652,7 @@ define([ } // update user ship data -------------------------------------------------------- - if(currentShipId !== newShipId){ console.log('update ship '); + if(currentShipId !== newShipId){ var showShipElement = true; if(newShipId === 0){ @@ -683,7 +683,7 @@ define([ }).done(function(data){ if(data.reroute !== undefined){ - window.location = Util.buildUrl(data.reroute); + window.location = Util.buildUrl(data.reroute) + '?logout'; } }).fail(function( jqXHR, status, error) { diff --git a/js/app/ui/dialog/map_info.js b/js/app/ui/dialog/map_info.js index 1689a91c..9379fb6d 100644 --- a/js/app/ui/dialog/map_info.js +++ b/js/app/ui/dialog/map_info.js @@ -339,7 +339,7 @@ define([ $.fn.showMapInfoDialog = function(){ var activeMap = Util.getMapModule().getActiveMap(); - var mapData = activeMap.getMapData(true); + var mapData = activeMap.getMapDataFromClient(true); if(mapData !== false){ requirejs(['text!templates/dialog/map_info.html', 'mustache'], function(template, Mustache) { @@ -384,7 +384,7 @@ define([ if(menuAction === 'refresh'){ // get new map data - var mapData = activeMap.getMapData(true); + var mapData = activeMap.getMapDataFromClient(true); mapElement.loadMapInfoData(mapData); systemsElement.loadSystemInfoTable(mapData); diff --git a/js/app/ui/dialog/map_settings.js b/js/app/ui/dialog/map_settings.js index 8ada160c..1b941875 100644 --- a/js/app/ui/dialog/map_settings.js +++ b/js/app/ui/dialog/map_settings.js @@ -13,20 +13,25 @@ define([ var config = { // map dialog - newMapDialogId: 'pf-map-new-dialog', // id for edit/update map dialog + newMapDialogId: 'pf-map-dialog', // id for map settings dialog dialogMapCreateContainerId: 'pf-map-dialog-create', // id for the "new map" container dialogMapEditContainerId: 'pf-map-dialog-edit', // id for the "edit" container dialogMapSettingsContainerId: 'pf-map-dialog-settings', // id for the "settings" container - dialogMessageContainerId: 'pf-map-dialog-message-container' // id for dialog form message container + dialogMessageContainerId: 'pf-map-dialog-message-container', // id for dialog form message container + + userSelectId: 'pf-map-dialog-user-select', // id for "user" select + corporationSelectId: 'pf-map-dialog-corporation-select', // id for "corporation" select + allianceSelectId: 'pf-map-dialog-alliance-select' // id for "alliance" select }; /** * shows the add/edit map dialog * @param mapData + * @param options */ - $.fn.showMapSettingsDialog = function(mapData){ + $.fn.showMapSettingsDialog = function(mapData, options){ // check if dialog is already open var mapInfoDialogElement = $('#' + config.newMapDialogId); @@ -52,7 +57,9 @@ define([ var data = { scope: Util.getMapScopes(), type: Util.getMapTypes(true), - icon: Util.getMapIcons() + icon: Util.getMapIcons(), + formErrorContainerClass: Util.config.formErrorContainerClass, + formWarningContainerClass: Util.config.formWarningContainerClass }; // render "new map" tab content ------------------------------------------- @@ -62,32 +69,73 @@ define([ var contentEditMap = Mustache.render(templateMapSettings, data); contentEditMap = $(contentEditMap); + // current map access info + var accessUser = []; + var accessCorporation = []; + var accessAlliance = []; + if(mapData !== false){ + // set current map information contentEditMap.find('input[name="id"]').val( mapData.config.id ); contentEditMap.find('select[name="icon"]').val( mapData.config.icon ); contentEditMap.find('input[name="name"]').val( mapData.config.name ); contentEditMap.find('select[name="scopeId"]').val( mapData.config.scope.id ); contentEditMap.find('select[name="typeId"]').val( mapData.config.type.id ); + + accessUser = mapData.config.access.user; + accessCorporation = mapData.config.access.corporation; + accessAlliance = mapData.config.access.alliance; } // render main dialog ----------------------------------------------------- data = { id: config.newMapDialogId, + + // default open tab ---------- + openTabNew: options.tab === 'new', + openTabEdit: options.tab === 'edit', + openTabSettings: options.tab === 'settings', + dialogMapCreateContainerId: config.dialogMapCreateContainerId, dialogMapEditContainerId: config.dialogMapEditContainerId, dialogMapSettingsContainerId: config.dialogMapSettingsContainerId, dialogMessageContainerId: config.dialogMessageContainerId, + hideEditTab: hideEditTab, - hideSettingsTab: hideSettingsTab + hideSettingsTab: hideSettingsTab, + + // settings tab -------------- + userSelectId: config.userSelectId, + corporationSelectId: config.corporationSelectId, + allianceSelectId: config.allianceSelectId, + + // map access objects -------- + accessUser: accessUser, + accessCorporation: accessCorporation, + accessAlliance: accessAlliance, + + // access limitations -------- + maxUser: Init.maxSharedCount.user, + maxCorporation: Init.maxSharedCount.corporation, + maxAlliance: Init.maxSharedCount.alliance }; var contentDialog = Mustache.render(templateMapDialog, data); contentDialog = $(contentDialog); + // set mapId for "settings" tab + if(mapData !== false){ + contentDialog.find('input[name="id"]').val( mapData.config.id ); + } + + // set tab content $('#' + config.dialogMapCreateContainerId, contentDialog).html(contentNewMap); $('#' + config.dialogMapEditContainerId, contentDialog).html(contentEditMap); + // disable modal focus event -> otherwise select2 is not working! -> quick fix + $.fn.modal.Constructor.prototype.enforceFocus = function() {}; + var mapInfoDialog = bootbox.dialog({ title: dialogTitle, message: contentDialog, @@ -103,33 +151,58 @@ define([ // get the current active form var form = $('#' + config.newMapDialogId).find('form').filter(':visible'); -console.log(form) + // validate form form.validator('validate'); + // validate select2 fields (settings tab) + form.find('select').each(function(){ + var selectField = $(this); + var selectValues = selectField.val(); + + if(selectValues === null){ + selectField.parents('.form-group').addClass('has-error'); + }else{ + selectField.parents('.form-group').removeClass('has-error'); + } + }); + // check weather the form is valid var formValid = form.isValidForm(); if(formValid === true){ - var newMapData = {mapData: form.getFormValues()}; + // lock dialog + var dialogContent = mapInfoDialog.find('.modal-content'); + dialogContent.showLoadingAnimation(); + + var newMapData = {formData: form.getFormValues()}; $.ajax({ type: 'POST', url: Init.path.saveMap, data: newMapData, dataType: 'json' - }).done(function(data){ - Util.showNotify({title: dialogTitle, text: 'Map: ' + data.name, type: 'success'}); + }).done(function(responseData){ + + dialogContent.hideLoadingAnimation(); + + if(responseData.error.length > 0){ + form.showFormMessage(responseData.error); + }else{ + // success + Util.showNotify({title: dialogTitle, text: 'Map: ' + responseData.name, type: 'success'}); + + $(mapInfoDialog).modal('hide'); + $(document).trigger('pf:closeMenu', [{}]); + } - $(mapInfoDialog).modal('hide'); - $(document).trigger('pf:closeMenu', [{}]); }).fail(function( jqXHR, status, error) { var reason = status + ' ' + error; Util.showNotify({title: jqXHR.status + ': saveMap', text: reason, type: 'warning'}); $(document).setProgramStatus('problem'); - }); + }); } return false; @@ -145,7 +218,37 @@ console.log(form) mapInfoDialog.initTooltips(); // set form validator - mapInfoDialog.find('form').validator(); + mapInfoDialog.find('form').initFormValidation(); + + // events for tab change + mapInfoDialog.find('.navbar a').on('shown.bs.tab', function (e) { + + var selectElementUser = mapInfoDialog.find('#' + config.userSelectId); + var selectElementCorporation = mapInfoDialog.find('#' + config.corporationSelectId); + var selectElementAlliance = mapInfoDialog.find('#' + config.allianceSelectId); + + if($(e.target).attr('href') === '#' + config.dialogMapSettingsContainerId){ + // "settings" tab + + initSettingsSelectFields(mapInfoDialog); + }else{ + + if( $(selectElementUser).data('select2') !== undefined ){ + $(selectElementUser).select2('destroy'); + } + + if( $(selectElementCorporation).data('select2') !== undefined ){ + $(selectElementCorporation).select2('destroy'); + } + + if( $(selectElementAlliance).data('select2') !== undefined ){ + $(selectElementAlliance).select2('destroy'); + } + } + }); + + // show form messages ------------------------------------- + $('#' + config.dialogMessageContainerId).showMessage({type: 'info', title: 'Hint', text: 'Creating new maps or change settings may take a few seconds'}); if(mapData === false){ // no map data found (probably new user @@ -153,11 +256,45 @@ console.log(form) } }); + // init select fields in case "settings" tab is open by default + if(options.tab === 'settings'){ + initSettingsSelectFields(mapInfoDialog); + } + }); } }; + /** + * init select2 fields within the settings dialog + * @param mapInfoDialog + */ + var initSettingsSelectFields = function(mapInfoDialog){ + + var selectElementUser = mapInfoDialog.find('#' + config.userSelectId); + var selectElementCorporation = mapInfoDialog.find('#' + config.corporationSelectId); + var selectElementAlliance = mapInfoDialog.find('#' + config.allianceSelectId); + + // init corporation select live search + selectElementUser.initAccessSelect({ + type: 'user', + maxSelectionLength: Init.maxSharedCount.user + }); + + // init corporation select live search + selectElementCorporation.initAccessSelect({ + type: 'corporation', + maxSelectionLength: Init.maxSharedCount.corporation + }); + + // init alliance select live search + selectElementAlliance.initAccessSelect({ + type: 'alliance', + maxSelectionLength: Init.maxSharedCount.alliance + }); + }; + /** * shows the delete map Dialog * @param mapElement diff --git a/js/app/ui/dialog/settings.js b/js/app/ui/dialog/settings.js index 8ae84e38..1c8d7e74 100644 --- a/js/app/ui/dialog/settings.js +++ b/js/app/ui/dialog/settings.js @@ -27,13 +27,6 @@ define([ settingsCloneRowButtonClass: 'pf-dialog-clone-button', // class for clone button (api row) settingsDeleteRowButtonClass: 'pf-dialog-delete-button', // class for delete button (api row) - // form messages - settingsErrorId: 'pf-dialog-error-id', // id for "error" form element - settingsWarningId: 'pf-dialog-warning-id', // id for "warning" form element - settingsMessageVelocityOptions: { - duration: 180 - }, - // captcha captchaImageWrapperId: 'pf-dialog-captcha-wrapper', // id for "captcha image" wrapper captchaImageId: 'pf-dialog-captcha-image', // id for "captcha image" @@ -107,80 +100,6 @@ define([ }); }; - /** - * show form messages - * @param errors - */ - var showFormMessage = function(errors){ - - var errorMessage = []; - var warningMessage = []; - for(var i = 0; i < errors.length; i++){ - if(errors[i].type === 'error'){ - errorMessage.push( errors[i].message ); - }else if(errors[i].type === 'warning'){ - warningMessage.push( errors[i].message ); - } - } - - if(errorMessage.length > 0){ - hideFormMessage('error', function(element){ - $(element).find('small').text( errorMessage.join('
') ); - $(element).velocity('transition.slideUpIn', config.settingsMessageVelocityOptions); - }); - } - - if(warningMessage.length > 0){ - hideFormMessage('warning', function(element){ - $(element).find('small').text( warningMessage.join('
') ); - $(element).velocity('transition.slideUpIn', config.settingsMessageVelocityOptions); - }); - } - }; - - /** - * ide all form messages - * @param type - * @param callback - */ - var hideFormMessage = function(type, callback){ - - var settingsMessageVelocityOptions = $.extend({}, config.settingsMessageVelocityOptions); - - - // check if callback exists - if(callback !== undefined){ - settingsMessageVelocityOptions.complete = callback; - - // new error will be shown afterwards -> keep display - settingsMessageVelocityOptions.display = 'block'; - } - - if(type === 'error'){ - var errorMessageElement = $('#' + config.settingsErrorId); - - // check if element is visible - if(errorMessageElement.is(':visible')){ - errorMessageElement.velocity('transition.slideDownOut', settingsMessageVelocityOptions); - }else if(callback){ - // skip hide animation - callback(errorMessageElement); - } - } - - if(type === 'warning'){ - var warningMessageElement = $('#' + config.settingsWarningId); - - // check if element is visible - if(warningMessageElement.is(':visible')){ - warningMessageElement.velocity('transition.slideDownOut', settingsMessageVelocityOptions); - }else if(callback){ - // skip hide animation - callback(warningMessageElement); - } - } - }; - /** * init popovers in dialog * @param dialogElement @@ -272,7 +191,6 @@ define([ }]; } - var data = { id: config.settingsDialogId, register: register, @@ -283,8 +201,8 @@ define([ deleteRowButtonClass: config.settingsDeleteRowButtonClass, captchaImageWrapperId: config.captchaImageWrapperId, captchaImageId: config.captchaImageId, - settingsErrorId: config.settingsErrorId, - settingsWarningId: config.settingsWarningId + formErrorContainerClass: Util.config.formErrorContainerClass, + formWarningContainerClass: Util.config.formWarningContainerClass }; var content = Mustache.render(template, data); @@ -376,7 +294,7 @@ define([ responseData.error && responseData.error.length > 0 ){ - showFormMessage(responseData.error); + form.showFormMessage(responseData.error); showCaptchaImage(); }else{ @@ -479,19 +397,7 @@ define([ initPopover( dialogElement ); // init form validation - form.validator(); - - // validation event listener - form.on('valid.bs.validator', function(validatorObj){ - var inputGroup = $(validatorObj.relatedTarget).parents('.form-group'); - inputGroup.removeClass('has-error').addClass('has-success'); - }); - - form.on('invalid.bs.validator', function(validatorObj){ - var field = $(validatorObj.relatedTarget); - var inputGroup = field.parents('.form-group'); - inputGroup.removeClass('has-success').addClass('has-error'); - }); + form.initFormValidation(); // on Tab switch ====================================================================== tabLinkElements.on('shown.bs.tab', function (e) { diff --git a/js/app/ui/form_element.js b/js/app/ui/form_element.js index 341672b9..83ad91b2 100644 --- a/js/app/ui/form_element.js +++ b/js/app/ui/form_element.js @@ -7,10 +7,10 @@ define([ 'app/init', 'app/util' ], function($, Init, Util) { - "use strict"; + 'use strict'; /** - * init a select element as an ajax based "select2" object + * init a select element as an ajax based "select2" object for system search * @param options */ $.fn.initSystemSelect = function(options){ @@ -50,7 +50,7 @@ define([ }; }, error: function (jqXHR, status, error) { - if(!Util.isXHRAborted){ + if( !Util.isXHRAborted(jqXHR) ){ // close select selectElement.select2('destroy'); @@ -60,17 +60,162 @@ define([ } }, + theme: 'pathfinder', minimumInputLength: 2, placeholder: 'Jita', - allowClear: true + allowClear: true, + escapeMarkup: function (markup) { + // let our custom formatter work + return markup; + } }) ).done(function(){ // open select selectElement.select2('open'); }); + }; + + /** + * init a select element as an ajax based "select2" object for Access resources + * user (private map), corporation (corp map), alliance (ally map) + * @param options + */ + $.fn.initAccessSelect = function(options){ + + return this.each(function(){ + + var selectElement = $(this); + + // format result data + function formatResultData (data) { + + if (data.loading){ + return data.text; + } + + // check if an option is already selected + // do not show the same result twice + var currentValues = selectElement.val(); + + if( + currentValues && + currentValues.indexOf( data.id.toString() ) !== -1 + ){ + return ; + } + + var imagePath = ''; + var previewContent = ''; + + switch(options.type){ + case 'user': + previewContent = ''; + break; + case 'corporation': + imagePath = Init.url.ccpImageServer + 'Corporation/' + data.id + '_32.png'; + previewContent = ''; + break; + case 'alliance': + imagePath = Init.url.ccpImageServer + 'Alliance/' + data.id + '_32.png'; + previewContent = ''; + break; + } + + var markup = '
'; + markup += '
' + previewContent + '
'; + markup += '
' + data.text + '
'; + + return markup; + } + + // format selection data + function formatSelectionData (data){ + + if (data.loading){ + return data.text; + } + + var markup = '
'; + markup += '
' + data.text + '
'; + + return markup; + } + + $.when( + selectElement.select2({ + ajax: { + url: function(params){ + // add params to URL + return Init.path.searchAccess + '/' + options.type + '/' + params.term; + }, + dataType: 'json', + delay: 250, + timeout: 5000, + cache: true, + data: function(params) { + // no url params here + return; + }, + processResults: function(data, page) { + // parse the results into the format expected by Select2. + return { + results: data.map( function(item){ + return { + id: item.id, + text: item.name + }; + }) + }; + }, + error: function (jqXHR, status, error) { + if( !Util.isXHRAborted(jqXHR) ){ + + var reason = status + ' ' + jqXHR.status + ': ' + error; + Util.showNotify({title: 'Access select error', text: reason + ' deleted', type: 'warning'}); + } + + } + }, + theme: 'pathfinder', + minimumInputLength: 2, + placeholder: '', + allowClear: false, + maximumSelectionLength: options.maxSelectionLength, + templateResult: formatResultData, + templateSelection: formatSelectionData, + escapeMarkup: function (markup) { + // let our custom formatter work + return markup; + } + }).on('change', function(e){ + // select changed + + }) + ).done(function(){ + // after init finish + }); + }); }; -}); \ No newline at end of file +}); + + + + + + + + + + + + + + + + + + diff --git a/js/app/ui/system_signature.js b/js/app/ui/system_signature.js index e624b330..52bc51df 100644 --- a/js/app/ui/system_signature.js +++ b/js/app/ui/system_signature.js @@ -8,7 +8,8 @@ define([ 'app/util', 'app/render', 'bootbox', - 'app/counter' + 'app/counter', + 'datatablesResponsive' ], function($, Init, Util, Render, bootbox) { 'use strict'; @@ -29,6 +30,9 @@ define([ // signature progress bar signatureScannedProgressBarClass: 'pf-system-progress-scanned', // class for signature progress bar + // toolbar + sigTableClearButtonClass: 'pf-sig-table-clear-button', // class for "clear" signatures button + // signature table sigTableClass: 'pf-sig-table', // Table class for all Signature Tables sigTablePrimaryClass: 'pf-sig-table-primary', // class for primary sig table @@ -38,11 +42,15 @@ define([ sigTableEditSigNameInput: 'pf-sig-table-edit-name-input', // class for editable fields (input) sigTableEditSigGroupSelect: 'pf-sig-table-edit-group-select', // class for editable fields (sig group) sigTableEditSigTypeSelect: 'pf-sig-table-edit-type-select', // class for editable fields (sig type) + sigTableEditSigDescriptionTextarea: 'pf-sig-table-edit-desc-text', // class for editable fields (sig description) sigTableCounterClass: 'pf-sig-table-counter', // class for signature table counter sigTableCreatedCellClass: 'pf-sig-table-created', // class for "created" cells sigTableUpdatedCellClass: 'pf-sig-table-updated', // class for "updated" cells sigTableActionCellClass: 'pf-sig-table-action-cell', // class for "action" cells - sigTableActionButtonClass: 'pf-sig-table-action-button' // class for row action button + sigTableActionButtonClass: 'pf-sig-table-action-button', // class for row action button + + // xEditable + editableDiscriptionInputClass: 'pf-editable-description' // class for "description" textarea }; // disable Signature Table update temporary (until. some requests/animations) are finished @@ -55,8 +63,9 @@ define([ var emptySignatureData = { id: 0, name: '', - typeId: 0, groupId: 0, + typeId: 0, + description: '', created: { created: null }, @@ -65,8 +74,14 @@ define([ } }; + var fullSignatureOptions = { + action: 'delete', + actionClass: ['fa-close', 'txt-color', 'txt-color-redDarker'].join(' ') + }; + // empty signatureData row Options var emptySignatureOptions = { + action: 'add', actionClass: ['fa-plus', 'txt-color', 'txt-color-grayLighter'].join(' ') }; @@ -403,6 +418,62 @@ define([ return signatureData; }; + /** + * get a labeled button + * @param options + * @returns {*|jQuery} + */ + var getLabledButton = function(options){ + + var buttonClasses = ['btn', 'btn-sm', 'btn-labeled']; + + switch(options.type){ + case 'default': + buttonClasses.push('btn-default'); + break; + case 'primary': + buttonClasses.push('btn-primary'); + break; + case 'danger': + buttonClasses.push('btn-danger'); + break; + } + + // add optional classes + if(options.classes){ + buttonClasses = buttonClasses.concat(options.classes); + } + + + var buttonElement = $('