decidim-navigation_maps 1.2.0 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +22 -3
  3. data/Rakefile +8 -0
  4. data/app/cells/decidim/navigation_maps/content_blocks/navigation_map/_tabs_content.erb +1 -1
  5. data/app/cells/decidim/navigation_maps/content_blocks/navigation_map/show.erb +2 -2
  6. data/app/cells/decidim/navigation_maps/content_blocks/navigation_map_cell.rb +5 -0
  7. data/app/cells/decidim/navigation_maps/content_blocks/navigation_map_settings_form/_form.erb +4 -4
  8. data/app/cells/decidim/navigation_maps/content_blocks/navigation_map_settings_form/_tabs_content.erb +0 -1
  9. data/app/cells/decidim/navigation_maps/content_blocks/navigation_map_settings_form/show.erb +4 -2
  10. data/app/cells/decidim/navigation_maps/content_blocks/navigation_map_settings_form_cell.rb +9 -0
  11. data/app/controllers/decidim/navigation_maps/admin/blueprints_controller.rb +1 -1
  12. data/app/jobs/decidim/navigation_maps/migrate_legacy_images_job.rb +32 -0
  13. data/app/models/decidim/navigation_maps/blueprint.rb +4 -3
  14. data/app/packs/entrypoints/decidim_admin_navigation_maps.js +4 -0
  15. data/app/packs/entrypoints/decidim_admin_navigation_maps.scss +1 -0
  16. data/app/packs/entrypoints/decidim_navigation_maps.js +7 -0
  17. data/app/packs/entrypoints/decidim_navigation_maps.scss +1 -0
  18. data/app/packs/src/decidim/navigation_maps/admin/map_editor.js +83 -0
  19. data/app/packs/src/decidim/navigation_maps/admin/navigation_maps.js +154 -0
  20. data/app/packs/src/decidim/navigation_maps/map_view.js +125 -0
  21. data/app/packs/src/decidim/navigation_maps/navigation_maps.js +43 -0
  22. data/app/{assets → packs}/stylesheets/decidim/navigation_maps/_variables.scss +0 -0
  23. data/app/{assets → packs}/stylesheets/decidim/navigation_maps/admin/navigation_maps.scss +4 -4
  24. data/app/{assets → packs}/stylesheets/decidim/navigation_maps/navigation_maps.scss +1 -2
  25. data/app/uploaders/decidim/navigation_maps/blueprint_uploader.rb +4 -12
  26. data/app/uploaders/decidim/navigation_maps/cw/blueprint_uploader.rb +24 -0
  27. data/config/assets.rb +41 -0
  28. data/config/locales/cs.yml +1 -1
  29. data/db/seeds.rb +6 -5
  30. data/lib/decidim/navigation_maps/admin_engine.rb +0 -4
  31. data/lib/decidim/navigation_maps/engine.rb +4 -4
  32. data/lib/decidim/navigation_maps/navigation_map_cell_helpers.rb +1 -1
  33. data/lib/decidim/navigation_maps/version.rb +2 -2
  34. data/lib/decidim/navigation_maps.rb +0 -2
  35. data/lib/tasks/decidim_navigation_maps_webpacker_tasks.rake +58 -0
  36. data/lib/tasks/navigation_maps_active_storage_migration_tasks.rake +37 -0
  37. metadata +31 -27
  38. data/app/assets/config/admin/decidim_navigation_maps_manifest.css +0 -3
  39. data/app/assets/config/admin/decidim_navigation_maps_manifest.js +0 -3
  40. data/app/assets/config/decidim_navigation_maps_manifest.css +0 -3
  41. data/app/assets/config/decidim_navigation_maps_manifest.js +0 -1
  42. data/app/assets/images/decidim/navigation_maps/icon.svg +0 -1
  43. data/app/assets/javascripts/decidim/navigation_maps/admin/map_editor.js +0 -88
  44. data/app/assets/javascripts/decidim/navigation_maps/admin/navigation_maps.js +0 -143
  45. data/app/assets/javascripts/decidim/navigation_maps/map_view.js +0 -123
  46. data/app/assets/javascripts/decidim/navigation_maps/navigation_maps.js +0 -44
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0e99886ba8f12f6602904f174d2d82542def72cc7e6eb2dcdaa07861876d7eed
4
- data.tar.gz: d57eef525c3197b6dcd5645cb1f5ed433762558223ee80895f78b34af4216230
3
+ metadata.gz: 555b93edb92b69f19e3cc10841179b04fb57502a13d347fe69132d01833f88d6
4
+ data.tar.gz: 76da0880561f5e1e5b1201a048d90c8a862194134f3e485c1d2d045401f61551
5
5
  SHA512:
