]> git.openstreetmap.org Git - rails.git/blob - app/assets/javascripts/index/new_note.js
Replace marker shadow image with vector graphic
[rails.git] / app / assets / javascripts / index / new_note.js
1 OSM.initializations.push(function () {
2   $(".control-note .control-button").on("click", function (e) {
3     e.preventDefault();
4     e.stopPropagation();
5
6     if ($(this).hasClass("disabled")) return;
7
8     OSM.router.route("/note/new");
9   });
10 });
11
12 OSM.NewNote = function (map) {
13   const noteLayer = map.noteLayer,
14         content = $("#sidebar_content"),
15         page = {},
16         control = $(".control-note"),
17         addNoteButton = control.find(".control-button");
18   let newNoteMarker,
19       halo,
20       errorPanel,
21       errorPanelDetail;
22
23   function createNote(location, text) {
24     return fetch("/api/0.6/notes.json", {
25       method: "POST",
26       headers: { ...OSM.oauth },
27       body: new URLSearchParams({
28         lat: location.lat,
29         lon: location.lng,
30         text
31       })
32     })
33       .then(resp => {
34         if (resp.ok) return resp.json();
35         throw new Error(`Got response with status ${resp.status} ${resp.statusText}`);
36       });
37   }
38
39   function addCreatedNoteMarker(feature) {
40     const marker = L.marker(feature.geometry.coordinates.reverse(), {
41       icon: OSM.noteMarkers[feature.properties.status],
42       opacity: 0.9,
43       interactive: true
44     });
45     marker.id = feature.properties.id;
46     marker.addTo(noteLayer);
47   }
48
49   function addHalo(latlng) {
50     if (halo) map.removeLayer(halo);
51
52     halo = L.circleMarker(latlng, {
53       weight: 2.5,
54       radius: 20,
55       fillOpacity: 0.5,
56       color: "#FF6200"
57     });
58
59     map.addLayer(halo);
60   }
61
62   function removeHalo() {
63     if (halo) map.removeLayer(halo);
64     halo = null;
65   }
66
67   function addNewNoteMarker(latlng) {
68     if (newNoteMarker) map.removeLayer(newNoteMarker);
69
70     newNoteMarker = L.marker(latlng, {
71       icon: OSM.noteMarkers.new,
72       opacity: 0.9,
73       draggable: true
74     });
75
76     newNoteMarker.on("dragstart dragend", function (a) {
77       removeHalo();
78       if (a.type === "dragend") {
79         addHalo(newNoteMarker.getLatLng());
80       }
81     });
82
83     newNoteMarker.addTo(map);
84     addHalo(newNoteMarker.getLatLng());
85
86     newNoteMarker.on("dragend", function () {
87       content.find("textarea").trigger("focus");
88     });
89   }
90
91   function removeNewNoteMarker() {
92     removeHalo();
93     if (newNoteMarker) map.removeLayer(newNoteMarker);
94     newNoteMarker = null;
95   }
96
97   function moveNewNoteMarkerToClick(e) {
98     if (newNoteMarker) newNoteMarker.setLatLng(e.latlng);
99     if (halo) halo.setLatLng(e.latlng);
100     content.find("textarea").trigger("focus");
101   }
102
103   function updateControls() {
104     const zoomedOut = addNoteButton.hasClass("disabled");
105     const withoutText = content.find("textarea").val() === "";
106
107     content.find("#new-note-zoom-warning").prop("hidden", !zoomedOut);
108     content.find("input[type=submit]").prop("disabled", zoomedOut || withoutText);
109     if (newNoteMarker) newNoteMarker.setOpacity(zoomedOut ? 0.5 : 0.9);
110   }
111
112   page.pushstate = page.popstate = function (path) {
113     OSM.loadSidebarContent(path, function () {
114       page.load(path);
115     });
116   };
117
118   page.load = function (path) {
119     control.addClass("active");
120
121     map.addLayer(noteLayer);
122
123     const params = new URLSearchParams(path.substring(path.indexOf("?")));
124     let markerLatlng;
125
126     if (params.has("lat") && params.has("lon")) {
127       markerLatlng = { lat: params.get("lat"), lng: params.get("lon") };
128     } else {
129       markerLatlng = map.getCenter();
130     }
131
132     map.panInside(markerLatlng, {
133       padding: [50, 50]
134     });
135
136     addNewNoteMarker(markerLatlng);
137
138     content.find("textarea")
139       .on("input", updateControls)
140       .attr("readonly", "readonly") // avoid virtual keyboard popping up on focus
141       .trigger("focus")
142       .removeAttr("readonly");
143
144     content.find("input[type=submit]").on("click", function (e) {
145       const location = newNoteMarker.getLatLng().wrap();
146       const text = content.find("textarea").val();
147
148       errorPanel = content.find(".new-note-error");
149       errorPanel.addClass("d-none");
150       errorPanelDetail = errorPanel.find(".new-note-error-detail");
151
152       e.preventDefault();
153       $(this).prop("disabled", true);
154       newNoteMarker.options.draggable = false;
155       newNoteMarker.dragging.disable();
156
157       createNote(location, text)
158         .then(feature => {
159           if (typeof OSM.user === "undefined") {
160             const anonymousNotesCount = Number(OSM.cookies.get("_osm_anonymous_notes_count")) || 0;
161             OSM.cookies.set("_osm_anonymous_notes_count", anonymousNotesCount + 1, { expires: 14 });
162           }
163           content.find("textarea").val("");
164           addCreatedNoteMarker(feature);
165           OSM.router.route("/note/" + feature.properties.id);
166         })
167         .catch(err => {
168           errorPanel.removeClass("d-none");
169           errorPanelDetail.text(err.message || err);
170           updateControls();
171         });
172     });
173
174     map.on("click", moveNewNoteMarkerToClick);
175     addNoteButton.on("disabled enabled", updateControls);
176     updateControls();
177
178     return map.getState();
179   };
180
181   page.unload = function () {
182     map.off("click", moveNewNoteMarkerToClick);
183     addNoteButton.off("disabled enabled", updateControls);
184     removeNewNoteMarker();
185     control.removeClass("active");
186   };
187
188   return page;
189 };