AJAXy changeset history
authorJohn Firebaugh <john.firebaugh@gmail.com>
Mon, 7 Oct 2013 23:14:18 +0000 (16:14 -0700)
committerJohn Firebaugh <john.firebaugh@gmail.com>
Sun, 13 Oct 2013 21:46:09 +0000 (14:46 -0700)
15 files changed:
app/assets/javascripts/index.js
app/assets/javascripts/index/changeset.js [deleted file]
app/assets/javascripts/index/history.js [new file with mode: 0644]
app/assets/javascripts/index/search.js
app/assets/stylesheets/common.css.scss
app/controllers/changeset_controller.rb
app/views/changeset/_user.html.erb [deleted file]
app/views/changeset/history.html.erb [new file with mode: 0644]
app/views/changeset/list.atom.builder
app/views/changeset/list.html.erb
app/views/geocoder/results.html.erb
app/views/geocoder/search.html.erb
app/views/layouts/_header.html.erb
config/locales/en.yml
config/routes.rb

index 11523df3940f5fbdc5a1b8fea3c12f91d8433bcb..ef23e340d36a6015896e7dd2d82c967ea8b3a9a5 100644 (file)
@@ -9,7 +9,7 @@
 //= require index/browse
 //= require index/export
 //= require index/notes
-//= require index/changeset
+//= require index/history
 //= require router
 
 $(document).ready(function () {
@@ -278,26 +278,31 @@ $(document).ready(function () {
     return page;
   };
 
-  var router = OSM.Router({
+  var history = OSM.History(map);
+
+  OSM.route = OSM.Router({
     "/":                           OSM.Index(map),
     "/search":                     OSM.Search(map),
     "/export":                     OSM.Export(map),
-    "/browse/changesets":          OSM.ChangesetList(map),
+    "/history":                    history,
+    "/user/:display_name/edits":   history,
+    "/browse/friends":             history,
+    "/browse/nearby":              history,
     "/browse/:type/:id(/history)": OSM.Browse(map)
   });
 
   $(document).on("click", "a", function(e) {
     if (e.isPropagationStopped()) return;
-    if (router(this.pathname + this.search + this.hash)) e.preventDefault();
+    if (OSM.route(this.pathname + this.search + this.hash)) e.preventDefault();
   });
 
   $("#search_form").on("submit", function(e) {
     e.preventDefault();
-    router("/search?query=" + encodeURIComponent($("#query").val()) + OSM.formatHash(map));
+    OSM.route("/search?query=" + encodeURIComponent($("#query").val()) + OSM.formatHash(map));
   });
 
   $("#describe_location").on("click", function(e) {
     e.preventDefault();
-    router("/search?query=" + encodeURIComponent(map.getCenter().lat + "," + map.getCenter().lng));
+    OSM.route("/search?query=" + encodeURIComponent(map.getCenter().lat + "," + map.getCenter().lng));
   });
 });
diff --git a/app/assets/javascripts/index/changeset.js b/app/assets/javascripts/index/changeset.js
deleted file mode 100644 (file)
index 5c600fe..0000000
+++ /dev/null
@@ -1,77 +0,0 @@
-OSM.ChangesetList = function(map) {
-  var page = {};
-
-  var group = L.featureGroup()
-    .on({
-      mouseover: function (e) {
-        highlightChangeset(e.layer.id);
-      },
-      mouseout: function (e) {
-        unHighlightChangeset(e.layer.id);
-      }
-    });
-
-  group.getLayerId = function(layer) {
-    return layer.id;
-  };
-
-  function highlightChangeset(id) {
-    group.getLayer(id).setStyle({fillOpacity: 0.5});
-    $("#changeset_" + id).addClass("selected");
-  }
-
-  function unHighlightChangeset(id) {
-    group.getLayer(id).setStyle({fillOpacity: 0});
-    $("#changeset_" + id).removeClass("selected");
-  }
-
-  page.pushstate = page.popstate = function(path) {
-    $("#history_tab").addClass("current");
-    $("#sidebar").removeClass("minimized");
-    map.invalidateSize();
-    $('#sidebar_content').load(path, page.load);
-  };
-
-  page.load = function() {
-    map.addLayer(group);
-
-    var changesets = [];
-    $("[data-changeset]").each(function () {
-      var changeset = $(this).data('changeset');
-      if (changeset.bbox) {
-        changeset.bounds = L.latLngBounds([changeset.bbox.minlat, changeset.bbox.minlon],
-          [changeset.bbox.maxlat, changeset.bbox.maxlon]);
-        changesets.push(changeset);
-      }
-    });
-
-    changesets.sort(function (a, b) {
-      return b.bounds.getSize() - a.bounds.getSize();
-    });
-
-    for (var i = 0; i < changesets.length; ++i) {
-      var changeset = changesets[i],
-        rect = L.rectangle(changeset.bounds,
-          {weight: 2, color: "#ee9900", fillColor: "#ffff55", fillOpacity: 0});
-      rect.id = changeset.id;
-      rect.addTo(group);
-    }
-
-    $("[data-changeset]").on({
-      mouseover: function () {
-        highlightChangeset($(this).data("changeset").id);
-      },
-      mouseout: function () {
-        unHighlightChangeset($(this).data("changeset").id);
-      }
-    });
-  };
-
-  page.unload = function() {
-    map.removeLayer(group);
-    group.clearLayers();
-    $("#history_tab").removeClass("current");
-  };
-
-  return page;
-};
diff --git a/app/assets/javascripts/index/history.js b/app/assets/javascripts/index/history.js
new file mode 100644 (file)
index 0000000..f63cee2
--- /dev/null
@@ -0,0 +1,125 @@
+OSM.History = function(map) {
+  var page = {};
+
+  $("#sidebar_content")
+    .on("click", ".changeset_more a", loadMore)
+    .on("mouseover", "[data-changeset]", function () {
+      highlightChangeset($(this).data("changeset").id);
+    })
+    .on("mouseout", "[data-changeset]", function () {
+      unHighlightChangeset($(this).data("changeset").id);
+    })
+    .on("click", "[data-changeset]", function () {
+      clickChangeset($(this).data("changeset").id);
+    });
+
+  var group = L.featureGroup()
+    .on("mouseover", function (e) {
+      highlightChangeset(e.layer.id);
+    })
+    .on("mouseout", function (e) {
+      unHighlightChangeset(e.layer.id);
+    })
+    .on("click", function (e) {
+      clickChangeset(e.layer.id);
+    });
+
+  group.getLayerId = function(layer) {
+    return layer.id;
+  };
+
+  function highlightChangeset(id) {
+    group.getLayer(id).setStyle({fillOpacity: 0.5});
+    $("#changeset_" + id).addClass("selected");
+  }
+
+  function unHighlightChangeset(id) {
+    group.getLayer(id).setStyle({fillOpacity: 0});
+    $("#changeset_" + id).removeClass("selected");
+  }
+
+  function clickChangeset(id) {
+    OSM.route($("#changeset_" + id).find(".changeset_id").attr("href"));
+  }
+
+  function loadData() {
+    $.ajax({
+      url: window.location.pathname,
+      method: "GET",
+      data: {bbox: map.getBounds().toBBoxString()},
+      success: function(html) {
+        $('#sidebar_content .changesets').html(html);
+        updateMap();
+      }
+    });
+  }
+
+  function loadMore(e) {
+    e.preventDefault();
+    e.stopPropagation();
+
+    var div = $(this).parents(".changeset_more");
+
+    $(this).hide();
+    div.find(".loader").show();
+
+    $.get($(this).attr("href"), function(data) {
+      div.replaceWith(data);
+      updateMap();
+    });
+  }
+
+  function updateMap() {
+    group.clearLayers();
+
+    var changesets = [];
+
+    $("[data-changeset]").each(function () {
+      var changeset = $(this).data('changeset');
+      if (changeset.bbox) {
+        changeset.bounds = L.latLngBounds(
+          [changeset.bbox.minlat, changeset.bbox.minlon],
+          [changeset.bbox.maxlat, changeset.bbox.maxlon]);
+        changesets.push(changeset);
+      }
+    });
+
+    changesets.sort(function (a, b) {
+      return b.bounds.getSize() - a.bounds.getSize();
+    });
+
+    for (var i = 0; i < changesets.length; ++i) {
+      var changeset = changesets[i],
+        rect = L.rectangle(changeset.bounds,
+          {weight: 2, color: "#ee9900", fillColor: "#ffff55", fillOpacity: 0});
+      rect.id = changeset.id;
+      rect.addTo(group);
+    }
+  }
+
+  page.pushstate = page.popstate = function(path) {
+    $("#history_tab").addClass("current");
+    $("#sidebar").removeClass("minimized");
+    map.invalidateSize();
+    $("#sidebar_content").load(path, page.load);
+  };
+
+  page.load = function() {
+    map
+      .on("moveend", loadData)
+      .addLayer(group);
+
+    loadData();
+  };
+
+  page.unload = function() {
+    map
+      .off("moveend", loadData)
+      .removeLayer(group);
+
+    group.clearLayers();
+    $("#history_tab").removeClass("current");
+  };
+
+  return page;
+};
index c32503e8e26e77c96f377b261f7e1dc34f7c17cf..52920d8bb90fbaf3eb176344f7ddf5721c4d859c 100644 (file)
@@ -17,8 +17,8 @@ OSM.Search = function(map) {
 
     var div = $(this).parents(".search_more");
 
-    div.find(".search_results_entry").hide();
-    div.find(".search_searching").show();
+    $(this).hide();
+    div.find(".loader").show();
 
     $.get($(this).attr("href"), function(data) {
       div.replaceWith(data);
index ab6a4dece1a96b1462fcfb82a6a47f685f43107e..6a05ae1d30402f7bac64ce536ad34d2c66f90aed 100644 (file)
@@ -777,6 +777,14 @@ nav.secondary {
   bottom: 0;
   width: 100%;
   overflow-y: auto;
+
+  .loader,
+  .load_more {
+    text-align: center;
+    margin: $lineheight auto;
+    width: $lineheight;
+    display: block;
+  }
 }
 
 /* Rules for the search box */
@@ -883,19 +891,7 @@ a.donate {
 
 /* Rules for search results which appear in the popout sidebar */
 
-.search_searching {
-  margin-top: $lineheight/4;
-  margin-bottom: $lineheight/4;
-}
-
 .search_results_entry {
-  .search_searching {
-    text-align: center;
-    margin: $lineheight auto;
-    width: $lineheight;
-    display: block;
-  }
-
   ul li {
     border-bottom: 1px solid #ccc;
   }
@@ -948,6 +944,11 @@ a.donate {
   li {
     padding: $lineheight;
     border-bottom: $keyline;
+    cursor: pointer;
+  }
+
+  li.selected {
+    background: #FCFEA4;
   }
 }
 
index 89d244907d7db35b6c21388dc22c3468c1c15149..bd908879fe423c71a861bab62d0339d53b7703a4 100644 (file)
@@ -252,6 +252,8 @@ class ChangesetController < ApplicationController
   def list
     if request.format == :atom and params[:page]
       redirect_to params.merge({ :page => nil }), :status => :moved_permanently
+    elsif request.format == :html and !params[:bbox]
+      render :action => :history, :layout => map_layout
     else
       changesets = conditions_nonempty(Changeset.all)
 
@@ -289,39 +291,16 @@ class ChangesetController < ApplicationController
       end
 
       if params[:bbox]
-        bbox = BoundingBox.from_bbox_params(params)
-      elsif params[:minlon] and params[:minlat] and params[:maxlon] and params[:maxlat]
-        bbox = BoundingBox.from_lon_lat_params(params)
+        changesets = conditions_bbox(changesets, BoundingBox.from_bbox_params(params))
       end
 
-      if bbox
-        changesets = conditions_bbox(changesets, bbox)
+      if params[:max_id]
+        changesets = changesets.where("changesets.id <= ?", params[:max_id])
       end
 
-      if user
-        user_link = render_to_string :partial => "user", :object => user
-      end
-
-      if params[:friends] and @user
-        @title =  t 'changeset.list.title_friend'
-        @heading =  t 'changeset.list.title_friend'
-      elsif params[:nearby] and @user
-        @title = t 'changeset.list.title_nearby'
-        @heading = t 'changeset.list.title_nearby'
-      elsif user
-        @title =  t 'changeset.list.title_user', :user => user.display_name
-        @heading =  t('changeset.list.title_user', :user => user_link).html_safe
-      else
-        @title =  t 'changeset.list.title'
-        @heading =  t 'changeset.list.title'
-      end
-
-      @page = (params[:page] || 1).to_i
-      @page_size = 20
-
-      @edits = changesets.order("changesets.created_at DESC").offset((@page - 1) * @page_size).limit(@page_size).preload(:user, :changeset_tags)
+      @edits = changesets.order("changesets.created_at DESC").limit(20).preload(:user, :changeset_tags)
 
-      render :action => :list, :layout => map_layout
+      render :action => :list, :layout => false
     end
   end
 
diff --git a/app/views/changeset/_user.html.erb b/app/views/changeset/_user.html.erb
deleted file mode 100644 (file)
index 0e95076..0000000
+++ /dev/null
@@ -1 +0,0 @@
-<%= link_to user.display_name, :controller => "user", :action => "view", :display_name => user.display_name %>
diff --git a/app/views/changeset/history.html.erb b/app/views/changeset/history.html.erb
new file mode 100644 (file)
index 0000000..23cee14
--- /dev/null
@@ -0,0 +1,27 @@
+<% content_for :head do -%>
+  <% unless params[:friends] or params[:nearby] -%>
+    <%= auto_discovery_link_tag :atom, params.merge({ :page => nil, :action => :feed }) %>
+  <% end -%>
+<% end -%>
+
+<%
+   if params[:friends] and @user
+     @title = t 'changeset.list.title_friend'
+     @heading = t 'changeset.list.title_friend'
+   elsif params[:nearby] and @user
+     @title = t 'changeset.list.title_nearby'
+     @heading = t 'changeset.list.title_nearby'
+   elsif params[:display_name]
+     @title = t 'changeset.list.title_user', :user => params[:display_name]
+     @heading = t('changeset.list.title_user', :user => link_to(params[:display_name], :controller => "user", :action => "view", :display_name => params[:display_name])).html_safe
+   else
+     @title = t 'changeset.list.title'
+     @heading = t 'changeset.list.title'
+   end
+%>
+
+<h2><%= @heading %></h2>
+
+<div class="changesets">
+  <%= image_tag "searching.gif", :class => "loader" %>
+</div>
index 591f269b611d765a075914d319c15d4c7f367310..9ee13a829e72a0bb1128a2c264ac45b4161db28e 100644 (file)
@@ -4,12 +4,6 @@ atom_feed(:language => I18n.locale, :schema_date => 2009,
           "xmlns:georss" => "http://www.georss.org/georss") do |feed|
   feed.title @title
 
-  feed.subtitle :type => 'xhtml' do |xhtml|
-    xhtml.p do |p|
-      p << @description
-    end
-  end
-
   feed.updated @edits.map {|e|  [e.created_at, e.closed_at].max }.max
   feed.icon "http://#{SERVER_URL}/favicon.ico"
   feed.logo "http://#{SERVER_URL}/images/mag_map-rss2.0.png"
index 42b5d3e1e9f17d45baa04144a27ac77ada4f4853..9f0301da02e77ba2eac3676c003a41873fe4a321 100644 (file)
@@ -1,25 +1,13 @@
-<% content_for :head do -%>
-  <% unless params[:friends] or params[:nearby] -%>
-    <%= auto_discovery_link_tag :atom, params.merge({ :page => nil, :action => :feed }) %>
-  <% end -%>
-<% end -%>
-
-<h2><%= @heading %></h2>
-
-<% if @edits.size > 0 %>
+<% if @edits.present? %>
   <ol class="changesets">
-    <%= render :partial => 'changeset',
-               :collection => @edits %>
+    <%= render :partial => 'changeset', :collection => @edits %>
   </ol>
-
-  <div class="search_more">
-    <div class="inner12 search_results_entry">
-      <%= link_to t('geocoder.results.more_results'), '#', :class => "button" %>
-    </div>
-    <%= image_tag "searching.gif", :class => ["search_searching", "hidden"] %>
+  <div class="changeset_more">
+    <%= link_to t('changeset.list.load_more'), url_for(params.merge(:max_id => @edits.last.id - 1)), :class => "button load_more" %>
+    <%= image_tag "searching.gif", :class => "loader", :style => "display: none;" %>
   </div>
-<% elsif @user and @user.display_name == params[:display_name] %>
-  <h4><%= t('changeset.list.empty_user_html') %></h4>
+<% elsif params[:max_id] %>
+  <p class="inner22"><%= t('changeset.list.no_more') %></p>
 <% else %>
-  <h4><%= t('changeset.list.empty_anon_html') %></h4>
+  <p class="inner22"><%= t('changeset.list.empty') %></p>
 <% end %>
index 3c471076cfb6f2782d689f3b41254634b4d4a8a7..d990b7f0ec14ae0f3ab7c5f4affb1d147c6cf51b 100644 (file)
@@ -2,16 +2,14 @@
   <p class="search_results_entry inner12"><%= t 'geocoder.results.no_results' %></p>
 <% else %>
   <ul class='results-list'>
-  <% @results.each do |result| %>
-    <li><p class="inner12 search_results_entry clearfix"><%= result_to_html(result) %></p></li>
-  <% end %>
-</ul>
+    <% @results.each do |result| %>
+      <li><p class="inner12 search_results_entry clearfix"><%= result_to_html(result) %></p></li>
+    <% end %>
+  </ul>
   <% if @more_params %>
     <div class="search_more">
-      <div class="inner12 search_results_entry">
-        <%= link_to t('geocoder.results.more_results'), url_for(@more_params), :class => "button" %>
-      </div>
-      <%= image_tag "searching.gif", :class => ["search_searching", "hidden"] %>
+      <%= link_to t('geocoder.results.more_results'), url_for(@more_params), :class => "button load_more" %>
+      <%= image_tag "searching.gif", :class => "loader", :style => "display: none;" %>
     </div>
   <% end %>
 <% end %>
index b030dc71a70f6eb9d404afce9ba540d66e024c0b..72afb548c7d6c5fe6a3e0c6f8960af6d815efff5 100644 (file)
@@ -2,6 +2,6 @@
 <% @sources.each do |source| %>
   <h4><%= raw(t "geocoder.search.title.#{source}") %></h4>
   <div class="search_results_entry" data-href="<%= url_for params.merge(:action => "search_#{source}") %>">
-    <%= image_tag "searching.gif", :class => "search_searching" %>
+    <%= image_tag "searching.gif", :class => "loader" %>
   </div>
 <% end %>
index 0b099e1c7bc1ac4a407f19a0187db51845fd16f0..130c7136327caab1e2feb313f7b5ea420835b2f1 100644 (file)
@@ -24,8 +24,8 @@
           </li>
         <% end %>
       </ul>
-      </li><li id="history_tab" class="<%= current_page_class(browse_changesets_path) %>">
-        <%= link_to t('layouts.history'), browse_changesets_path, :class => 'tab geolink' %>
+      </li><li id="history_tab" class="<%= current_page_class(history_path) %>">
+        <%= link_to t('layouts.history'), history_path, :class => 'tab geolink' %>
       </li><li id="export_tab" class="<%= current_page_class(export_path) %>">
         <%= link_to t('layouts.export'), export_path, :class => 'tab geolink' %>
       </li>
index d09ea4e27c9b8e9f37348acb9aaa7a2cbae9378e..575db00f6e76724b273290fe577da5e13a3275c9 100644 (file)
@@ -313,8 +313,9 @@ en:
       title_user: "Changesets by %{user}"
       title_friend: "Changesets by your friends"
       title_nearby: "Changesets by nearby users"
-      empty_user_html: "It looks you haven't made any edits yet. To get started, check out the <a href='http://wiki.openstreetmap.org/wiki/Beginners_Guide_1.3'>Beginners Guide</a>."
-      empty_anon_html: "No edits made yet."
+      empty: "No changesets in this area."
+      no_more: "No more changesets in this area."
+      load_more: "Load more"
     timeout:
       sorry: "Sorry, the list of changesets you requested took too long to retrieve."
   diary_entry:
index 231be4f43c93d44f924b4bb8b6e5ce15d0176e35..2bba5cfe00b3e26f64174836a2f27e7420f6c63f 100644 (file)
@@ -116,9 +116,10 @@ OpenStreetMap::Application.routes.draw do
   match '/user/:display_name/notes' => 'notes#mine', :via => :get
   match '/browse/friends' => 'changeset#list', :via => :get, :friends => true, :as => "friend_changesets"
   match '/browse/nearby' => 'changeset#list', :via => :get, :nearby => true, :as => "nearby_changesets"
-  match '/browse/changesets' => 'changeset#list', :via => :get
-  match '/browse/changesets/feed' => 'changeset#feed', :via => :get, :defaults => { :format => :atom }
-  match '/browse' => 'changeset#list', :via => :get
+
+  get '/browse/changesets/feed', :to => redirect('/history/feed')
+  get '/browse/changesets',      :to => redirect('/history')
+  get '/browse',                 :to => redirect('/history')
 
   # web site
   root :to => 'site#index', :via => [:get, :post]