6
- metadata.gz: b4a1b325b560400a2ee97b8ebd1d2e67e40a5472922e3cf631630233f8552576f54574c453ca4c73d6093410059cb154d3062861b8e6f29729a40db4f62cb6f7
7
- data.tar.gz: 71a153f0cc0c8e4675c5292e0a40248cd7510c39052e1b83856006bf3798340a9489078a0bfc23f02f68bc4087d377a8a9f724a5e5a52e1a8f14a2266d6e6a31
6
+ metadata.gz: d90681644ff2111df383e19b5e7e7568c97cc225aad75645dcd86369332e91d4ef0d0fcc0ea6413c7bcb83bab0239333fb926958d8a32e62df5e2b52bd7353d1
7
+ data.tar.gz: bbc246049572a74e697a0382bd084213cdff071149ea1c2ef1a93eae78da2d1ceeba110b394c16c9877b5c88ecd96fca0c674b1f72193b7b86b993a5707c6daf
data/README.md CHANGED
@@ -24,7 +24,7 @@ homepage only).
24
24
  Add this line to your application's Gemfile:
25
25
 
26
26
  ```ruby
27
- gem "decidim-navigation_maps", "~> 1.2.0"
27
+ gem "decidim-navigation_maps"
28
28
  ```
29
29
 
30
30
  And then execute (remember to repeat this if you are upgrading from version 1.1):
@@ -32,16 +32,35 @@ And then execute (remember to repeat this if you are upgrading from version 1.1)
32
32
  ```bash
33
33
  bundle
34
34
  bundle exec rails decidim_navigation_maps:install:migrations
35
+ bundle exec rails decidim_navigation_maps:webpacker:install
35
36
  bundle exec rails db:migrate
36
37
  ```
37
38
 
39
+ > NOTE: the `decidim_notify:webpacker:install` is only necessary for Decidim versions starting at 0.25.
40
+
41
+ If you are upgrading from a version prior to 1.3, make sure that you migrate your existing images to Active Storae:
42
+
43
+ ```
44
+ RAILS_ENV=production bin/rails navigation_maps:active_storage_migrations:migrate_from_carrierwave
45
+ ```
46
+
47
+ Or check your migration status with:
48
+ ```
49
+ RAILS_ENV=production bin/rails navigation_maps:active_storage_migrations:check_migration_from_carrierwave
50
+ ```
51
+
52
+ The correct version of Navigation Maps should resolved automatically by the Bundler.
53
+ However you can force some specific version using `gem "decidim-navigation_maps", "~> 1.3.0"` in the Gemfile.
54
+
55
+
38
56
  Depending on your Decidim version, choose the corresponding Plugin version to ensure compatibility:
39
57
 
40
58
  | Navigation Maps version | Compatible Decidim versions |
41
59
  |---|---|
42
- | 1.0.x | 0.18.x - 0.21.x |
43
- | 1.1.x | 0.22.x, 0.23.x |
60
+ | 1.3.x | 0.25.x, 0.26.x |
44
61
  | 1.2.x | 0.24.x |
62
+ | 1.1.x | 0.22.x, 0.23.x |
63
+ | 1.0.x | 0.18.x - 0.21.x |
45
64
 
46
65
  ## Contributing
47
66
 
data/Rakefile CHANGED
@@ -9,6 +9,12 @@ def install_module(path)
9
9
  end
