AJAXy changeset history

This commit is contained in:
John Firebaugh 2013-10-07 16:14:18 -07:00
parent bcc4897ef6
commit 8530e3eedb
15 changed files with 209 additions and 168 deletions

View file

@ -9,7 +9,7 @@
//= require index/browse //= require index/browse
//= require index/export //= require index/export
//= require index/notes //= require index/notes
//= require index/changeset //= require index/history
//= require router //= require router
$(document).ready(function () { $(document).ready(function () {
@ -278,26 +278,31 @@ $(document).ready(function () {
return page; return page;
}; };
var router = OSM.Router({ var history = OSM.History(map);
OSM.route = OSM.Router({
"/": OSM.Index(map), "/": OSM.Index(map),
"/search": OSM.Search(map), "/search": OSM.Search(map),
"/export": OSM.Export(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) "/browse/:type/:id(/history)": OSM.Browse(map)
}); });
$(document).on("click", "a", function(e) { $(document).on("click", "a", function(e) {
if (e.isPropagationStopped()) return; 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) { $("#search_form").on("submit", function(e) {
e.preventDefault(); 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) { $("#describe_location").on("click", function(e) {
e.preventDefault(); e.preventDefault();
router("/search?query=" + encodeURIComponent(map.getCenter().lat + "," + map.getCenter().lng)); OSM.route("/search?query=" + encodeURIComponent(map.getCenter().lat + "," + map.getCenter().lng));
}); });
}); });

View file

@ -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;
};

View file

@ -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;
};

View file

@ -17,8 +17,8 @@ OSM.Search = function(map) {
var div = $(this).parents(".search_more"); var div = $(this).parents(".search_more");
div.find(".search_results_entry").hide(); $(this).hide();
div.find(".search_searching").show(); div.find(".loader").show();
$.get($(this).attr("href"), function(data) { $.get($(this).attr("href"), function(data) {
div.replaceWith(data); div.replaceWith(data);

View file

@ -777,6 +777,14 @@ nav.secondary {
bottom: 0; bottom: 0;
width: 100%; width: 100%;
overflow-y: auto; overflow-y: auto;
.loader,
.load_more {
text-align: center;
margin: $lineheight auto;
width: $lineheight;
display: block;
}
} }
/* Rules for the search box */ /* Rules for the search box */
@ -883,19 +891,7 @@ a.donate {
/* Rules for search results which appear in the popout sidebar */ /* Rules for search results which appear in the popout sidebar */
.search_searching {
margin-top: $lineheight/4;
margin-bottom: $lineheight/4;
}
.search_results_entry { .search_results_entry {
.search_searching {
text-align: center;
margin: $lineheight auto;
width: $lineheight;
display: block;
}
ul li { ul li {
border-bottom: 1px solid #ccc; border-bottom: 1px solid #ccc;
} }
@ -948,6 +944,11 @@ a.donate {
li { li {
padding: $lineheight; padding: $lineheight;
border-bottom: $keyline; border-bottom: $keyline;
cursor: pointer;
}
li.selected {
background: #FCFEA4;
} }
} }

View file

@ -252,6 +252,8 @@ class ChangesetController < ApplicationController
def list def list
if request.format == :atom and params[:page] if request.format == :atom and params[:page]
redirect_to params.merge({ :page => nil }), :status => :moved_permanently redirect_to params.merge({ :page => nil }), :status => :moved_permanently
elsif request.format == :html and !params[:bbox]
render :action => :history, :layout => map_layout
else else
changesets = conditions_nonempty(Changeset.all) changesets = conditions_nonempty(Changeset.all)
@ -289,39 +291,16 @@ class ChangesetController < ApplicationController
end end
if params[:bbox] if params[:bbox]
bbox = BoundingBox.from_bbox_params(params) changesets = conditions_bbox(changesets, BoundingBox.from_bbox_params(params))
elsif params[:minlon] and params[:minlat] and params[:maxlon] and params[:maxlat]
bbox = BoundingBox.from_lon_lat_params(params)
end end
if bbox if params[:max_id]
changesets = conditions_bbox(changesets, bbox) changesets = changesets.where("changesets.id <= ?", params[:max_id])
end end
if user @edits = changesets.order("changesets.created_at DESC").limit(20).preload(:user, :changeset_tags)
user_link = render_to_string :partial => "user", :object => user
end
if params[:friends] and @user render :action => :list, :layout => false
@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)
render :action => :list, :layout => map_layout
end end
end end

View file

@ -1 +0,0 @@
<%= link_to user.display_name, :controller => "user", :action => "view", :display_name => user.display_name %>

View file

@ -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>

View file

@ -4,12 +4,6 @@ atom_feed(:language => I18n.locale, :schema_date => 2009,
"xmlns:georss" => "http://www.georss.org/georss") do |feed| "xmlns:georss" => "http://www.georss.org/georss") do |feed|
feed.title @title 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.updated @edits.map {|e| [e.created_at, e.closed_at].max }.max
feed.icon "http://#{SERVER_URL}/favicon.ico" feed.icon "http://#{SERVER_URL}/favicon.ico"
feed.logo "http://#{SERVER_URL}/images/mag_map-rss2.0.png" feed.logo "http://#{SERVER_URL}/images/mag_map-rss2.0.png"

View file

@ -1,25 +1,13 @@
<% content_for :head do -%> <% if @edits.present? %>
<% 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 %>
<ol class="changesets"> <ol class="changesets">
<%= render :partial => 'changeset', <%= render :partial => 'changeset', :collection => @edits %>
:collection => @edits %>
</ol> </ol>
<div class="changeset_more">
<div class="search_more"> <%= link_to t('changeset.list.load_more'), url_for(params.merge(:max_id => @edits.last.id - 1)), :class => "button load_more" %>
<div class="inner12 search_results_entry"> <%= image_tag "searching.gif", :class => "loader", :style => "display: none;" %>
<%= link_to t('geocoder.results.more_results'), '#', :class => "button" %>
</div>
<%= image_tag "searching.gif", :class => ["search_searching", "hidden"] %>
</div> </div>
<% elsif @user and @user.display_name == params[:display_name] %> <% elsif params[:max_id] %>
<h4><%= t('changeset.list.empty_user_html') %></h4> <p class="inner22"><%= t('changeset.list.no_more') %></p>
<% else %> <% else %>
<h4><%= t('changeset.list.empty_anon_html') %></h4> <p class="inner22"><%= t('changeset.list.empty') %></p>
<% end %> <% end %>

View file

@ -2,16 +2,14 @@
<p class="search_results_entry inner12"><%= t 'geocoder.results.no_results' %></p> <p class="search_results_entry inner12"><%= t 'geocoder.results.no_results' %></p>
<% else %> <% else %>
<ul class='results-list'> <ul class='results-list'>
<% @results.each do |result| %> <% @results.each do |result| %>
<li><p class="inner12 search_results_entry clearfix"><%= result_to_html(result) %></p></li> <li><p class="inner12 search_results_entry clearfix"><%= result_to_html(result) %></p></li>
<% end %> <% end %>
</ul> </ul>
<% if @more_params %> <% if @more_params %>
<div class="search_more"> <div class="search_more">
<div class="inner12 search_results_entry"> <%= link_to t('geocoder.results.more_results'), url_for(@more_params), :class => "button load_more" %>
<%= link_to t('geocoder.results.more_results'), url_for(@more_params), :class => "button" %> <%= image_tag "searching.gif", :class => "loader", :style => "display: none;" %>
</div>
<%= image_tag "searching.gif", :class => ["search_searching", "hidden"] %>
</div> </div>
<% end %> <% end %>
<% end %> <% end %>

View file

@ -2,6 +2,6 @@
<% @sources.each do |source| %> <% @sources.each do |source| %>
<h4><%= raw(t "geocoder.search.title.#{source}") %></h4> <h4><%= raw(t "geocoder.search.title.#{source}") %></h4>
<div class="search_results_entry" data-href="<%= url_for params.merge(:action => "search_#{source}") %>"> <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> </div>
<% end %> <% end %>

View file

@ -24,8 +24,8 @@
</li> </li>
<% end %> <% end %>
</ul> </ul>
</li><li id="history_tab" class="<%= current_page_class(browse_changesets_path) %>"> </li><li id="history_tab" class="<%= current_page_class(history_path) %>">
<%= link_to t('layouts.history'), browse_changesets_path, :class => 'tab geolink' %> <%= link_to t('layouts.history'), history_path, :class => 'tab geolink' %>
</li><li id="export_tab" class="<%= current_page_class(export_path) %>"> </li><li id="export_tab" class="<%= current_page_class(export_path) %>">
<%= link_to t('layouts.export'), export_path, :class => 'tab geolink' %> <%= link_to t('layouts.export'), export_path, :class => 'tab geolink' %>
</li> </li>

View file

@ -313,8 +313,9 @@ en:
title_user: "Changesets by %{user}" title_user: "Changesets by %{user}"
title_friend: "Changesets by your friends" title_friend: "Changesets by your friends"
title_nearby: "Changesets by nearby users" 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: "No changesets in this area."
empty_anon_html: "No edits made yet." no_more: "No more changesets in this area."
load_more: "Load more"
timeout: timeout:
sorry: "Sorry, the list of changesets you requested took too long to retrieve." sorry: "Sorry, the list of changesets you requested took too long to retrieve."
diary_entry: diary_entry:

View file

@ -116,9 +116,10 @@ OpenStreetMap::Application.routes.draw do
match '/user/:display_name/notes' => 'notes#mine', :via => :get match '/user/:display_name/notes' => 'notes#mine', :via => :get
match '/browse/friends' => 'changeset#list', :via => :get, :friends => true, :as => "friend_changesets" 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/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 } get '/browse/changesets/feed', :to => redirect('/history/feed')
match '/browse' => 'changeset#list', :via => :get get '/browse/changesets', :to => redirect('/history')
get '/browse', :to => redirect('/history')
# web site # web site
root :to => 'site#index', :via => [:get, :post] root :to => 'site#index', :via => [:get, :post]