Preserve in progress note comments when the map is moved
[rails.git] / app / assets / javascripts / index / notes.js.erb
1 //= require templates/notes/show
2 //= require templates/notes/new
3
4 $(document).ready(function () {
5   var params = OSM.mapParams();
6
7   var noteIcons = {
8     "new": L.icon({
9       iconUrl: "<%= image_path 'new_note_marker.png' %>",
10       iconSize: [22, 22],
11       iconAnchor: [11, 11]
12     }),
13     "open": L.icon({
14       iconUrl: "<%= image_path 'open_note_marker.png' %>",
15       iconSize: [22, 22],
16       iconAnchor: [11, 11]
17     }),
18     "closed": L.icon({
19       iconUrl: "<%= image_path 'closed_note_marker.png' %>",
20       iconSize: [22, 22],
21       iconAnchor: [11, 11]
22     })
23   };
24
25   var noteLayer = new L.LayerGroup();
26   var notes = {};
27   var newNote;
28
29   layers.push({
30     layer: noteLayer,
31     layerCode: "N"
32   });
33
34   map.on("layeradd", function (e) {
35     if (e.layer == noteLayer) {
36       loadNotes();
37       map.on("moveend", loadNotes);
38     }
39   });
40
41   map.on("layerremove", function (e) {
42     if (e.layer == noteLayer) {
43       map.off("moveend", loadNotes);
44       noteLayer.clearLayers();
45       notes = {};
46     }
47   });
48
49   // Don't focus the text area on touch devices to avoid flashing the keyboard
50   if (!('ontouchstart' in document.documentElement)) {
51     map.on("popupopen", function (e) {
52       $(e.popup._container).find(".comment").focus();
53     });
54   }
55
56   map.on("popupclose", function (e) {
57     if (newNote && e.popup == newNote._popup) {
58       $(newNote).oneTime(10, "removenote", function () {
59         map.removeLayer(newNote);
60         newNote = null;
61       });
62     }
63   });
64
65   if (OSM.STATUS != 'api_offline' && OSM.STATUS != 'database_offline') {
66     map.layersControl.addOverlay(noteLayer, I18n.t("browse.start_rjs.notes_layer_name"));
67
68     if (params.layers) setMapLayers(params.layers);
69     if (params.notes) map.addLayer(noteLayer);
70
71     if (params.note) {
72       $.ajax({
73         url: "/api/" + OSM.API_VERSION + "/notes/" + params.note + ".json",
74         success: function (feature) {
75           var marker = updateMarker(notes[feature.properties.id], feature);
76
77           notes[feature.properties.id] = marker;
78
79           map.addLayer(noteLayer);
80           marker.openPopup();
81         }
82       });
83     }
84   }
85
86   function updateMarker(marker, feature) {
87     if (marker)
88     {
89       marker.setIcon(noteIcons[feature.properties.status]);
90       marker._popup.setContent(createPopupContent(
91         marker, feature.properties, 
92         $(marker._popup._content).find("textarea").val()
93       ));
94     }
95     else
96     {
97       marker = L.marker(feature.geometry.coordinates.reverse(), {
98         icon: noteIcons[feature.properties.status],
99         opacity: 0.7
100       });
101
102       marker.addTo(noteLayer).bindPopup(
103         createPopupContent(marker, feature.properties),
104         popupOptions()
105       );
106     }
107
108     return marker;
109   }
110
111   var noteLoader;
112
113   function loadNotes() {
114     var bounds = map.getBounds();
115     var size = bounds.getSize();
116
117     if (size <= OSM.MAX_NOTE_REQUEST_AREA) {
118       var url = "/api/" + OSM.API_VERSION + "/notes.json?bbox=" + bounds.toBBOX();
119
120       if (noteLoader) noteLoader.abort();
121
122       noteLoader = $.ajax({
123         url: url,
124         success: function (json) {
125           var oldNotes = notes;
126
127           notes = {};
128
129           json.features.forEach(function (feature) {
130             var marker = oldNotes[feature.properties.id];
131
132             delete oldNotes[feature.properties.id];
133
134             notes[feature.properties.id] = updateMarker(marker, feature);
135           });
136
137           for (id in oldNotes) {
138             noteLayer.removeLayer(oldNotes[id]);
139           }
140
141           noteLoader = null;
142         }
143       });
144     }
145   };
146
147   function popupOptions() {
148     var mapSize = map.getSize();
149
150     return {
151       minWidth: 320,
152       maxWidth: mapSize.y * 1 / 3,
153       maxHeight: mapSize.y * 2 / 3,
154       offset: new L.Point(0, -3),
155       autoPanPadding: new L.Point(60, 40)
156     };
157   }
158
159   function createPopupContent(marker, properties, comment) {
160     var content = $(JST["templates/notes/show"]({ note: properties }));
161
162     content.find("textarea").on("input", function (e) {
163       var form = e.target.form;
164
165       if ($(e.target).val() == "") {
166         $(form.close).val(I18n.t("javascripts.notes.show.resolve"));
167         $(form.comment).prop("disabled", true);
168       } else {
169         $(form.close).val(I18n.t("javascripts.notes.show.comment_and_resolve"));
170         $(form.comment).prop("disabled", false);
171       }
172     });
173
174     content.find("input[type=submit]").on("click", function (e) {
175       e.preventDefault();
176       var data = $(e.target).data();
177       updateNote(marker, e.target.form, data.method, data.url);
178     });
179
180     if (comment) {
181       content.find("textarea").val(comment).trigger("input");
182     }
183
184     return content[0];
185   }
186
187   function createNote(marker, form, url) {
188     var location = marker.getLatLng();
189
190     marker.options.draggable = false;
191     marker.dragging.disable();
192
193     $(form).find("input[type=submit]").prop("disabled", true);
194
195     $.ajax({
196       url: url,
197       type: "POST",
198       oauth: true,
199       data: {
200         lat: location.lat,
201         lon: location.lng,
202         text: $(form.text).val()
203       },
204       success: function (feature) {
205         notes[feature.properties.id] = updateMarker(marker, feature);
206         newNote = null;
207
208         $("#createnoteanchor").removeClass("disabled").addClass("geolink");
209       }
210     });
211   }
212
213   function updateNote(marker, form, method, url) {
214     $(form).find("input[type=submit]").prop("disabled", true);
215
216     $.ajax({
217       url: url,
218       type: method,
219       oauth: true,
220       data: {
221         text: $(form.text).val()
222       },
223       success: function (feature) {
224         if (feature.properties.status == "hidden") {
225           noteLayer.removeLayer(marker);
226
227           delete notes[feature.properties.id];
228         } else {
229           var popupContent = createPopupContent(marker, feature.properties);
230
231           marker.setIcon(noteIcons[feature.properties.status]);
232           marker._popup.setContent(popupContent);
233         }
234       }
235     });
236   }
237
238   $(".leaflet-control-attribution").on("click", "#createnoteanchor", function (e) {
239     e.preventDefault();
240
241     if ($(e.target).hasClass("disabled")) return;
242
243     $(e.target).removeClass("geolink").addClass("disabled");
244
245     map.addLayer(noteLayer);
246
247     var mapSize = map.getSize();
248     var markerPosition;
249
250     if (mapSize.y > 800)
251     {
252       markerPosition = [mapSize.x / 2, mapSize.y / 2];
253     }
254     else if (mapSize.y > 400)
255     {
256       markerPosition = [mapSize.x / 2, 400];
257     }
258     else
259     {
260       markerPosition = [mapSize.x / 2, mapSize.y];
261     }
262
263     newNote = L.marker(map.containerPointToLatLng(markerPosition), {
264       icon: noteIcons["new"],
265       opacity: 0.7,
266       draggable: true
267     });
268
269     var popupContent = $(JST["templates/notes/new"]({ create_url: $(e.target).attr("href") }));
270
271     popupContent.find("textarea").on("input", function (e) {
272       var form = e.target.form;
273
274       if ($(e.target).val() == "") {
275         $(form.add).prop("disabled", true);
276       } else {
277         $(form.add).prop("disabled", false);
278       }
279     });
280
281     popupContent.find("input[type=submit]").on("click", function (e) {
282       e.preventDefault();
283       createNote(newNote, e.target.form, $(e.target).data("url"));
284     });
285
286     newNote.addTo(noteLayer).bindPopup(popupContent[0], popupOptions()).openPopup();
287
288     newNote.on("remove", function (e) {
289       $("#createnoteanchor").removeClass("disabled").addClass("geolink");
290     });
291
292     newNote.on("dragstart", function (e) {
293       $(newNote).stopTime("removenote");
294     });
295
296     newNote.on("dragend", function (e) {
297       e.target.openPopup();
298     });
299   });
300 });