diff options
Diffstat (limited to 'web/js')
-rw-r--r-- | web/js/contact.js | 14 | ||||
-rw-r--r-- | web/js/dashboard.js | 36 | ||||
-rw-r--r-- | web/js/duplicates.js | 188 | ||||
-rw-r--r-- | web/js/front.js | 16 | ||||
-rw-r--r-- | web/js/map-OpenLayers.js | 309 | ||||
-rw-r--r-- | web/js/map-OpenStreetMap.js | 2 | ||||
-rw-r--r-- | web/js/map-bing-ol.js | 10 | ||||
-rw-r--r-- | web/js/map-fms.js | 20 | ||||
-rw-r--r-- | web/js/map-google-ol.js | 2 | ||||
-rw-r--r-- | web/js/map-streetview.js | 2 | ||||
-rw-r--r-- | web/js/map-toner-lite.js | 2 | ||||
-rw-r--r-- | web/js/map-wmts-bristol.js | 2 | ||||
-rw-r--r-- | web/js/map-wmts-zurich.js | 2 | ||||
-rw-r--r-- | web/js/validation_rules.js | 4 |
14 files changed, 510 insertions, 99 deletions
diff --git a/web/js/contact.js b/web/js/contact.js new file mode 100644 index 000000000..9529ede16 --- /dev/null +++ b/web/js/contact.js @@ -0,0 +1,14 @@ +$('[name=dest]').change(function() { + var err = $('.form-error--' + this.value), + inputs = $(this).closest('form').find('input[type=text], input[type=submit]'); + $('.form-error__box').addClass('hidden'); + if (err.length) { + $('#dest-error').removeClass('hidden'); + $('#dest-error .form-error').show(); // might have been hidden by normal validate + inputs.prop('disabled', true); + $('.form-error--' + this.value).removeClass('hidden'); + } else { + $('#dest-error').addClass('hidden'); + inputs.prop('disabled', false); + } +}); diff --git a/web/js/dashboard.js b/web/js/dashboard.js index 82b5e6188..48a273ed6 100644 --- a/web/js/dashboard.js +++ b/web/js/dashboard.js @@ -30,6 +30,9 @@ $(function(){ var lasty = 0; $.each(chart.config.data.datasets, function(datasetIndex, dataset){ + if (dataset.data.length == 0) { + return; + } var $label = $('.label[data-datasetIndex="' + datasetIndex + '"]', $parent); var latestPoint = chart.getDatasetMeta(datasetIndex).data[ dataset.data.length - 1 ]; var y = latestPoint._model.y; @@ -136,21 +139,26 @@ $(function(){ data0 = $allReports.data('values-reports'), data1 = $allReports.data('values-fixed'); - window.chartAllReports = new Chart($allReports, { - type: 'line', - data: { - labels: labels, - datasets: [{ - data: data0, - pointRadius: pointRadiusFinalDot(data0.length, 4), - pointBackgroundColor: colours[1], - borderColor: colours[1] - }, { + var data = [{ + data: data0, + pointRadius: pointRadiusFinalDot(data0.length, 4), + pointBackgroundColor: colours[1], + borderColor: colours[1] + }]; + if ( data1 ) { + data.push({ data: data1, pointRadius: pointRadiusFinalDot(data1.length, 4), pointBackgroundColor: colours[3], borderColor: colours[3] - }] + }); + } + + window.chartAllReports = new Chart($allReports, { + type: 'line', + data: { + labels: labels, + datasets: data }, options: { animation: { @@ -193,9 +201,11 @@ $(function(){ var $table = $(this); var $trs = $table.find('tr'); var $wrapper = $('<div>').addClass('responsive-bar-chart').insertBefore($table); + var canvasWidth = $table.attr('data-canvas-width') || 600; + var rowHeight = $table.attr('data-row-height') || 30; var $canvas = $('<canvas>').attr({ - 'width': 600, - 'height': 30 * $trs.length + 'width': canvasWidth, + 'height': rowHeight * $trs.length }).appendTo($wrapper); var rowLabels = []; var rowValues = []; diff --git a/web/js/duplicates.js b/web/js/duplicates.js new file mode 100644 index 000000000..723c357e9 --- /dev/null +++ b/web/js/duplicates.js @@ -0,0 +1,188 @@ +(function() { + + // Store a reference to the "duplicate" report pins so we can + // quickly remove them when we’re finished showing duplicates. + var current_duplicate_markers; + + // Report ID will be available on report inspect page, + // but undefined on new report page. + var report_id = $("#report_inspect_form .js-report-id").text() || undefined; + + function refresh_duplicate_list() { + var category = $('select[name="category"]').val(); + if (category === '-- Pick a category --') { + return; + } + + var nearby_url; + var url_params = { + filter_category: category, + latitude: $('input[name="latitude"]').val(), + longitude: $('input[name="longitude"]').val() + }; + + if ( report_id ) { + nearby_url = '/report/' + report_id + '/nearby.json'; + url_params.distance = 1000; // Inspectors might want to see reports fairly far away (1000 metres) + url_params.pin_size = 'small'; // How it's always been + } else { + nearby_url = '/around/nearby'; + url_params.distance = 250; // Only want to bother public with very nearby reports (250 metres) + url_params.pin_size = 'normal'; + } + + $.ajax({ + url: nearby_url, + data: url_params, + dataType: 'json' + }).done(function(response) { + if ( response.pins.length ){ + render_duplicate_list(response); + render_duplicate_pins(response); + } else { + remove_duplicate_pins(); + remove_duplicate_list(); + } + }).fail(function(){ + remove_duplicate_pins(); + remove_duplicate_list(); + }); + } + + function render_duplicate_list(api_response) { + var $reports = $( api_response.reports_list ); + + var duplicate_of = $('#report_inspect_form [name="duplicate_of"]').val(); + if ( duplicate_of ) { + $reports.filter('[data-report-id="' + duplicate_of + '"]') + .addClass("item-list__item--selected"); + } + + $("#js-duplicate-reports ul").empty().prepend( $reports ); + fixmystreet.set_up.fancybox_images(); + + $('#js-duplicate-reports').hide().removeClass('hidden').slideDown(); + if ( $('#problem_form').length ) { + $('.js-hide-if-invalid-category').slideUp(); + } + + // Highlight map pin when hovering associated list item. + var timeout; + $reports.on('mouseenter', function(){ + var id = parseInt( $(this).data('reportId'), 10 ); + clearTimeout( timeout ); + fixmystreet.maps.markers_highlight( id ); + }).on('mouseleave', function(){ + timeout = setTimeout( fixmystreet.maps.markers_highlight, 50 ); + }); + + // Add a "select this report" button, when on the report inspect form. + if ( $('#report_inspect_form').length ) { + $reports.each(function(){ + var $button = $('<button>').addClass('btn btn--small btn--primary'); + $button.text(translation_strings.this_report); + $button.on('click', function(e) { + e.preventDefault(); // Prevent button from submitting parent form + var report_id = $(this).closest('li').data('reportId'); + $('#report_inspect_form [name="duplicate_of"]').val(report_id); + $(this).closest('li') + .addClass('item-list__item--selected') + .siblings('.item-list__item--selected') + .removeClass('item-list__item--selected'); + }); + $(this).find('.item-list__item--expandable__actions').append($button); + }); + } + + // Add a "track this report" button when on the regular reporting form. + if ( $('#problem_form').length ) { + $reports.each(function() { + var $li = $(this); + var id = parseInt( $li.data('reportId'), 10 ); + var alert_url = '/alert/subscribe?id=' + encodeURIComponent(id); + var $button = $('<a>').addClass('btn btn--small btn--primary'); + $button.text(translation_strings.this_is_the_problem); + $button.attr('href', alert_url); + $button.on('click', function(e){ + e.preventDefault(); + var $div = $('.js-template-get-updates > div').clone(); + $div.find('input[name="id"]').val(id); + $div.find('input[disabled]').prop('disabled', false); + $div.hide().appendTo($li).slideDown(250, function(){ + $div.find('input[type="email"]').focus(); + }); + $li.find('.item-list__item--expandable__actions').slideUp(250); + $li.removeClass('js-expandable'); + $li.addClass('item-list__item--selected'); + }); + $li.find('.item-list__item--expandable__actions').append($button); + }); + } + } + + function render_duplicate_pins(api_response) { + var markers = fixmystreet.maps.markers_list( api_response.pins, true ); + fixmystreet.markers.removeFeatures( current_duplicate_markers ); + fixmystreet.markers.addFeatures( markers ); + current_duplicate_markers = markers; + } + + function remove_duplicate_list(cb) { + var animations = []; + + animations.push( $.Deferred() ); + $('#js-duplicate-reports').slideUp(function(){ + $(this).addClass('hidden'); + $(this).find('ul').empty(); + animations[0].resolve(); + }); + if ( $('#problem_form').length ) { + animations.push( $.Deferred() ); + $('.js-hide-if-invalid-category').slideDown(function(){ + animations[1].resolve(); + }); + } + + $.when.apply(this, animations).then(cb); + } + + function remove_duplicate_pins() { + fixmystreet.markers.removeFeatures( current_duplicate_markers ); + } + + function inspect_form_state_change() { + // The duplicate report list only makes sense when state is 'duplicate' + if ($(this).val() !== "duplicate") { + $("#js-duplicate-reports").addClass("hidden"); + return; + } else { + $("#js-duplicate-reports").removeClass("hidden"); + } + // If this report is already marked as a duplicate of another, then + // there's no need to refresh the list of duplicate reports + var duplicate_of = $("#report_inspect_form [name=duplicate_of]").val(); + if (!!duplicate_of) { + return; + } + refresh_duplicate_list(); + } + + // Want to show potential duplicates when a regular user starts a new + // report, or changes the category/location of a partial report. + $(fixmystreet).on('report_new:category_change', refresh_duplicate_list); + + // Want to show duplicates when an inspector sets a report’s state to "duplicate". + $(document).on('change.state', "#report_inspect_form select#state", inspect_form_state_change); + + // Also want to give inspectors a way to select a *new* duplicate report. + $(document).on('click', "#js-change-duplicate-report", refresh_duplicate_list); + + $('.js-hide-duplicate-suggestions').on('click', function(e){ + e.preventDefault(); + remove_duplicate_pins(); + remove_duplicate_list(function(){ + $('#form_title').focus(); + }); + }); + +})(); diff --git a/web/js/front.js b/web/js/front.js index 67486888b..1efa516fd 100644 --- a/web/js/front.js +++ b/web/js/front.js @@ -10,6 +10,7 @@ document.getElementById('pc').focus(); el.value = 1; form.insertBefore(el, form.firstChild); } + var around_links = document.querySelectorAll('a[href*="around"]'); for (i=0; i<around_links.length; i++) { var link = around_links[i]; @@ -17,9 +18,20 @@ document.getElementById('pc').focus(); } var lk = document.querySelector('span.report-a-problem-btn'); - if (lk.addEventListener) { - lk.addEventListener('click', function(){ + if (lk && lk.addEventListener) { + lk.addEventListener('click', function(e){ + e.preventDefault(); + scrollTo(0,0); + document.getElementById('pc').focus(); + }); + } + + var cta = document.getElementById('report-cta'); + if (cta && cta.addEventListener) { + cta.addEventListener('click', function(e) { + e.preventDefault(); scrollTo(0,0); + document.getElementById('pc').focus(); }); } })(); diff --git a/web/js/map-OpenLayers.js b/web/js/map-OpenLayers.js index 8f84e5c94..ae86269c9 100644 --- a/web/js/map-OpenLayers.js +++ b/web/js/map-OpenLayers.js @@ -15,6 +15,60 @@ var fixmystreet = fixmystreet || {}; fixmystreet.utils = fixmystreet.utils || {}; $.extend(fixmystreet.utils, { + array_to_csv_line: function(arr) { + var out = [], s; + for (var i=0; i<arr.length; i++) { + s = arr[i]; + if (/[",]/.test(s)) { + s = '"' + s.replace('"', '""') + '"'; + } + out.push(s); + } + return out.join(','); + }, + + // https://stackoverflow.com/questions/1293147/javascript-code-to-parse-csv-data/1293163#1293163 + csv_to_array: function( strData, strDelimiter ) { + strDelimiter = (strDelimiter || ","); + + var objPattern = new RegExp( + ( + "(\\" + strDelimiter + "|\\r?\\n|\\r|^)" + + "(?:\"([^\"]*(?:\"\"[^\"]*)*)\"|" + + "([^\"\\" + strDelimiter + "\\r\\n]*))" + ), + "gi" + ); + + var arrData = [[]]; + + var arrMatches = objPattern.exec( strData ); + while (arrMatches) { + + var strMatchedDelimiter = arrMatches[ 1 ]; + + if ( strMatchedDelimiter.length && + strMatchedDelimiter !== strDelimiter) { + arrData.push( [] ); + } + + var strMatchedValue; + if (arrMatches[ 2 ]) { + strMatchedValue = arrMatches[ 2 ].replace( + new RegExp( "\"\"", "g" ), + "\"" + ); + } else { + strMatchedValue = arrMatches[ 3 ]; + } + + arrData[ arrData.length - 1 ].push( strMatchedValue ); + arrMatches = objPattern.exec( strData ); + } + + return( arrData ); + }, + parse_query_string: function() { var qs = {}; if (!location.search) { @@ -124,12 +178,19 @@ $.extend(fixmystreet.utils, { return lonlat; }, + setup_inspector: function() { + setup_inspector_marker_drag(); + }, + markers_list: function(pins, transform) { var markers = []; var size = fixmystreet.maps.marker_size(); var selected_size = fixmystreet.maps.selected_marker_size(); for (var i=0; i<pins.length; i++) { var pin = pins[i]; + if (pin[1] == 0 && pin[0] == 0) { + continue; + } var loc = new OpenLayers.Geometry.Point(pin[1], pin[0]); if (transform) { // The Strategy does this for us, so don't do it in that case. @@ -140,13 +201,14 @@ $.extend(fixmystreet.utils, { } var id = pin[3] === undefined ? pin[3] : +pin[3]; var marker_size = (id === window.selected_problem_id) ? selected_size : size; + var draggable = (id === window.selected_problem_id) ? true : (pin[6] === false ? false : true); var marker = new OpenLayers.Feature.Vector(loc, { colour: pin[2], size: pin[5] || marker_size, faded: 0, id: id, title: pin[4] || '', - draggable: pin[6] === false ? false : true + draggable: draggable }); markers.push( marker ); } @@ -157,10 +219,13 @@ $.extend(fixmystreet.utils, { var size = fixmystreet.maps.marker_size(); var selected_size = fixmystreet.maps.selected_marker_size(); for (var i = 0; i < fixmystreet.markers.features.length; i++) { - if (fixmystreet.markers.features[i].attributes.id == window.selected_problem_id) { - fixmystreet.markers.features[i].attributes.size = selected_size; + var attr = fixmystreet.markers.features[i].attributes; + if (attr.id == window.selected_problem_id) { + attr.size = selected_size; + attr.draggable = true; } else { - fixmystreet.markers.features[i].attributes.size = size; + attr.size = size; + attr.draggable = false; } } fixmystreet.markers.redraw(); @@ -219,6 +284,8 @@ $.extend(fixmystreet.utils, { } } } ); + // Allow handled feature click propagation to other click handlers + drag.handlers.feature.stopClick = false; fixmystreet.map.addControl( drag ); drag.activate(); }, @@ -276,13 +343,29 @@ $.extend(fixmystreet.utils, { $('#loading-indicator').attr('aria-hidden', true); } } + }, + + get_map_state: function() { + var centre = fixmystreet.map.getCenter(); + return { + zoom: fixmystreet.map.getZoom(), + lat: centre.lat, + lon: centre.lon, + }; + }, + + set_map_state: function(state) { + fixmystreet.map.setCenter( + new OpenLayers.LonLat( state.lon, state.lat ), + state.zoom + ); } }); /* Make sure pins aren't going to reload just because we're zooming out, * we already have the pins when the page loaded */ function zoomToBounds(bounds) { - if (!bounds) { return; } + if (!bounds || !fixmystreet.markers.strategies) { return; } var strategy = fixmystreet.markers.strategies[0]; strategy.deactivate(); var center = bounds.getCenterLonLat(); @@ -298,15 +381,15 @@ $.extend(fixmystreet.utils, { function sidebar_highlight(problem_id) { if (typeof problem_id !== 'undefined') { - var $a = $('.item-list--reports a[href$="/' + problem_id + '"]'); - $a.parent().addClass('hovered'); + var $li = $('[data-report-id="' + problem_id + '"]'); + $li.addClass('hovered'); } else { - $('.item-list--reports .hovered').removeClass('hovered'); + $('.item-list .hovered').removeClass('hovered'); } } function marker_click(problem_id, evt) { - var $a = $('.item-list--reports a[href$="/' + problem_id + '"]'); + var $a = $('.item-list a[href$="/' + problem_id + '"]'); if (!$a[0]) { return; } @@ -348,11 +431,25 @@ $.extend(fixmystreet.utils, { } function replace_query_parameter(qs, id, key) { - var value = $('#' + id).val(); - if (value) { - qs[key] = (typeof value === 'string') ? value : value.join(','); + var value, + $el = $('#' + id); + if (!$el[0]) { + return; + } + if ( $el[0].type === 'checkbox' ) { + value = $el[0].checked ? '1' : ''; + if (value) { + qs[key] = value; + } else { + delete qs[key]; + } } else { - delete qs[key]; + value = $el.val(); + if (value) { + qs[key] = (typeof value === 'string') ? value : fixmystreet.utils.array_to_csv_line(value); + } else { + delete qs[key]; + } } return value; } @@ -369,22 +466,33 @@ $.extend(fixmystreet.utils, { return new_url; } + function update_history(qs, data) { + var new_url = update_url(qs); + history.pushState(data, null, new_url); + + // Ensure the permalink control is updated when the filters change + var permalink_controls = fixmystreet.map.getControlsByClass(/Permalink/); + if (permalink_controls.length) { + permalink_controls[0].updateLink(); + } + } + function page_changed_history() { if (!('pushState' in history)) { return; } var qs = fixmystreet.utils.parse_query_string(); - var page = $('.pagination').data('page'); + var show_old_reports = replace_query_parameter(qs, 'show_old_reports', 'show_old_reports'); + var page = $('.pagination:first').data('page'); if (page > 1) { qs.p = page; } else { delete qs.p; } - var new_url = update_url(qs); - history.pushState({ - page_change: { 'page': page } - }, null, new_url); + update_history(qs, { + page_change: { 'page': page, 'show_old_reports': show_old_reports } + }); } function categories_or_status_changed_history() { @@ -395,11 +503,11 @@ $.extend(fixmystreet.utils, { var filter_categories = replace_query_parameter(qs, 'filter_categories', 'filter_category'); var filter_statuses = replace_query_parameter(qs, 'statuses', 'status'); var sort_key = replace_query_parameter(qs, 'sort', 'sort'); + var show_old_reports = replace_query_parameter(qs, 'show_old_reports', 'show_old_reports'); delete qs.p; - var new_url = update_url(qs); - history.pushState({ - filter_change: { 'filter_categories': filter_categories, 'statuses': filter_statuses, 'sort': sort_key } - }, null, new_url); + update_history(qs, { + filter_change: { 'filter_categories': filter_categories, 'statuses': filter_statuses, 'sort': sort_key, 'show_old_reports': show_old_reports } + }); } function setup_inspector_marker_drag() { @@ -418,8 +526,8 @@ $.extend(fixmystreet.utils, { $("#problem_easting").text(bng.x.toFixed(1)); $("#problem_latitude").text(lonlat.y.toFixed(6)); $("#problem_longitude").text(lonlat.x.toFixed(6)); - $("form#report_inspect_form input[name=latitude]").val(lonlat.y); - $("form#report_inspect_form input[name=longitude]").val(lonlat.x); + $("input[name=latitude]").val(lonlat.y.toFixed(6)); + $("input[name=longitude]").val(lonlat.x.toFixed(6)); }, false); } @@ -452,7 +560,8 @@ $.extend(fixmystreet.utils, { $.extend(style.defaultStyle, { fillColor: 'black', strokeColor: 'black' }); } var geometry = this.features[0].geometry; - if (geometry.CLASS_NAME == 'OpenLayers.Geometry.Collection') { + if (geometry.CLASS_NAME == 'OpenLayers.Geometry.Collection' || + geometry.CLASS_NAME == 'OpenLayers.Geometry.MultiPolygon') { $.each(geometry.components, function(i, polygon) { new_geometry.addComponents(polygon.components); extent.extend(polygon.getBounds()); @@ -466,8 +575,10 @@ $.extend(fixmystreet.utils, { f.geometry = new_geometry; this.removeAllFeatures(); this.addFeatures([f]); - var qs = fixmystreet.utils.parse_query_string(); - if (!qs.bbox) { + // Look at original href here to know if location was present at load. + // If it was, we don't want to zoom out to the bounds of the area. + var qs = OpenLayers.Util.getParameters(fixmystreet.original.href); + if (!qs.bbox && !qs.lat && !qs.lon) { zoomToBounds(extent); } } else { @@ -479,8 +590,8 @@ $.extend(fixmystreet.utils, { renderers: ['SVGBig', 'VML', 'Canvas'], strategies: [ new OpenLayers.Strategy.Fixed() ], protocol: new OpenLayers.Protocol.HTTP({ - url: "/mapit/area/" + fixmystreet.area[i] + ".kml?simplify_tolerance=0.0001", - format: new OpenLayers.Format.KML() + url: "/mapit/area/" + fixmystreet.area[i] + ".geojson?simplify_tolerance=0.0001", + format: new OpenLayers.Format.GeoJSON() }) }); fixmystreet.map.addLayer(area); @@ -619,23 +730,26 @@ $.extend(fixmystreet.utils, { ); fixmystreet.map.addControl( fixmystreet.select_feature ); fixmystreet.select_feature.activate(); - fixmystreet.map.events.register( 'zoomend', null, fixmystreet.maps.markers_resize ); fixmystreet.map.events.register( 'zoomend', null, function() { - fixmystreet.run(fixmystreet.maps.show_shortlist_control); + fixmystreet.maps.markers_resize(); + $(fixmystreet).trigger('map:zoomend'); }); // Set up the event handlers to populate the filters and react to them changing $("#filter_categories").on("change.filters", categories_or_status_changed); $("#statuses").on("change.filters", categories_or_status_changed); $("#sort").on("change.filters", categories_or_status_changed); + $("#show_old_reports").on("change.filters", categories_or_status_changed); $('.js-pagination').on('change.filters', categories_or_status_changed); $('.js-pagination').on('click', 'a', function(e) { e.preventDefault(); - var page = $('.pagination').data('page'); - if ($(this).hasClass('next')) { - $('.pagination').data('page', page + 1); + var page = $('.pagination:first').data('page'); + if ($(this).hasClass('show_old')) { + $("#show_old_reports").prop('checked', true); + } else if ($(this).hasClass('next')) { + $('.pagination:first').data('page', page + 1); } else { - $('.pagination').data('page', page - 1); + $('.pagination:first').data('page', page - 1); } fixmystreet.markers.protocol.use_page = true; $(this).trigger('change'); @@ -643,6 +757,7 @@ $.extend(fixmystreet.utils, { $("#filter_categories").on("change.user", categories_or_status_changed_history); $("#statuses").on("change.user", categories_or_status_changed_history); $("#sort").on("change.user", categories_or_status_changed_history); + $("#show_old_reports").on("change.user", categories_or_status_changed_history); $('.js-pagination').on('click', 'a', page_changed_history); } else if (fixmystreet.page == 'new') { drag.activate(); @@ -715,14 +830,15 @@ $.extend(fixmystreet.utils, { fixmystreet.map.addLayer(layer); } - if (!fixmystreet.map.getCenter()) { - var centre = new OpenLayers.LonLat( fixmystreet.longitude, fixmystreet.latitude ); - centre.transform( - new OpenLayers.Projection("EPSG:4326"), - fixmystreet.map.getProjectionObject() + // map.getCenter() returns a position in "map units", but sometimes you + // want the center in GPS-style latitude/longitude coordinates (WGS84) + // for example, to pass as GET params to fixmystreet.com/report/new. + fixmystreet.map.getCenterWGS84 = function() { + return fixmystreet.map.getCenter().transform( + fixmystreet.map.getProjectionObject(), + new OpenLayers.Projection("EPSG:4326") ); - fixmystreet.map.setCenter(centre, fixmystreet.zoom || 3); - } + }; if (document.getElementById('mapForm')) { var click = fixmystreet.maps.click_control = new OpenLayers.Control.Click(); @@ -737,17 +853,26 @@ $.extend(fixmystreet.utils, { onload(); } - (function() { - var timeout; - $('#js-reports-list').on('mouseenter', '.item-list--reports__item', function(){ - var href = $('a', this).attr('href'); - var id = parseInt(href.replace(/^.*[\/]([0-9]+)$/, '$1'),10); - clearTimeout(timeout); - fixmystreet.maps.markers_highlight(id); - }).on('mouseleave', '.item-list--reports__item', function(){ - timeout = setTimeout(fixmystreet.maps.markers_highlight, 50); - }); - })(); + // Allow external scripts to react to pans/zooms on the map, + // by subscribing to $(fixmystreet).on('maps:update_view') + fixmystreet.map.events.register('moveend', null, function(){ + $(fixmystreet).trigger('maps:update_view'); + }); + + if (!fixmystreet.map.events.extensions.buttonclick.isDeviceTouchCapable) { + // On touchscreens go straight to the report (see #2294). + (function() { + var timeout; + $('#js-reports-list').on('mouseenter', '.item-list--reports__item', function(){ + var href = $('a', this).attr('href'); + var id = parseInt(href.replace(/^.*[\/]([0-9]+)$/, '$1'),10); + clearTimeout(timeout); + fixmystreet.maps.markers_highlight(id); + }).on('mouseleave', '.item-list--reports__item', function(){ + timeout = setTimeout(fixmystreet.maps.markers_highlight, 50); + }); + })(); + } }); // End maps closure @@ -786,9 +911,34 @@ OpenLayers.Control.PanZoomFMS = OpenLayers.Class(OpenLayers.Control.PanZoom, { } }); +OpenLayers.Control.ArgParserFMS = OpenLayers.Class(OpenLayers.Control.ArgParser, { + getParameters: function(url) { + var args = OpenLayers.Control.ArgParser.prototype.getParameters.apply(this, arguments); + // Get defaults from provided data if not in URL + if (!args.lat && !args.lon) { + args.lon = fixmystreet.longitude; + args.lat = fixmystreet.latitude; + } + if (args.lat && !args.zoom) { + args.zoom = fixmystreet.zoom || 3; + } + return args; + }, + + CLASS_NAME: "OpenLayers.Control.ArgParserFMS" +}); + /* Overriding Permalink so that it can pass the correct zoom to OSM */ OpenLayers.Control.PermalinkFMS = OpenLayers.Class(OpenLayers.Control.Permalink, { _updateLink: function(alter_zoom) { + // this.base was originally set in initialize(), but the window's href + // may have changed since then if e.g. the map filters have been updated. + // NB this won't change the base of the 'problems nearby' permalink on + // /report, as this would result in it pointing at the wrong page. + if (this.base !== '/around' && fixmystreet.page !== 'report') { + this.base = window.location.href; + } + var separator = this.anchor ? '#' : '?'; var href = this.base; if (href.indexOf(separator) != -1) { @@ -801,7 +951,17 @@ OpenLayers.Control.PermalinkFMS = OpenLayers.Class(OpenLayers.Control.Permalink, if ( alter_zoom ) { zoom += fixmystreet.zoomOffset; } - href += separator + OpenLayers.Util.getParameterString(this.createParams(center, zoom)); + + var params = this.createParams(center, zoom); + + // Strip out the ugly OpenLayers layers state string + delete params.layers; + if (params.lat && params.lon) { + // No need for the postcode string either, if we have a latlon + delete params.pc; + } + + href += separator + OpenLayers.Util.getParameterString(params); // Could use mlat/mlon here as well if we are on a page with a marker if (this.base == '/around') { href += '&js=1'; @@ -813,15 +973,21 @@ OpenLayers.Control.PermalinkFMS = OpenLayers.Class(OpenLayers.Control.Permalink, else { this.element.href = href; } + + if ('replaceState' in history) { + if (fixmystreet.page.match(/around|reports/)) { + history.replaceState( + history.state, + null, + href + ); + } + } }, updateLink: function() { this._updateLink(0); - } -}); -OpenLayers.Control.PermalinkFMSz = OpenLayers.Class(OpenLayers.Control.PermalinkFMS, { - updateLink: function() { - this._updateLink(1); - } + }, + CLASS_NAME: "OpenLayers.Control.PermalinkFMS" }); OpenLayers.Strategy.FixMyStreet = OpenLayers.Class(OpenLayers.Strategy.BBOX, { @@ -889,6 +1055,13 @@ OpenLayers.Strategy.FixMyStreetFixed = OpenLayers.Class(OpenLayers.Strategy.Fixe // is dragged (modulo a buffer extending outside the viewport). // This subclass is required so we can pass the 'filter_category' and 'status' query // params to /around?ajax if the user has filtered the map. + +fixmystreet.protocol_params = { + filter_category: 'filter_categories', + status: 'statuses', + sort: 'sort' +}; + OpenLayers.Protocol.FixMyStreet = OpenLayers.Class(OpenLayers.Protocol.HTTP, { initial_page: null, use_page: false, @@ -896,15 +1069,18 @@ OpenLayers.Protocol.FixMyStreet = OpenLayers.Class(OpenLayers.Protocol.HTTP, { read: function(options) { // Pass the values of the category, status, and sort fields as query params options.params = options.params || {}; - $.each({ filter_category: 'filter_categories', status: 'statuses', sort: 'sort' }, function(key, id) { + $.each(fixmystreet.protocol_params, function(key, id) { var val = $('#' + id).val(); - if (val !== undefined) { - options.params[key] = val; + if (val && val.length) { + options.params[key] = val.join ? fixmystreet.utils.array_to_csv_line(val) : val; } }); + if ( $('#show_old_reports').is(':checked') ) { + options.params.show_old_reports = 1; + } var page; if (this.use_page) { - page = $('.pagination').data('page'); + page = $('.pagination:first').data('page'); this.use_page = false; } else if (this.initial_page) { page = 1; @@ -930,6 +1106,11 @@ OpenLayers.Format.FixMyStreet = OpenLayers.Class(OpenLayers.Format.JSON, { var reports_list; if (typeof(obj.reports_list) != 'undefined' && (reports_list = document.getElementById('js-reports-list'))) { reports_list.innerHTML = obj.reports_list; + if ( $('.item-list--reports').data('show-old-reports') ) { + $('#show_old_reports_wrapper').removeClass('hidden'); + } else { + $('#show_old_reports_wrapper').addClass('hidden'); + } } if (typeof(obj.pagination) != 'undefined') { $('.js-pagination').html(obj.pagination); diff --git a/web/js/map-OpenStreetMap.js b/web/js/map-OpenStreetMap.js index 4165f8ee4..52eb95493 100644 --- a/web/js/map-OpenStreetMap.js +++ b/web/js/map-OpenStreetMap.js @@ -4,7 +4,7 @@ fixmystreet.maps.config = function() { permalink_id = 'map_permalink'; } fixmystreet.controls = [ - new OpenLayers.Control.ArgParser(), + new OpenLayers.Control.ArgParserFMS(), new OpenLayers.Control.Attribution(), //new OpenLayers.Control.LayerSwitcher(), new OpenLayers.Control.Navigation(), diff --git a/web/js/map-bing-ol.js b/web/js/map-bing-ol.js index 6662bf91a..6c9ab8a62 100644 --- a/web/js/map-bing-ol.js +++ b/web/js/map-bing-ol.js @@ -6,7 +6,7 @@ fixmystreet.maps.config = function() { fixmystreet.controls = [ new OpenLayers.Control.Attribution(), - new OpenLayers.Control.ArgParser(), + new OpenLayers.Control.ArgParserFMS(), new OpenLayers.Control.Navigation(), new OpenLayers.Control.PermalinkFMS(permalink_id), new OpenLayers.Control.PanZoomFMS({id: 'fms_pan_zoom' }) @@ -95,10 +95,10 @@ OpenLayers.Layer.Bing = OpenLayers.Class(OpenLayers.Layer.XYZ, { get_urls: function(bounds, z) { return [ - "//ecn.t0.tiles.virtualearth.net/tiles/r${id}.png?g=5941", - "//ecn.t1.tiles.virtualearth.net/tiles/r${id}.png?g=5941", - "//ecn.t2.tiles.virtualearth.net/tiles/r${id}.png?g=5941", - "//ecn.t3.tiles.virtualearth.net/tiles/r${id}.png?g=5941" + "//ecn.t0.tiles.virtualearth.net/tiles/r${id}.png?g=6570", + "//ecn.t1.tiles.virtualearth.net/tiles/r${id}.png?g=6570", + "//ecn.t2.tiles.virtualearth.net/tiles/r${id}.png?g=6570", + "//ecn.t3.tiles.virtualearth.net/tiles/r${id}.png?g=6570" ]; }, diff --git a/web/js/map-fms.js b/web/js/map-fms.js index 014bd58bb..ac27cfbce 100644 --- a/web/js/map-fms.js +++ b/web/js/map-fms.js @@ -1,4 +1,4 @@ -fixmystreet.maps.tile_base = [ [ '', 'a-', 'b-', 'c-' ], '//{S}tilma.mysociety.org/sv' ]; +fixmystreet.maps.tile_base = [ [ '', 'a-', 'b-', 'c-' ], '//{S}tilma.mysociety.org/oml' ]; fixmystreet.maps.config = (function(original) { return function(){ @@ -39,10 +39,14 @@ OpenLayers.Layer.BingUK = OpenLayers.Class(OpenLayers.Layer.Bing, { var c = this.map.getCenter(); var in_uk = c ? this.in_uk(c) : true; if (z >= 16 && in_uk) { - copyrights = 'Contains Ordnance Survey data © Crown copyright and database right 2016'; + copyrights = 'Contains Highways England and Ordnance Survey data © Crown copyright and database right 2016'; } else { logo = '<a href="https://www.bing.com/maps/"><img border=0 src="//dev.virtualearth.net/Branding/logo_powered_by.png"></a>'; - copyrights = '© 2016 <a href="https://www.bing.com/maps/">Microsoft</a>. © AND, Navteq, Ordnance Survey'; + if (in_uk) { + copyrights = '© 2016 <a href="https://www.bing.com/maps/">Microsoft</a>. © AND, Navteq, Highways England, Ordnance Survey'; + } else { + copyrights = '© 2016 <a href="https://www.bing.com/maps/">Microsoft</a>. © AND, Navteq, Ordnance Survey'; + } } this._updateAttribution(copyrights, logo); }, @@ -57,14 +61,14 @@ OpenLayers.Layer.BingUK = OpenLayers.Class(OpenLayers.Layer.Bing, { } } else { var type = ''; - if (z > 10 && in_uk) { + if (z > 11 && in_uk) { type = '&productSet=mmOS&key=' + fixmystreet.key; } urls = [ - "//ecn.t0.tiles.virtualearth.net/tiles/r${id}.png?g=5941" + type, - "//ecn.t1.tiles.virtualearth.net/tiles/r${id}.png?g=5941" + type, - "//ecn.t2.tiles.virtualearth.net/tiles/r${id}.png?g=5941" + type, - "//ecn.t3.tiles.virtualearth.net/tiles/r${id}.png?g=5941" + type + "//ecn.t0.tiles.virtualearth.net/tiles/r${id}.png?g=6570" + type, + "//ecn.t1.tiles.virtualearth.net/tiles/r${id}.png?g=6570" + type, + "//ecn.t2.tiles.virtualearth.net/tiles/r${id}.png?g=6570" + type, + "//ecn.t3.tiles.virtualearth.net/tiles/r${id}.png?g=6570" + type ]; } return urls; diff --git a/web/js/map-google-ol.js b/web/js/map-google-ol.js index 99670d4f2..4b2d818c9 100644 --- a/web/js/map-google-ol.js +++ b/web/js/map-google-ol.js @@ -23,7 +23,7 @@ fixmystreet.maps.config = function() { } fixmystreet.controls = [ - new OpenLayers.Control.ArgParser(), + new OpenLayers.Control.ArgParserFMS(), new OpenLayers.Control.Navigation(), new OpenLayers.Control.PermalinkFMS(permalink_id), new OpenLayers.Control.PanZoomFMS({id: 'fms_pan_zoom' }) diff --git a/web/js/map-streetview.js b/web/js/map-streetview.js index 6d9195246..4701a7f20 100644 --- a/web/js/map-streetview.js +++ b/web/js/map-streetview.js @@ -1,6 +1,6 @@ fixmystreet.maps.config = function() { fixmystreet.controls = [ - new OpenLayers.Control.ArgParser(), + new OpenLayers.Control.ArgParserFMS(), new OpenLayers.Control.Navigation(), new OpenLayers.Control.Permalink(), new OpenLayers.Control.PanZoomFMS() diff --git a/web/js/map-toner-lite.js b/web/js/map-toner-lite.js index eda12ff28..0700dbb55 100644 --- a/web/js/map-toner-lite.js +++ b/web/js/map-toner-lite.js @@ -4,7 +4,7 @@ fixmystreet.maps.config = function() { permalink_id = 'map_permalink'; } fixmystreet.controls = [ - new OpenLayers.Control.ArgParser(), + new OpenLayers.Control.ArgParserFMS(), new OpenLayers.Control.Navigation(), new OpenLayers.Control.PermalinkFMS(permalink_id), new OpenLayers.Control.PanZoomFMS({id: 'fms_pan_zoom' }) diff --git a/web/js/map-wmts-bristol.js b/web/js/map-wmts-bristol.js index 35f5ed0d6..88db20c52 100644 --- a/web/js/map-wmts-bristol.js +++ b/web/js/map-wmts-bristol.js @@ -104,7 +104,7 @@ fixmystreet.maps.config = function() { } fixmystreet.controls = [ - new OpenLayers.Control.ArgParser(), + new OpenLayers.Control.ArgParserFMS(), new OpenLayers.Control.Navigation(), new OpenLayers.Control.PermalinkFMS(permalink_id) ]; diff --git a/web/js/map-wmts-zurich.js b/web/js/map-wmts-zurich.js index eda0fbf44..346e9b89a 100644 --- a/web/js/map-wmts-zurich.js +++ b/web/js/map-wmts-zurich.js @@ -135,7 +135,7 @@ fixmystreet.maps.config = function() { // This stuff is copied from js/map-bing-ol.js fixmystreet.controls = [ - new OpenLayers.Control.ArgParser(), + new OpenLayers.Control.ArgParserFMS(), new OpenLayers.Control.Navigation() ]; if ( fixmystreet.page != 'report' || !$('html').hasClass('mobile') ) { diff --git a/web/js/validation_rules.js b/web/js/validation_rules.js index e6d745336..3e7b010f2 100644 --- a/web/js/validation_rules.js +++ b/web/js/validation_rules.js @@ -1,4 +1,4 @@ - validation_rules = { + core_validation_rules = { title: { required: true }, detail: { required: true }, update: { required: true }, @@ -9,3 +9,5 @@ } } }; + + validation_rules = core_validation_rules; |