aboutsummaryrefslogtreecommitdiffstats
path: root/web
diff options
context:
space:
mode:
authorZarino Zappia <zarino@mysociety.org>2016-06-17 13:39:06 +0100
committerMatthew Somerville <matthew-github@dracos.co.uk>2016-07-11 15:15:14 +0100
commitee7234891fd1d3d57f9b3e51df3d6dda49e465d2 (patch)
treeed2bcdb4739b0083ca5db5b010d55ab8c3dad1bd /web
parentbdaf17a566b235865ad004e427c6f2230df39f24 (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.js229
-rw-r--r--web/cobrands/sass/_base.scss25
-rw-r--r--web/js/map-OpenLayers.js51
-rw-r--r--web/js/map-google.js2
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">&nbsp;</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('&nbsp;')
+ .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 */