diff --git a/CHANGELOG.md b/CHANGELOG.md index a56fb04e..5cd50f42 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -133,7 +133,6 @@ These changes were targeted at removing any hardcoded projection-specific logic * Fixed `ImageOverlay` mercator distortion on lower zoom levels. * Fixed a bug where layers didn't fire `popupopen` and `popupclose` events when manually creating a popup object and passing it to `bindPopup`. [#2354](https://github.com/Leaflet/Leaflet/issues/2354) * Fixed box-zoom overlay appearing under markers. [#1813](https://github.com/Leaflet/Leaflet/issues/1813) -* Fixed an issue where clicks on Android were skipped when happened too fast. [#2303](https://github.com/Leaflet/Leaflet/issues/2303) ### Misc improvements @@ -143,6 +142,19 @@ These changes were targeted at removing any hardcoded projection-specific logic * Added reference to Leaflet CSS in `package.json` (by [@bclinkinbeard](https://github.com/bclinkinbeard)). [#2432](https://github.com/Leaflet/Leaflet/pull/2432) +## 0.7.3 (May 23, 2014) + +* Added proper **bower** and **component** support (by [@calvinmetcalf](https://github.com/calvinmetcalf)). [#2561](https://github.com/Leaflet/Leaflet/pull/2561) [#1903](https://github.com/Leaflet/Leaflet/issues/1903) +* Fixed a bug where dragging the map outside the window caused an error on FF. [#2610](https://github.com/Leaflet/Leaflet/issues/2610) +* Fixed a bug where some taps on Android where not working, often falsely perceived as drags (by [@axefrog](https://github.com/axefrog)). [#2503](https://github.com/Leaflet/Leaflet/pull/2503) +* Fixed a bug where clicks on Android were skipped when happened too fast. [#2303](https://github.com/Leaflet/Leaflet/issues/2303) +* Fixed a bug where calling `setView` (or similar methods) several times in succession could freeze the map. [#2521](https://github.com/Leaflet/Leaflet/issues/2521) [#2236](https://github.com/Leaflet/Leaflet/issues/2236) [#2485](https://github.com/Leaflet/Leaflet/issues/2485) +* Fixed a bug where `Control.Layers` wasn't properly removed (by [@jack-kerouac](https://github.com/jack-kerouac)). [#2569](https://github.com/Leaflet/Leaflet/pull/2569) +* Fixed a bug that caused `TileLayer` `load` event not to fire properly. [#2510](https://github.com/Leaflet/Leaflet/issues/2510) +* Fixed Canvas-based paths not triggering `remove` event when removed (by @adimitrov). [#2486](https://github.com/Leaflet/Leaflet/pull/2486) +* Fixed a bug where you could end up with fractional zoom after pinch-zooming in some cases (by [@danzel](https://github.com/danzel). [#2400](https://github.com/Leaflet/Leaflet/pull/2400) [#1943](https://github.com/Leaflet/Leaflet/issues/1934) + + ## 0.7.2 (January 17, 2014) * Fixed a bug that appeared with **Chrome 32 update** that made all **mouse events shifted on scrolled pages**. [#2352](https://github.com/Leaflet/Leaflet/issues/2352) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 052240d1..1e4ec7f6 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -44,7 +44,7 @@ try asking [on the Leaflet forum](https://groups.google.com/forum/#!forum/leafle ### Considerations for Accepting Patches -While we happily accept patches, we're also commited to keeping Leaflet simple, lightweight and blazingly fast. +While we happily accept patches, we're also committed to keeping Leaflet simple, lightweight and blazingly fast. So bugfixes, performance optimizations and small improvements that don't add a lot of code are much more likely to get accepted quickly. @@ -53,7 +53,7 @@ Before sending a pull request with a new feature, first check if it's been discu or [Leaflet UserVoice](http://leaflet.uservoice.com/)), and then ask yourself two questions: - 1. Are you sure that this new feature is important enough to justify its presense in the Leaflet core? + 1. Are you sure that this new feature is important enough to justify its presence in the Leaflet core? Or will it look better as a plugin in a separate repository? 2. Is it written in a simple, concise way that doesn't add bulk to the codebase? @@ -85,7 +85,7 @@ Please do not commit to the `master` branch, or your unrelated changes will go i You should also follow the code style and whitespace conventions of the original codebase. In particular, use tabs for indentation and spaces for alignment. -Before commiting your changes, run `jake lint` to catch any JS errors in the code and fix them. +Before committing your changes, run `jake lint` to catch any JS errors in the code and fix them. If you add any new files to the Leaflet source, make sure to also add them to `build/deps.js` so that the build system knows about them. @@ -142,7 +142,7 @@ If you need to make edits in a local repository to see how it looks in the proce 4. Open `localhost:4000` in your browser. Now any file changes will be updated when you reload pages automatically. -After commiting the changes, just send a pull request. +After committing the changes, just send a pull request. If you need to update documentation according to a new feature that only appeared in the master version (not stable one), you need to make changes to `gh-pages-master` branch instead of `gh-pages`. diff --git a/FAQ.md b/FAQ.md index 669662ae..47f24966 100644 --- a/FAQ.md +++ b/FAQ.md @@ -26,10 +26,10 @@ You can roll your own tiles as well. but there are providers that use other sources. Check out [this example](http://leaflet-extras.github.io/leaflet-providers/preview/) -with half a hundred different layers to choose from. +with over seventy different layers to choose from. Popular commercial options, free up to a particular number of requests, include [MapBox](http://mapbox.com), -[Bing Maps](http://www.microsoft.com/maps/choose-your-binge's-maps-API.aspx) (using a [plugin](https://github.com/shramov/leaflet-plugins)), +[Bing Maps](http://www.microsoft.com/maps/choose-your-bing-maps-API.aspx) (using a [plugin](https://github.com/shramov/leaflet-plugins)), [Esri ArcGIS](http://www.arcgis.com/features/maps/imagery.html) ([official plugin](https://github.com/Esri/esri-leaflet)) and [Nokia Here](http://developer.here.com/web-experiences). A notable exception is [MapQuest Open](http://developer.mapquest.com/web/products/open/map), which is free for any number of requests. @@ -45,7 +45,7 @@ and [MapQuest Open](http://developer.mapquest.com/web/products/open/map) provide #### I want to use Google Maps API tiles with Leaflet, can I do that? -The problem with Google is that its [Terms of Use](https://developers.google.com/maps/terms?hl=ru) forbid any means of tile access other than through the Google Maps API. +The problem with Google is that its [Terms of Use](https://developers.google.com/maps/terms) forbid any means of tile access other than through the Google Maps API. You can add the Google Maps API as a Leaflet layer with a [plugin](https://github.com/shramov/leaflet-plugins). But note that the map experience will not be perfect, because Leaflet will just act as a proxy to the Google Maps JS engine, so you won't get all the performance and usability benefits of using Leaflet when the Google layer is on. diff --git a/Jakefile.js b/Jakefile.js index bb965e41..1e3599c9 100644 --- a/Jakefile.js +++ b/Jakefile.js @@ -23,7 +23,7 @@ function hint(msg, paths) { console.log('\tCheck passed.\n'); complete(); }); - } + }; } desc('Check Leaflet source for errors with JSHint'); @@ -36,12 +36,14 @@ desc('Combine and compress Leaflet source files'); task('build', {async: true}, function (compsBase32, buildName) { var v; - jake.exec('git log -1 --pretty=format:"%h"', {}, function () { + jake.exec('git log -1 --pretty=format:"%h"', {breakOnError: false}, function () { build.build(complete, v, compsBase32, buildName); }).on('stdout', function (data) { v = version + ' (' + data.toString() + ')'; - }) + }).on('error', function () { + v = version; + }); }); desc('Run PhantomJS tests'); diff --git a/build/bower.json b/build/bower.json index 7dde3788..ca85bde2 100644 --- a/build/bower.json +++ b/build/bower.json @@ -2,7 +2,6 @@ "name": "leaflet", "description": "JavaScript library for mobile-friendly interactive maps", "main": [ - "dist/leaflet.js", "dist/leaflet.css", "dist/leaflet-src.js", "dist/images/layers-2x.png", @@ -20,4 +19,4 @@ "src", "build" ] -} \ No newline at end of file +} diff --git a/debug/map/markers.html b/debug/map/markers.html new file mode 100644 index 00000000..1bf803cf --- /dev/null +++ b/debug/map/markers.html @@ -0,0 +1,68 @@ + + + + Leaflet debug page + + + + + + + + + + + + +
+ + + + diff --git a/debug/tests/click_on_canvas.html b/debug/tests/click_on_canvas.html index f4a6b9c0..ac9b21f4 100644 --- a/debug/tests/click_on_canvas.html +++ b/debug/tests/click_on_canvas.html @@ -35,7 +35,7 @@ opacity: 1, smoothFactor: 1, color: 'red', - clickable:true + interactive:true })); polygons.on('click', function(m) { diff --git a/debug/tests/svg_clicks.html b/debug/tests/svg_clicks.html index 11be258e..706f3456 100644 --- a/debug/tests/svg_clicks.html +++ b/debug/tests/svg_clicks.html @@ -43,7 +43,7 @@ [51, 7.000], [51.002, 7.004] ], - { clickable:false,color:'#f00' } + { interactive:false,color:'#f00' } ).addTo(map); // when the mouse hovers over the red route2, you cannot click through the blue route1 beneath diff --git a/dist/leaflet.css b/dist/leaflet.css index 1edf2b14..5f3d8abc 100644 --- a/dist/leaflet.css +++ b/dist/leaflet.css @@ -24,7 +24,7 @@ -webkit-user-select: none; -moz-user-select: none; user-select: none; - -webkit-user-drag: none; + -webkit-user-drag: none; } /* Safari renders non-retina tile on retina better with this, but Chrome is worse */ .leaflet-safari .leaflet-tile { @@ -40,7 +40,9 @@ .leaflet-marker-shadow { display: block; } -/* map is broken in FF if you have max-width: 100% on tiles */ +/* .leaflet-container svg: reset svg max-width decleration shipped in Joomla! (joomla.org) 3.x */ +/* .leaflet-container img: map is broken in FF if you have max-width: 100% on tiles */ +.leaflet-container svg, .leaflet-container img { max-width: none !important; } @@ -171,7 +173,7 @@ /* cursors */ -.leaflet-clickable { +.leaflet-interactive { cursor: pointer; } .leaflet-container { @@ -179,7 +181,7 @@ cursor: -moz-grab; } .leaflet-crosshair, -.leaflet-crosshair .leaflet-clickable { +.leaflet-crosshair .leaflet-interactive { cursor: crosshair; } .leaflet-popup-pane, @@ -187,7 +189,7 @@ cursor: auto; } .leaflet-dragging .leaflet-container, -.leaflet-dragging .leaflet-clickable { +.leaflet-dragging .leaflet-interactive { cursor: move; cursor: -webkit-grabbing; cursor: -moz-grabbing; diff --git a/package.json b/package.json index 802f5d23..33bf5f43 100644 --- a/package.json +++ b/package.json @@ -3,18 +3,18 @@ "version": "0.7.0", "description": "JavaScript library for mobile-friendly interactive maps", "devDependencies": { - "jake": "~0.7.9", - "jshint": "~2.4.4", + "jake": "~0.7.14", + "jshint": "~2.5.1", "uglify-js": "~2.4.13", - "mocha": "~1.17.1", + "mocha": "~1.19.0", "happen": "~0.1.3", - "karma": "~0.12.0", - "karma-mocha": "~0.1.1", - "karma-coverage": "~0.2.0", - "karma-phantomjs-launcher": "^0.1.2", - "karma-chrome-launcher": "^0.1.2", - "tin": "^0.4.0", - "copyfiles": "0.0.1" + "karma": "~0.12.16", + "karma-mocha": "~0.1.3", + "karma-coverage": "~0.2.3", + "karma-phantomjs-launcher": "^0.1.4", + "karma-chrome-launcher": "^0.1.4", + "tin": "^0.5.0", + "copyfiles": "0.1.0" }, "main": "dist/leaflet-src.js", "style": "dist/leaflet.css", diff --git a/spec/index.html b/spec/index.html index 2be65391..fe8a8c1b 100644 --- a/spec/index.html +++ b/spec/index.html @@ -4,6 +4,7 @@ Leaflet Spec Runner +
diff --git a/spec/suites/geo/LatLngSpec.js b/spec/suites/geo/LatLngSpec.js index 5552aff9..1c050df0 100644 --- a/spec/suites/geo/LatLngSpec.js +++ b/spec/suites/geo/LatLngSpec.js @@ -64,6 +64,12 @@ describe('LatLng', function () { expect(Math.abs(Math.round(a.distanceTo(b) / 1000) - 2084) < 5).to.eql(true); }); + it('does not return NaN if input points are equal', function () { + var a = new L.LatLng(50.5, 30.5); + var b = new L.LatLng(50.5, 30.5); + + expect(a.distanceTo(b)).to.eql(0); + }); }); describe('L.latLng factory', function () { diff --git a/spec/suites/layer/tile/TileLayerSpec.js b/spec/suites/layer/tile/TileLayerSpec.js index 34c9b498..f4439b9b 100644 --- a/spec/suites/layer/tile/TileLayerSpec.js +++ b/spec/suites/layer/tile/TileLayerSpec.js @@ -105,4 +105,98 @@ describe('TileLayer', function () { }); }); }); + + describe("'loading' event", function() { + var tileUrl1 = 'http://{s}.tile.osm.org/{z}/{x}/{y}.png', + tileUrl2 = 'http://{s}.tile.stamen.com/toner/{z}/{x}/{y}.png'; + + // Add map div to DOM. The map panning tests do not work reliably unless + // the Leaflet map is properly styled and part of the DOM + var mapDiv = document.createElement('div'); + mapDiv.style.height = '256px'; + mapDiv.style.width = '256px'; + document.body.appendChild(mapDiv); + + this.afterAll(function() { + document.body.removeChild(mapDiv); + }); + + // Set the map zoom high enough that panning by 256 pixels necessarily loads more tiles + var myMap = L.map(mapDiv).setView([0, 0], 13); + + describe("after a tilelayer has been initialized with an empty string", function() { + var layer = L.tileLayer(''); + var updateInterval = layer.options.updateInterval + 500; + + var loadingSpy; + beforeEach(function() { + loadingSpy = sinon.spy(); + layer.on('loading', function() { loadingSpy(); }); + }); + afterEach(function() { + layer.off('loading'); + }); + + it("is fired when the tilelayer is added to the map", function() { + layer.addTo(myMap); + expect(loadingSpy.calledOnce).to.be.ok(); + }); + + it("is fired again when the tilelayer has its url set to a real tile url", function(done) { + layer.setUrl(tileUrl2); + + setTimeout(function() { + expect(loadingSpy.calledOnce).to.be.ok(); + done(); + }, updateInterval); + }); + + it("is fired again when the map is panned enough to load more tiles", function(done) { + myMap.panBy([256,256]); + + setTimeout(function() { + expect(loadingSpy.calledOnce).to.be.ok(); + done(); + }, updateInterval); + }); + }); + + describe("after a tilelayer has been initialized with a real tile url", function() { + var layer = L.tileLayer(tileUrl1); + var updateInterval = layer.options.updateInterval + 500; + + var loadingSpy; + beforeEach(function() { + loadingSpy = sinon.spy(); + layer.on('loading', function() { loadingSpy(); }); + }); + afterEach(function() { + layer.off('loading'); + }); + + it("is fired when the tilelayer is added to the map", function() { + layer.addTo(myMap); + expect(loadingSpy.calledOnce).to.be.ok(); + }); + + it("is fired again when the tilelayer has its url set to a real tile url", function(done) { + layer.setUrl(tileUrl2); + + setTimeout(function() { + expect(loadingSpy.calledOnce).to.be.ok(); + done(); + }, updateInterval); + }); + + it("is fired again when the map is panned enough to load more tiles", function(done) { + myMap.panBy([256,256]); + + setTimeout(function() { + expect(loadingSpy.calledOnce).to.be.ok(); + done(); + }, updateInterval); + }); + }); + }); + }); diff --git a/src/Leaflet.js b/src/Leaflet.js index 507c781d..14604f42 100644 --- a/src/Leaflet.js +++ b/src/Leaflet.js @@ -21,8 +21,9 @@ if (typeof module === 'object' && typeof module.exports === 'object') { // define Leaflet as an AMD module } else if (typeof define === 'function' && define.amd) { define(L); +} // define Leaflet as a global L variable, saving the original L to restore later if needed -} else { +if (typeof window !== 'undefined') { expose(); } diff --git a/src/control/Control.Layers.js b/src/control/Control.Layers.js index 3e8417ff..f5234a00 100644 --- a/src/control/Control.Layers.js +++ b/src/control/Control.Layers.js @@ -124,8 +124,8 @@ L.Control.Layers = L.Control.extend({ _update: function () { if (!this._container) { return; } - this._baseLayersList.innerHTML = ''; - this._overlaysList.innerHTML = ''; + L.DomUtil.empty(this._baseLayersList); + L.DomUtil.empty(this._overlaysList); var baseLayersPresent, overlaysPresent, i, obj; diff --git a/src/control/Control.Scale.js b/src/control/Control.Scale.js index 80b1a146..2dab3100 100644 --- a/src/control/Control.Scale.js +++ b/src/control/Control.Scale.js @@ -80,7 +80,7 @@ L.Control.Scale = L.Control.extend({ }, _updateScale: function (scale, text, ratio) { - scale.style.width = (Math.round(this.options.maxWidth * ratio) - 10) + 'px'; + scale.style.width = Math.round(this.options.maxWidth * ratio) + 'px'; scale.innerHTML = text; }, diff --git a/src/core/Util.js b/src/core/Util.js index bd9ccafc..fc66a76e 100644 --- a/src/core/Util.js +++ b/src/core/Util.js @@ -5,11 +5,10 @@ L.Util = { // extend an object with properties of one or more other objects extend: function (dest) { - var sources = Array.prototype.slice.call(arguments, 1), - i, j, len, src; + var i, j, len, src; - for (j = 0, len = sources.length; j < len; j++) { - src = sources[j]; + for (j = 1, len = arguments.length; j < len; j++) { + src = arguments[j]; for (i in src) { dest[i] = src[i]; } @@ -174,11 +173,11 @@ L.Util = { getPrefixed('CancelRequestAnimationFrame') || function (id) { window.clearTimeout(id); }; - L.Util.requestAnimFrame = function (fn, context, immediate, element) { + L.Util.requestAnimFrame = function (fn, context, immediate) { if (immediate && requestFn === timeoutDefer) { fn.call(context); } else { - return requestFn.call(window, L.bind(fn, context), element); + return requestFn.call(window, L.bind(fn, context)); } }; diff --git a/src/dom/DomUtil.js b/src/dom/DomUtil.js index ba77e735..c13c41e7 100644 --- a/src/dom/DomUtil.js +++ b/src/dom/DomUtil.js @@ -38,6 +38,12 @@ L.DomUtil = { } }, + empty: function (el) { + while (el.firstChild) { + el.removeChild(el.firstChild); + } + }, + toFront: function (el) { el.parentNode.appendChild(el); }, diff --git a/src/dom/Draggable.js b/src/dom/Draggable.js index a6650ec6..c743c11c 100644 --- a/src/dom/Draggable.js +++ b/src/dom/Draggable.js @@ -90,7 +90,9 @@ L.Draggable = L.Evented.extend({ this._startPos = L.DomUtil.getPosition(this._element).subtract(offset); L.DomUtil.addClass(document.body, 'leaflet-dragging'); - L.DomUtil.addClass(e.target || e.srcElement, 'leaflet-drag-target'); + + this._lastTarget = e.target || e.srcElement; + L.DomUtil.addClass(this._lastTarget, 'leaflet-drag-target'); } this._newPos = this._startPos.add(offset); @@ -106,9 +108,13 @@ L.Draggable = L.Evented.extend({ this.fire('drag'); }, - _onUp: function (e) { + _onUp: function () { L.DomUtil.removeClass(document.body, 'leaflet-dragging'); - L.DomUtil.removeClass(e.target || e.srcElement, 'leaflet-drag-target'); + + if (this._lastTarget) { + L.DomUtil.removeClass(this._lastTarget, 'leaflet-drag-target'); + this._lastTarget = null; + } for (var i in L.Draggable.MOVE) { L.DomEvent diff --git a/src/dom/PosAnimation.js b/src/dom/PosAnimation.js index f5b00da5..ff67853a 100644 --- a/src/dom/PosAnimation.js +++ b/src/dom/PosAnimation.js @@ -19,9 +19,6 @@ L.PosAnimation = L.Evented.extend({ L.DomEvent.on(el, L.DomUtil.TRANSITION_END, this._onTransitionEnd, this); L.DomUtil.setPosition(el, newPos); - // toggle reflow, Chrome flickers for some reason if you don't do this - L.Util.falseFn(el.offsetWidth); - // there's no native way to track value updates of transitioned properties, so we imitate this this._stepTimer = setInterval(L.bind(this._onStep, this), 50); }, @@ -32,8 +29,11 @@ L.PosAnimation = L.Evented.extend({ // if we just removed the transition property, the element would jump to its final position, // so we need to make it stay at the current position + // Only setPosition if _getPos actually returns a valid position. this._newPos = this._getPos(); - L.DomUtil.setPosition(this._el, this._newPos); + if (this._newPos) { + L.DomUtil.setPosition(this._el, this._newPos); + } this._onTransitionEnd(); L.Util.falseFn(this._el.offsetWidth); // force reflow in case we are about to start a new animation diff --git a/src/geo/crs/CRS.Earth.js b/src/geo/crs/CRS.Earth.js index 3eb09873..b1fb4458 100644 --- a/src/geo/crs/CRS.Earth.js +++ b/src/geo/crs/CRS.Earth.js @@ -11,9 +11,10 @@ L.CRS.Earth = L.extend({}, L.CRS, { distance: function (latlng1, latlng2) { var rad = Math.PI / 180, lat1 = latlng1.lat * rad, - lat2 = latlng2.lat * rad; + lat2 = latlng2.lat * rad, + a = Math.sin(lat1) * Math.sin(lat2) + + Math.cos(lat1) * Math.cos(lat2) * Math.cos((latlng2.lng - latlng1.lng) * rad); - return this.R * Math.acos(Math.sin(lat1) * Math.sin(lat2) + - Math.cos(lat1) * Math.cos(lat2) * Math.cos((latlng2.lng - latlng1.lng) * rad)); + return this.R * Math.acos(Math.min(a, 1)); } }); diff --git a/src/layer/ImageOverlay.js b/src/layer/ImageOverlay.js index 42deb3b8..0c9bca02 100644 --- a/src/layer/ImageOverlay.js +++ b/src/layer/ImageOverlay.js @@ -5,7 +5,8 @@ L.ImageOverlay = L.Layer.extend({ options: { - opacity: 1 + opacity: 1, + alt: '' }, initialize: function (url, bounds, options) { // (String, LatLngBounds, Object) @@ -80,6 +81,10 @@ L.ImageOverlay = L.Layer.extend({ return events; }, + + getBounds: function() { + return this._bounds; + }, _initImage: function () { var img = this._image = L.DomUtil.create('img', @@ -90,6 +95,7 @@ L.ImageOverlay = L.Layer.extend({ img.onload = L.bind(this.fire, this, 'load'); img.src = this._url; + img.alt = this.options.alt; }, _animateZoom: function (e) { diff --git a/src/layer/marker/Marker.js b/src/layer/marker/Marker.js index 434b4e43..6256cd84 100644 --- a/src/layer/marker/Marker.js +++ b/src/layer/marker/Marker.js @@ -10,7 +10,7 @@ L.Marker = L.Layer.extend({ icon: new L.Icon.Default(), // title: '', // alt: '', - clickable: true, + interactive: true, // draggable: false, keyboard: true, zIndexOffset: 0, @@ -76,7 +76,7 @@ L.Marker = L.Layer.extend({ } if (this._popup) { - this.bindPopup(this._popup); + this.bindPopup(this._popup, this._popup.options); } return this; @@ -201,14 +201,18 @@ L.Marker = L.Layer.extend({ _initInteraction: function () { - if (!this.options.clickable) { return; } + if (!this.options.interactive) { return; } - L.DomUtil.addClass(this._icon, 'leaflet-clickable'); + L.DomUtil.addClass(this._icon, 'leaflet-interactive'); - L.DomEvent.on(this._icon, 'click dblclick mousedown mouseup mouseover mouseout contextmenu keypress', + L.DomEvent.on(this._icon, 'click dblclick mousedown mouseup mouseover mousemove mouseout contextmenu keypress', this._fireMouseEvent, this); if (L.Handler.MarkerDrag) { + if (this.dragging) { + this.dragging.disable(); + } + this.dragging = new L.Handler.MarkerDrag(this); if (this.options.draggable) { @@ -223,8 +227,6 @@ L.Marker = L.Layer.extend({ L.DomEvent.preventDefault(e); } - if (e.type === 'click' && this.dragging && this.dragging.moved()) { return; } - if (e.type === 'keypress' && e.keyCode === 13) { type = 'click'; } diff --git a/src/layer/tile/GridLayer.js b/src/layer/tile/GridLayer.js index dec97f1c..2999f358 100644 --- a/src/layer/tile/GridLayer.js +++ b/src/layer/tile/GridLayer.js @@ -191,11 +191,15 @@ L.GridLayer = L.Layer.extend({ }); } + if (this._abortLoading) { + this._abortLoading(); + } + this._tiles = {}; this._tilesToLoad = 0; this._tilesTotal = 0; - this._tileContainer.innerHTML = ''; + L.DomUtil.empty(this._tileContainer); if (this._zoomAnimated && e && e.hard) { this._clearBgBuffer(); @@ -241,7 +245,10 @@ L.GridLayer = L.Layer.extend({ tileSize = this._getTileSize(); if (zoom > this.options.maxZoom || - zoom < this.options.minZoom) { return; } + zoom < this.options.minZoom) { + this._clearBgBuffer(); + return; + } // tile coordinates range for the current view var tileBounds = L.bounds( @@ -324,7 +331,7 @@ L.GridLayer = L.Layer.extend({ _tileCoordsToBounds: function (coords) { var map = this._map, - tileSize = this.options.tileSize, + tileSize = this._getTileSize(), nwPoint = coords.multiplyBy(tileSize), sePoint = nwPoint.add([tileSize, tileSize]), @@ -340,7 +347,7 @@ L.GridLayer = L.Layer.extend({ return coords.x + ':' + coords.y; }, - // converts tile cache key to coordiantes + // converts tile cache key to coordinates _keyToTileCoords: function (key) { var kArr = key.split(':'), x = parseInt(kArr[0], 10), @@ -494,7 +501,7 @@ L.GridLayer = L.Layer.extend({ bg = this._bgBuffer; if (map && !map._animatingZoom && !map.touchZoom._zooming && bg) { - bg.innerHTML = ''; + L.DomUtil.empty(bg); L.DomUtil.setTransform(bg); } }, diff --git a/src/layer/tile/TileLayer.WMS.js b/src/layer/tile/TileLayer.WMS.js index 48da0ab1..c76dcee2 100644 --- a/src/layer/tile/TileLayer.WMS.js +++ b/src/layer/tile/TileLayer.WMS.js @@ -22,7 +22,9 @@ L.TileLayer.WMS = L.TileLayer.extend({ // all keys that are not TileLayer options go to WMS params for (var i in options) { - if (!this.options.hasOwnProperty(i) && i !== 'crs') { + if (!this.options.hasOwnProperty(i) && + i !== 'crs' && + i !== 'uppercase') { wmsParams[i] = options[i]; } } @@ -38,6 +40,7 @@ L.TileLayer.WMS = L.TileLayer.extend({ onAdd: function (map) { this._crs = this.options.crs || map.options.crs; + this._uppercase = this.options.uppercase || false; this._wmsVersion = parseFloat(this.wmsParams.version); @@ -57,9 +60,11 @@ L.TileLayer.WMS = L.TileLayer.extend({ [se.y, nw.x, nw.y, se.x] : [nw.x, se.y, se.x, nw.y]).join(','), - url = L.Util.template(this._url, {s: this._getSubdomain(coords)}); + url = L.TileLayer.prototype.getTileUrl.call(this, coords); - return url + L.Util.getParamString(this.wmsParams, url, true) + '&BBOX=' + bbox; + return url + + L.Util.getParamString(this.wmsParams, url, this._uppercase) + + (this._uppercase ? '&BBOX=' : '&bbox=') + bbox; }, setParams: function (params, noRedraw) { diff --git a/src/layer/tile/TileLayer.js b/src/layer/tile/TileLayer.js index 276f50f2..2e5aa9d5 100644 --- a/src/layer/tile/TileLayer.js +++ b/src/layer/tile/TileLayer.js @@ -16,7 +16,8 @@ L.TileLayer = L.GridLayer.extend({ maxNativeZoom: , tms: , zoomReverse: , - detectRetina: , + detectRetina: , + crossOrigin: , */ }, @@ -55,7 +56,11 @@ L.TileLayer = L.GridLayer.extend({ tile.onload = L.bind(this._tileOnLoad, this, done, tile); tile.onerror = L.bind(this._tileOnError, this, done, tile); - + + if (this.options.crossOrigin) { + tile.crossOrigin = ''; + } + /* Alt tag is set to empty string to keep screen readers from reading URL and for compliance reasons http://www.w3.org/TR/WCAG20-TECHS/H67 @@ -138,11 +143,11 @@ L.TileLayer = L.GridLayer.extend({ for (i in this._tiles) { tile = this._tiles[i]; - if (!tile.complete) { - tile.onload = L.Util.falseFn; - tile.onerror = L.Util.falseFn; - tile.src = L.Util.emptyImageUrl; + tile.onload = L.Util.falseFn; + tile.onerror = L.Util.falseFn; + if (!tile.complete) { + tile.src = L.Util.emptyImageUrl; L.DomUtil.remove(tile); } } diff --git a/src/layer/vector/Canvas.js b/src/layer/vector/Canvas.js index 59580090..e05f2d3e 100644 --- a/src/layer/vector/Canvas.js +++ b/src/layer/vector/Canvas.js @@ -207,12 +207,12 @@ L.Canvas = L.Renderer.extend({ }, _handleHover: function (layer, e, point) { - if (!layer.options.clickable) { return; } + if (!layer.options.interactive) { return; } if (layer._containsPoint(point)) { // if we just got inside the layer, fire mouseover if (!layer._mouseInside) { - L.DomUtil.addClass(this._container, 'leaflet-clickable'); // change cursor + L.DomUtil.addClass(this._container, 'leaflet-interactive'); // change cursor layer._fireMouseEvent(e, 'mouseover'); layer._mouseInside = true; } @@ -221,7 +221,7 @@ L.Canvas = L.Renderer.extend({ } else if (layer._mouseInside) { // if we're leaving the layer, fire mouseout - L.DomUtil.removeClass(this._container, 'leaflet-clickable'); + L.DomUtil.removeClass(this._container, 'leaflet-interactive'); layer._fireMouseEvent(e, 'mouseout'); layer._mouseInside = false; } diff --git a/src/layer/vector/Path.js b/src/layer/vector/Path.js index d4dea2a2..70e575ad 100644 --- a/src/layer/vector/Path.js +++ b/src/layer/vector/Path.js @@ -19,7 +19,7 @@ L.Path = L.Layer.extend({ fillOpacity: 0.2, // className: '' - clickable: true + interactive: true }, onAdd: function () { diff --git a/src/layer/vector/SVG.VML.js b/src/layer/vector/SVG.VML.js index 2cc1d244..95b3735b 100644 --- a/src/layer/vector/SVG.VML.js +++ b/src/layer/vector/SVG.VML.js @@ -106,7 +106,7 @@ L.SVG.include(!L.Browser.vml ? {} : { }, _updateCircle: function (layer) { - var p = layer._point, + var p = layer._point.round(), r = Math.round(layer._radius), r2 = Math.round(layer._radiusY || r); diff --git a/src/layer/vector/SVG.js b/src/layer/vector/SVG.js index 196a269d..553bb7e4 100644 --- a/src/layer/vector/SVG.js +++ b/src/layer/vector/SVG.js @@ -31,11 +31,17 @@ L.SVG = L.Renderer.extend({ L.DomUtil.setPosition(container, b.min); - // update container viewBox so that we don't have to change coordinates of individual layers - container.setAttribute('width', size.x); - container.setAttribute('height', size.y); - container.setAttribute('viewBox', [b.min.x, b.min.y, size.x, size.y].join(' ')); + // set size of svg-container if changed + if (!this._svgSize || !this._svgSize.equals(size)) { + this._svgSize = size; + container.setAttribute('width', size.x); + container.setAttribute('height', size.y); + } + // movement: update container viewBox so that we don't have to change coordinates of individual layers + L.DomUtil.setPosition(container, b.min); + container.setAttribute('viewBox', [b.min.x, b.min.y, size.x, size.y].join(' ')); + if (L.Browser.mobileWebkit) { pane.appendChild(container); } @@ -50,8 +56,8 @@ L.SVG = L.Renderer.extend({ L.DomUtil.addClass(path, layer.options.className); } - if (layer.options.clickable) { - L.DomUtil.addClass(path, 'leaflet-clickable'); + if (layer.options.interactive) { + L.DomUtil.addClass(path, 'leaflet-interactive'); } this._updateStyle(layer); @@ -110,7 +116,7 @@ L.SVG = L.Renderer.extend({ path.setAttribute('fill', 'none'); } - path.setAttribute('pointer-events', options.pointerEvents || (options.clickable ? 'visiblePainted' : 'none')); + path.setAttribute('pointer-events', options.pointerEvents || (options.interactive ? 'visiblePainted' : 'none')); }, _updatePoly: function (layer, closed) { @@ -152,7 +158,10 @@ L.SVG = L.Renderer.extend({ }, _fireMouseEvent: function (e) { - this._paths[L.stamp(e.target || e.srcElement)]._fireMouseEvent(e); + var path = this._paths[L.stamp(e.target || e.srcElement)]; + if (path) { + path._fireMouseEvent(e); + } } }); diff --git a/src/map/Map.js b/src/map/Map.js index 31c7d072..7fda5050 100644 --- a/src/map/Map.js +++ b/src/map/Map.js @@ -33,6 +33,10 @@ L.Map = L.Evented.extend({ this.setMaxBounds(options.maxBounds); } + if (options.zoom !== undefined) { + this._zoom = this._limitZoom(options.zoom); + } + if (options.center && options.zoom !== undefined) { this.setView(L.latLng(options.center), options.zoom, {reset: true}); } @@ -566,9 +570,9 @@ L.Map = L.Evented.extend({ type = type || e.type; if (L.DomEvent._skipped(e)) { return; } - if (type === 'click') { - if (!e._simulated && ((this.dragging && this.dragging.moved()) || + var draggableObj = obj.options.draggable === true ? obj : this; + if (!e._simulated && ((draggableObj.dragging && draggableObj.dragging.moved()) || (this.boxZoom && this.boxZoom.moved()))) { return; } obj.fire('preclick'); } diff --git a/src/map/anim/Map.PanAnimation.js b/src/map/anim/Map.PanAnimation.js index 777ea7b9..8de67e3c 100644 --- a/src/map/anim/Map.PanAnimation.js +++ b/src/map/anim/Map.PanAnimation.js @@ -46,6 +46,11 @@ L.Map.include({ if (!offset.x && !offset.y) { return this; } + //If we pan too far then chrome gets issues with tiles + // and makes them disappear or appear in the wrong place (slightly offset) #2602 + if (options.animate !== true && !this.getSize().contains(offset)) { + return this._resetView(this.unproject(this.project(this.getCenter()).add(offset)), this.getZoom()); + } if (!this._panAnim) { this._panAnim = new L.PosAnimation(); diff --git a/src/map/handler/Map.BoxZoom.js b/src/map/handler/Map.BoxZoom.js index cad8a236..fcca466c 100644 --- a/src/map/handler/Map.BoxZoom.js +++ b/src/map/handler/Map.BoxZoom.js @@ -27,19 +27,20 @@ L.Map.BoxZoom = L.Handler.extend({ }, _onMouseDown: function (e) { - this._moved = false; - if (!e.shiftKey || ((e.which !== 1) && (e.button !== 1))) { return false; } + this._moved = false; + L.DomUtil.disableTextSelection(); L.DomUtil.disableImageDrag(); this._startPoint = this._map.mouseEventToContainerPoint(e); L.DomEvent.on(document, { + contextmenu: L.DomEvent.stop, mousemove: this._onMouseMove, - mouseup: this._onMouseUp, - keydown: this._onKeyDown + mouseup: this._onMouseUp, + keydown: this._onKeyDown }, this); }, @@ -74,13 +75,15 @@ L.Map.BoxZoom = L.Handler.extend({ L.DomUtil.enableImageDrag(); L.DomEvent.off(document, { + contextmenu: L.DomEvent.stop, mousemove: this._onMouseMove, - mouseup: this._onMouseUp, - keydown: this._onKeyDown + mouseup: this._onMouseUp, + keydown: this._onKeyDown }, this); }, - _onMouseUp: function () { + _onMouseUp: function (e) { + if ((e.which !== 1) && (e.button !== 1)) { return false; } this._finish(); diff --git a/src/map/handler/Map.ScrollWheelZoom.js b/src/map/handler/Map.ScrollWheelZoom.js index df2e614b..77ada016 100644 --- a/src/map/handler/Map.ScrollWheelZoom.js +++ b/src/map/handler/Map.ScrollWheelZoom.js @@ -3,7 +3,8 @@ */ L.Map.mergeOptions({ - scrollWheelZoom: true + scrollWheelZoom: true, + wheelDebounceTime: 40 }); L.Map.ScrollWheelZoom = L.Handler.extend({ @@ -25,6 +26,7 @@ L.Map.ScrollWheelZoom = L.Handler.extend({ _onWheelScroll: function (e) { var delta = L.DomEvent.getWheelDelta(e); + var debounce = this._map.options.wheelDebounceTime; this._delta += delta; this._lastMousePos = this._map.mouseEventToContainerPoint(e); @@ -33,7 +35,7 @@ L.Map.ScrollWheelZoom = L.Handler.extend({ this._startTime = +new Date(); } - var left = Math.max(40 - (+new Date() - this._startTime), 0); + var left = Math.max(debounce - (+new Date() - this._startTime), 0); clearTimeout(this._timer); this._timer = setTimeout(L.bind(this._performZoom, this), left); diff --git a/src/map/handler/Map.TouchZoom.js b/src/map/handler/Map.TouchZoom.js index 741f83ea..ab90795e 100644 --- a/src/map/handler/Map.TouchZoom.js +++ b/src/map/handler/Map.TouchZoom.js @@ -54,9 +54,11 @@ L.Map.TouchZoom = L.Handler.extend({ this._scale = p1.distanceTo(p2) / this._startDist; this._delta = p1._add(p2)._divideBy(2)._subtract(this._startCenter); - if (!map.options.bounceAtZoomLimits && - ((map.getZoom() === map.getMinZoom() && this._scale < 1) || - (map.getZoom() === map.getMaxZoom() && this._scale > 1))) { return; } + if (!map.options.bounceAtZoomLimits) { + var currentZoom = map.getScaleZoom(this._scale); + if ((currentZoom <= map.getMinZoom() && this._scale < 1) || + (currentZoom >= map.getMaxZoom() && this._scale > 1)) { return; } + } if (!this._moved) { map