10
10
  end
11
11
 
12
+ def override_webpacker_config_files(path)
13
+ Dir.chdir(path) do
14
+ system("bundle exec rake decidim_navigation_maps:webpacker:install")
15
+ end
16
+ end
17
+
12
18
  def seed_db(path)
13
19
  Dir.chdir(path) do
14
20
  system("bundle exec rake db:seed")
@@ -25,6 +31,7 @@ desc "Generates a dummy app for testing"
25
31
  task test_app: "decidim:generate_external_test_app" do
26
32
  ENV["RAILS_ENV"] = "test"
27
33
  install_module("spec/decidim_dummy_app")
34
+ override_webpacker_config_files("spec/decidim_dummy_app")
28
35
  end
29
36
 
30
37
  desc "Generates a development app"
@@ -42,6 +49,7 @@ task :development_app do
42
49
  end
43
50
 
44
51
  install_module("development_app")
52
+ override_webpacker_config_files("development_app")
45
53
  ENV["SKIP_MODULE_SEEDS"] = "1"
46
54
  seed_db("development_app")
47
55
  # manually seed navigation maps to ensure participatory process groups are created
@@ -2,7 +2,7 @@
2
2
  <div class="tabs-content admin" data-tabs-content="navigation_maps-tabs">
3
3
  <% tabs.each_with_index do |item, index| %>
4
4
  <div class="tabs-panel<%= " is-active" if index.zero? %>" id="map<%= index %>">
5
- <%= content_tag(:div, "", id: "navigation_maps-map-#{item.id}", class: "map", style: "height: #{item.height}px", data: { id: item.id, image: item.image.url, blueprint: item.blueprint }) %>
5
+ <%= content_tag(:div, "", id: "navigation_maps-map-#{item.id}", class: "map", style: "height: #{item.height}px", data: { id: item.id, image: image_path(item.image), blueprint: item.blueprint }) %>
6
6
  </div>
7
7
  <% end %>
8
8
  </div>
@@ -1,4 +1,4 @@
1
- <%= stylesheet_link_tag "decidim/navigation_maps/navigation_maps" %>
1
+ <%= stylesheet_pack_tag "decidim_navigation_maps" %>
2
2
  <%= render partial: "styles", locals: { blueprints: valid_blueprints } %>
3
3
 
4
4
  <section<%= class_tag(section_classes) %>>
@@ -18,4 +18,4 @@
18
18
  </section>
19
19
 
20
20
  <%= render partial: "template" %>
21
- <%= javascript_include_tag "decidim/navigation_maps/navigation_maps" %>
21
+ <%= javascript_pack_tag "decidim_navigation_maps" %>
@@ -42,6 +42,11 @@ module Decidim
42
42
 
43
43
  " class=\"#{class_string}\""
44
44
  end
45
+
46
+ def image_path(image, options = {})
47
+ options.merge!({ only_path: true })
48
+ Rails.application.routes.url_helpers.rails_blob_url(image, options)
49
+ end
45
50
  end
46
51
  end
47
52
  end
@@ -1,5 +1,5 @@
1
1
  <ul class="accordion" data-accordion>
2
- <li class="accordion-item<%= " is-active" unless form.image? %>" data-accordion-item>
2
+ <li class="accordion-item<%= " is-active" unless image?(form) %>" data-accordion-item>
3
3
  <a href="#" class="accordion-title"><%= t("navigation_map_settings_form.info", scope: "decidim.navigation_maps.content_blocks") %></a>
4
4
 
5
5
  <div class="accordion-content" data-tab-content>
@@ -9,7 +9,7 @@
9
9
 
10
10
  <%= label_tag t("navigation_map_settings_form.blueprint_image", scope: "decidim.navigation_maps.content_blocks") %>
11
11
  <div class="thumbnail">
12
- <%= image_tag form.image.thumbnail.url if form.image? %>
12
+ <%= image_tag image_path(form.image, variant: :thumbnail) if image?(form) %>
13
13
  </div>
