Rework notes UI using leaflet
authorTom Hughes <tom@compton.nu>
Sat, 1 Dec 2012 17:46:26 +0000 (17:46 +0000)
committerTom Hughes <tom@compton.nu>
Sun, 2 Dec 2012 14:13:54 +0000 (14:13 +0000)
app/assets/javascripts/browse.js
app/assets/javascripts/index/notes.js.erb
app/assets/javascripts/map.js.erb
app/assets/javascripts/templates/notes/new.jst.ejs
app/assets/javascripts/templates/notes/show.jst.ejs
app/assets/stylesheets/common.css.scss
app/views/site/index.html.erb
config/locales/en.yml

index fb6d2d304e01383dd45da85ea188e7256370ccdd..2934b8bd92593c28fa45a2287c896f5e65d71fb3 100644 (file)
@@ -45,12 +45,11 @@ $(document).ready(function () {
     var centre = bbox.getCenter();
     updatelinks(centre.lon, centre.lat, 16, null, params.minlon, params.minlat, params.maxlon, params.maxlat);
   } else if (params.type == "note") {
-    var centre = new OpenLayers.LonLat(params.lon, params.lat);
+    map.setView([params.lat, params.lon], 16);
 
-    setMapCenter(centre, 16);
-    addMarkerToMap(centre);
+    L.marker([params.lat, params.lon], { icon: getUserIcon() }).addTo(map);
 
-    var bbox = unproj(map.getExtent());
+    var bbox = map.getBounds();
 
     $("#loading").hide();
     $("#browse_map .geolink").show();
@@ -59,7 +58,9 @@ $(document).ready(function () {
       return remoteEditHandler(bbox);
     });
 
-    updatelinks(centre.lon, centre.lat, 16, null, bbox.left, bbox.bottom, bbox.right, bbox.top)
+    updatelinks(params.lon, params.lat, 16, null, 
+                bbox.getWestLng(), bbox.getSouthLat(),
+                bbox.getEastLng(), bbox.getNorthLat());
   } else {
     $("#object_larger_map").hide();
     $("#object_edit").hide();
index 5ecaca7dd61c3f20d3db0eff4dbc4eca16f7a8a5..876aeb193d46924a670ee0175cf8881964ddcc02 100644 (file)
 
 $(document).ready(function () {
   var params = OSM.mapParams();
-  var newNotes;
 
-  function saveNewNotes(o) {
-    var layer = o.object;
-    newNotes = layer.getFeaturesByAttribute("status", "new")
-    layer.removeFeatures(newNotes, { silent: true });
-  }
-
-  function restoreNewNotes(o) {
-    var layer = o.object;
-    layer.addFeatures(newNotes);
-    newNotes = undefined;
-  }
-
-  function createNote(feature, form) {
-    var location = unproj(feature.geometry.getBounds().getCenterLonLat());
-
-    $(form).find("input[type=submit]").prop("disabled", true);
-
-    $.ajax($("#createnoteanchor").attr("href"), {
-      type: "POST",
-      data: {
-        lon: location.lon,
-        lat: location.lat,
-        text: $(form.text).val()
-      },
-      success: function (data) {
-        map.noteSelector.unselect(feature);
+  var noteIcons = {
+    "new": L.icon({
+      iconUrl: "<%= image_path 'new_note_marker.png' %>",
+      iconSize: [22, 22],
+      iconAnchor: [11, 11]
+    }),
+    "open": L.icon({
+      iconUrl: "<%= image_path 'open_note_marker.png' %>",
+      iconSize: [22, 22],
+      iconAnchor: [11, 11]
+    }),
+    "closed": L.icon({
+      iconUrl: "<%= image_path 'closed_note_marker.png' %>",
+      iconSize: [22, 22],
+      iconAnchor: [11, 11]
+    })
+  };
+
+  var noteLayer = new L.LayerGroup();
+  var notes = {};
+
+  map.on("layeradd", function (e) {
+    if (e.layer == noteLayer) {
+      loadNotes();
+      map.on("moveend", loadNotes);
+    }
+  });
 
-        feature.attributes = data.properties;
+  map.on("layerremove", function (e) {
+    if (e.layer == noteLayer) {
+      map.off("moveend", loadNotes);
+      noteLayer.clearLayers();
+    }
+  });
 
-        map.noteLayer.drawFeature(feature);
+  if (OSM.STATUS != 'api_offline' && OSM.STATUS != 'database_offline') {
+    map.layersControl.addOverlay(noteLayer, I18n.t("browse.start_rjs.notes_layer_name"));
 
-        map.noteMover.deactivate();
-      }
-    });
+    if (params.notes) map.addLayer(noteLayer);
   }
 
-  function updateNote(feature, form, close) {
-    var url = close ? feature.attributes.close_url : feature.attributes.comment_url;
-
-    $(form).find("input[type=submit]").prop("disabled", true);
-
-    $.ajax(url, {
-      type: "POST",
-      data: {
-        text: $(form.text).val()
-      },
-      success: function (data) {
-        map.noteSelector.unselect(feature)
-        
-        feature.attributes = data.properties;
-        
-        map.noteSelector.select(feature)
-      }
-    });
+  function updateMarker(marker, feature) {
+    var icon = noteIcons[feature.properties.status];
+    var popupContent = createPopupContent(marker, feature.properties);
+
+    if (marker)
+    {
+      marker.setIcon(noteIcons[feature.properties.status]);
+      marker._popup.setContent(popupContent);
+    }
+    else
+    {
+      marker = L.marker(feature.geometry.coordinates.reverse(), {
+        icon: icon,
+        opacity: 0.7
+      });
+
+      marker.addTo(noteLayer).bindPopup(popupContent);
+    }
+
+    return marker;
   }
 
-  function noteSelected(o) {
-    var feature = o.feature;
-    var location = feature.geometry.getBounds().getCenterLonLat();
-    var content;
-    var onClose;
-
-    if (feature.attributes.status === "new") {
-      content = JST["templates/notes/new"]();
+  function loadNotes() {
+    var bounds = map.getBounds();
+    var url = "/api/" + OSM.API_VERSION + "/notes.json?bbox=" + bounds.toBBOX();
 
-      onClose = function (e) {
-        feature.attributes.status = "cancelled";
+    $.ajax({
+      url: url,
+      success: function (json) {
+        var oldNotes = notes;
 
-        map.noteSelector.unselect(feature);
-        map.noteLayer.removeFeatures(feature);
+        notes = {};
 
-        feature.destroy();
+        json.features.forEach(function (feature) {
+          var marker = oldNotes[feature.properties.id];
 
-        map.noteMover.deactivate();
-      };
-    } else {
-      content = JST["templates/notes/show"]({ note: feature.attributes });
+          delete oldNotes[feature.properties.id];
 
-      onClose = function (e) {
-        map.noteSelector.unselect(feature)
-      };
-    };
+          notes[feature.properties.id] = updateMarker(marker, feature);
+        });
 
-    feature.popup = new OpenLayers.Popup.FramedCloud(
-      feature.attributes.id, location, null, content, null, true, onClose
-    );
-
-    map.addPopup(feature.popup);
-    // feature.popup.show();
+        for (id in oldNotes) {
+          noteLayer.removeLayer(oldNotes[id]);
+        }
+      }
+    });
+  };
 
-    $(feature.popup.contentDiv).find("textarea").autoGrow();
+  function createPopupContent(marker, properties) {
+    var content = $(JST["templates/notes/show"]({ note: properties }));
 
-    $(feature.popup.contentDiv).find("textarea").on("input", function (e) {
+    content.find("textarea").on("input", function (e) {
       var form = e.target.form;
 
       if ($(e.target).val() == "") {
         $(form.close).val(I18n.t("javascripts.notes.show.close"));
+        $(form.comment).prop("disabled", true);
       } else {
         $(form.close).val(I18n.t("javascripts.notes.show.comment_and_close"));
+        $(form.comment).prop("disabled", false);
       }
     });
 
-    $(feature.popup.contentDiv).find("input#note-add").click(function (e) {
+    content.find("input[type=submit]").on("click", function (e) {
       e.preventDefault();
-
-      createNote(feature, e.target.form);
+      updateNote(marker, e.target.form, $(e.target).data("url"));
     });
 
-    $(feature.popup.contentDiv).find("input#note-comment").click(function (e) {
-      e.preventDefault();
+    return content[0];
+  }
 
-      updateNote(feature, e.target.form, false);
-    });
+  function createNote(marker, form, url) {
+    var location = marker.getLatLng();
 
-    $(feature.popup.contentDiv).find("input#note-close").click(function (e) {
-      e.preventDefault();
+    $(form).find("input[type=submit]").prop("disabled", true);
 
-      updateNote(feature, e.target.form, true);
-    });
+    $.ajax({
+      url: url,
+      type: "POST",
+      data: {
+        lat: location.lat,
+        lon: location.lng,
+        text: $(form.text).val()
+      },
+      success: function (feature) {
+        notes[feature.properties.id] = updateMarker(marker, feature);
 
-    feature.popup.updateSize();
+        $(".leaflet-popup-close-button").off("click.close");
+      }
+    });
   }
 
-  function noteUnselected(o) {
-    var feature = o.feature;
+  function updateNote(marker, form, url) {
+    $(form).find("input[type=submit]").prop("disabled", true);
 
-    map.removePopup(feature.popup);
-  }
+    $.ajax({
+      url: url,
+      type: "POST",
+      data: {
+        text: $(form.text).val()
+      },
+      success: function (feature) {
+        var popupContent = createPopupContent(marker, feature.properties);
 
-  function addNote() {
-    var lonlat = map.getCenter();
-    var layer = map.noteLayer;
-    var geometry = new OpenLayers.Geometry.Point(lonlat.lon, lonlat.lat);
-    var feature = new OpenLayers.Feature.Vector(geometry, {
-      status: "new"
+        marker.setIcon(noteIcons[feature.properties.status]);
+        marker._popup.setContent(popupContent);
+      }
     });
-
-    layer.addFeatures(feature);
-    map.noteSelector.unselectAll();
-    map.noteSelector.select(feature);
-    map.noteMover.activate();
-    map.noteLayer.setVisibility(true);
   }
 
-  $("#map").on("initialised", function () {
-    map.noteLayer = new OpenLayers.Layer.Vector("Notes", {
-      visibility: params.notes,
-      displayInLayerSwitcher: false,
-      projection: new OpenLayers.Projection("EPSG:4326"),
-      styleMap: new OpenLayers.StyleMap(new OpenLayers.Style({
-        graphicWidth: 22,
-        graphicHeight: 22,
-        graphicOpacity: 0.7,
-        graphicXOffset: -11,
-        graphicYOffset: -11
-      }, {
-        rules: [
-          new OpenLayers.Rule({
-            filter: new OpenLayers.Filter.Comparison({
-              type: OpenLayers.Filter.Comparison.EQUAL_TO,
-              property: "status",
-              value: "new"
-            }),
-            symbolizer: {
-              externalGraphic: "<%= image_path 'new_note_marker.png' %>"
-            }
-          }),
-          new OpenLayers.Rule({
-            filter: new OpenLayers.Filter.Comparison({
-              type: OpenLayers.Filter.Comparison.EQUAL_TO,
-              property: "status",
-              value: "open"
-            }),
-            symbolizer: {
-              externalGraphic: "<%= image_path 'open_note_marker.png' %>"
-            }
-          }),
-          new OpenLayers.Rule({
-            filter: new OpenLayers.Filter.Comparison({
-              type: OpenLayers.Filter.Comparison.EQUAL_TO,
-              property: "status",
-              value: "closed"
-            }),
-            symbolizer: {
-              externalGraphic: "<%= image_path 'closed_note_marker.png' %>"
-            }
-          })
-        ]
-      })),
-      strategies: [
-        new OpenLayers.Strategy.BBOX()
-      ],
-      protocol: new OpenLayers.Protocol.HTTP({
-        url: $("#show_notes").attr("href"),
-        format: new OpenLayers.Format.GeoJSON()
-      })
-    });
-
-    map.noteLayer.events.register("beforefeaturesremoved", map, saveNewNotes);
-    map.noteLayer.events.register("featuresremoved", map, restoreNewNotes);
-    map.noteLayer.events.register("featureselected", map, noteSelected);
-    map.noteLayer.events.register("featureunselected", map, noteUnselected);
+  $("#createnoteanchor").click(function (e) {
+    e.preventDefault();
 
-    map.addLayer(map.noteLayer);
+    map.addLayer(noteLayer);
 
-    map.noteSelector = new OpenLayers.Control.SelectFeature(map.noteLayer, {
-      autoActivate: true
+    var marker = L.marker(map.getCenter(), {
+      icon: noteIcons["new"],
+      opacity: 0.7,
+      draggable: true
     });
 
-    map.addControl(map.noteSelector);
-
-    map.noteMover = new OpenLayers.Control.DragFeature(map.noteLayer, {
-      onDrag: function (feature, pixel) {
-        feature.popup.lonlat = feature.geometry.getBounds().getCenterLonLat();
-        feature.popup.updatePosition();
-      },
-      featureCallbacks: {
-        over: function (feature) {
-          if (feature.attributes.status === "new") {
-            map.noteMover.overFeature.apply(map.noteMover, [feature]);
-          }
-        }
-      }
-    });
-
-    map.addControl(map.noteMover);
-
-    $("#show_notes").click(function (e) {
-      map.noteLayer.setVisibility(true);
+    var popupContent = $(JST["templates/notes/new"]({ create_url: $(e.target).attr("href") }));
 
+    popupContent.find("input[type=submit]").on("click", function (e) {
       e.preventDefault();
+      createNote(marker, e.target.form, $(e.target).data("url"));
     });
 
-    $("#createnoteanchor").click(function (e) {
-      map.noteLayer.setVisibility(true);
+    marker.addTo(noteLayer).bindPopup(popupContent[0]).openPopup();
 
-      addNote();
+    $(".leaflet-popup-close-button").on("click.close", function (e) {
+      map.removeLayer(marker);
+    });
 
-      e.preventDefault();
+    marker.on("dragend", function (e) {
+      e.target.openPopup();
     });
   });
 });
index f069eee993e76d90a6842e48cabf3dd70534f521..291633f53c1c04079311a7afd4437526ea9a2d48 100644 (file)
@@ -109,8 +109,6 @@ function createMap(divName, options) {
     map.invalidateSize();
   });
 
- $("#" + divName).trigger("initialised");
-
   return map;
 }
 
index a2796a7a265af17f7768b281dfd5873ebb4bf684..2c2e77cfe2e9dad41c7aade53961bc13a0fe6246 100644 (file)
@@ -1,15 +1,12 @@
 <div class="note">
-  <p>
-    <%- I18n.t('javascripts.notes.new.intro_1') %><br/>
-    <%- I18n.t('javascripts.notes.new.intro_2') %>
-  </p>
+  <p><%- I18n.t('javascripts.notes.new.intro') %></p>
   <form action="#">
     <input type="hidden" name="lon">
     <input type="hidden" name="lat">
     <textarea name="text" cols="40" rows="10"></textarea>
     <br/>
     <div class="buttons">
-      <input type="submit" name="add" value="<%- I18n.t('javascripts.notes.new.add') %>" id="note-add">
+      <input type="submit" name="add" value="<%- I18n.t('javascripts.notes.new.add') %>" data-url="<%- create_url %>">
     </div>
   </form>
 </div>
index cf8ea0a40ce2121fffb82f590efef120d01c905e..8b95fea2e3bdd75c578b83016e0ba74e667b5760 100644 (file)
@@ -16,8 +16,8 @@
     <textarea name="text" cols="40" rows="5"></textarea>
     <br/>
     <div class="buttons">
-      <input type="submit" name="close" value="<%- I18n.t('javascripts.notes.show.close') %>" id="note-close">
-      <input type="submit" name="comment" value="<%- I18n.t('javascripts.notes.show.comment') %>" id="note-comment">
+      <input type="submit" name="close" value="<%- I18n.t('javascripts.notes.show.close') %>" data-url="<%- note.close_url %>">
+      <input type="submit" name="comment" value="<%- I18n.t('javascripts.notes.show.comment') %>" data-url="<%- note.comment_url %>" disabled="1">
     </div>
   </form>
   <% } %>
index 413133e456d533a1c5ee3d0437795f01a9990bf0..d0839844c16487c5a6b94f3bba72436d562b876c 100644 (file)
@@ -1417,11 +1417,7 @@ abbr.geo {
 
 /* Rules for the notes interface */
 
-.note {
-  width: 300px;
-
-  .buttons {
-    margin-top: 5px;
-    text-align: right;
-  }
+.note .buttons {
+  margin-top: 5px;
+  text-align: right;
 }
index 06516a16f200bbf9800da170b82fee07eec12761..c979c52bb857cfc205c4a820d5ae5645b9a0b16e 100644 (file)
@@ -2,12 +2,6 @@
   <%= javascript_include_tag "index" %>
 <% end %>
 
-<% unless STATUS == :api_offline or STATUS == :database_offline -%>
-  <% content_for :editmenu do -%>
-    <li><%= link_to t("browse.start_rjs.notes_layer_name"), notes_url(:format => :json), :id => "show_notes" %></li>
-  <% end -%>
-<% end -%>
-
 <% content_for :left_menu do %>
   <li><h4><%= link_to t("site.key.map_key"), {:action => :key}, :id => "open_map_key", :title => t("site.key.map_key_tooltip") %></h4></li>
 <% end %>
index 806b9ddd3df1143b0028ea7231ef320db0c79e8f..62b6b90f846858bfd8dfdc9c00901b0aaf84fbc2 100644 (file)
@@ -2017,8 +2017,7 @@ en:
       createnote_zoom_alert: You must zoom in to add a note to the map
     notes:
       new:
-        intro_1: Move the marker to the correct position and
-        intro_2: "add your comment in the box below:"
+        intro: "Move the marker to the correct position and add your comment in the box below:"
         add: Add Note
       show:
         title: Note %{id}