From a56d1036d58d45777d6adeffd5a950519bc70318 Mon Sep 17 00:00:00 2001 From: John Firebaugh Date: Wed, 2 Oct 2013 16:41:44 -0700 Subject: [PATCH] pushState based navigation between map-based layouts --- app/assets/javascripts/application.js | 1 - app/assets/javascripts/changeset.js | 55 ----------- app/assets/javascripts/index.js | 61 +++++++++--- app/assets/javascripts/index/changeset.js | 75 ++++++++++++++ app/assets/javascripts/index/export.js | 115 +++++++++++----------- app/assets/javascripts/router.js | 78 +++++++++++++++ app/assets/javascripts/sidebar.js | 11 --- app/controllers/application_controller.rb | 4 + app/controllers/browse_controller.rb | 2 +- app/controllers/changeset_controller.rb | 2 +- app/controllers/site_controller.rb | 2 +- app/views/changeset/list.html.erb | 2 - config/environments/production.rb | 2 +- 13 files changed, 271 insertions(+), 139 deletions(-) delete mode 100644 app/assets/javascripts/changeset.js create mode 100644 app/assets/javascripts/index/changeset.js create mode 100644 app/assets/javascripts/router.js delete mode 100644 app/assets/javascripts/sidebar.js diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index 493c03d91..c23976641 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -17,7 +17,6 @@ //= require oauth //= require piwik //= require map -//= require sidebar //= require richtext //= require geocoder //= require querystring diff --git a/app/assets/javascripts/changeset.js b/app/assets/javascripts/changeset.js deleted file mode 100644 index 0aecced85..000000000 --- a/app/assets/javascripts/changeset.js +++ /dev/null @@ -1,55 +0,0 @@ -function initializeChangesets(map) { - var changesets = [], rects = {}; - - var group = L.featureGroup().addTo(map); - - $("[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; - rects[changeset.id] = rect; - rect.addTo(group); - } - - function highlightChangeset(id) { - rects[id].setStyle({fillOpacity: 0.5}); - $("#changeset_" + id).addClass("selected"); - } - - function unHighlightChangeset(id) { - rects[id].setStyle({fillOpacity: 0}); - $("#changeset_" + id).removeClass("selected"); - } - - group.on({ - mouseover: function (e) { - highlightChangeset(e.layer.id); - }, - mouseout: function (e) { - unHighlightChangeset(e.layer.id); - } - }); - - $("[data-changeset]").on({ - mouseover: function () { - highlightChangeset($(this).data("changeset").id); - }, - mouseout: function () { - unHighlightChangeset($(this).data("changeset").id); - } - }); -} diff --git a/app/assets/javascripts/index.js b/app/assets/javascripts/index.js index 525be7e52..62d47a3af 100644 --- a/app/assets/javascripts/index.js +++ b/app/assets/javascripts/index.js @@ -9,6 +9,8 @@ //= require index/browse //= require index/export //= require index/notes +//= require index/changeset +//= require router $(document).ready(function () { var params = OSM.mapParams(); @@ -141,12 +143,12 @@ $(document).ready(function () { map.getLayersCode(), map._object); - var expiry = new Date(); - expiry.setYear(expiry.getFullYear() + 10); - $.cookie("_osm_location", cookieContent(map), { expires: expiry }); + var expiry = new Date(); + expiry.setYear(expiry.getFullYear() + 10); + $.cookie("_osm_location", cookieContent(map), { expires: expiry }); - // Trigger hash update on layer changes. - map.hash.onMapMove(); + // Trigger hash update on layer changes. + map.hash.onMapMove(); }); if (OSM.PIWIK) { @@ -175,10 +177,6 @@ $(document).ready(function () { marker.setLatLng([params.mlat, params.mlon]).addTo(map); } - if (params.object) { - map.addObject(params.object, { zoom: params.object_zoom }); - } - $("#homeanchor").on("click", function(e) { e.preventDefault(); @@ -213,9 +211,50 @@ $(document).ready(function () { } initializeSearch(map); - initializeExport(map); initializeBrowse(map, params); initializeNotes(map, params); - if ('undefined' !== typeof initializeChangesets) initializeChangesets(map); + OSM.Index = function(map) { + var page = {}; + + page.pushstate = page.popstate = function(path) { + $("#view_tab").addClass("current"); + $('#sidebar_content').load(path); + }; + + page.unload = function() { + $("#view_tab").removeClass("current"); + }; + + return page; + }; + + OSM.Browse = function(map) { + var page = {}; + + page.pushstate = page.popstate = function(path) { + $('#sidebar_content').load(path, page.load); + }; + + page.load = function() { + map.addObject(OSM.mapParams().object, {zoom: true}); + }; + + page.unload = function() { + map.removeObject(); + }; + + return page; + }; + + var router = OSM.Router({ + "/": OSM.Index(map), + "/export": OSM.Export(map), + "/browse/changesets": OSM.ChangesetList(map), + "/browse/:type/:id(/history)": OSM.Browse(map) + }); + + $(document).on("click", "a", function(e) { + if (router(this.pathname + this.search + this.hash)) e.preventDefault(); + }); }); diff --git a/app/assets/javascripts/index/changeset.js b/app/assets/javascripts/index/changeset.js new file mode 100644 index 000000000..d87bf637b --- /dev/null +++ b/app/assets/javascripts/index/changeset.js @@ -0,0 +1,75 @@ +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_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/export.js b/app/assets/javascripts/index/export.js index 01d21cea2..70684eca9 100644 --- a/app/assets/javascripts/index/export.js +++ b/app/assets/javascripts/index/export.js @@ -1,71 +1,76 @@ -function initializeExport(map) { - if (window.location.pathname == "/export") { - startExport(); +OSM.Export = function(map) { + var page = {}; + + var locationFilter = new L.LocationFilter({ + enableButton: false, + adjustButton: false + }).on("change", update); + + function getBounds() { + return L.latLngBounds( + L.latLng($("#minlat").val(), $("#minlon").val()), + L.latLng($("#maxlat").val(), $("#maxlon").val())); } - function startExport() { - var locationFilter = new L.LocationFilter({ - enableButton: false, - adjustButton: false - }).addTo(map); + function boundsChanged() { + var bounds = getBounds(); - update(); - - locationFilter.on("change", update); - - map.on("moveend", update); + map.fitBounds(bounds); + locationFilter.setBounds(bounds); - $("#maxlat,#minlon,#maxlon,#minlat").change(boundsChanged); + enableFilter(); + validateControls(); + } - $("#drag_box").click(enableFilter); + function enableFilter() { + if (!locationFilter.getBounds().isValid()) { + locationFilter.setBounds(map.getBounds().pad(-0.2)); + } - setBounds(map.getBounds()); + $("#drag_box").hide(); + locationFilter.enable(); + } - $("#sidebar").one("closed", function () { - map.removeLayer(locationFilter); - map.off("moveend", update); - locationFilter.off("change", update); - }); + function update() { + setBounds(locationFilter.isEnabled() ? locationFilter.getBounds() : map.getBounds()); + validateControls(); + } - function getBounds() { - return L.latLngBounds(L.latLng($("#minlat").val(), $("#minlon").val()), - L.latLng($("#maxlat").val(), $("#maxlon").val())); - } + function setBounds(bounds) { + var precision = zoomPrecision(map.getZoom()); + $("#minlon").val(bounds.getWest().toFixed(precision)); + $("#minlat").val(bounds.getSouth().toFixed(precision)); + $("#maxlon").val(bounds.getEast().toFixed(precision)); + $("#maxlat").val(bounds.getNorth().toFixed(precision)); + } - function boundsChanged() { - var bounds = getBounds(); + function validateControls() { + $("#export_osm_too_large").toggle(getBounds().getSize() > OSM.MAX_REQUEST_AREA); + } - map.fitBounds(bounds); - locationFilter.setBounds(bounds); + page.pushstate = page.popstate = function(path) { + $("#export_tab").addClass("current"); + $('#sidebar_content').load(path, page.load); + }; - enableFilter(); - validateControls(); - } + page.load = function() { + map + .addLayer(locationFilter) + .on("moveend", update); - function enableFilter() { - if (!locationFilter.getBounds().isValid()) { - locationFilter.setBounds(map.getBounds().pad(-0.2)); - } + $("#maxlat, #minlon, #maxlon, #minlat").change(boundsChanged); + $("#drag_box").click(enableFilter); - $("#drag_box").hide(); - locationFilter.enable(); - } + update(); + }; - function update() { - setBounds(locationFilter.isEnabled() ? locationFilter.getBounds() : map.getBounds()); - validateControls(); - } + page.unload = function() { + map + .removeLayer(locationFilter) + .off("moveend", update); - function setBounds(bounds) { - var precision = zoomPrecision(map.getZoom()); - $("#minlon").val(bounds.getWest().toFixed(precision)); - $("#minlat").val(bounds.getSouth().toFixed(precision)); - $("#maxlon").val(bounds.getEast().toFixed(precision)); - $("#maxlat").val(bounds.getNorth().toFixed(precision)); - } + $("#export_tab").removeClass("current"); + }; - function validateControls() { - $("#export_osm_too_large").toggle(getBounds().getSize() > OSM.MAX_REQUEST_AREA); - } - } -} + return page; +}; diff --git a/app/assets/javascripts/router.js b/app/assets/javascripts/router.js new file mode 100644 index 000000000..7b2e99546 --- /dev/null +++ b/app/assets/javascripts/router.js @@ -0,0 +1,78 @@ +OSM.Router = function(rts) { + var escapeRegExp = /[\-{}\[\]+?.,\\\^$|#\s]/g; + var optionalParam = /\((.*?)\)/g; + var namedParam = /(\(\?)?:\w+/g; + var splatParam = /\*\w+/g; + + function Route(path, controller) { + var regexp = new RegExp('^' + + path.replace(escapeRegExp, '\\$&') + .replace(optionalParam, '(?:$1)?') + .replace(namedParam, function(match, optional){ + return optional ? match : '([^\/]+)'; + }) + .replace(splatParam, '(.*?)') + '(?:$|[?#])'); + + var route = {}; + + route.match = function(path) { + return regexp.test(path); + }; + + route.run = function(action, path) { + var params = []; + + if (path) { + params = regexp.exec(path).map(function(param, i) { + return (i > 0 && param) ? decodeURIComponent(param) : param; + }); + } + + (controller[action] || $.noop).apply(controller, params); + }; + + return route; + } + + var routes = []; + for (var r in rts) + routes.push(Route(r, rts[r])); + + routes.recognize = function(path) { + for (var i = 0; i < this.length; i++) { + if (this[i].match(path)) return this[i]; + } + }; + + var currentPath = window.location.pathname, + currentRoute = routes.recognize(currentPath); + + currentRoute.run('load', currentPath); + + if (window.history && window.history.pushState) { + $(window).on('popstate', function() { + var path = window.location.pathname; + if (path === currentPath) return; + currentRoute.run('unload'); + currentPath = path; + currentRoute = routes.recognize(currentPath); + currentRoute.run('popstate', currentPath); + }); + + return function (url) { + var path = url.replace(/#.*/, ''), + route = routes.recognize(path); + if (!route) return false; + window.history.pushState({}, document.title, url); + currentRoute.run('unload'); + currentPath = path; + currentRoute = route; + currentRoute.run('pushstate', currentPath); + return true; + } + } else { + return function (url) { + window.location.assign(url); + } + } +}; diff --git a/app/assets/javascripts/sidebar.js b/app/assets/javascripts/sidebar.js deleted file mode 100644 index 21a4380dc..000000000 --- a/app/assets/javascripts/sidebar.js +++ /dev/null @@ -1,11 +0,0 @@ -function closeSidebar() { - $("#sidebar") - .trigger("closed"); -} - -$(document).ready(function () { - $(".sidebar_close").click(function (e) { - closeSidebar(); - e.preventDefault(); - }); -}); diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 12cdb15d4..97ab5abfc 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -422,6 +422,10 @@ class ApplicationController < ActionController::Base request.body.rewind end + def map_layout + request.xhr? ? false : 'map' + end + def preferred_editor editor = if params[:editor] params[:editor] diff --git a/app/controllers/browse_controller.rb b/app/controllers/browse_controller.rb index 638cb7302..9f0683aef 100644 --- a/app/controllers/browse_controller.rb +++ b/app/controllers/browse_controller.rb @@ -1,5 +1,5 @@ class BrowseController < ApplicationController - layout 'map' + layout :map_layout before_filter :authorize_web before_filter :set_locale diff --git a/app/controllers/changeset_controller.rb b/app/controllers/changeset_controller.rb index 727522510..89d244907 100644 --- a/app/controllers/changeset_controller.rb +++ b/app/controllers/changeset_controller.rb @@ -321,7 +321,7 @@ class ChangesetController < ApplicationController @edits = changesets.order("changesets.created_at DESC").offset((@page - 1) * @page_size).limit(@page_size).preload(:user, :changeset_tags) - render :action => :list, :layout => 'map' + render :action => :list, :layout => map_layout end end diff --git a/app/controllers/site_controller.rb b/app/controllers/site_controller.rb index cb57ddc3a..eb8749f6f 100644 --- a/app/controllers/site_controller.rb +++ b/app/controllers/site_controller.rb @@ -1,6 +1,6 @@ class SiteController < ApplicationController layout 'site' - layout 'map', :only => [:index, :export] + layout :map_layout, :only => [:index, :export] before_filter :authorize_web before_filter :set_locale diff --git a/app/views/changeset/list.html.erb b/app/views/changeset/list.html.erb index fc135c6ca..42b5d3e1e 100644 --- a/app/views/changeset/list.html.erb +++ b/app/views/changeset/list.html.erb @@ -1,6 +1,4 @@ <% content_for :head do -%> - <%= javascript_include_tag "changeset" %> - <% unless params[:friends] or params[:nearby] -%> <%= auto_discovery_link_tag :atom, params.merge({ :page => nil, :action => :feed }) %> <% end -%> diff --git a/config/environments/production.rb b/config/environments/production.rb index bbb1c55ef..3eb08ac6c 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -69,7 +69,7 @@ OpenStreetMap::Application.configure do # Precompile additional assets. # application.js, application.css, and all non-JS/CSS in app/assets folder are already added. - config.assets.precompile += %w( index.js browse.js changeset.js welcome.js ) + config.assets.precompile += %w( index.js browse.js welcome.js ) config.assets.precompile += %w( user.js diary_entry.js pngfix.js swfobject.js ) config.assets.precompile += %w( large-ltr.css small-ltr.css print-ltr.css ) config.assets.precompile += %w( large-rtl.css small-rtl.css print-rtl.css ) -- 2.39.5