14
14
  <%= file_field_tag "blueprints[#{form.ident}][image]", { id: "blueprints_#{form.ident}_image", label: t("navigation_map_settings_form.title", scope: "decidim.navigation_maps.content_blocks") } %>
15
15
 
@@ -25,12 +25,12 @@
25
25
  </div>
26
26
  </li>
27
27
 
28
- <% if form.image? %>
28
+ <% if image?(form) %>
29
29
  <li class="accordion-item is-active" data-accordion-item>
30
30
  <a href="#" class="accordion-title"><%= t("navigation_map_settings_form.editor", scope: "decidim.navigation_maps.content_blocks") %></a>
31
31
  <div class="accordion-content navigation_maps-content" data-tab-content>
32
32
 
33
- <%= content_tag(:div, "", id: "navigation_maps-map-#{form.ident}", class: "map", data: { id: form.ident, image: form.image.url, blueprint: form.blueprint }) %>
33
+ <%= content_tag(:div, "", id: "navigation_maps-map-#{form.ident}", class: "map", data: { id: form.ident, image: image_path(form.image), blueprint: form.blueprint }) %>
34
34
 
35
35
  </div>
36
36
  </li>
@@ -1,4 +1,3 @@
1
-
2
1
  <div class="tabs-content" data-tabs-content="navigation_maps-tabs">
3
2
  <% tabs.each_with_index do |item, index| %>
4
3
  <div class="tabs-panel<%= " is-active" if index.zero? %>" id="map<%= index %>">
@@ -33,5 +33,7 @@
33
33
  </div>
34
34
 
35
35
  <%= render partial: "modal" %>
36
- <%= javascript_include_tag "decidim/navigation_maps/admin/navigation_maps" %>
37
- <%= stylesheet_link_tag "decidim/navigation_maps/admin/navigation_maps" %>
36
+ <%# we need to rely on a CDN due a bug in webpacker that renders twice jquery if importing a plugin %>
37
+ <%= javascript_include_tag "https://cdnjs.cloudflare.com/ajax/libs/jquery.form/4.3.0/jquery.form.min.js", integrity: "sha384-qlmct0AOBiA2VPZkMY3+2WqkHtIQ9lSdAsAn5RUJD/3vA5MKDgSGcdmIv4ycVxyn", crossorigin: "anonymous" %>
38
+ <%= javascript_pack_tag "decidim_admin_navigation_maps" %>
39
+ <%= stylesheet_pack_tag "decidim_admin_navigation_maps" %>
@@ -26,6 +26,15 @@ module Decidim
26
26
  def label
27
27
  I18n.t("decidim.content_blocks.html.html_content")
28
28
  end
29
+
30
+ def image?(frm)
31
+ frm.image.attached?
32
+ end
33
+
34
+ def image_path(image, options = {})
35
+ options.merge!({ only_path: true })
36
+ Rails.application.routes.url_helpers.rails_blob_url(image, options)
37
+ end
29
38
  end
30
39
  end
31
40
  end
@@ -13,7 +13,7 @@ module Decidim
13
13
  end
14
14
 
15
15
  def show
16
- render json: organization_blueprints.find(params[:id])
16
+ render json: organization_blueprints.find(params[:id]).to_json(except: :image)
17
17
  end
18
18
 
19
19
  def create
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module NavigationMaps
5
+ class MigrateLegacyImagesJob < ApplicationJob
6
+ queue_as :default
7
+
8
+ def perform(organization_id, mappings = [], logger = Rails.logger)
9
+ @organization_id = organization_id
10
+ @routes_mappings = mappings
11
+ @logger = logger
12
+
13
+ migrate_all!
14
+ end
15
+
16
+ private
17
+
18
+ attr_reader :routes_mappings, :logger
19
+
20
+ def migrate_all!
21
+ Decidim::CarrierWaveMigratorService.migrate_attachment!(
22
+ klass: Decidim::NavigationMaps::BluePrint,
23
+ cw_attribute: "image",
24
+ cw_uploader: Decidim::NavigationMaps::Cw::ImageUploader,
25
+ as_attribute: "image",
26
+ logger: @logger,
27
+ routes_mappings: routes_mappings
28
+ )
29
+ end
30
+ end
31
+ end
32
+ end
@@ -4,6 +4,8 @@ module Decidim
4
4
  module NavigationMaps
