diff options
author | Zarino Zappia <zarino@mysociety.org> | 2016-06-17 13:39:06 +0100 |
---|---|---|
committer | Matthew Somerville <matthew-github@dracos.co.uk> | 2016-07-11 15:15:14 +0100 |
commit | ee7234891fd1d3d57f9b3e51df3d6dda49e465d2 (patch) | |
tree | ed2bcdb4739b0083ca5db5b010d55ab8c3dad1bd /web | |
parent | bdaf17a566b235865ad004e427c6f2230df39f24 (diff) |
Load in report details on around page via ajax.
Include URL changing, using history.pushState.
Show a larger marker icon for the selected report.
Make sure title is updated, and correct sub_map_links are shown.
The /report/new template is now wrapped in a <div id="side-form">
rather than <div id="side"> for consistency with the /around page,
which keeps display_all_reports_in_area() simpler, because it can
always assume "#side" means "list of reports".
Diffstat (limited to 'web')
-rw-r--r-- | web/cobrands/fixmystreet/fixmystreet.js | 229 | ||||
-rw-r--r-- | web/cobrands/sass/_base.scss | 25 | ||||
-rw-r--r-- | web/js/map-OpenLayers.js | 51 | ||||
-rw-r--r-- | web/js/map-google.js | 2 |
4 files changed, 270 insertions, 37 deletions
diff --git a/web/cobrands/fixmystreet/fixmystreet.js b/web/cobrands/fixmystreet/fixmystreet.js index a7a6977bd..b971b423d 100644 --- a/web/cobrands/fixmystreet/fixmystreet.js +++ b/web/cobrands/fixmystreet/fixmystreet.js @@ -208,13 +208,23 @@ $.extend(fixmystreet.set_up, { }).resize(); }, - dropzone: function() { + dropzone: function($context) { + + // Pass a jQuery element, eg $('.foobar'), into this function + // to limit all the selectors to that element. Handy if you want + // to only bind/detect Dropzones in a particular part of the page, + // or if your selectors (eg: "#form_photo") aren't unique across + // the whole page. + if (typeof $context === undefined) { + $context = $(document); + } + if ('Dropzone' in window) { Dropzone.autoDiscover = false; } - if ('Dropzone' in window && $('#form_photo').length) { - var $originalLabel = $('[for="form_photo"]'); - var $originalInput = $('#form_photos'); + if ('Dropzone' in window && $('#form_photo', $context).length) { + var $originalLabel = $('[for="form_photo"]', $context); + var $originalInput = $('#form_photos', $context); var $dropzone = $('<div>').addClass('dropzone'); $originalLabel.removeAttr('for'); @@ -241,24 +251,24 @@ $.extend(fixmystreet.set_up, { }, init: function() { this.on("addedfile", function(file) { - $('input[type=submit]').prop("disabled", true).removeClass('green-btn'); + $('input[type=submit]', $context).prop("disabled", true).removeClass('green-btn'); }); this.on("queuecomplete", function() { - $('input[type=submit]').removeAttr('disabled').addClass('green-btn'); + $('input[type=submit]', $context).removeAttr('disabled').addClass('green-btn'); }); this.on("success", function(file, xhrResponse) { - var ids = $('input[name=upload_fileid]').val().split(','), + var ids = $('input[name=upload_fileid]', $context).val().split(','), id = (file.server_id = xhrResponse.id), l = ids.push(id), newstr = ids.join(','); - $('input[name=upload_fileid]').val(newstr); + $('input[name=upload_fileid]', $context).val(newstr); }); this.on("error", function(file, errorMessage, xhrResponse) { }); this.on("removedfile", function(file) { - var ids = $('input[name=upload_fileid]').val().split(','), + var ids = $('input[name=upload_fileid]', $context).val().split(','), newstr = $.grep(ids, function(n) { return (n!=file.server_id); }).join(','); - $('input[name=upload_fileid]').val(newstr); + $('input[name=upload_fileid]', $context).val(newstr); }); this.on("maxfilesexceeded", function(file) { this.removeFile(file); @@ -274,7 +284,7 @@ $.extend(fixmystreet.set_up, { } }); - $.each($('input[name=upload_fileid]').val().split(','), function(i, f) { + $.each($('input[name=upload_fileid]', $context).val().split(','), function(i, f) { if (!f) { return; } @@ -314,7 +324,9 @@ $.extend(fixmystreet.set_up, { if ($('#sub_map_links').length === 0) { $('<p id="sub_map_links" />').insertAfter($('#map')); } - $('#sub_map_links').append('<a href="#" id="map_permalink">' + translation_strings.permalink + '</a>'); + if ($('#map_permalink').length === 0) { + $('#sub_map_links').append('<a href="#" id="map_permalink">' + translation_strings.permalink + '</a>'); + } } if ($('.mobile').length) { @@ -324,22 +336,25 @@ $.extend(fixmystreet.set_up, { $('#report-updates-data').insertAfter($('#map_box')); } - //add open/close toggle button on desk - $('#sub_map_links').prepend('<span id="map_links_toggle"> </span>'); - - //set up map_links_toggle click event - $('#map_links_toggle').on('click', function() { - var sub_map_links_css = {}, - left_right = isR2L() ? 'left' : 'right'; - if ($(this).hasClass('closed')) { - $(this).removeClass('closed'); - sub_map_links_css[left_right] = '0'; - } else { - $(this).addClass('closed'); - sub_map_links_css[left_right] = -$('#sub_map_links').width(); - } - $('#sub_map_links').animate(sub_map_links_css, 1200); - }); + //add open/close toggle button (if its not there) + if ($('#map_links_toggle').length === 0) { + $('<span>') + .html(' ') + .attr('id', 'map_links_toggle') + .on('click', function() { + var sub_map_links_css = {}, + left_right = isR2L() ? 'left' : 'right'; + if ($(this).hasClass('closed')) { + $(this).removeClass('closed'); + sub_map_links_css[left_right] = '0'; + } else { + $(this).addClass('closed'); + sub_map_links_css[left_right] = -$('#sub_map_links').width(); + } + $('#sub_map_links').animate(sub_map_links_css, 1200); + }) + .prependTo('#sub_map_links'); + } }, map_sidebar_key_tools: function() { @@ -439,6 +454,65 @@ $.extend(fixmystreet.set_up, { queue:false }).fadeOut(500); }); + }, + + ajax_history: function() { + $('#map_sidebar').on('click', '.item-list--reports a', function(e) { + e.preventDefault(); + var reportPageUrl = $(this).attr('href'); + var reportId = parseInt(reportPageUrl.replace(/^.*\/([0-9]+)$/, '$1'), 10); + + // If we've already selected this report + if (reportId == window.selected_problem_id) { + return; + } + + fixmystreet.display.report(reportPageUrl, reportId, function() { + // Since this navigation was the result of a user action, + // we want to record the navigation as a state, so the user + // can return to it later using their Back button. + if ('pushState' in history) { + history.pushState({ + reportId: reportId, + reportPageUrl: reportPageUrl + }, null, reportPageUrl); + } + }); + }); + + $('#map_sidebar').on('click', '.js-back-to-report-list', function(e) { + e.preventDefault(); + var reportListUrl = $(this).attr('href'); + fixmystreet.display.around(reportListUrl, function() { + // Since this navigation was the result of a user action, + // we want to record the navigation as a state, so the user + // can return to it later using their Back button. + if ('pushState' in history) { + history.pushState(null, null, reportListUrl); + } + }); + }); + + window.addEventListener('popstate', function(e) { + // The user has pressed the Back button, and there is a + // stored History state for them to return to. + + // Note: no pushState callbacks in these display_* calls, + // because we're already inside a popstate: We want to roll + // back to a previous state, not create a new one! + + if (e.state === null) { + // User has navigated Back from a pushStated state, presumably to + // see the list of all reports (which was shown on pageload). By + // this point, the browser has *already* updated the URL bar so + // location.href is something like foo.com/around?pc=abc-123, + // which we pass into fixmystreet.display.around() as a fallback + // incase the list isn't already in the DOM. + fixmystreet.display.around(window.location.href); + } else if ('reportId' in e.state) { + fixmystreet.display.report(e.state.reportPageUrl, e.state.reportId); + } + }); } }); @@ -493,7 +567,8 @@ fixmystreet.update_pin = function(lonlat) { }; -fixmystreet.begin_report = function(lonlat) { +fixmystreet.display = { + begin_report: function(lonlat) { fixmystreet.maps.begin_report(lonlat); // Store pin location in form fields, and check coverage of point @@ -582,10 +657,106 @@ fixmystreet.begin_report = function(lonlat) { fixmystreet.page = 'new'; location.hash = 'report'; + }, + + report: function(reportPageUrl, reportId, callback) { + $.ajax(reportPageUrl).done(function(html, textStatus, jqXHR) { + var $reportPage = $(html); + var $sideReport = $reportPage.find('#side-report'); + + if ($sideReport.length) { + $('#side').hide(); // Hide the list of reports + $('#side-report').remove(); // Remove any existing report page content from sidebar + $sideReport.appendTo('#map_sidebar'); // Insert this report's content + + var found = html.match(/<title>([\s\S]*?)<\/title>/); + var page_title = found[1]; + document.title = page_title; + fixmystreet.page = 'report'; + + fixmystreet.mobile_reporting.remove_ui(); + if ($('html').hasClass('mobile') && fixmystreet.map.updateSize) { + fixmystreet.map.updateSize(); + } + + // If this is the first individual report we've loaded, remove the + // "all reports" sub_map_links but store them in a global variable + // so we can reinsert them when the user returns to the all reports + // view. With #sub_map_links detached from the DOM, we set up the + // individual report's sub_map_links using map_controls(). + if (!('originalSubMapLinks' in window)) { + window.originalSubMapLinks = $('#sub_map_links').detach(); + } + fixmystreet.set_up.map_controls(); + + $sideReport.find('#key-tool-problems-nearby').addClass('js-back-to-report-list'); + fixmystreet.set_up.map_sidebar_key_tools(); + + fixmystreet.set_up.fancybox_images(); + fixmystreet.set_up.dropzone($sideReport); + fixmystreet.set_up.form_focus_triggers(); + + window.selected_problem_id = reportId; + var marker = fixmystreet.maps.get_marker_by_id(reportId); + if (fixmystreet.map.panTo && ($('html').hasClass('mobile') || !marker.onScreen())) { + fixmystreet.map.panTo( + marker.geometry.getBounds().getCenterLonLat() + ); + } + if (fixmystreet.maps.markers_resize) { + fixmystreet.maps.markers_resize(); // force a redraw so the selected marker gets bigger + } + + if (typeof callback === 'function') { + callback(); + } + + } else { + window.location.href = reportPageUrl; + } + + }).fail(function(jqXHR, textStatus, errorThrown) { + window.location.href = reportPageUrl; + + }); + }, + + around: function(reportListUrl, callback) { + // If the report list is already in the DOM, + // just reveal it, rather than loading new page. + if ($('#side').length) { + $('#side').show(); + $('#side-report').remove(); + + document.title = fixmystreet.original_title; + fixmystreet.page = 'around'; + if ($('html').hasClass('mobile')) { + fixmystreet.mobile_reporting.apply_ui(); + fixmystreet.map.updateSize(); + } + + if ('originalSubMapLinks' in window) { + $('#sub_map_links').replaceWith(window.originalSubMapLinks); + delete window.originalSubMapLinks; + } + fixmystreet.set_up.map_controls(); + + window.selected_problem_id = undefined; + fixmystreet.markers.refresh({force: true}); // force a redraw to return (de)selected marker to normal size + + if (typeof callback === 'function') { + callback(); + } + } else { + window.location.href = reportListUrl; + } + } }; + $(function() { window.cobrand = $('meta[name="cobrand"]').attr('content'); + fixmystreet.original_title = document.title; if (typeof variation !== 'undefined' && variation === 1) { $('input[name=variant]').val(1); diff --git a/web/cobrands/sass/_base.scss b/web/cobrands/sass/_base.scss index 6fd2c6a7b..c5f9db467 100644 --- a/web/cobrands/sass/_base.scss +++ b/web/cobrands/sass/_base.scss @@ -995,6 +995,22 @@ input.final-submit { } } +.problem-back { + display: block; + font-size: 1em; + line-height: 1.2em; + margin-bottom: 1em; + padding-bottom: 0.8em; + padding-#{$left}: 22px; + background: transparent url(/cobrands/fixmystreet/images/chevron-grey-#{$left}.svg) #{$left} 0 no-repeat; + background-size: 13px 16px; + border-bottom: 1px solid #eee; + + &:link, &:visited, &:hover { + color: #666; + } +} + .problem-header { margin-bottom: 1em; } @@ -1731,6 +1747,15 @@ table.nicetable { float: left; // float fallback for browsers that don't support flexbox flex: 1 0 auto; + @media(max-width: 400px){ + // Shameful hack to stop the control expanding wider than the window + // on narrow devices (eg: 320px iPhone), which would cause horizontal + // scrolling, and clipped text on the new report page, for example. + // Flexbox will add the spacing back in anyway. Ideally we'd only apply + // this style if flexbox is supported, but there's no easy way to do that. + padding: 0.75em 0; + } + &:hover, &:focus { background: #f3f3f3 linear-gradient(to bottom, #f9f9f9 0%, #e9e9e9 100%) 0 0 no-repeat; } diff --git a/web/js/map-OpenLayers.js b/web/js/map-OpenLayers.js index 345ebeced..872956716 100644 --- a/web/js/map-OpenLayers.js +++ b/web/js/map-OpenLayers.js @@ -38,7 +38,12 @@ var fixmystreet = fixmystreet || {}; markers_list: function(pins, transform) { var markers = []; - var size = fixmystreet.maps.marker_size_for_zoom(fixmystreet.map.getZoom() + fixmystreet.zoomOffset); + var size = fixmystreet.maps.marker_size_for_zoom( + fixmystreet.map.getZoom() + fixmystreet.zoomOffset + ); + var selected_size = fixmystreet.maps.selected_marker_size_for_zoom( + fixmystreet.map.getZoom() + fixmystreet.zoomOffset + ); for (var i=0; i<pins.length; i++) { var pin = pins[i]; var loc = new OpenLayers.Geometry.Point(pin[1], pin[0]); @@ -49,9 +54,10 @@ var fixmystreet = fixmystreet || {}; fixmystreet.map.getProjectionObject() ); } + var marker_size = (pin[3] === window.selected_problem_id) ? selected_size : size; var marker = new OpenLayers.Feature.Vector(loc, { colour: pin[2], - size: pin[5] || size, + size: pin[5] || marker_size, faded: 0, id: pin[3], title: pin[4] || '' @@ -62,21 +68,44 @@ var fixmystreet = fixmystreet || {}; }, markers_resize: function() { - var size = fixmystreet.maps.marker_size_for_zoom(fixmystreet.map.getZoom() + fixmystreet.zoomOffset); + var size = fixmystreet.maps.marker_size_for_zoom( + fixmystreet.map.getZoom() + fixmystreet.zoomOffset + ); + var selected_size = fixmystreet.maps.selected_marker_size_for_zoom( + fixmystreet.map.getZoom() + fixmystreet.zoomOffset + ); for (var i = 0; i < fixmystreet.markers.features.length; i++) { - fixmystreet.markers.features[i].attributes.size = size; + if (fixmystreet.markers.features[i].attributes.id == window.selected_problem_id) { + fixmystreet.markers.features[i].attributes.size = selected_size; + } else { + fixmystreet.markers.features[i].attributes.size = size; + } } fixmystreet.markers.redraw(); }, + get_marker_by_id: function(problem_id) { + return fixmystreet.markers.getFeaturesByAttribute('id', problem_id)[0]; + }, + marker_size_for_zoom: function(zoom) { if (zoom >= 15) { - return 'normal'; + return window.selected_problem_id ? 'small' : 'normal'; } else if (zoom >= 13) { - return 'small'; + return window.selected_problem_id ? 'mini' : 'small'; } else { return 'mini'; } + }, + + selected_marker_size_for_zoom: function(zoom) { + if (zoom >= 15) { + return 'big'; + } else if (zoom >= 13) { + return 'normal'; + } else { + return 'small'; + } } }; @@ -671,8 +700,16 @@ OpenLayers.Control.Click = OpenLayers.Class(OpenLayers.Control, { }, trigger: function(e) { + // If we are looking at an individual report, and the report was + // ajaxed into the DOM from the all reports page, then clicking + // the map background should take us back to the all reports list. + if ($('.js-back-to-report-list').length) { + $('.js-back-to-report-list').trigger('click'); + return true; + } + var lonlat = fixmystreet.map.getLonLatFromViewPortPx(e.xy); - fixmystreet.begin_report(lonlat); + fixmystreet.display.begin_report(lonlat); if ( typeof ga !== 'undefined' && window.cobrand == 'fixmystreet' ) { ga('send', 'pageview', { 'page': '/map_click' } ); diff --git a/web/js/map-google.js b/web/js/map-google.js index a00d52ba1..fa2b6d90e 100644 --- a/web/js/map-google.js +++ b/web/js/map-google.js @@ -82,7 +82,7 @@ fixmystreet.maps = {}; function map_clicked(e) { var lonlat = e.latLng; - fixmystreet.begin_report(lonlat); + fixmystreet.display.begin_report(lonlat); } /* Pan data handler */ |