From 8530e3eedb2918c4602295fef8dd34028c8077b8 Mon Sep 17 00:00:00 2001 From: John Firebaugh Date: Mon, 7 Oct 2013 16:14:18 -0700 Subject: [PATCH] AJAXy changeset history --- app/assets/javascripts/index.js | 17 +-- app/assets/javascripts/index/changeset.js | 77 ------------- app/assets/javascripts/index/history.js | 125 ++++++++++++++++++++++ app/assets/javascripts/index/search.js | 4 +- app/assets/stylesheets/common.css.scss | 25 ++--- app/controllers/changeset_controller.rb | 35 ++---- app/views/changeset/_user.html.erb | 1 - app/views/changeset/history.html.erb | 27 +++++ app/views/changeset/list.atom.builder | 6 -- app/views/changeset/list.html.erb | 28 ++--- app/views/geocoder/results.html.erb | 14 ++- app/views/geocoder/search.html.erb | 2 +- app/views/layouts/_header.html.erb | 4 +- config/locales/en.yml | 5 +- config/routes.rb | 7 +- 15 files changed, 209 insertions(+), 168 deletions(-) delete mode 100644 app/assets/javascripts/index/changeset.js create mode 100644 app/assets/javascripts/index/history.js delete mode 100644 app/views/changeset/_user.html.erb create mode 100644 app/views/changeset/history.html.erb diff --git a/app/assets/javascripts/index.js b/app/assets/javascripts/index.js index 11523df39..ef23e340d 100644 --- a/app/assets/javascripts/index.js +++ b/app/assets/javascripts/index.js @@ -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 index 5c600fe87..000000000 --- a/app/assets/javascripts/index/changeset.js +++ /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 index 000000000..f63cee250 --- /dev/null +++ b/app/assets/javascripts/index/history.js @@ -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; +}; diff --git a/app/assets/javascripts/index/search.js b/app/assets/javascripts/index/search.js index c32503e8e..52920d8bb 100644 --- a/app/assets/javascripts/index/search.js +++ b/app/assets/javascripts/index/search.js @@ -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); diff --git a/app/assets/stylesheets/common.css.scss b/app/assets/stylesheets/common.css.scss index ab6a4dece..6a05ae1d3 100644 --- a/app/assets/stylesheets/common.css.scss +++ b/app/assets/stylesheets/common.css.scss @@ -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; } } diff --git a/app/controllers/changeset_controller.rb b/app/controllers/changeset_controller.rb index 89d244907..bd908879f 100644 --- a/app/controllers/changeset_controller.rb +++ b/app/controllers/changeset_controller.rb @@ -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 index 0e9507650..000000000 --- a/app/views/changeset/_user.html.erb +++ /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 index 000000000..23cee14f7 --- /dev/null +++ b/app/views/changeset/history.html.erb @@ -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 +%> + +

<%= @heading %>

+ +
+ <%= image_tag "searching.gif", :class => "loader" %> +
diff --git a/app/views/changeset/list.atom.builder b/app/views/changeset/list.atom.builder index 591f269b6..9ee13a829 100644 --- a/app/views/changeset/list.atom.builder +++ b/app/views/changeset/list.atom.builder @@ -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" diff --git a/app/views/changeset/list.html.erb b/app/views/changeset/list.html.erb index 42b5d3e1e..9f0301da0 100644 --- a/app/views/changeset/list.html.erb +++ b/app/views/changeset/list.html.erb @@ -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 -%> - -

<%= @heading %>

- -<% if @edits.size > 0 %> +<% if @edits.present? %>
    - <%= render :partial => 'changeset', - :collection => @edits %> + <%= render :partial => 'changeset', :collection => @edits %>
- -
-
- <%= link_to t('geocoder.results.more_results'), '#', :class => "button" %> -
- <%= image_tag "searching.gif", :class => ["search_searching", "hidden"] %> +
+ <%= 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;" %>
-<% elsif @user and @user.display_name == params[:display_name] %> -

<%= t('changeset.list.empty_user_html') %>

+<% elsif params[:max_id] %> +

<%= t('changeset.list.no_more') %>

<% else %> -

<%= t('changeset.list.empty_anon_html') %>

+

<%= t('changeset.list.empty') %>

<% end %> diff --git a/app/views/geocoder/results.html.erb b/app/views/geocoder/results.html.erb index 3c471076c..d990b7f0e 100644 --- a/app/views/geocoder/results.html.erb +++ b/app/views/geocoder/results.html.erb @@ -2,16 +2,14 @@

<%= t 'geocoder.results.no_results' %>

<% else %> + <% @results.each do |result| %> +
  • <%= result_to_html(result) %>

  • + <% end %> + <% if @more_params %>
    -
    - <%= link_to t('geocoder.results.more_results'), url_for(@more_params), :class => "button" %> -
    - <%= 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;" %>
    <% end %> <% end %> diff --git a/app/views/geocoder/search.html.erb b/app/views/geocoder/search.html.erb index b030dc71a..72afb548c 100644 --- a/app/views/geocoder/search.html.erb +++ b/app/views/geocoder/search.html.erb @@ -2,6 +2,6 @@ <% @sources.each do |source| %>

    <%= raw(t "geocoder.search.title.#{source}") %>

    "> - <%= image_tag "searching.gif", :class => "search_searching" %> + <%= image_tag "searching.gif", :class => "loader" %>
    <% end %> diff --git a/app/views/layouts/_header.html.erb b/app/views/layouts/_header.html.erb index 0b099e1c7..130c71363 100644 --- a/app/views/layouts/_header.html.erb +++ b/app/views/layouts/_header.html.erb @@ -24,8 +24,8 @@ <% end %> -
  • - <%= link_to t('layouts.history'), browse_changesets_path, :class => 'tab geolink' %> +
  • + <%= link_to t('layouts.history'), history_path, :class => 'tab geolink' %>
  • <%= link_to t('layouts.export'), export_path, :class => 'tab geolink' %>
  • diff --git a/config/locales/en.yml b/config/locales/en.yml index d09ea4e27..575db00f6 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -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 Beginners Guide." - 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: diff --git a/config/routes.rb b/config/routes.rb index 231be4f43..2bba5cfe0 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -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] -- 2.43.2