5
5
  # Abstract class from which all models in this engine inherit.
6
6
  class Blueprint < ApplicationRecord
7
+ include Decidim::HasUploadValidations
8
+
7
9
  self.table_name = "decidim_navigation_maps_blueprints"
8
10
 
9
11
  attribute :height, :integer, default: 475
@@ -16,11 +18,10 @@ module Decidim
16
18
  dependent: :destroy
17
19
 
18
20
  validates :organization, presence: true
19
- validates :image,
20
- file_content_type: { allow: ["image/jpeg", "image/png", "image/svg+xml"] }
21
21
  validates :height, numericality: { greater_than: 0 }
22
22
 
23
- mount_uploader :image, Decidim::NavigationMaps::BlueprintUploader
23
+ has_one_attached :image
24
+ validates_upload :image, uploader: Decidim::NavigationMaps::BlueprintUploader
24
25
 
25
26
  def blueprint
26
27
  areas.map { |area| [area.area_id.to_s, area.to_geoson] }.to_h
@@ -0,0 +1,4 @@
1
+ import "src/decidim/navigation_maps/admin/navigation_maps.js"
2
+
3
+ // CSS
4
+ import "entrypoints/decidim_admin_navigation_maps.scss";
@@ -0,0 +1 @@
1
+ @import "stylesheets/decidim/navigation_maps/admin/navigation_maps";
@@ -0,0 +1,7 @@
1
+ import "src/decidim/navigation_maps/navigation_maps.js"
2
+
3
+ // Images
4
+ require.context("../images", true)
5
+
6
+ // CSS
7
+ import "entrypoints/decidim_navigation_maps.scss";
@@ -0,0 +1 @@
1
+ @import "stylesheets/decidim/navigation_maps/navigation_maps";
@@ -0,0 +1,83 @@
1
+ // Creates a map
2
+ import NavigationMapView from "src/decidim/navigation_maps/map_view.js";
3
+
4
+ export default class NavigationMapEditor extends NavigationMapView {
5
+ constructor(map_object, table_object) {
6
+ // Call constructor of superclass to initialize superclass-derived members.
7
+ super(map_object, () => {
8
+ this.createControls();
9
+ if (this.blueprint) {
10
+ this.createAreas();
11
+ }
12
+ });
13
+ this.table_object = table_object;
14
+ this.createAreaCallback = function () {};
15
+ this.editAreaCallback = function () {};
16
+ this.removeAreaCallback = function () {};
17
+ }
18
+
19
+ createControls() {
20
+ this.map.pm.addControls({
21
+ position: "topleft",
22
+ drawCircle: false,
23
+ drawMarker: false,
24
+ drawCircleMarker: false,
25
+ drawPolyline: false,
26
+ cutPolygon: false
27
+ });
28
+
29
+ this.map.on("pm:create", (e) => {
30
+ let geojson = e.layer.toGeoJSON();
31
+ this.blueprint[e.layer._leaflet_id] = geojson;
32
+ this.attachEditorEvents(e.layer);
33
+ this.createAreaCallback(e.layer._leaflet_id, e.layer, this);
34
+ });
35
+
36
+ this.map.on("pm:remove", (e) => {
37
+ delete this.blueprint[e.layer._leaflet_id];
38
+ this.removeAreaCallback(e.layer._leaflet_id, e.layer, this);
39
+ });
40
+ };
41
+
42
+ editing() {
43
+ let pm = this.map.pm;
44
+ return pm.globalRemovalEnabled() || pm.globalDragModeEnabled() || pm.globalEditEnabled();
45
+ };
46
+
47
+ // register callback to handle area edits,removals and creations
48
+ onCreateArea(callback) {
49
+ this.createAreaCallback = callback;
50
+ };
51
+ onEditArea(callback) {
52
+ this.editAreaCallback = callback;
53
+ };
54
+ onRemoveArea(callback) {
55
+ this.removeCreateCallback = callback;
56
+ };
57
+
58
+ attachEditorEvents (layer) {
59
+ layer.on("mouseover", (e) => {
60
+ e.target.getElement().classList.add("selected")
61
+ });
62
+
63
+ layer.on("mouseout", (e) => {
64
+ e.target.getElement().classList.remove("selected")
65
+ });
66
+
67
+ layer.on("pm:edit", (e) => {
68
+ this.blueprint[e.target._leaflet_id] = e.target.toGeoJSON();
69
+ this.editAreaCallback(e.target._leaflet_id, e.target, this);
70
+ });
71
+
72
+ layer.on("click", (e) => {
73
+ if (!this.editing()) {
74
+ this.clickAreaCallback(e.target._leaflet_id, e.target, this);
75
+ }
76
+ });
77
+ };
78
+
79
+ getBlueprint () {
80
+ return this.blueprint;
81
+ };
82
+
83
+ }
@@ -0,0 +1,154 @@
1
+ // Place all the behaviors and hooks related to the matching controller here.
2
+ // All this logic will automatically be available in application.js.
3
+ // import "jquery-form"; // we use a CDN instead due a bug in webpacker
4
+ import NavigationMapEditor from "src/decidim/navigation_maps/admin/map_editor.js";
5
+
6
+ $(() => {
7
+
8
+ let $maps = $(".navigation_maps.admin .map");
9
+ let $progress = $(".navigation_maps.admin .progress");
10
+ let $bar = $(".navigation_maps.admin .progress-meter");
11
+ let $loading = $(".navigation_maps.admin .loading");
12
+ let $callout = $(".navigation_maps.admin .callout");
13
+ let $modal = $("#mapEditModal");
14
+ let $form = $("form");
15
+ let $tabs = $("#navigation_maps-tabs");
16
+ let $accordion = $(".navigation_maps.admin .accordion");
17
+ let editors = {};
18
+ let new_areas = {};
19
+
20
+ $maps.each((_i, el) => {
21
+ let id = $(el).data("id");
22
+ let table = document.getElementById(`navigation_maps-table-${id}`);
23
+ editors[id] = new NavigationMapEditor(el, table);
24
+ editors[id].onCreateArea((area_id) => {
25
+ new_areas[area_id] = true;
26
+ });
27
+
28
+ editors[id].onClickArea((area_id, area) => {
29
+ $modal.find(".modal-content").html("");
30
+ $modal.addClass("loading").foundation("open");
31
+ $callout.hide();
32
+ $callout.removeClass("alert success");
33
+ // "new" form insted of editing
34
+ let rel = new_areas[area_id]
35
+ ? "new"
36
+ : area_id;
37
+ $modal.find(".modal-content").load(`/admin/navigation_maps/blueprints/${id}/areas/${rel}`, () => {
38
+ let $input1 = $modal.find('input[name="blueprint_area[area_id]"]');
39
+ let $input2 = $modal.find('input[name="blueprint_area[area_type]"]');
40
+ let $input3 = $modal.find('input[name="blueprint_area[area]"]');
41
+ let a = area.toGeoJSON();
42
+ $modal.removeClass("loading");
43
+ if ($input1.length) {
44
+ $input1.val(area_id);
45
+ }
46
+ if ($input2.length) {
47
+ $input2.val(a.type);
48
+ }
49
+ if ($input3.length) {
50
+ $input3.val(JSON.stringify(a));
51
+ }
52
+ $modal.find("ul[data-tabs=true]").each(() => {
53
+ new Foundation.Tabs($(el)); // eslint-disable-line
54
+ });
55
+ });
56
+ });
57
+ });
58
+
59
+ // Rails AJAX events
60
+ document.body.addEventListener("ajax:error", (responseText) => {
61
+ $callout.contents("p").html(`${responseText.detail[0].message}: <strong>${responseText.detail[0].error}</strong>`);
62
+ $callout.addClass("alert");
63
+ });
64
+
65
+ document.body.addEventListener("ajax:success", (responseText) => {
66
+ if (new_areas[responseText.detail[0].area]) {
67
+ delete new_areas[responseText.detail[0].area]
68
+ }
69
+ let blueprint_id = responseText.detail[0].blueprint_id;
70
+ let area_id = responseText.detail[0].area_id;
71
+ let area = responseText.detail[0].area;
72
+ editors[blueprint_id].setLayerProperties(editors[blueprint_id].map._layers[area_id], area);
73
+ editors[blueprint_id].blueprint[area_id] = area;
74
+ $callout.contents("p").html(responseText.detail[0].message);
75
+ $callout.addClass("success");
76
+ });
77
+
78
+ document.body.addEventListener("ajax:complete", () => {
79
+ $callout.show();
80
+ $modal.foundation("close");
81
+ })
82
+
83
+ $tabs.on("change.zf.tabs", (e, $tab, $content) => {
84
+ let id = $content.find(".map").data("id");
85
+ if (id) {
86
+ editors[id].reload();
87
+ }
88
+ });
89
+
90
+ $accordion.on("down.zf.accordion", () => {
91
+ let id = $accordion.find(".map").data("id");
92
+ if (id) {
93
+ editors[id].reload();
94
+ }
95
+ });
96
+
97
+ // If a new item si going to be created o the image is changed a reload is needed
98
+ let needsReload = () => {
99
+ let reload = false;
100
+ if ($form.find("#map-new input:checked").length) {
101
+ return true;
102
+ }
103
+ if ($form.find(".delete-tab input[type=checkbox]:checked").length) {
104
+ return true;
105
+ }
106
+
107
+ $form.find("input[type=file],input[tabs_id=blueprints___title]").each((_i, el) => {
108
+ if ($(el).val()) {
109
+ reload = true;
110
+ return false;
111
+ }
112
+ });
113
+ return reload;
114
+ };
115
+
116
+ $form.ajaxForm({
117
+ url: $form.find("[name=action]").val(),
118
+ beforeSerialize: () => {
119
+ Object.keys(editors).forEach((key) => {
120
+ let editor = editors[key];
121
+ $(`#blueprints_${editor.id}_blueprint`).val(JSON.stringify(editor.getBlueprint()));
122
+ });
123
+ },
124
+ beforeSend: () => {
125
+ let percentVal = "0%";
126
+ $bar.width(percentVal).html(percentVal);
127
+ $progress.show();
128
+ $callout.hide();
129
+ $callout.removeClass("alert success");
130
+ $loading.show();
131
+ },
132
+ uploadProgress: (event, position, total, percentComplete) => { // eslint-disable-line
133
+ let percentVal = `${percentComplete}%`;
134
+ $bar.width(percentVal).html(percentVal);
135
+ },
136
+ success: (responseText) => {
137
+ $callout.show();
138
+ $progress.hide();
139
+ $callout.contents("p").html(responseText);
140
+ $callout.addClass("success");
141
+ $loading.hide();
142
+ if (needsReload()) {
143
+ $loading.show();
144
+ location.reload();
145
+ }
146
+ },
147
+ error: (xhr) => {
148
+ $loading.hide();
149
+ $callout.show();
150
+ $callout.contents("p").html(xhr.responseText);
151
+ $callout.addClass("alert");
152
+ }
153
+ });
154
+ });
@@ -0,0 +1,125 @@
1
+ // Creates a map view
2
+ import "leaflet";
3
+ import "leaflet/dist/leaflet.css";
4
+ import "@geoman-io/leaflet-geoman-free";
5
+ import "@geoman-io/leaflet-geoman-free/dist/leaflet-geoman.css";
6
+
7
+ export default class NavigationMapView {
8
+ constructor(map_object, imageDecorator) {
9
+ this.features = {};
10
+ this.map_object = map_object;
11
+ this.id = map_object.dataset.id;
12
+ this.image_path = map_object.dataset.image;
13
+ this.blueprint = map_object.dataset.blueprint
14
+ ? JSON.parse(map_object.dataset.blueprint)
15
+ : {};
16
+ this.image = new Image();
17
+ this.image.onload = () => {
18
+ this.createMap();
19
+ if (typeof imageDecorator === "function") {
20
+ imageDecorator(this);
21
+ } else if (this.blueprint) {
22
+ this.createAreas();
23
+ }
24
+ };
25
+ this.image.src = this.image_path;
26
+ this.clickAreaCallback = () => {};
27
+ this.setLayerPropertiesCallback = () => {};
28
+ }
29
+
30
+ createMap() {
31
+ let bounds = [[0, 0], [this.image.height, this.image.width]];
32
+ this.map = L.map(this.map_object, {
33
+ minZoom: -1,
34
+ maxZoom: 2,
35
+ crs: L.CRS.Simple,
36
+ noWrap: true,
37
+ zoomSnap: 0,
38
+ // zoomDelta: 0.1,
39
+ maxBounds: [[0, 0], [this.image.height, this.image.width]],
40
+ center: [this.image.height / 2, this.image.width / 2],
41
+ zoom: -1,
42
+ scrollWheelZoom: false,
43
+ attributionControl: false
44
+ });
45
+
46
+ L.imageOverlay(this.image.src, bounds).addTo(this.map);
47
+ this.fitBounds();
48
+ };
49
+
50
+ fitBounds() {
51
+ let image_ratio = this.image.height / this.image.width;
52
+ let map_ratio = this.map_object.offsetHeight / this.map_object.offsetWidth;
53
+
54
+ if (image_ratio > map_ratio) {
55
+ this.map.fitBounds([[0, 0], [0, this.image.width]]);
56
+ }
57
+ else {
58
+ this.map.fitBounds([[0, 0], [this.image.height, 0]]);
59
+ }
60
+ this.map.setView([this.image.height / 2, this.image.width / 2]);
61
+ };
62
+
63
+ createAreas() {
64
+ this.forEachBlueprint((id, geoarea) => {
65
+ new L.GeoJSON(geoarea, {
66
+ onEachFeature: (feature, layer) => {
67
+ layer._leaflet_id = id;
68
+ this.setLayerProperties(layer, geoarea);
69
+ this.attachEditorEvents(layer);
70
+ }
71
+ }).addTo(this.map);
72
+ });
73
+ };
74
+
75
+ setLayerProperties (layer, area) {
76
+ let props = area.properties;
77
+ if (props) {
78
+ if (props.color) {
79
+ layer.setStyle({fillColor: props.color, color: props.color});
80
+ }
81
+ this.setLayerPropertiesCallback(layer, props);
82
+ }
83
+ };
84
+
85
+ attachEditorEvents (layer) {
86
+ layer.on("mouseover", (e) => {
87
+ e.target.getElement().classList.add("selected")
88
+ });
89
+
90
+ layer.on("mouseout", (e) => {
91
+ e.target.getElement().classList.remove("selected")
92
+ });
93
+
94
+ layer.on("click", (e) => {
95
+ this.clickAreaCallback(e.target, this);
96
+ });
97
+ };
98
+
99
+ // register callback to handle area clicks
100
+ onClickArea(callback) {
101
+ this.clickAreaCallback = callback;
102
+ };
103
+
104
+ onSetLayerProperties(callback) {
105
+ this.setLayerPropertiesCallback = callback;
106
+ };
107
+
108
+ forEachBlueprint (decorator) {
109
+ for (let id in this.blueprint) {
110
+ let geoarea = this.blueprint[id];
111
+ // avoid non-polygons for the moment
112
+ if (geoarea.geometry && geoarea.geometry.type === "Polygon") {
113
+ decorator(id, geoarea);
114
+ }
115
+ }
116
+ };
117
+
118
+ reload () {
119
+ if (this.map) {
120
+ this.map.invalidateSize(true);
121
+ this.fitBounds();
122
+ }
123
+ };
124
+ }
125
+