diff options
Diffstat (limited to 'web/js')
-rw-r--r-- | web/js/dropzone.js.patch | 287 | ||||
-rw-r--r-- | web/js/loading-attribute-polyfill.js | 213 | ||||
-rw-r--r-- | web/js/map-OpenLayers.js | 57 | ||||
-rw-r--r-- | web/js/map-OpenStreetMap.js | 45 | ||||
-rw-r--r-- | web/js/map-bing-ol.js | 67 | ||||
-rw-r--r-- | web/js/map-fms.js | 46 | ||||
-rw-r--r-- | web/js/map-google.js | 2 | ||||
-rw-r--r-- | web/js/map-mastermap.js | 5 |
8 files changed, 594 insertions, 128 deletions
diff --git a/web/js/dropzone.js.patch b/web/js/dropzone.js.patch index b325b45d8..60a82709a 100644 --- a/web/js/dropzone.js.patch +++ b/web/js/dropzone.js.patch @@ -1,17 +1,92 @@ ---- dropzone.5.1.1.js 2017-06-30 09:46:43.000000000 +0100 -+++ dropzone.exiffixes.js 2017-06-30 18:25:27.000000000 +0100 -@@ -1175,9 +1175,7 @@ - }; - if ((typeof EXIF !== "undefined" && EXIF !== null) && fixOrientation) { - loadExif = function(callback) { +--- dropzone.5.1.1.js 2020-06-30 15:56:05.557790000 +0100 ++++ dropzone.exiffixes.js 2020-06-30 16:40:22.794951100 +0100 +@@ -26,7 +26,7 @@ + */ + + (function() { +- var Dropzone, Emitter, ExifRestore, camelize, contentLoaded, detectVerticalSquash, drawImageIOSFix, noop, without, ++ var Dropzone, Emitter, camelize, contentLoaded, detectVerticalSquash, drawImageIOSFix, noop, without, + slice = [].slice, + extend1 = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }, + hasProp = {}.hasOwnProperty; +@@ -1123,7 +1123,7 @@ + }; + + Dropzone.prototype.resizeImage = function(file, width, height, resizeMethod, callback) { +- return this.createThumbnail(file, width, height, resizeMethod, false, (function(_this) { ++ return this.createThumbnail(file, width, height, resizeMethod, true, (function(_this) { + return function(dataUrl, canvas) { + var resizeMimeType, resizedDataURL; + if (canvas === null) { +@@ -1134,9 +1134,6 @@ + resizeMimeType = file.type; + } + resizedDataURL = canvas.toDataURL(resizeMimeType, _this.options.resizeQuality); +- if (resizeMimeType === 'image/jpeg' || resizeMimeType === 'image/jpg') { +- resizedDataURL = ExifRestore.restore(file.dataURL, resizedDataURL); +- } + return callback(Dropzone.dataURItoBlob(resizedDataURL)); + } + }; +@@ -1164,23 +1161,17 @@ + Dropzone.prototype.createThumbnailFromUrl = function(file, width, height, resizeMethod, fixOrientation, callback, crossOrigin) { + var img; + img = document.createElement("img"); ++ ++ // FixOrientation not needed anymore with browsers handling imageOrientation ++ fixOrientation = (getComputedStyle(document.body)['imageOrientation'] == 'from-image') ? false : fixOrientation; ++ + if (crossOrigin) { + img.crossOrigin = crossOrigin; + } + img.onload = (function(_this) { + return function() { +- var loadExif; +- loadExif = function(callback) { +- return callback(1); +- }; +- if ((typeof EXIF !== "undefined" && EXIF !== null) && fixOrientation) { +- loadExif = function(callback) { - return EXIF.getData(img, function() { - return callback(EXIF.getTag(this, 'Orientation')); - }); -+ return callback(EXIF.getData(img)); - }; - } - return loadExif(function(orientation) { -@@ -1601,7 +1599,7 @@ +- }; +- } +- return loadExif(function(orientation) { ++ var orientation = fixOrientation ? EXIF.getData(img) : 1; ++ + var canvas, ctx, ref, ref1, ref2, ref3, resizeInfo, thumbnail; + file.width = img.width; + file.height = img.height; +@@ -1212,23 +1203,23 @@ + break; + case 6: + ctx.rotate(0.5 * Math.PI); +- ctx.translate(0, -canvas.height); ++ ctx.translate(0, -canvas.width); + break; + case 7: + ctx.rotate(0.5 * Math.PI); +- ctx.translate(canvas.width, -canvas.height); ++ ctx.translate(canvas.height, -canvas.width); + ctx.scale(-1, 1); + break; + case 8: + ctx.rotate(-0.5 * Math.PI); +- ctx.translate(-canvas.width, 0); ++ ctx.translate(-canvas.height, 0); + } + drawImageIOSFix(ctx, img, (ref = resizeInfo.srcX) != null ? ref : 0, (ref1 = resizeInfo.srcY) != null ? ref1 : 0, resizeInfo.srcWidth, resizeInfo.srcHeight, (ref2 = resizeInfo.trgX) != null ? ref2 : 0, (ref3 = resizeInfo.trgY) != null ? ref3 : 0, resizeInfo.trgWidth, resizeInfo.trgHeight); + thumbnail = canvas.toDataURL("image/png"); + if (callback != null) { + return callback(thumbnail, canvas); + } +- }); ++ + }; + })(this); + if (callback != null) { +@@ -1601,7 +1592,7 @@ return results; }; @@ -20,31 +95,165 @@ Dropzone.isBrowserSupported = function() { var capableBrowser, j, len, ref, regex; -@@ -1904,6 +1902,27 @@ - var array, ato, buf, imageData, mae, separatePoint; - imageData = resizedFileBase64.replace('data:image/jpeg;base64,', ''); - buf = this.decode64(imageData); -+ -+ // Certain browsers (I'm looking at you, Safari) 'helpfully' provide their -+ // own EXIF data in the JPEG returned from HTMLCanvasElement.toDataURL. -+ // Dropzone doesn't take this into account when restoring the original -+ // file's EXIF, meaning the final uploaded file has two sets of EXIF. -+ // Certain JPEG tools (I'm looking at you, jhead) don't really handle this -+ // very well, either ignoring the duplicate EXIF, picking the wrong one -+ // or refusing to process the file entirely. -+ // Seems like the best way out of this mess is to make sure the uploaded -+ // JPEG only ever has one EXIF header. In this case, we want to keep the -+ // EXIF from the original file. -+ // This little loop inspects the new JPEG from the toDataURL call and -+ // strips out any existing EXIF headers (technically any APP1 headers, -+ // but same difference in this case). -+ for (var i = 0; i < buf.length; i++) { -+ if (buf[i] === 255 && buf[i+1] === 225) { -+ var length = buf[i + 2] * 256 + buf[i + 3] + 2; -+ buf.splice(i, length); -+ } -+ } -+ - separatePoint = buf.indexOf(255, 3); - mae = buf.slice(0, separatePoint); - ato = buf.slice(separatePoint); +@@ -1828,161 +1819,6 @@ + return ctx.drawImage(img, sx, sy, sw, sh, dx, dy, dw, dh / vertSquashRatio); + }; + +- ExifRestore = (function() { +- function ExifRestore() {} +- +- ExifRestore.KEY_STR = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='; +- +- ExifRestore.encode64 = function(input) { +- var chr1, chr2, chr3, enc1, enc2, enc3, enc4, i, output; +- output = ''; +- chr1 = void 0; +- chr2 = void 0; +- chr3 = ''; +- enc1 = void 0; +- enc2 = void 0; +- enc3 = void 0; +- enc4 = ''; +- i = 0; +- while (true) { +- chr1 = input[i++]; +- chr2 = input[i++]; +- chr3 = input[i++]; +- enc1 = chr1 >> 2; +- enc2 = (chr1 & 3) << 4 | chr2 >> 4; +- enc3 = (chr2 & 15) << 2 | chr3 >> 6; +- enc4 = chr3 & 63; +- if (isNaN(chr2)) { +- enc3 = enc4 = 64; +- } else if (isNaN(chr3)) { +- enc4 = 64; +- } +- output = output + this.KEY_STR.charAt(enc1) + this.KEY_STR.charAt(enc2) + this.KEY_STR.charAt(enc3) + this.KEY_STR.charAt(enc4); +- chr1 = chr2 = chr3 = ''; +- enc1 = enc2 = enc3 = enc4 = ''; +- if (!(i < input.length)) { +- break; +- } +- } +- return output; +- }; +- +- ExifRestore.restore = function(origFileBase64, resizedFileBase64) { +- var image, rawImage, segments; +- if (!origFileBase64.match('data:image/jpeg;base64,')) { +- return resizedFileBase64; +- } +- rawImage = this.decode64(origFileBase64.replace('data:image/jpeg;base64,', '')); +- segments = this.slice2Segments(rawImage); +- image = this.exifManipulation(resizedFileBase64, segments); +- return 'data:image/jpeg;base64,' + this.encode64(image); +- }; +- +- ExifRestore.exifManipulation = function(resizedFileBase64, segments) { +- var aBuffer, exifArray, newImageArray; +- exifArray = this.getExifArray(segments); +- newImageArray = this.insertExif(resizedFileBase64, exifArray); +- aBuffer = new Uint8Array(newImageArray); +- return aBuffer; +- }; +- +- ExifRestore.getExifArray = function(segments) { +- var seg, x; +- seg = void 0; +- x = 0; +- while (x < segments.length) { +- seg = segments[x]; +- if (seg[0] === 255 & seg[1] === 225) { +- return seg; +- } +- x++; +- } +- return []; +- }; +- +- ExifRestore.insertExif = function(resizedFileBase64, exifArray) { +- var array, ato, buf, imageData, mae, separatePoint; +- imageData = resizedFileBase64.replace('data:image/jpeg;base64,', ''); +- buf = this.decode64(imageData); +- separatePoint = buf.indexOf(255, 3); +- mae = buf.slice(0, separatePoint); +- ato = buf.slice(separatePoint); +- array = mae; +- array = array.concat(exifArray); +- array = array.concat(ato); +- return array; +- }; +- +- ExifRestore.slice2Segments = function(rawImageArray) { +- var endPoint, head, length, seg, segments; +- head = 0; +- segments = []; +- while (true) { +- if (rawImageArray[head] === 255 & rawImageArray[head + 1] === 218) { +- break; +- } +- if (rawImageArray[head] === 255 & rawImageArray[head + 1] === 216) { +- head += 2; +- } else { +- length = rawImageArray[head + 2] * 256 + rawImageArray[head + 3]; +- endPoint = head + length + 2; +- seg = rawImageArray.slice(head, endPoint); +- segments.push(seg); +- head = endPoint; +- } +- if (head > rawImageArray.length) { +- break; +- } +- } +- return segments; +- }; +- +- ExifRestore.decode64 = function(input) { +- var base64test, buf, chr1, chr2, chr3, enc1, enc2, enc3, enc4, i, output; +- output = ''; +- chr1 = void 0; +- chr2 = void 0; +- chr3 = ''; +- enc1 = void 0; +- enc2 = void 0; +- enc3 = void 0; +- enc4 = ''; +- i = 0; +- buf = []; +- base64test = /[^A-Za-z0-9\+\/\=]/g; +- if (base64test.exec(input)) { +- console.warning('There were invalid base64 characters in the input text.\n' + 'Valid base64 characters are A-Z, a-z, 0-9, \'+\', \'/\',and \'=\'\n' + 'Expect errors in decoding.'); +- } +- input = input.replace(/[^A-Za-z0-9\+\/\=]/g, ''); +- while (true) { +- enc1 = this.KEY_STR.indexOf(input.charAt(i++)); +- enc2 = this.KEY_STR.indexOf(input.charAt(i++)); +- enc3 = this.KEY_STR.indexOf(input.charAt(i++)); +- enc4 = this.KEY_STR.indexOf(input.charAt(i++)); +- chr1 = enc1 << 2 | enc2 >> 4; +- chr2 = (enc2 & 15) << 4 | enc3 >> 2; +- chr3 = (enc3 & 3) << 6 | enc4; +- buf.push(chr1); +- if (enc3 !== 64) { +- buf.push(chr2); +- } +- if (enc4 !== 64) { +- buf.push(chr3); +- } +- chr1 = chr2 = chr3 = ''; +- enc1 = enc2 = enc3 = enc4 = ''; +- if (!(i < input.length)) { +- break; +- } +- } +- return buf; +- }; +- +- return ExifRestore; +- +- })(); +- +- + /* + * contentloaded.js + * diff --git a/web/js/loading-attribute-polyfill.js b/web/js/loading-attribute-polyfill.js new file mode 100644 index 000000000..f11397985 --- /dev/null +++ b/web/js/loading-attribute-polyfill.js @@ -0,0 +1,213 @@ +/* + * Loading attribute polyfill - https://github.com/mfranzke/loading-attribute-polyfill + * @license Copyright(c) 2019 by Maximilian Franzke + * Credits for the initial kickstarter / script to @Sora2455, and supported by @cbirdsong, @eklingen, @DaPo, @nextgenthemes, @diogoterremoto, @dracos, @Flimm, @TomS- and @vinyfc93 - many thanks for that ! + */ +/* + * A minimal and dependency-free vanilla JavaScript loading attribute polyfill. + * Supports standard's functionality and tests for native support upfront. + * Elsewhere the functionality gets emulated with the support of noscript wrapper tags. + * Use an IntersectionObserver polyfill in case of IE11 support necessary. + * + * MS - Removed iframe/picture/srcset parts, unneeded at present, and added external API + */ + +(function () { + 'use strict'; + + var config = { + // Start download if the item gets within 256px in the Y axis + rootMargin: '256px 0px', + threshold: 0.01 + }; + + // Device/browser capabilities object + var capabilities = { + loading: 'loading' in HTMLImageElement.prototype, + scrolling: 'onscroll' in window + }; + + // Nodelist foreach polyfill / source: https://stackoverflow.com/a/46929259 + if ( + typeof NodeList !== 'undefined' && + NodeList.prototype && + !NodeList.prototype.forEach + ) { + // Yes, there's really no need for `Object.defineProperty` here + NodeList.prototype.forEach = Array.prototype.forEach; + } + + // Define according to browsers support of the IntersectionObserver feature (missing e.g. on IE11 or Safari 11) + var intersectionObserver; + + if ('IntersectionObserver' in window) { + intersectionObserver = new IntersectionObserver(onIntersection, config); + } + + // On using a browser w/o requestAnimationFrame support (IE9, Opera Mini), just run the passed function + var rAFWrapper; + + if ('requestAnimationFrame' in window) { + rAFWrapper = window.requestAnimationFrame; + } else { + rAFWrapper = function (func) { + func(); + }; + } + + /** + * Put the source back where it belongs - now that the elements content is attached to the document, it will load now + * @param {Object} lazyItem Current item to be restored after lazy loading. + */ + function restoreSource(lazyItem) { + lazyItem.setAttribute('src', lazyItem.getAttribute('data-lazy-src')); + lazyItem.removeAttribute('data-lazy-src'); // Not using delete .dataset here for compatibility down to IE9 + } + + /** + * Handle IntersectionObservers callback + * @param {Object} entries Target elements Intersection observed changes + * @param {Object} observer IntersectionObserver instance reference + */ + function onIntersection(entries, observer) { + entries.forEach(function (entry) { + // Mitigation for EDGE lacking support of .isIntersecting until v15, compare to e.g. https://github.com/w3c/IntersectionObserver/issues/211#issuecomment-309144669 + if (entry.intersectionRatio === 0) { + return; + } + + // If the item is visible now, load it and stop watching it + var lazyItem = entry.target; + + observer.unobserve(lazyItem); + + restoreSource(lazyItem); + }); + } + + /** + * Handle printing the page + */ + function onPrinting() { + if (typeof window.matchMedia === 'undefined') { + return; + } + + var mediaQueryList = window.matchMedia('print'); + + mediaQueryList.addListener(function (mql) { + if (mql.matches) { + document + .querySelectorAll('img[loading="lazy"][data-lazy-src]') + .forEach(function (lazyItem) { + restoreSource(lazyItem); + }); + } + }); + } + + /** + * Get and prepare the HTML code depending on feature detection, + * and if not scrolling supported, because it's a Google or Bing Bot + * @param {String} lazyAreaHtml Noscript inner HTML code that src-urls need to get rewritten + */ + function getAndPrepareHTMLCode(noScriptTag) { + // The contents of a <noscript> tag are treated as text to JavaScript + var lazyAreaHtml = noScriptTag.textContent || noScriptTag.innerHTML; + + var getImageWidth = lazyAreaHtml.match(/width=['"](\d+)['"]/) || false; + var temporaryImageWidth = getImageWidth[1] || 1; + var getImageHeight = lazyAreaHtml.match(/height=['"](\d+)['"]/) || false; + var temporaryImageHeight = getImageHeight[1] || 1; + + var temporaryImage = + 'data:image/svg+xml,%3Csvg xmlns=%27http://www.w3.org/2000/svg%27 viewBox=%270 0 ' + + temporaryImageWidth + + ' ' + + temporaryImageHeight + + '%27%3E%3C/svg%3E'; + + if (!capabilities.loading && capabilities.scrolling) { + // Check for IntersectionObserver support + if (typeof intersectionObserver === 'undefined') { + // Attach abandonned attribute 'lazyload' to the HTML tags on browsers w/o IntersectionObserver being available + lazyAreaHtml = lazyAreaHtml.replace( + /(?:\r\n|\r|\n|\t| )src=/g, + ' lazyload="1" src=' + ); + } else { + // Temporarily replace a expensive resource load with a simple one by storing the actual source for later and point src to a temporary replacement (data URI) + lazyAreaHtml = lazyAreaHtml + .replace( + /(?:\r\n|\r|\n|\t| )src=/g, + ' src="' + temporaryImage + '" data-lazy-src=' + ); + } + } + + return lazyAreaHtml; + } + + /** + * Retrieve the elements from the 'lazy load' <noscript> tag and prepare them for display + * @param {Object} noScriptTag noscript HTML tag that should get initially transformed + */ + function prepareElement(noScriptTag) { + // Sticking the noscript HTML code in the innerHTML of a new <div> tag to 'load' it after creating that <div> + var lazyArea = document.createElement('div'); + + lazyArea.innerHTML = getAndPrepareHTMLCode(noScriptTag); + + // Move all children out of the element + while (lazyArea.firstChild) { + if ( + !capabilities.loading && + capabilities.scrolling && + typeof intersectionObserver !== 'undefined' && + lazyArea.firstChild.tagName && + lazyArea.firstChild.tagName.toLowerCase() === 'img' + ) { + // Observe the item so that loading could start when it gets close to the viewport + intersectionObserver.observe(lazyArea.firstChild); + } + + noScriptTag.parentNode.insertBefore(lazyArea.firstChild, noScriptTag); + } + + // Remove the empty element - not using .remove() here for IE11 compatibility + noScriptTag.parentNode.removeChild(noScriptTag); // Preferred .removeChild over .remove here for IE + } + + /* Add a function we can call externally */ + fixmystreet.loading_recheck = function() { + var lazyLoadAreas = document.querySelectorAll('noscript.loading-lazy'); + lazyLoadAreas.forEach(prepareElement); + }; + + /** + * Get all the <noscript> tags on the page and setup the printing + */ + function prepareElements() { + fixmystreet.loading_recheck(); + + // Bind for someone printing the page + onPrinting(); + } + + // If the page has loaded already, run setup - if it hasn't, run as soon as it has. + // Use requestAnimationFrame as this will propably cause repaints + // document.readyState values: https://www.w3schools.com/jsref/prop_doc_readystate.asp + if (/comp|inter/.test(document.readyState)) { + rAFWrapper(prepareElements); + } else if ('addEventListener' in document) { + document.addEventListener('DOMContentLoaded', function () { + rAFWrapper(prepareElements); + }); + } else { + document.attachEvent('onreadystatechange', function () { + if (document.readyState === 'complete') { + prepareElements(); + } + }); + } +})(); diff --git a/web/js/map-OpenLayers.js b/web/js/map-OpenLayers.js index 182cd79a1..ada51cbc0 100644 --- a/web/js/map-OpenLayers.js +++ b/web/js/map-OpenLayers.js @@ -132,20 +132,32 @@ $.extend(fixmystreet.utils, { new OpenLayers.Projection("EPSG:4326") ); - var lat = transformedLonlat.lat.toFixed(6); - var lon = transformedLonlat.lon.toFixed(6); - - document.getElementById('fixmystreet.latitude').value = lat; - document.getElementById('fixmystreet.longitude').value = lon; - + fixmystreet.maps.update_pin_input_fields(transformedLonlat); $(fixmystreet).trigger('maps:update_pin', [ lonlat ]); + var lat = transformedLonlat.lat.toFixed(6); + var lon = transformedLonlat.lon.toFixed(6); return { 'url': { 'lon': lon, 'lat': lat }, 'state': { 'lon': lonlat.lon, 'lat': lonlat.lat } }; }, + update_pin_input_fields: function(lonlat) { + var bng = lonlat.clone().transform( + new OpenLayers.Projection("EPSG:4326"), + new OpenLayers.Projection("EPSG:27700") // TODO: Handle other projections + ); + var lat = lonlat.lat.toFixed(6); + var lon = lonlat.lon.toFixed(6); + $("#problem_northing").text(bng.lat.toFixed(1)); + $("#problem_easting").text(bng.lon.toFixed(1)); + $("#problem_latitude").text(lat); + $("#problem_longitude").text(lon); + $("input[name=latitude]").val(lat); + $("input[name=longitude]").val(lon); + }, + display_around: function() { // Required after changing the size of the map element fixmystreet.map.updateSize(); @@ -278,9 +290,12 @@ $.extend(fixmystreet.utils, { // pin_moved_callback is called with a new EPSG:4326 OpenLayers.LonLat if // the user drags the pin and confirms its new location. admin_drag: function(pin_moved_callback, confirm_change) { + if (fixmystreet.maps.admin_drag_control) { + return; + } confirm_change = confirm_change || false; var original_lonlat; - var drag = new OpenLayers.Control.DragFeatureFMS( fixmystreet.markers, { + var drag = fixmystreet.maps.admin_drag_control = new OpenLayers.Control.DragFeatureFMS( fixmystreet.markers, { onStart: function(feature, e) { // Keep track of where the feature started, so we can put it // back if the user cancels the operation. @@ -627,17 +642,9 @@ $.extend(fixmystreet.utils, { // Not actually on the inspect report page return; } - fixmystreet.maps.admin_drag(function(lonlat) { - var bng = lonlat.clone().transform( - new OpenLayers.Projection("EPSG:4326"), - new OpenLayers.Projection("EPSG:27700") // TODO: Handle other projections - ); - $("#problem_northing").text(bng.y.toFixed(1)); - $("#problem_easting").text(bng.x.toFixed(1)); - $("#problem_latitude").text(lonlat.y.toFixed(6)); - $("#problem_longitude").text(lonlat.x.toFixed(6)); - $("input[name=latitude]").val(lonlat.y.toFixed(6)); - $("input[name=longitude]").val(lonlat.x.toFixed(6)); + fixmystreet.maps.admin_drag(function(geom) { + var lonlat = new OpenLayers.LonLat(geom.x, geom.y); + fixmystreet.maps.update_pin_input_fields(lonlat); }, false); } @@ -958,6 +965,8 @@ $.extend(fixmystreet.utils, { ); } else if (layer_options.matrixIds) { layer = new fixmystreet.map_type(layer_options); + } else if (fixmystreet.layer_options[i].map_type) { + layer = new fixmystreet.layer_options[i].map_type(fixmystreet.layer_name, layer_options); } else { layer = new fixmystreet.map_type(fixmystreet.layer_name, layer_options); } @@ -1278,6 +1287,9 @@ 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 (fixmystreet.loading_recheck) { + fixmystreet.loading_recheck(); + } if ( $('.item-list--reports').data('show-old-reports') ) { $('#show_old_reports_wrapper').removeClass('hidden'); } else { @@ -1318,8 +1330,13 @@ OpenLayers.Control.Click = OpenLayers.Class(OpenLayers.Control, { // 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'); + var asset_button_clicked = $('.btn--change-asset').hasClass('asset-spot'); + if (asset_button_clicked) { + return true; + } + var back_link = $('.js-back-to-report-list'); + if (back_link.length) { + back_link.trigger('click'); return true; } diff --git a/web/js/map-OpenStreetMap.js b/web/js/map-OpenStreetMap.js index 9ed3a2ee3..46aba1c91 100644 --- a/web/js/map-OpenStreetMap.js +++ b/web/js/map-OpenStreetMap.js @@ -7,6 +7,13 @@ fixmystreet.maps.config = function() { new OpenLayers.Control.PermalinkFMS('map'), new OpenLayers.Control.PanZoomFMS({id: 'fms_pan_zoom' }) ]; + + if (OpenLayers.Layer.BingAerial) { + fixmystreet.layer_options = [ + { map_type: fixmystreet.map_type }, + { map_type: OpenLayers.Layer.BingAerial } + ]; + } }; // http://www.openstreetmap.org/openlayers/OpenStreetMap.js (added maxResolution) @@ -34,7 +41,7 @@ OpenLayers.Layer.OSM.Mapnik = OpenLayers.Class(OpenLayers.Layer.OSM, { options = OpenLayers.Util.extend({ /* Below line added to OSM's file in order to allow minimum zoom level */ maxResolution: 156543.03390625/Math.pow(2, options.zoomOffset || 0), - numZoomLevels: 19, + numZoomLevels: 20, buffer: 0 }, options); var newArguments = [name, url, options]; @@ -45,40 +52,6 @@ OpenLayers.Layer.OSM.Mapnik = OpenLayers.Class(OpenLayers.Layer.OSM, { }); /** - * Class: OpenLayers.Layer.OSM.MapQuestOpen - * - * Inherits from: - * - <OpenLayers.Layer.OSM> - */ -OpenLayers.Layer.OSM.MapQuestOpen = OpenLayers.Class(OpenLayers.Layer.OSM, { - /** - * Constructor: OpenLayers.Layer.OSM.MapQuestOpen - * - * Parameters: - * name - {String} - * options - {Object} Hashtable of extra options to tag onto the layer - */ - initialize: function(name, options) { - var url = [ - "https://otile1-s.mqcdn.com/tiles/1.0.0/map/${z}/${x}/${y}.png", - "https://otile2-s.mqcdn.com/tiles/1.0.0/map/${z}/${x}/${y}.png", - "https://otile3-s.mqcdn.com/tiles/1.0.0/map/${z}/${x}/${y}.png", - "https://otile4-s.mqcdn.com/tiles/1.0.0/map/${z}/${x}/${y}.png" - ]; - options = OpenLayers.Util.extend({ - /* Below line added to OSM's file in order to allow minimum zoom level */ - maxResolution: 156543.03390625/Math.pow(2, options.zoomOffset || 0), - numZoomLevels: 19, - buffer: 0 - }, options); - var newArguments = [name, url, options]; - OpenLayers.Layer.OSM.prototype.initialize.apply(this, newArguments); - }, - - CLASS_NAME: "OpenLayers.Layer.OSM.MapQuestOpen" -}); - -/** * Class: OpenLayers.Layer.OSM.CycleMap * * Inherits from: @@ -101,7 +74,7 @@ OpenLayers.Layer.OSM.CycleMap = OpenLayers.Class(OpenLayers.Layer.OSM, { options = OpenLayers.Util.extend({ /* Below line added to OSM's file in order to allow minimum zoom level */ maxResolution: 156543.03390625/Math.pow(2, options.zoomOffset || 0), - numZoomLevels: 19, + numZoomLevels: 20, buffer: 0 }, options); var newArguments = [name, url, options]; diff --git a/web/js/map-bing-ol.js b/web/js/map-bing-ol.js index 4e01ff58b..c44cf96f8 100644 --- a/web/js/map-bing-ol.js +++ b/web/js/map-bing-ol.js @@ -10,10 +10,30 @@ fixmystreet.maps.config = function() { if ( fixmystreet.page == 'report' ) { fixmystreet.controls.push( new OpenLayers.Control.PermalinkFMS('key-tool-problems-nearby', '/around') ); } - fixmystreet.map_type = OpenLayers.Layer.Bing; }; +(function() { + $(function(){ + $('#map_layer_toggle').click(function(e) { + e.preventDefault(); + var $this = $(this); + if ($this.text() == translation_strings.map_aerial) { + $this.text(translation_strings.map_roads); + fixmystreet.map.setBaseLayer(fixmystreet.map.layers[1]); + } else { + $this.text(translation_strings.map_aerial); + fixmystreet.map.setBaseLayer(fixmystreet.map.layers[0]); + } + }); + // If page loaded with Aerial as starting, rather than default road + if ($('#map_layer_toggle').text() == translation_strings.map_roads) { + fixmystreet.map.setBaseLayer(fixmystreet.map.layers[1]); + } + }); +})(); + OpenLayers.Layer.Bing = OpenLayers.Class(OpenLayers.Layer.XYZ, { + tile_base: '//t{S}.ssl.ak.dynamic.tiles.virtualearth.net/comp/ch/${id}?mkt=en-US&it=G,L&src=t&shading=hill&og=969&n=z', attributionTemplate: '${logo}${copyrights}', setMap: function() { @@ -35,7 +55,8 @@ OpenLayers.Layer.Bing = OpenLayers.Class(OpenLayers.Layer.XYZ, { }, updateAttribution: function() { - var copyrights = '© 2011 <a href="https://www.bing.com/maps/">Microsoft</a>. © AND, Navteq'; + var year = (new Date()).getFullYear(); + var copyrights = '© ' + year + ' <a href="https://www.bing.com/maps/">Microsoft</a>, HERE'; var logo = '<a href="https://www.bing.com/maps/"><img border=0 src="//dev.virtualearth.net/Branding/logo_powered_by.png"></a>'; this._updateAttribution(copyrights, logo); }, @@ -45,7 +66,7 @@ OpenLayers.Layer.Bing = OpenLayers.Class(OpenLayers.Layer.XYZ, { options = OpenLayers.Util.extend({ /* Below line added to OSM's file in order to allow minimum zoom level */ maxResolution: 156543.03390625/Math.pow(2, options.zoomOffset || 0), - numZoomLevels: 19, + numZoomLevels: 20, sphericalMercator: true, buffer: 0 }, options); @@ -89,13 +110,41 @@ OpenLayers.Layer.Bing = OpenLayers.Class(OpenLayers.Layer.XYZ, { }, get_urls: function(bounds, z) { - return [ - "//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" - ]; + var urls = []; + for (var i=0; i<4; i++) { + urls.push(this.tile_base.replace('{S}', i)); + } + return urls; }, CLASS_NAME: "OpenLayers.Layer.Bing" }); + +OpenLayers.Layer.BingAerial = OpenLayers.Class(OpenLayers.Layer.Bing, { + tile_base: '//t{S}.ssl.ak.dynamic.tiles.virtualearth.net/comp/ch/${id}?mkt=en-US&it=A,G,L&src=t&og=969&n=z', + + setMap: function() { + OpenLayers.Layer.Bing.prototype.setMap.apply(this, arguments); + this.map.events.register("moveend", this, this.updateAttribution); + }, + + updateAttribution: function() { + var z = this.map.getZoom() + this.zoomOffset; + var year = (new Date()).getFullYear(); + var copyrights = '© ' + year + ' <a href="https://www.bing.com/maps/">Microsoft</a>, HERE, '; + if (z >= 13) { + copyrights += 'Maxar, CNES Distribution Airbus DS'; + } else { + copyrights += 'Earthstar Geographics SIO'; + } + var logo = '<a href="https://www.bing.com/maps/"><img border=0 src="//dev.virtualearth.net/Branding/logo_powered_by.png"></a>'; + this._updateAttribution(copyrights, logo); + }, + + CLASS_NAME: "OpenLayers.Layer.BingAerial" +}); + +fixmystreet.layer_options = [ + { map_type: OpenLayers.Layer.Bing }, + { map_type: OpenLayers.Layer.BingAerial } +]; diff --git a/web/js/map-fms.js b/web/js/map-fms.js index ac27cfbce..bb51467a7 100644 --- a/web/js/map-fms.js +++ b/web/js/map-fms.js @@ -1,11 +1,4 @@ -fixmystreet.maps.tile_base = [ [ '', 'a-', 'b-', 'c-' ], '//{S}tilma.mysociety.org/oml' ]; - -fixmystreet.maps.config = (function(original) { - return function(){ - original(); - fixmystreet.map_type = OpenLayers.Layer.BingUK; - }; -})(fixmystreet.maps.config); +fixmystreet.maps.tile_base = '//{S}tilma.mysociety.org/oml'; OpenLayers.Layer.BingUK = OpenLayers.Class(OpenLayers.Layer.Bing, { uk_bounds: [ @@ -38,41 +31,48 @@ OpenLayers.Layer.BingUK = OpenLayers.Class(OpenLayers.Layer.Bing, { var logo = ''; var c = this.map.getCenter(); var in_uk = c ? this.in_uk(c) : true; + var year = (new Date()).getFullYear(); if (z >= 16 && in_uk) { - copyrights = 'Contains Highways England and Ordnance Survey data © Crown copyright and database right 2016'; + copyrights = 'Contains Highways England and Ordnance Survey data © Crown copyright and database right ' + year; } else { logo = '<a href="https://www.bing.com/maps/"><img border=0 src="//dev.virtualearth.net/Branding/logo_powered_by.png"></a>'; if (in_uk) { - copyrights = '© 2016 <a href="https://www.bing.com/maps/">Microsoft</a>. © AND, Navteq, Highways England, Ordnance Survey'; + copyrights = '© ' + year + ' <a href="https://www.bing.com/maps/">Microsoft</a>, HERE, Highways England, Ordnance Survey'; } else { - copyrights = '© 2016 <a href="https://www.bing.com/maps/">Microsoft</a>. © AND, Navteq, Ordnance Survey'; + copyrights = '© ' + year + ' <a href="https://www.bing.com/maps/">Microsoft</a>, HERE, Ordnance Survey'; } } this._updateAttribution(copyrights, logo); }, + tile_prefix: [ '', 'a-', 'b-', 'c-' ], + get_urls: function(bounds, z) { - var urls; + var urls = [], i; var in_uk = this.in_uk(bounds.getCenterLonLat()); if (z >= 16 && in_uk) { urls = []; - for (var i=0; i< fixmystreet.maps.tile_base[0].length; i++) { - urls.push( fixmystreet.maps.tile_base[1].replace('{S}', fixmystreet.maps.tile_base[0][i]) + "/${z}/${x}/${y}.png" ); + for (i=0; i< this.tile_prefix.length; i++) { + urls.push( fixmystreet.maps.tile_base.replace('{S}', this.tile_prefix[i]) + "/${z}/${x}/${y}.png" ); + } + } else if (z > 11 && in_uk) { + var type = 'g=8702&lbl=l1&productSet=mmOS&key=' + fixmystreet.key; + var tile_base = "//ecn.t{S}.tiles.virtualearth.net/tiles/r${id}?" + type; + for (i=0; i<4; i++) { + urls.push(tile_base.replace('{S}', i)); } } else { - var type = ''; - if (z > 11 && in_uk) { - type = '&productSet=mmOS&key=' + fixmystreet.key; + for (i=0; i<4; i++) { + urls.push(this.tile_base.replace('{S}', i)); } - urls = [ - "//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; }, CLASS_NAME: "OpenLayers.Layer.BingUK" }); + +fixmystreet.layer_options = [ + { map_type: OpenLayers.Layer.BingUK }, + { map_type: OpenLayers.Layer.BingAerial } +]; diff --git a/web/js/map-google.js b/web/js/map-google.js index fc515b9dd..801fed210 100644 --- a/web/js/map-google.js +++ b/web/js/map-google.js @@ -156,7 +156,7 @@ fixmystreet.maps = {}; }; if (!fixmystreet.zoomToBounds) { map_args.minZoom = 13; - map_args.maxZoom = 18; + map_args.maxZoom = 19; } fixmystreet.map = new google.maps.Map(document.getElementById("map"), map_args); diff --git a/web/js/map-mastermap.js b/web/js/map-mastermap.js index bb9adf532..663ccbdfb 100644 --- a/web/js/map-mastermap.js +++ b/web/js/map-mastermap.js @@ -23,3 +23,8 @@ OpenLayers.Layer.MasterMap = OpenLayers.Class(OpenLayers.Layer.BingUK, { CLASS_NAME: "OpenLayers.Layer.MasterMap" }); + +fixmystreet.layer_options = [ + { map_type: OpenLayers.Layer.MasterMap }, + { map_type: OpenLayers.Layer.BingAerial } +]; |