Merge branch 'master' of github.com:Leaflet/Leaflet into popup-debugging

This commit is contained in:
Patrick Arlt 2015-03-14 10:22:25 -07:00
commit 52be81b18b
65 changed files with 678 additions and 481 deletions

29
.eslintrc Normal file
View File

@ -0,0 +1,29 @@
{
"rules": {
"camelcase": 2,
"quotes": [2, "single"],
"no-mixed-spaces-and-tabs": [2, "smart-tabs"],
"space-after-function-name": 2,
"space-in-parens": 2,
"space-in-brackets": 2,
"space-before-blocks": 2,
"space-after-keywords": 2,
"no-lonely-if": 2,
"comma-style": 2,
"indent": [2, "tab"],
"no-underscore-dangle": 0,
"no-constant-condition": 0,
"no-multi-spaces": 0,
"strict": 0,
"key-spacing": 0,
"no-shadow": 0
},
"globals": {
"L": true,
"module": false,
"define": false
},
"env": {
"browser": true
}
}

View File

@ -18,8 +18,8 @@ before_script: >
test ${TRAVIS_BRANCH} = master || test ${TRAVIS_BRANCH} = master ||
test ${TRAVIS_BRANCH} = stable && test ${TRAVIS_BRANCH} = stable &&
test ${TRAVIS_PULL_REQUEST} = false && test ${TRAVIS_PULL_REQUEST} = false &&
gem install --no-rdoc --no-ri --version 0.8.9 faraday && gem install --no-document --version 0.8.9 faraday &&
gem install --no-rdoc --no-ri travis-artifacts || true gem install --no-document travis-artifacts || true
after_success: > after_success: >
test ${TRAVIS_BRANCH} = master || test ${TRAVIS_BRANCH} = master ||
test ${TRAVIS_BRANCH} = stable && test ${TRAVIS_BRANCH} = stable &&

View File

@ -31,7 +31,7 @@ here are some tips for creating a helpful report that will make fixing it much e
* Write a **descriptive, specific title**. Bad: *Problem with polylines*. Good: *Doing X in IE9 causes Z*. * Write a **descriptive, specific title**. Bad: *Problem with polylines*. Good: *Doing X in IE9 causes Z*.
* Include **browser, OS and Leaflet version** info in the description. * Include **browser, OS and Leaflet version** info in the description.
* Create a **simple test case** that demonstrates the bug (e.g. using [JSFiddle](http://jsfiddle.net/)). * Create a **simple test case** that demonstrates the bug (e.g. using [JSFiddle](http://jsfiddle.net/) or [JS Bin](http://jsbin.com/)).
* Check whether the bug can be reproduced in **other browsers**. * Check whether the bug can be reproduced in **other browsers**.
* Check if the bug occurs in the stable version, master, or both. * Check if the bug occurs in the stable version, master, or both.
* *Bonus tip:* if the bug only appears in the master version but the stable version is fine, * *Bonus tip:* if the bug only appears in the master version but the stable version is fine,
@ -125,7 +125,7 @@ From there you can click through folders/files to get details on their individua
## Improving Documentation ## Improving Documentation
The code of the live Leaflet website that contains all documentation and examples is located in the `gh-pages` branch The code of the live Leaflet website that contains all documentation and examples is located in the `gh-pages` branch
and is automatically generated from a set of HTML and Markdown files by [Jekyll](https://github.com/mojombo/jekyll). and is automatically generated from a set of HTML and Markdown files by [Jekyll](http://jekyllrb.com/).
The easiest way to make little improvements such as fixing typos without even leaving the browser The easiest way to make little improvements such as fixing typos without even leaving the browser
is by editing one of the files with the online GitHub editor: is by editing one of the files with the online GitHub editor:

View File

@ -15,10 +15,10 @@ For a custom build, open build/build.html in the browser and follow the instruct
var build = require('./build/build.js'), var build = require('./build/build.js'),
version = require('./src/Leaflet.js').version; version = require('./src/Leaflet.js').version;
function hint(msg, paths) { function hint(msg, args) {
return function () { return function () {
console.log(msg); console.log(msg);
jake.exec('node node_modules/jshint/bin/jshint -c ' + paths, jake.exec('node node_modules/eslint/bin/eslint.js ' + args,
{printStdout: true}, function () { {printStdout: true}, function () {
console.log('\tCheck passed.\n'); console.log('\tCheck passed.\n');
complete(); complete();
@ -26,11 +26,11 @@ function hint(msg, paths) {
}; };
} }
desc('Check Leaflet source for errors with JSHint'); desc('Check Leaflet source for errors with ESLint');
task('lint', {async: true}, hint('Checking for JS errors...', 'build/hintrc.js src')); task('lint', {async: true}, hint('Checking for JS errors...', 'src --config .eslintrc'));
desc('Check Leaflet specs source for errors with JSHint'); desc('Check Leaflet specs source for errors with ESLint');
task('lintspec', {async: true}, hint('Checking for specs JS errors...', 'spec/spec.hintrc.js spec/suites')); task('lintspec', {async: true}, hint('Checking for specs JS errors...', 'spec/suites --config spec/.eslintrc'));
desc('Combine and compress Leaflet source files'); desc('Combine and compress Leaflet source files');
task('build', {async: true}, function (compsBase32, buildName) { task('build', {async: true}, function (compsBase32, buildName) {

View File

@ -1,4 +1,4 @@
Copyright (c) 2010-2014, Vladimir Agafonkin Copyright (c) 2010-2015, Vladimir Agafonkin
Copyright (c) 2010-2011, CloudMade Copyright (c) 2010-2011, CloudMade
All rights reserved. All rights reserved.

View File

@ -113,7 +113,7 @@ Function, method and property names should be in `camelCase`.<br>
Class names should be in `CapitalizedCamelCase`. Class names should be in `CapitalizedCamelCase`.
If you have a lot of arguments in your function, consider accepting an options object instead If you have a lot of arguments in your function, consider accepting an options object instead
(putting default values where possible so that users don't need specify all of them): (putting default values where possible so that users don't need to specify all of them):
```js ```js
// bad // bad
@ -168,12 +168,11 @@ You can add support for AMD/CommonJS loaders to your Leaflet plugin by following
// define a Common JS module that relies on 'leaflet' // define a Common JS module that relies on 'leaflet'
} else if (typeof exports === 'object') { } else if (typeof exports === 'object') {
module.exports = factory(require('leaflet')); module.exports = factory(require('leaflet'));
} }
// attach your plugin to the global 'L' variable // attach your plugin to the global 'L' variable
if(typeof window !== 'undefined' && window.L){ if (typeof window !== 'undefined' && window.L) {
window.L.YourPlugin = factory(L); window.L.YourPlugin = factory(L);
} }
}(function (L) { }(function (L) {
var MyLeafletPlugin = {}; var MyLeafletPlugin = {};

View File

@ -19,7 +19,7 @@ If you want to **get involved** with Leaflet development, check out the [contrib
Let's make the best mapping library that will ever exist, Let's make the best mapping library that will ever exist,
and push the limits of what's possible with online maps! and push the limits of what's possible with online maps!
[![Build Status](https://travis-ci.org/Leaflet/Leaflet.png?branch=master)](https://travis-ci.org/Leaflet/Leaflet) [![Build Status](https://travis-ci.org/Leaflet/Leaflet.svg?branch=master)](https://travis-ci.org/Leaflet/Leaflet)
[Vladimir Agafonkin]: http://agafonkin.com/en [Vladimir Agafonkin]: http://agafonkin.com/en
[contributors]: https://github.com/Leaflet/Leaflet/graphs/contributors [contributors]: https://github.com/Leaflet/Leaflet/graphs/contributors

View File

@ -1,40 +0,0 @@
{
// environment
"browser": true,
"node": true,
"globals": {
"L": true,
"define": true
},
"strict": false,
"es3": true,
// code style
"bitwise": true,
"camelcase": true,
"curly": true,
"eqeqeq": true,
"forin": false,
"immed": true,
"latedef": true,
"newcap": true,
"noarg": true,
"noempty": true,
"nonew": true,
"undef": true,
"unused": true,
"quotmark": "single",
// whitespace
"indent": 4,
"trailing": true,
"white": true,
"smarttabs": true,
"maxlen": 120
// code simplicity - not enforced but nice to check from time to time
// "maxstatements": 20,
// "maxcomplexity": 5
// "maxparams": 4,
// "maxdepth": 4
}

View File

@ -40,8 +40,8 @@
map.addLayer(overlay); map.addLayer(overlay);
overlay.on('dblclick',function (e) { overlay.on('dblclick',function (e) {
console.log('Double click on image.') console.log('Double click on image.');
}) });
</script> </script>
</body> </body>

11
dist/leaflet.css vendored
View File

@ -139,7 +139,9 @@
/* zoom and fade animations */ /* zoom and fade animations */
.leaflet-fade-anim .leaflet-tile, .leaflet-fade-anim .leaflet-tile {
will-change: opacity;
}
.leaflet-fade-anim .leaflet-popup { .leaflet-fade-anim .leaflet-popup {
opacity: 0; opacity: 0;
-webkit-transition: opacity 0.2s linear; -webkit-transition: opacity 0.2s linear;
@ -147,11 +149,12 @@
-o-transition: opacity 0.2s linear; -o-transition: opacity 0.2s linear;
transition: opacity 0.2s linear; transition: opacity 0.2s linear;
} }
.leaflet-fade-anim .leaflet-tile-loaded,
.leaflet-fade-anim .leaflet-map-pane .leaflet-popup { .leaflet-fade-anim .leaflet-map-pane .leaflet-popup {
opacity: 1; opacity: 1;
} }
.leaflet-zoom-anim .leaflet-zoom-animated {
will-change: transform;
}
.leaflet-zoom-anim .leaflet-zoom-animated { .leaflet-zoom-anim .leaflet-zoom-animated {
-webkit-transition: -webkit-transform 0.25s cubic-bezier(0,0,0.25,1); -webkit-transition: -webkit-transform 0.25s cubic-bezier(0,0,0.25,1);
-moz-transition: -moz-transform 0.25s cubic-bezier(0,0,0.25,1); -moz-transition: -moz-transform 0.25s cubic-bezier(0,0,0.25,1);
@ -440,7 +443,7 @@
.leaflet-popup-content-wrapper, .leaflet-popup-content-wrapper,
.leaflet-popup-tip { .leaflet-popup-tip {
background: white; background: white;
color: #333;
box-shadow: 0 3px 14px rgba(0,0,0,0.4); box-shadow: 0 3px 14px rgba(0,0,0,0.4);
} }
.leaflet-container a.leaflet-popup-close-button { .leaflet-container a.leaflet-popup-close-button {

View File

@ -3,20 +3,20 @@
"version": "0.8.0-dev", "version": "0.8.0-dev",
"description": "JavaScript library for mobile-friendly interactive maps", "description": "JavaScript library for mobile-friendly interactive maps",
"devDependencies": { "devDependencies": {
"jake": "~8.0.10", "copyfiles": "0.1.0",
"jshint": "~2.5.7", "eslint": "^0.15.1",
"uglify-js": "~2.4.15",
"mocha": "~2.0.1",
"happen": "~0.1.3", "happen": "~0.1.3",
"karma": "~0.12.24", "jake": "~8.0.10",
"karma-mocha": "~0.1.9", "karma": "~0.12.31",
"karma-coverage": "~0.2.6", "karma-chrome-launcher": "^0.1.7",
"karma-coverage": "~0.2.7",
"karma-firefox-launcher": "~0.1.4",
"karma-mocha": "~0.1.10",
"karma-phantomjs-launcher": "^0.1.4", "karma-phantomjs-launcher": "^0.1.4",
"karma-chrome-launcher": "^0.1.5",
"karma-firefox-launcher": "~0.1.3",
"karma-safari-launcher": "~0.1.1", "karma-safari-launcher": "~0.1.1",
"mocha": "~2.1.0",
"tin": "^0.5.0", "tin": "^0.5.0",
"copyfiles": "0.1.0" "uglify-js": "~2.4.16"
}, },
"main": "dist/leaflet-src.js", "main": "dist/leaflet-src.js",
"style": "dist/leaflet.css", "style": "dist/leaflet.css",

38
spec/.eslintrc Normal file
View File

@ -0,0 +1,38 @@
{
"rules": {
"camelcase": 2,
"no-mixed-spaces-and-tabs": [2, "smart-tabs"],
"space-after-function-name": 2,
"space-in-parens": 2,
"space-in-brackets": 2,
"space-before-blocks": 2,
"space-after-keywords": 2,
"no-lonely-if": 2,
"comma-style": 2,
"no-unused-vars": 0,
"quotes": 0,
"no-underscore-dangle": 0,
"no-constant-condition": 0,
"no-multi-spaces": 0,
"strict": 0,
"key-spacing": 0,
"no-shadow": 0,
"no-irregular-whitespace": 0,
"no-console": 0
},
"globals": {
"L": true,
"module": false,
"define": false,
"expect": false,
"it": false,
"describe": false,
"sinon": false,
"happen": false,
"beforeEach": false,
"afterEach": false
},
"env": {
"browser": true
}
}

View File

@ -63,6 +63,10 @@ module.exports = function (config) {
// If browser does not capture in given timeout [ms], kill it // If browser does not capture in given timeout [ms], kill it
captureTimeout: 5000, captureTimeout: 5000,
// Workaround for PhantomJS random DISCONNECTED error
browserDisconnectTimeout: 10000, // default 2000
browserDisconnectTolerance: 1, // default 0
// Continuous Integration mode // Continuous Integration mode
// if true, it capture browsers, run tests and exit // if true, it capture browsers, run tests and exit
singleRun: true singleRun: true

View File

@ -1,4 +1,5 @@
if (!Array.prototype.map) { if (!Array.prototype.map) {
/*eslint no-extend-native:0*/
Array.prototype.map = function (fun /*, thisp */) { Array.prototype.map = function (fun /*, thisp */) {
"use strict"; "use strict";
@ -7,7 +8,6 @@ if (!Array.prototype.map) {
} }
var t = Object(this); var t = Object(this);
// jshint bitwise: false
var len = t.length >>> 0; var len = t.length >>> 0;
if (typeof fun !== "function") { if (typeof fun !== "function") {
throw new TypeError(); throw new TypeError();

View File

@ -14,7 +14,7 @@ describe('Events', function () {
obj.addEventListener('test', spy1); obj.addEventListener('test', spy1);
obj.addEventListener('test', spy2); obj.addEventListener('test', spy2);
obj.addEventListener('other', spy3); obj.addEventListener('other', spy3);
obj.addEventListener({ test: spy4, other: spy5 }); obj.addEventListener({test: spy4, other: spy5});
// obj.addEventListener({'test other': spy6 }); // obj.addEventListener({'test other': spy6 });
expect(spy1.called).to.be(false); expect(spy1.called).to.be(false);
@ -72,8 +72,8 @@ describe('Events', function () {
obj.addEventListener('test', listener1); obj.addEventListener('test', listener1);
obj2.addEventListener('test', listener2, foo); obj2.addEventListener('test', listener2, foo);
obj3.addEventListener({ test: listener3 }); obj3.addEventListener({test: listener3});
obj4.addEventListener({ test: listener4 }, foo); obj4.addEventListener({test: listener4}, foo);
obj.fireEvent('test', {baz: 1}); obj.fireEvent('test', {baz: 1});
obj2.fireEvent('test', {baz: 2}); obj2.fireEvent('test', {baz: 2});
@ -358,15 +358,15 @@ describe('Events', function () {
}); });
it("doesn't call listeners to events that have been removed", function () { it("doesn't call listeners to events that have been removed", function () {
var obj = new L.Evented(), var obj = new L.Evented(),
spy = sinon.spy(); spy = sinon.spy();
obj.once('test', spy, obj); obj.once('test', spy, obj);
obj.off('test', spy, obj); obj.off('test', spy, obj);
obj.fire('test'); obj.fire('test');
expect(spy.called).to.be(false); expect(spy.called).to.be(false);
}); });
it('works if called from a context that doesnt implement #Events', function () { it('works if called from a context that doesnt implement #Events', function () {

View File

@ -40,9 +40,9 @@ describe('Util', function () {
return this; return this;
}; };
var fn2 = L.Util.bind(fn, { foo: 'bar' }); var fn2 = L.Util.bind(fn, {foo: 'bar'});
expect(fn2()).to.eql({ foo: 'bar' }); expect(fn2()).to.eql({foo: 'bar'});
}); });
it('passes additional arguments to the bound function', function () { it('passes additional arguments to the bound function', function () {
@ -230,6 +230,7 @@ describe('Util', function () {
describe('#isArray', function () { describe('#isArray', function () {
expect(L.Util.isArray([1, 2, 3])).to.be(true); expect(L.Util.isArray([1, 2, 3])).to.be(true);
/*eslint no-array-constructor:0*/
expect(L.Util.isArray(new Array(1, 2, 3))).to.be(true); expect(L.Util.isArray(new Array(1, 2, 3))).to.be(true);
expect(L.Util.isArray('blabla')).to.be(false); expect(L.Util.isArray('blabla')).to.be(false);
expect(L.Util.isArray({0: 1, 1: 2})).to.be(false); expect(L.Util.isArray({0: 1, 1: 2})).to.be(false);

View File

@ -112,9 +112,8 @@ describe('LatLng', function () {
}); });
it('accepts an object with alt', function () { it('accepts an object with alt', function () {
expect(L.latLng({lat: 50, lng: 30, alt:100})).to.eql(new L.LatLng(50, 30, 100)); expect(L.latLng({lat: 50, lng: 30, alt: 100})).to.eql(new L.LatLng(50, 30, 100));
expect(L.latLng({lat: 50, lon: 30, alt:100})).to.eql(new L.LatLng(50, 30, 100)); expect(L.latLng({lat: 50, lon: 30, alt: 100})).to.eql(new L.LatLng(50, 30, 100));
}); });
}); });
}); });

View File

@ -33,6 +33,21 @@ describe('LineUtil', function () {
expect(segment).to.be(false); expect(segment).to.be(false);
}); });
it('can round numbers in clipped bounds', function () {
var a = new L.Point(4, 5);
var b = new L.Point(8, 6);
var segment1 = L.LineUtil.clipSegment(a, b, bounds);
expect(segment1[0]).to.eql(new L.Point(5, 5.25));
expect(segment1[1]).to.eql(b);
var segment2 = L.LineUtil.clipSegment(a, b, bounds, false, true);
expect(segment2[0]).to.eql(new L.Point(5, 5));
expect(segment2[1]).to.eql(b);
});
}); });
describe('#pointToSegmentDistance & #closestPointOnSegment', function () { describe('#pointToSegmentDistance & #closestPointOnSegment', function () {

View File

@ -10,6 +10,7 @@ describe('PolyUtil', function () {
new L.Point(10, 15) new L.Point(10, 15)
]; ];
//check clip without rounding
var clipped = L.PolyUtil.clipPolygon(points, bounds); var clipped = L.PolyUtil.clipPolygon(points, bounds);
for (var i = 0, len = clipped.length; i < len; i++) { for (var i = 0, len = clipped.length; i < len; i++) {
@ -17,6 +18,20 @@ describe('PolyUtil', function () {
} }
expect(clipped).to.eql([ expect(clipped).to.eql([
new L.Point(7.5, 10),
new L.Point(5, 5),
new L.Point(10, 7.5),
new L.Point(10, 10)
]);
//check clip with rounding
var clippedRounded = L.PolyUtil.clipPolygon(points, bounds, true);
for (i = 0, len = clippedRounded.length; i < len; i++) {
delete clippedRounded[i]._code;
}
expect(clippedRounded).to.eql([
new L.Point(8, 10), new L.Point(8, 10),
new L.Point(5, 5), new L.Point(5, 5),
new L.Point(10, 8), new L.Point(10, 8),

View File

@ -58,7 +58,7 @@
it('iterates over all layers', function () { it('iterates over all layers', function () {
var lg = L.layerGroup(), var lg = L.layerGroup(),
marker = L.marker([0, 0]), marker = L.marker([0, 0]),
ctx = { foo: 'bar' }; ctx = {foo: 'bar'};
lg.addLayer(marker); lg.addLayer(marker);

View File

@ -112,6 +112,41 @@ describe('Popup', function () {
marker1.closePopup(); marker1.closePopup();
expect(spy.callCount).to.be(2); expect(spy.callCount).to.be(2);
}); });
it("should take into account icon popupAnchor option", function () {
var autoPanBefore = L.Popup.prototype.options.autoPan;
L.Popup.prototype.options.autoPan = false;
var popupAnchorBefore = L.Icon.Default.prototype.options.popupAnchor;
L.Icon.Default.prototype.options.popupAnchor = [0, 0];
var latlng = new L.LatLng(55.8, 37.6),
offset = new L.Point(20, 30),
icon = new L.DivIcon({popupAnchor: offset}),
marker1 = new L.Marker(latlng),
marker2 = new L.Marker(latlng, {icon: icon});
marker1.bindPopup('Popup').addTo(map);
marker1.openPopup();
var defaultLeft = parseInt(marker1._popup._container.style.left, 10);
var defaultBottom = parseInt(marker1._popup._container.style.bottom, 10);
marker2.bindPopup('Popup').addTo(map);
marker2.openPopup();
var offsetLeft = parseInt(marker2._popup._container.style.left, 10);
var offsetBottom = parseInt(marker2._popup._container.style.bottom, 10);
expect(offsetLeft - offset.x).to.eql(defaultLeft);
expect(offsetBottom + offset.y).to.eql(defaultBottom);
// Now retry passing a popup instance to bindPopup
marker2.bindPopup(new L.Popup());
marker2.openPopup();
offsetLeft = parseInt(marker2._popup._container.style.left, 10);
offsetBottom = parseInt(marker2._popup._container.style.bottom, 10);
expect(offsetLeft - offset.x).to.eql(defaultLeft);
expect(offsetBottom + offset.y).to.eql(defaultBottom);
L.Popup.prototype.options.autoPan = autoPanBefore;
L.Icon.Default.prototype.options.popupAnchor = popupAnchorBefore;
});
}); });
describe("L.Map#openPopup", function () { describe("L.Map#openPopup", function () {

View File

@ -1,11 +1,16 @@
describe("Marker", function () { describe("Marker", function () {
var map, var map,
spy, spy,
div,
icon1, icon1,
icon2; icon2;
beforeEach(function () { beforeEach(function () {
map = L.map(document.createElement('div')).setView([0, 0], 0); div = document.createElement('div');
div.style.height = '100px';
document.body.appendChild(div);
map = L.map(div).setView([0, 0], 0);
icon1 = new L.Icon.Default(); icon1 = new L.Icon.Default();
icon2 = new L.Icon.Default({ icon2 = new L.Icon.Default({
iconUrl: icon1._getIconUrl('icon') + '?2', iconUrl: icon1._getIconUrl('icon') + '?2',
@ -13,6 +18,10 @@ describe("Marker", function () {
}); });
}); });
afterEach(function () {
document.body.removeChild(div);
});
describe("#setIcon", function () { describe("#setIcon", function () {
it("changes the icon to another image", function () { it("changes the icon to another image", function () {
var marker = new L.Marker([0, 0], {icon: icon1}); var marker = new L.Marker([0, 0], {icon: icon1});
@ -40,14 +49,25 @@ describe("Marker", function () {
marker.setIcon(icon1); marker.setIcon(icon1);
expect(marker.dragging.enabled()).to.be(true); expect(marker.dragging.enabled()).to.be(true);
map.removeLayer(marker);
map.addLayer(marker);
expect(marker.dragging.enabled()).to.be(true);
map.removeLayer(marker);
// Dragging is still enabled, we should be able to disable it,
// even if marker is off the map.
marker.dragging.disable();
map.addLayer(marker);
}); });
it("changes the icon to another DivIcon", function () { it("changes the icon to another DivIcon", function () {
var marker = new L.Marker([0, 0], {icon: new L.DivIcon({html: 'Inner1Text' }) }); var marker = new L.Marker([0, 0], {icon: new L.DivIcon({html: 'Inner1Text'})});
map.addLayer(marker); map.addLayer(marker);
var beforeIcon = marker._icon; var beforeIcon = marker._icon;
marker.setIcon(new L.DivIcon({html: 'Inner2Text' })); marker.setIcon(new L.DivIcon({html: 'Inner2Text'}));
var afterIcon = marker._icon; var afterIcon = marker._icon;
expect(beforeIcon).to.be(afterIcon); expect(beforeIcon).to.be(afterIcon);
@ -55,7 +75,7 @@ describe("Marker", function () {
}); });
it("removes text when changing to a blank DivIcon", function () { it("removes text when changing to a blank DivIcon", function () {
var marker = new L.Marker([0, 0], {icon: new L.DivIcon({html: 'Inner1Text' }) }); var marker = new L.Marker([0, 0], {icon: new L.DivIcon({html: 'Inner1Text'})});
map.addLayer(marker); map.addLayer(marker);
marker.setIcon(new L.DivIcon()); marker.setIcon(new L.DivIcon());
@ -65,7 +85,7 @@ describe("Marker", function () {
}); });
it("changes a DivIcon to an image", function () { it("changes a DivIcon to an image", function () {
var marker = new L.Marker([0, 0], {icon: new L.DivIcon({html: 'Inner1Text' }) }); var marker = new L.Marker([0, 0], {icon: new L.DivIcon({html: 'Inner1Text'})});
map.addLayer(marker); map.addLayer(marker);
var oldIcon = marker._icon; var oldIcon = marker._icon;
@ -87,7 +107,7 @@ describe("Marker", function () {
map.addLayer(marker); map.addLayer(marker);
var oldIcon = marker._icon; var oldIcon = marker._icon;
marker.setIcon(new L.DivIcon({html: 'Inner1Text' })); marker.setIcon(new L.DivIcon({html: 'Inner1Text'}));
expect(oldIcon).to.not.be(marker._icon); expect(oldIcon).to.not.be(marker._icon);
expect(oldIcon.parentNode).to.be(null); expect(oldIcon.parentNode).to.be(null);
@ -97,7 +117,7 @@ describe("Marker", function () {
}); });
it("reuses the icon/shadow when changing icon", function () { it("reuses the icon/shadow when changing icon", function () {
var marker = new L.Marker([0, 0], { icon: icon1}); var marker = new L.Marker([0, 0], {icon: icon1});
map.addLayer(marker); map.addLayer(marker);
var oldIcon = marker._icon; var oldIcon = marker._icon;
var oldShadow = marker._shadow; var oldShadow = marker._shadow;
@ -115,7 +135,7 @@ describe("Marker", function () {
describe("#setLatLng", function () { describe("#setLatLng", function () {
it("fires a move event", function () { it("fires a move event", function () {
var marker = new L.Marker([0, 0], { icon: icon1 }); var marker = new L.Marker([0, 0], {icon: icon1});
map.addLayer(marker); map.addLayer(marker);
var beforeLatLng = marker._latlng; var beforeLatLng = marker._latlng;
@ -134,4 +154,17 @@ describe("Marker", function () {
expect(marker.getLatLng()).to.be(afterLatLng); expect(marker.getLatLng()).to.be(afterLatLng);
}); });
}); });
describe('events', function () {
it('fires click event when clicked', function () {
var spy = sinon.spy();
var marker = L.marker([0, 0]).addTo(map);
marker.on('click', spy);
happen.click(marker._icon);
expect(spy.called).to.be.ok();
});
});
}); });

View File

@ -104,32 +104,6 @@ describe('GridLayer', function () {
map.setZoom(0, {animate: false}); map.setZoom(0, {animate: false});
clock.tick(250); clock.tick(250);
}); });
it('prunes and retains the correct tiles for back-to-back zooms', function () {
map.setView([0, 0], 1);
var grid = L.gridLayer();
var tiles = {};
grid.createTile = function (coords) {
tiles[grid._tileCoordsToKey(coords)] = true;
return document.createElement('div');
};
map.addLayer(grid);
clock.tick(500);
map.setZoom(0, {animate: false});
clock.tick(250);
map.setZoom(1, {animate: false});
clock.tick(500);
var tileContainers = div.querySelectorAll('.leaflet-tile-container');
expect(tileContainers.length).to.eql(2);
expect(tileContainers[0].childNodes.length).to.equal(8);
expect(tileContainers[1].childNodes.length).to.equal(0);
});
}); });
describe("#onAdd", function () { describe("#onAdd", function () {
@ -202,11 +176,12 @@ describe('GridLayer', function () {
}); });
describe("when a tilelayer is removed from a map", function () { describe("when a tilelayer is removed from a map", function () {
it("has its zoomlevels updated to only fit the layers it currently has", function () { it("has its zoomlevels updated to only fit the layers it currently has", function () {
var tiles = [ L.gridLayer({minZoom: 10, maxZoom: 15}).addTo(map), var tiles = [
L.gridLayer({minZoom: 5, maxZoom: 10}).addTo(map), L.gridLayer({minZoom: 10, maxZoom: 15}).addTo(map),
L.gridLayer({minZoom: 10, maxZoom: 20}).addTo(map), L.gridLayer({minZoom: 5, maxZoom: 10}).addTo(map),
L.gridLayer({minZoom: 0, maxZoom: 25}).addTo(map) L.gridLayer({minZoom: 10, maxZoom: 20}).addTo(map),
]; L.gridLayer({minZoom: 0, maxZoom: 25}).addTo(map)
];
map.whenReady(function () { map.whenReady(function () {
expect(map.getMinZoom()).to.be(0); expect(map.getMinZoom()).to.be(0);
expect(map.getMaxZoom()).to.be(25); expect(map.getMaxZoom()).to.be(25);

View File

@ -8,7 +8,7 @@
describe("when a CircleMarker is added to the map ", function () { describe("when a CircleMarker is added to the map ", function () {
describe("with a radius set as an option", function () { describe("with a radius set as an option", function () {
it("takes that radius", function () { it("takes that radius", function () {
var marker = L.circleMarker([0, 0], { radius: 20 }).addTo(map); var marker = L.circleMarker([0, 0], {radius: 20}).addTo(map);
expect(marker._radius).to.be(20); expect(marker._radius).to.be(20);
}); });
@ -16,7 +16,7 @@
describe("and radius is set before adding it", function () { describe("and radius is set before adding it", function () {
it("takes that radius", function () { it("takes that radius", function () {
var marker = L.circleMarker([0, 0], { radius: 20 }); var marker = L.circleMarker([0, 0], {radius: 20});
marker.setRadius(15); marker.setRadius(15);
marker.addTo(map); marker.addTo(map);
expect(marker._radius).to.be(15); expect(marker._radius).to.be(15);
@ -25,7 +25,7 @@
describe("and radius is set after adding it", function () { describe("and radius is set after adding it", function () {
it("takes that radius", function () { it("takes that radius", function () {
var marker = L.circleMarker([0, 0], { radius: 20 }); var marker = L.circleMarker([0, 0], {radius: 20});
marker.addTo(map); marker.addTo(map);
marker.setRadius(15); marker.setRadius(15);
expect(marker._radius).to.be(15); expect(marker._radius).to.be(15);
@ -34,16 +34,16 @@
describe("and setStyle is used to change the radius after adding", function () { describe("and setStyle is used to change the radius after adding", function () {
it("takes the given radius", function () { it("takes the given radius", function () {
var marker = L.circleMarker([0, 0], { radius: 20 }); var marker = L.circleMarker([0, 0], {radius: 20});
marker.addTo(map); marker.addTo(map);
marker.setStyle({ radius: 15 }); marker.setStyle({radius: 15});
expect(marker._radius).to.be(15); expect(marker._radius).to.be(15);
}); });
}); });
describe("and setStyle is used to change the radius before adding", function () { describe("and setStyle is used to change the radius before adding", function () {
it("takes the given radius", function () { it("takes the given radius", function () {
var marker = L.circleMarker([0, 0], { radius: 20 }); var marker = L.circleMarker([0, 0], {radius: 20});
marker.setStyle({ radius: 15 }); marker.setStyle({radius: 15});
marker.addTo(map); marker.addTo(map);
expect(marker._radius).to.be(15); expect(marker._radius).to.be(15);
}); });

View File

@ -570,4 +570,24 @@ describe("Map", function () {
expect(spy.called).to.be.ok(); expect(spy.called).to.be.ok();
}); });
}); });
describe('#flyTo', function () {
it('move to requested center and zoom, and call zoomend once', function (done) {
var spy = sinon.spy(),
newCenter = new L.LatLng(10, 11),
newZoom = 12,
callback = function () {
expect(map.getCenter()).to.eql(newCenter);
expect(map.getZoom()).to.eql(newZoom);
spy();
expect(spy.calledOnce).to.be.ok();
done();
};
map.setView([0, 0], 0);
map.once('zoomend', callback).flyTo(newCenter, newZoom);
});
});
}); });

View File

@ -39,7 +39,7 @@ L.Control.Attribution = L.Control.extend({
}, },
addAttribution: function (text) { addAttribution: function (text) {
if (!text) { return; } if (!text) { return this; }
if (!this._attributions[text]) { if (!this._attributions[text]) {
this._attributions[text] = 0; this._attributions[text] = 0;
@ -52,7 +52,7 @@ L.Control.Attribution = L.Control.extend({
}, },
removeAttribution: function (text) { removeAttribution: function (text) {
if (!text) { return; } if (!text) { return this; }
if (this._attributions[text]) { if (this._attributions[text]) {
this._attributions[text]--; this._attributions[text]--;

View File

@ -123,7 +123,7 @@ L.Control.Layers = L.Control.extend({
}, },
_update: function () { _update: function () {
if (!this._container) { return; } if (!this._container) { return this; }
L.DomUtil.empty(this._baseLayersList); L.DomUtil.empty(this._baseLayersList);
L.DomUtil.empty(this._overlaysList); L.DomUtil.empty(this._overlaysList);

View File

@ -31,12 +31,28 @@ L.Control.Zoom = L.Control.extend({
map.off('zoomend zoomlevelschange', this._updateDisabled, this); map.off('zoomend zoomlevelschange', this._updateDisabled, this);
}, },
disable: function () {
this._disabled = true;
this._updateDisabled();
return this;
},
enable: function () {
this._disabled = false;
this._updateDisabled();
return this;
},
_zoomIn: function (e) { _zoomIn: function (e) {
this._map.zoomIn(e.shiftKey ? 3 : 1); if (!this._disabled) {
this._map.zoomIn(e.shiftKey ? 3 : 1);
}
}, },
_zoomOut: function (e) { _zoomOut: function (e) {
this._map.zoomOut(e.shiftKey ? 3 : 1); if (!this._disabled) {
this._map.zoomOut(e.shiftKey ? 3 : 1);
}
}, },
_createButton: function (html, title, className, container, fn) { _createButton: function (html, title, className, container, fn) {
@ -61,10 +77,10 @@ L.Control.Zoom = L.Control.extend({
L.DomUtil.removeClass(this._zoomInButton, className); L.DomUtil.removeClass(this._zoomInButton, className);
L.DomUtil.removeClass(this._zoomOutButton, className); L.DomUtil.removeClass(this._zoomOutButton, className);
if (map._zoom === map.getMinZoom()) { if (this._disabled || map._zoom === map.getMinZoom()) {
L.DomUtil.addClass(this._zoomOutButton, className); L.DomUtil.addClass(this._zoomOutButton, className);
} }
if (map._zoom === map.getMaxZoom()) { if (this._disabled || map._zoom === map.getMaxZoom()) {
L.DomUtil.addClass(this._zoomInButton, className); L.DomUtil.addClass(this._zoomInButton, className);
} }
} }
@ -84,4 +100,3 @@ L.Map.addInitHook(function () {
L.control.zoom = function (options) { L.control.zoom = function (options) {
return new L.Control.Zoom(options); return new L.Control.Zoom(options);
}; };

View File

@ -71,8 +71,9 @@ L.Control = L.Class.extend({
return this; return this;
}, },
_refocusOnMap: function () { _refocusOnMap: function (e) {
if (this._map) { // if map exists and event is not a keyboard event
if (this._map && e && e.screenX > 0 && e.screenY > 0) {
this._map.getContainer().focus(); this._map.getContainer().focus();
} }
} }

View File

@ -1,4 +1,4 @@
/* /*
Leaflet {VERSION}, a JS library for interactive maps. http://leafletjs.com Leaflet {VERSION}, a JS library for interactive maps. http://leafletjs.com
(c) 2010-2014 Vladimir Agafonkin, (c) 2010-2011 CloudMade (c) 2010-2015 Vladimir Agafonkin, (c) 2010-2011 CloudMade
*/ */

View File

@ -19,7 +19,6 @@ L.Class.extend = function (props) {
this.callInitHooks(); this.callInitHooks();
}; };
// jshint camelcase: false
var parentProto = NewClass.__super__ = this.prototype; var parentProto = NewClass.__super__ = this.prototype;
var proto = L.Util.create(parentProto); var proto = L.Util.create(parentProto);
@ -27,7 +26,7 @@ L.Class.extend = function (props) {
NewClass.prototype = proto; NewClass.prototype = proto;
//inherit parent's statics // inherit parent's statics
for (var i in this) { for (var i in this) {
if (this.hasOwnProperty(i) && i !== 'prototype') { if (this.hasOwnProperty(i) && i !== 'prototype') {
NewClass[i] = this[i]; NewClass[i] = this[i];

View File

@ -134,8 +134,8 @@ L.Evented = L.Class.extend({
events = this._events; events = this._events;
if (events) { if (events) {
var typeIndex = events[type + '_idx'], var typeIndex = events[type + '_idx'],
i, len, listeners, id; i, len, listeners, id;
if (events[type]) { if (events[type]) {
// make sure adding/removing listeners inside other listeners won't cause infinite loop // make sure adding/removing listeners inside other listeners won't cause infinite loop

View File

@ -42,9 +42,10 @@ L.Util = {
// return unique ID of an object // return unique ID of an object
stamp: function (obj) { stamp: function (obj) {
// jshint camelcase: false /*eslint-disable */
obj._leaflet_id = obj._leaflet_id || ++L.Util.lastId; obj._leaflet_id = obj._leaflet_id || ++L.Util.lastId;
return obj._leaflet_id; return obj._leaflet_id;
/*eslint-enable */
}, },
lastId: 0, lastId: 0,
@ -116,7 +117,7 @@ L.Util = {
return obj.options; return obj.options;
}, },
// make an URL with GET parameters out of a set of properties/values // make a URL with GET parameters out of a set of properties/values
getParamString: function (obj, existingUrl, uppercase) { getParamString: function (obj, existingUrl, uppercase) {
var params = []; var params = [];
for (var i in obj) { for (var i in obj) {

View File

@ -54,7 +54,7 @@ L.DomEvent = {
if (L.Browser.pointer && type.indexOf('touch') === 0) { if (L.Browser.pointer && type.indexOf('touch') === 0) {
this.addPointerListener(obj, type, handler, id); this.addPointerListener(obj, type, handler, id);
} else if (L.Browser.touch && (type === 'dblclick') && this.addDoubleTapListener) { } else if (L.Browser.touch && (type === 'dblclick') && this.addDoubleTapListener) {
this.addDoubleTapListener(obj, handler, id); this.addDoubleTapListener(obj, handler, id);
@ -67,8 +67,9 @@ L.DomEvent = {
} else if ((type === 'mouseenter') || (type === 'mouseleave')) { } else if ((type === 'mouseenter') || (type === 'mouseleave')) {
handler = function (e) { handler = function (e) {
e = e || window.event; e = e || window.event;
if (!L.DomEvent._checkMouse(obj, e)) { return; } if (L.DomEvent._checkMouse(obj, e)) {
return originalHandler(e); originalHandler(e);
}
}; };
obj.addEventListener(type === 'mouseenter' ? 'mouseover' : 'mouseout', handler, false); obj.addEventListener(type === 'mouseenter' ? 'mouseover' : 'mouseout', handler, false);
@ -240,7 +241,7 @@ L.DomEvent = {
} }
L.DomEvent._lastClick = timeStamp; L.DomEvent._lastClick = timeStamp;
return handler(e); handler(e);
} }
}; };

View File

@ -100,27 +100,30 @@ L.DomUtil = {
el.style.opacity = value; el.style.opacity = value;
} else if ('filter' in el.style) { } else if ('filter' in el.style) {
L.DomUtil._setOpacityIE(el, value);
}
},
var filter = false, _setOpacityIE: function (el, value) {
filterName = 'DXImageTransform.Microsoft.Alpha'; var filter = false,
filterName = 'DXImageTransform.Microsoft.Alpha';
// filters collection throws an error if we try to retrieve a filter that doesn't exist // filters collection throws an error if we try to retrieve a filter that doesn't exist
try { try {
filter = el.filters.item(filterName); filter = el.filters.item(filterName);
} catch (e) { } catch (e) {
// don't set opacity to 1 if we haven't already set an opacity, // don't set opacity to 1 if we haven't already set an opacity,
// it isn't needed and breaks transparent pngs. // it isn't needed and breaks transparent pngs.
if (value === 1) { return; } if (value === 1) { return; }
} }
value = Math.round(value * 100); value = Math.round(value * 100);
if (filter) { if (filter) {
filter.Enabled = (value !== 100); filter.Enabled = (value !== 100);
filter.Opacity = value; filter.Opacity = value;
} else { } else {
el.style.filter += ' progid:' + filterName + '(opacity=' + value + ')'; el.style.filter += ' progid:' + filterName + '(opacity=' + value + ')';
}
} }
}, },
@ -145,8 +148,9 @@ L.DomUtil = {
setPosition: function (el, point, no3d) { // (HTMLElement, Point[, Boolean]) setPosition: function (el, point, no3d) { // (HTMLElement, Point[, Boolean])
// jshint camelcase: false /*eslint-disable */
el._leaflet_pos = point; el._leaflet_pos = point;
/*eslint-enable */
if (L.Browser.any3d && !no3d) { if (L.Browser.any3d && !no3d) {
L.DomUtil.setTransform(el, point); L.DomUtil.setTransform(el, point);
@ -160,7 +164,6 @@ L.DomUtil = {
// this method is only used for elements previously positioned using setPosition, // this method is only used for elements previously positioned using setPosition,
// so it's safe to cache the position for performance // so it's safe to cache the position for performance
// jshint camelcase: false
return el._leaflet_pos; return el._leaflet_pos;
} }
}; };
@ -216,4 +219,19 @@ L.DomUtil = {
L.DomUtil.enableImageDrag = function () { L.DomUtil.enableImageDrag = function () {
L.DomEvent.off(window, 'dragstart', L.DomEvent.preventDefault); L.DomEvent.off(window, 'dragstart', L.DomEvent.preventDefault);
}; };
L.DomUtil.preventOutline = function (element) {
L.DomUtil.restoreOutline();
this._outlineElement = element;
this._outlineStyle = element.style.outline;
element.style.outline = 'none';
L.DomEvent.on(window, 'keydown', L.DomUtil.restoreOutline, this);
};
L.DomUtil.restoreOutline = function () {
if (!this._outlineElement) { return; }
this._outlineElement.style.outline = this._outlineStyle;
delete this._outlineElement;
delete this._outlineStyle;
L.DomEvent.off(window, 'keydown', L.DomUtil.restoreOutline, this);
};
})(); })();

View File

@ -20,9 +20,10 @@ L.Draggable = L.Evented.extend({
} }
}, },
initialize: function (element, dragStartTarget) { initialize: function (element, dragStartTarget, preventOutline) {
this._element = element; this._element = element;
this._dragStartTarget = dragStartTarget || element; this._dragStartTarget = dragStartTarget || element;
this._preventOutline = preventOutline;
}, },
enable: function () { enable: function () {
@ -45,10 +46,14 @@ L.Draggable = L.Evented.extend({
_onDown: function (e) { _onDown: function (e) {
this._moved = false; this._moved = false;
if (e.shiftKey || ((e.which !== 1) && (e.button !== 1) && !e.touches)) { return; } if (e.shiftKey || ((e.which !== 1) && (e.button !== 0) && !e.touches)) { return; }
L.DomEvent.stopPropagation(e); L.DomEvent.stopPropagation(e);
if (this._preventOutline) {
L.DomUtil.preventOutline(this._element);
}
if (L.DomUtil.hasClass(this._element, 'leaflet-zoom-anim')) { return; } if (L.DomUtil.hasClass(this._element, 'leaflet-zoom-anim')) { return; }
L.DomUtil.disableImageDrag(); L.DomUtil.disableImageDrag();
@ -99,13 +104,15 @@ L.Draggable = L.Evented.extend({
this._moving = true; this._moving = true;
L.Util.cancelAnimFrame(this._animRequest); L.Util.cancelAnimFrame(this._animRequest);
this._lastEvent = e;
this._animRequest = L.Util.requestAnimFrame(this._updatePosition, this, true, this._dragStartTarget); this._animRequest = L.Util.requestAnimFrame(this._updatePosition, this, true, this._dragStartTarget);
}, },
_updatePosition: function () { _updatePosition: function () {
this.fire('predrag'); var e = {originalEvent: this._lastEvent};
this.fire('predrag', e);
L.DomUtil.setPosition(this._element, this._newPos); L.DomUtil.setPosition(this._element, this._newPos);
this.fire('drag'); this.fire('drag', e);
}, },
_onUp: function () { _onUp: function () {

View File

@ -80,4 +80,3 @@ L.latLng = function (a, b, c) {
} }
return new L.LatLng(a, b, c); return new L.LatLng(a, b, c);
}; };

View File

@ -7,7 +7,7 @@ L.CRS.Earth = L.extend({}, L.CRS, {
R: 6378137, R: 6378137,
// distane between two geographical points using spherical law of cosines approximation // distance between two geographical points using spherical law of cosines approximation
distance: function (latlng1, latlng2) { distance: function (latlng1, latlng2) {
var rad = Math.PI / 180, var rad = Math.PI / 180,
lat1 = latlng1.lat * rad, lat1 = latlng1.lat * rad,

View File

@ -3,14 +3,12 @@
* and polylines (clipping, simplification, distances, etc.) * and polylines (clipping, simplification, distances, etc.)
*/ */
/*jshint bitwise:false */ // allow bitwise operations for this file
L.LineUtil = { L.LineUtil = {
// Simplify polyline with vertex reduction and Douglas-Peucker simplification. // Simplify polyline with vertex reduction and Douglas-Peucker simplification.
// Improves rendering performance dramatically by lessening the number of points to draw. // Improves rendering performance dramatically by lessening the number of points to draw.
simplify: function (/*Point[]*/ points, /*Number*/ tolerance) { simplify: function (points, tolerance) {
if (!tolerance || !points.length) { if (!tolerance || !points.length) {
return points.slice(); return points.slice();
} }
@ -27,11 +25,11 @@ L.LineUtil = {
}, },
// distance from a point to a segment between two points // distance from a point to a segment between two points
pointToSegmentDistance: function (/*Point*/ p, /*Point*/ p1, /*Point*/ p2) { pointToSegmentDistance: function (p, p1, p2) {
return Math.sqrt(this._sqClosestPointOnSegment(p, p1, p2, true)); return Math.sqrt(this._sqClosestPointOnSegment(p, p1, p2, true));
}, },
closestPointOnSegment: function (/*Point*/ p, /*Point*/ p1, /*Point*/ p2) { closestPointOnSegment: function (p, p1, p2) {
return this._sqClosestPointOnSegment(p, p1, p2); return this._sqClosestPointOnSegment(p, p1, p2);
}, },
@ -99,7 +97,7 @@ L.LineUtil = {
// Cohen-Sutherland line clipping algorithm. // Cohen-Sutherland line clipping algorithm.
// Used to avoid rendering parts of a polyline that are not currently visible. // Used to avoid rendering parts of a polyline that are not currently visible.
clipSegment: function (a, b, bounds, useLastCode) { clipSegment: function (a, b, bounds, useLastCode, round) {
var codeA = useLastCode ? this._lastCode : this._getBitCode(a, bounds), var codeA = useLastCode ? this._lastCode : this._getBitCode(a, bounds),
codeB = this._getBitCode(b, bounds), codeB = this._getBitCode(b, bounds),
@ -118,7 +116,7 @@ L.LineUtil = {
// other cases // other cases
} else { } else {
codeOut = codeA || codeB; codeOut = codeA || codeB;
p = this._getEdgeIntersection(a, b, codeOut, bounds); p = this._getEdgeIntersection(a, b, codeOut, bounds, round);
newCode = this._getBitCode(p, bounds); newCode = this._getBitCode(p, bounds);
if (codeOut === codeA) { if (codeOut === codeA) {
@ -132,7 +130,7 @@ L.LineUtil = {
} }
}, },
_getEdgeIntersection: function (a, b, code, bounds) { _getEdgeIntersection: function (a, b, code, bounds, round) {
var dx = b.x - a.x, var dx = b.x - a.x,
dy = b.y - a.y, dy = b.y - a.y,
min = bounds.min, min = bounds.min,
@ -156,7 +154,7 @@ L.LineUtil = {
y = a.y + dy * (min.x - a.x) / dx; y = a.y + dy * (min.x - a.x) / dx;
} }
return new L.Point(x, y, true); return new L.Point(x, y, round);
}, },
_getBitCode: function (/*Point*/ p, bounds) { _getBitCode: function (/*Point*/ p, bounds) {

View File

@ -2,7 +2,7 @@
* L.Point represents a point with x and y coordinates. * L.Point represents a point with x and y coordinates.
*/ */
L.Point = function (/*Number*/ x, /*Number*/ y, /*Boolean*/ round) { L.Point = function (x, y, round) {
this.x = (round ? Math.round(x) : x); this.x = (round ? Math.round(x) : x);
this.y = (round ? Math.round(y) : y); this.y = (round ? Math.round(y) : y);
}; };

View File

@ -2,15 +2,13 @@
* L.PolyUtil contains utility functions for polygons (clipping, etc.). * L.PolyUtil contains utility functions for polygons (clipping, etc.).
*/ */
/*jshint bitwise:false */ // allow bitwise operations here
L.PolyUtil = {}; L.PolyUtil = {};
/* /*
* Sutherland-Hodgeman polygon clipping algorithm. * Sutherland-Hodgeman polygon clipping algorithm.
* Used to avoid rendering parts of a polygon that are not currently visible. * Used to avoid rendering parts of a polygon that are not currently visible.
*/ */
L.PolyUtil.clipPolygon = function (points, bounds) { L.PolyUtil.clipPolygon = function (points, bounds, round) {
var clippedPoints, var clippedPoints,
edges = [1, 4, 2, 8], edges = [1, 4, 2, 8],
i, j, k, i, j, k,
@ -35,7 +33,7 @@ L.PolyUtil.clipPolygon = function (points, bounds) {
if (!(a._code & edge)) { if (!(a._code & edge)) {
// if b is outside the clip window (a->b goes out of screen) // if b is outside the clip window (a->b goes out of screen)
if (b._code & edge) { if (b._code & edge) {
p = lu._getEdgeIntersection(b, a, edge, bounds); p = lu._getEdgeIntersection(b, a, edge, bounds, round);
p._code = lu._getBitCode(p, bounds); p._code = lu._getBitCode(p, bounds);
clippedPoints.push(p); clippedPoints.push(p);
} }
@ -43,7 +41,7 @@ L.PolyUtil.clipPolygon = function (points, bounds) {
// else if b is inside the clip window (a->b enters the screen) // else if b is inside the clip window (a->b enters the screen)
} else if (!(b._code & edge)) { } else if (!(b._code & edge)) {
p = lu._getEdgeIntersection(b, a, edge, bounds); p = lu._getEdgeIntersection(b, a, edge, bounds, round);
p._code = lu._getBitCode(p, bounds); p._code = lu._getBitCode(p, bounds);
clippedPoints.push(p); clippedPoints.push(p);
} }

View File

@ -31,7 +31,7 @@ L.GeoJSON = L.FeatureGroup.extend({
var options = this.options; var options = this.options;
if (options.filter && !options.filter(geojson)) { return; } if (options.filter && !options.filter(geojson)) { return this; }
var layer = L.GeoJSON.geometryToLayer(geojson, options); var layer = L.GeoJSON.geometryToLayer(geojson, options);
layer.feature = L.GeoJSON.asFeature(geojson); layer.feature = L.GeoJSON.asFeature(geojson);
@ -146,7 +146,7 @@ L.extend(L.GeoJSON, {
for (var i = 0, len = latlngs.length; i < len; i++) { for (var i = 0, len = latlngs.length; i < len; i++) {
coords.push(levelsDeep ? coords.push(levelsDeep ?
L.GeoJSON.latLngsToCoords(latlngs[i], levelsDeep - 1, closed): L.GeoJSON.latLngsToCoords(latlngs[i], levelsDeep - 1, closed) :
L.GeoJSON.latLngToCoords(latlngs[i])); L.GeoJSON.latLngToCoords(latlngs[i]));
} }

View File

@ -26,13 +26,20 @@ L.ImageOverlay = L.Layer.extend({
} }
} }
if (this.options.interactive) {
L.DomUtil.addClass(this._image, 'leaflet-interactive');
this.addInteractiveTarget(this._image);
}
this.getPane().appendChild(this._image); this.getPane().appendChild(this._image);
this._initInteraction();
this._reset(); this._reset();
}, },
onRemove: function () { onRemove: function () {
L.DomUtil.remove(this._image); L.DomUtil.remove(this._image);
if (this.options.interactive) {
this.removeInteractiveTarget(this._image);
}
}, },
setOpacity: function (opacity) { setOpacity: function (opacity) {
@ -65,19 +72,6 @@ L.ImageOverlay = L.Layer.extend({
return this; return this;
}, },
_initInteraction: function () {
if (!this.options.interactive) { return; }
L.DomUtil.addClass(this._image, 'leaflet-interactive');
L.DomEvent.on(this._image, 'click dblclick mousedown mouseup mouseover mousemove mouseout contextmenu',
this._fireMouseEvent, this);
},
_fireMouseEvent: function (e, type) {
if (this._map) {
this._map._fireMouseEvent(this, e, type, true);
}
},
setUrl: function (url) { setUrl: function (url) {
this._url = url; this._url = url;

View File

@ -7,6 +7,7 @@ L.Layer.include({
bindPopup: function (content, options) { bindPopup: function (content, options) {
if (content instanceof L.Popup) { if (content instanceof L.Popup) {
L.setOptions(content, options);
this._popup = content; this._popup = content;
content._source = this; content._source = this;
} else { } else {
@ -124,4 +125,4 @@ L.Layer.include({
_popupLatLng: function(){ _popupLatLng: function(){
return this._latlng || this.getCenter(); return this._latlng || this.getCenter();
} }
}); });

View File

@ -25,6 +25,16 @@ L.Layer = L.Evented.extend({
return this._map.getPane(name ? (this.options[name] || name) : this.options.pane); return this._map.getPane(name ? (this.options[name] || name) : this.options.pane);
}, },
addInteractiveTarget: function (targetEl) {
this._map._targets[L.stamp(targetEl)] = this;
return this;
},
removeInteractiveTarget: function (targetEl) {
delete this._map._targets[L.stamp(targetEl)];
return this;
},
_layerAdd: function (e) { _layerAdd: function (e) {
var map = e.target; var map = e.target;

View File

@ -11,7 +11,7 @@ L.Handler.MarkerDrag = L.Handler.extend({
var icon = this._marker._icon; var icon = this._marker._icon;
if (!this._draggable) { if (!this._draggable) {
this._draggable = new L.Draggable(icon, icon); this._draggable = new L.Draggable(icon, icon, true);
} }
this._draggable.on({ this._draggable.on({
@ -30,7 +30,9 @@ L.Handler.MarkerDrag = L.Handler.extend({
dragend: this._onDragEnd dragend: this._onDragEnd
}, this).disable(); }, this).disable();
L.DomUtil.removeClass(this._marker._icon, 'leaflet-marker-draggable'); if (this._marker._icon) {
L.DomUtil.removeClass(this._marker._icon, 'leaflet-marker-draggable');
}
}, },
moved: function () { moved: function () {
@ -44,7 +46,7 @@ L.Handler.MarkerDrag = L.Handler.extend({
.fire('dragstart'); .fire('dragstart');
}, },
_onDrag: function () { _onDrag: function (e) {
var marker = this._marker, var marker = this._marker,
shadow = marker._shadow, shadow = marker._shadow,
iconPos = L.DomUtil.getPosition(marker._icon), iconPos = L.DomUtil.getPosition(marker._icon),
@ -56,10 +58,11 @@ L.Handler.MarkerDrag = L.Handler.extend({
} }
marker._latlng = latlng; marker._latlng = latlng;
e.latlng = latlng;
marker marker
.fire('move', {latlng: latlng}) .fire('move', e)
.fire('drag'); .fire('drag', e);
}, },
_onDragEnd: function (e) { _onDragEnd: function (e) {

View File

@ -32,8 +32,8 @@ L.Marker = L.Layer.extend({
}, },
onRemove: function () { onRemove: function () {
if (this.dragging) { if (this.dragging && this.dragging.enabled()) {
this.dragging.disable(); this.dragging.removeHooks();
} }
this._removeIcon(); this._removeIcon();
@ -58,7 +58,7 @@ L.Marker = L.Layer.extend({
var oldLatLng = this._latlng; var oldLatLng = this._latlng;
this._latlng = L.latLng(latlng); this._latlng = L.latLng(latlng);
this.update(); this.update();
return this.fire('move', { oldLatLng: oldLatLng, latlng: this._latlng }); return this.fire('move', {oldLatLng: oldLatLng, latlng: this._latlng});
}, },
setZIndexOffset: function (offset) { setZIndexOffset: function (offset) {
@ -123,11 +123,11 @@ L.Marker = L.Layer.extend({
this._icon = icon; this._icon = icon;
this._initInteraction(); this._initInteraction();
if (L.DomEvent && options.riseOnHover) { if (options.riseOnHover) {
L.DomEvent.on(icon, { this.on({
mouseover: this._bringToFront, mouseover: this._bringToFront,
mouseout: this._resetZIndex mouseout: this._resetZIndex
}, this); });
} }
var newShadow = options.icon.createShadow(this._shadow), var newShadow = options.icon.createShadow(this._shadow),
@ -158,14 +158,15 @@ L.Marker = L.Layer.extend({
}, },
_removeIcon: function () { _removeIcon: function () {
if (L.DomEvent && this.options.riseOnHover) { if (this.options.riseOnHover) {
L.DomEvent.off(this._icon, { this.off({
mouseover: this._bringToFront, mouseover: this._bringToFront,
mouseout: this._resetZIndex mouseout: this._resetZIndex
}, this); });
} }
L.DomUtil.remove(this._icon); L.DomUtil.remove(this._icon);
this.removeInteractiveTarget(this._icon);
this._icon = null; this._icon = null;
}, },
@ -205,11 +206,7 @@ L.Marker = L.Layer.extend({
L.DomUtil.addClass(this._icon, 'leaflet-interactive'); L.DomUtil.addClass(this._icon, 'leaflet-interactive');
if (L.DomEvent) { this.addInteractiveTarget(this._icon);
L.DomEvent.on(this._icon,
'click dblclick mousedown mouseup mouseover mousemove mouseout contextmenu keypress',
this._fireMouseEvent, this);
}
if (L.Handler.MarkerDrag) { if (L.Handler.MarkerDrag) {
var draggable = this.options.draggable; var draggable = this.options.draggable;
@ -226,21 +223,6 @@ L.Marker = L.Layer.extend({
} }
}, },
_fireMouseEvent: function (e, type) {
// to prevent outline when clicking on keyboard-focusable marker
if (e.type === 'mousedown') {
L.DomEvent.preventDefault(e);
}
if (e.type === 'keypress' && e.keyCode === 13) {
type = 'click';
}
if (this._map) {
this._map._fireMouseEvent(this, e, type, true, this._latlng);
}
},
setOpacity: function (opacity) { setOpacity: function (opacity) {
this.options.opacity = opacity; this.options.opacity = opacity;
if (this._map) { if (this._map) {

View File

@ -10,7 +10,6 @@ L.GridLayer = L.Layer.extend({
tileSize: 256, tileSize: 256,
opacity: 1, opacity: 1,
unloadInvisibleTiles: L.Browser.mobile,
updateWhenIdle: L.Browser.mobile, updateWhenIdle: L.Browser.mobile,
updateInterval: 200, updateInterval: 200,
@ -29,16 +28,10 @@ L.GridLayer = L.Layer.extend({
onAdd: function () { onAdd: function () {
this._initContainer(); this._initContainer();
this._pruneTiles = L.Util.throttle(this._pruneTiles, 200, this);
this._levels = {}; this._levels = {};
this._tiles = {}; this._tiles = {};
this._loaded = {};
this._retain = {};
this._tilesToLoad = 0;
this._reset(); this._viewReset();
this._update(); this._update();
}, },
@ -103,13 +96,13 @@ L.GridLayer = L.Layer.extend({
getEvents: function () { getEvents: function () {
var events = { var events = {
viewreset: this._reset, viewreset: this._viewReset,
moveend: this._update moveend: this._move
}; };
if (!this.options.updateWhenIdle) { if (!this.options.updateWhenIdle) {
// update tiles on move, but not more often than once per given interval // update tiles on move, but not more often than once per given interval
events.move = L.Util.throttle(this._update, this.options.updateInterval, this); events.move = L.Util.throttle(this._move, this.options.updateInterval, this);
} }
if (this._zoomAnimated) { if (this._zoomAnimated) {
@ -153,13 +146,33 @@ L.GridLayer = L.Layer.extend({
_updateOpacity: function () { _updateOpacity: function () {
var opacity = this.options.opacity; var opacity = this.options.opacity;
if (L.Browser.ielt9) { // IE doesn't inherit filter opacity properly, so we're forced to set it on tiles
// IE doesn't inherit filter opacity properly, so we're forced to set it on tiles if (!L.Browser.ielt9 && !this._map._fadeAnimated) {
for (var i in this._tiles) {
L.DomUtil.setOpacity(this._tiles[i], opacity);
}
} else {
L.DomUtil.setOpacity(this._container, opacity); L.DomUtil.setOpacity(this._container, opacity);
return;
}
var now = +new Date(),
nextFrame = false;
for (var key in this._tiles) {
var tile = this._tiles[key];
if (!tile.current || !tile.loaded || tile.active) { continue; }
var fade = Math.min(1, (now - tile.loaded) / 200);
if (fade < 1) {
L.DomUtil.setOpacity(tile.el, opacity * fade);
nextFrame = true;
} else {
L.DomUtil.setOpacity(tile.el, opacity);
tile.active = true;
this._pruneTiles();
}
}
if (nextFrame) {
L.Util.cancelAnimFrame(this._fadeFrame);
this._fadeFrame = L.Util.requestAnimFrame(this._updateOpacity, this);
} }
}, },
@ -177,10 +190,16 @@ L.GridLayer = L.Layer.extend({
}, },
_updateLevels: function () { _updateLevels: function () {
var zoom = this._tileZoom; var zoom = this._tileZoom,
maxZoom = this.options.maxZoom;
for (var z in this._levels) { for (var z in this._levels) {
this._levels[z].el.style.zIndex = -Math.abs(zoom - z); if (this._levels[z].el.children.length || z === zoom) {
this._levels[z].el.style.zIndex = maxZoom - Math.abs(zoom - z);
} else {
L.DomUtil.remove(this._levels[z].el);
delete this._levels[z];
}
} }
var level = this._levels[zoom], var level = this._levels[zoom],
@ -190,10 +209,15 @@ L.GridLayer = L.Layer.extend({
level = this._levels[zoom] = {}; level = this._levels[zoom] = {};
level.el = L.DomUtil.create('div', 'leaflet-tile-container leaflet-zoom-animated', this._container); level.el = L.DomUtil.create('div', 'leaflet-tile-container leaflet-zoom-animated', this._container);
level.el.style.zIndex = 0; level.el.style.zIndex = maxZoom;
level.origin = map.project(map.unproject(map.getPixelOrigin()), zoom).round(); level.origin = map.project(map.unproject(map.getPixelOrigin()), zoom).round();
level.zoom = zoom; level.zoom = zoom;
this._setZoomTransform(level, map.getCenter(), map.getZoom());
// force the browser to consider the newly added element for transition
L.Util.falseFn(level.el.offsetWidth);
} }
this._level = level; this._level = level;
@ -202,39 +226,26 @@ L.GridLayer = L.Layer.extend({
}, },
_pruneTiles: function () { _pruneTiles: function () {
var key, tile;
if (!this._map) { return; } for (key in this._tiles) {
tile = this._tiles[key];
tile.retain = tile.current;
}
this._retain = {}; for (key in this._tiles) {
tile = this._tiles[key];
var bounds = this._map.getBounds(), if (tile.current && !tile.active) {
z = this._tileZoom, var coords = tile.coords;
range = this._getTileRange(bounds, z), if (!this._retainParent(coords.x, coords.y, coords.z, coords.z - 5)) {
i, j, key, found; this._retainChildren(coords.x, coords.y, coords.z, coords.z + 2);
for (i = range.min.x; i <= range.max.x; i++) {
for (j = range.min.y; j <= range.max.y; j++) {
key = i + ':' + j + ':' + z;
this._retain[key] = true;
if (!this._loaded[key]) {
found = this._retainParent(i, j, z, z - 5) || this._retainChildren(i, j, z, z + 2);
} }
} }
} }
for (key in this._tiles) { for (key in this._tiles) {
if (!this._retain[key]) { if (!this._tiles[key].retain) {
if (!this._loaded[key]) { this._removeTile(key);
this._removeTile(key);
this._tilesToLoad--;
} else if (this._map._fadeAnimated) {
setTimeout(L.bind(this._deferRemove, this, key), 250);
} else {
this._removeTile(key);
}
} }
} }
}, },
@ -243,13 +254,6 @@ L.GridLayer = L.Layer.extend({
for (var key in this._tiles) { for (var key in this._tiles) {
this._removeTile(key); this._removeTile(key);
} }
this._tilesToLoad = 0;
},
_deferRemove: function (key) {
if (!this._retain[key]) {
this._removeTile(key);
}
}, },
_retainParent: function (x, y, z, minZoom) { _retainParent: function (x, y, z, minZoom) {
@ -257,13 +261,18 @@ L.GridLayer = L.Layer.extend({
y2 = Math.floor(y / 2), y2 = Math.floor(y / 2),
z2 = z - 1; z2 = z - 1;
var key = x2 + ':' + y2 + ':' + z2; var key = x2 + ':' + y2 + ':' + z2,
tile = this._tiles[key];
if (this._loaded[key]) { if (tile && tile.active) {
this._retain[key] = true; tile.retain = true;
return true; return true;
} else if (z2 > minZoom) { } else if (tile && tile.loaded) {
tile.retain = true;
}
if (z2 > minZoom) {
return this._retainParent(x2, y2, z2, minZoom); return this._retainParent(x2, y2, z2, minZoom);
} }
@ -275,34 +284,56 @@ L.GridLayer = L.Layer.extend({
for (var i = 2 * x; i < 2 * x + 2; i++) { for (var i = 2 * x; i < 2 * x + 2; i++) {
for (var j = 2 * y; j < 2 * y + 2; j++) { for (var j = 2 * y; j < 2 * y + 2; j++) {
var key = i + ':' + j + ':' + (z + 1); var key = i + ':' + j + ':' + (z + 1),
tile = this._tiles[key];
if (this._loaded[key]) { if (tile && tile.active) {
this._retain[key] = true; tile.retain = true;
continue;
} else if (z + 1 < maxZoom) { } else if (tile && tile.loaded) {
tile.retain = true;
}
if (z + 1 < maxZoom) {
this._retainChildren(i, j, z + 1, maxZoom); this._retainChildren(i, j, z + 1, maxZoom);
} }
} }
} }
}, },
_reset: function (e) { _viewReset: function (e) {
var map = this._map, this._reset(this._map.getCenter(), this._map.getZoom(), e && e.hard);
zoom = map.getZoom(), },
tileZoom = Math.round(zoom),
tileZoomChanged = this._tileZoom !== tileZoom; _animateZoom: function (e) {
this._reset(e.center, e.zoom, false, true, e.noUpdate);
},
_reset: function (center, zoom, hard, noPrune, noUpdate) {
var tileZoom = Math.round(zoom),
tileZoomChanged = this._tileZoom !== tileZoom;
if (!noUpdate && (hard || tileZoomChanged)) {
if (tileZoomChanged || (e && e.hard)) {
if (this._abortLoading) { if (this._abortLoading) {
this._abortLoading(); this._abortLoading();
} }
this._tileZoom = tileZoom; this._tileZoom = tileZoom;
this._updateLevels(); this._updateLevels();
this._resetGrid(); this._resetGrid();
if (!L.Browser.mobileWebkit) {
this._update(center, tileZoom);
}
if (!noPrune) {
this._pruneTiles();
}
} }
this._setZoomTransforms(map.getCenter(), zoom); this._setZoomTransforms(center, zoom);
}, },
_setZoomTransforms: function (center, zoom) { _setZoomTransforms: function (center, zoom) {
@ -344,8 +375,14 @@ L.GridLayer = L.Layer.extend({
return this.options.tileSize; return this.options.tileSize;
}, },
_update: function () { _move: function () {
if (!this._map) { return; } this._update();
this._pruneTiles();
},
_update: function (center, zoom) {
var map = this._map;
if (!map) { return; }
// TODO move to reset // TODO move to reset
// var zoom = this._map.getZoom(); // var zoom = this._map.getZoom();
@ -353,38 +390,30 @@ L.GridLayer = L.Layer.extend({
// if (zoom > this.options.maxZoom || // if (zoom > this.options.maxZoom ||
// zoom < this.options.minZoom) { return; } // zoom < this.options.minZoom) { return; }
var bounds = this._map.getBounds(); if (center === undefined) { center = map.getCenter(); }
if (zoom === undefined) { zoom = Math.round(map.getZoom()); }
if (this.options.unloadInvisibleTiles) { var pixelBounds = map.getPixelBounds(center, zoom),
this._removeOtherTiles(bounds); tileRange = this._pxBoundsToTileRange(pixelBounds),
tileCenter = tileRange.getCenter(),
queue = [];
for (var key in this._tiles) {
this._tiles[key].current = false;
} }
this._addTiles(bounds);
},
// tile coordinates range for particular geo bounds and zoom
_getTileRange: function (bounds, zoom) {
var pxBounds = new L.Bounds(
this._map.project(bounds.getNorthWest(), zoom),
this._map.project(bounds.getSouthEast(), zoom));
return this._pxBoundsToTileRange(pxBounds);
},
_addTiles: function (bounds) {
var queue = [],
tileRange = this._getTileRange(bounds, this._tileZoom),
center = tileRange.getCenter(),
j, i, coords;
// create a queue of coordinates to load tiles from // create a queue of coordinates to load tiles from
for (j = tileRange.min.y; j <= tileRange.max.y; j++) { for (var j = tileRange.min.y; j <= tileRange.max.y; j++) {
for (i = tileRange.min.x; i <= tileRange.max.x; i++) { for (var i = tileRange.min.x; i <= tileRange.max.x; i++) {
var coords = new L.Point(i, j);
coords.z = zoom;
coords = new L.Point(i, j); if (!this._isValidTile(coords)) { continue; }
coords.z = this._tileZoom;
// add tile to queue if it's not in cache or out of bounds var tile = this._tiles[this._tileCoordsToKey(coords)];
if (!(this._tileCoordsToKey(coords) in this._tiles) && this._isValidTile(coords)) { if (tile) {
tile.current = true;
} else {
queue.push(coords); queue.push(coords);
} }
} }
@ -392,31 +421,24 @@ L.GridLayer = L.Layer.extend({
// sort tile queue to load tiles in order of their distance to center // sort tile queue to load tiles in order of their distance to center
queue.sort(function (a, b) { queue.sort(function (a, b) {
return a.distanceTo(center) - b.distanceTo(center); return a.distanceTo(tileCenter) - b.distanceTo(tileCenter);
}); });
var tilesToLoad = queue.length; if (queue.length !== 0) {
if (tilesToLoad !== 0) {
// if its the first batch of tiles to load // if its the first batch of tiles to load
if (!this._tilesToLoad) { if (this._noTilesToLoad()) {
this.fire('loading'); this.fire('loading');
} }
this._tilesToLoad += tilesToLoad;
// create DOM fragment to append tiles in one batch // create DOM fragment to append tiles in one batch
var fragment = document.createDocumentFragment(); var fragment = document.createDocumentFragment();
for (i = 0; i < tilesToLoad; i++) { for (i = 0; i < queue.length; i++) {
this._addTile(queue[i], fragment); this._addTile(queue[i], fragment);
} }
this._level.el.appendChild(fragment); this._level.el.appendChild(fragment);
} }
this._pruneTiles();
}, },
_isValidTile: function (coords) { _isValidTile: function (coords) {
@ -468,27 +490,16 @@ L.GridLayer = L.Layer.extend({
return coords; return coords;
}, },
// remove any present tiles that are off the specified bounds
_removeOtherTiles: function (bounds) {
for (var key in this._tiles) {
var tileBounds = this._keyToBounds(key);
if (!bounds.intersects(tileBounds)) {
this._removeTile(key);
}
}
},
_removeTile: function (key) { _removeTile: function (key) {
var tile = this._tiles[key]; var tile = this._tiles[key];
if (!tile) { return; } if (!tile) { return; }
L.DomUtil.remove(tile); L.DomUtil.remove(tile.el);
delete this._tiles[key]; delete this._tiles[key];
delete this._loaded[key];
this.fire('tileunload', { this.fire('tileunload', {
tile: tile, tile: tile.el,
coords: this._keyToTileCoords(key) coords: this._keyToTileCoords(key)
}); });
}, },
@ -534,7 +545,11 @@ L.GridLayer = L.Layer.extend({
L.DomUtil.setPosition(tile, tilePos, true); L.DomUtil.setPosition(tile, tilePos, true);
// save tile in cache // save tile in cache
this._tiles[key] = tile; this._tiles[key] = {
el: tile,
coords: coords,
current: true
};
container.appendChild(tile); container.appendChild(tile);
this.fire('tileloadstart', { this.fire('tileloadstart', {
@ -544,6 +559,8 @@ L.GridLayer = L.Layer.extend({
}, },
_tileReady: function (coords, err, tile) { _tileReady: function (coords, err, tile) {
if (!this._map) { return; }
if (err) { if (err) {
this.fire('tileerror', { this.fire('tileerror', {
error: err, error: err,
@ -554,21 +571,27 @@ L.GridLayer = L.Layer.extend({
var key = this._tileCoordsToKey(coords); var key = this._tileCoordsToKey(coords);
if (!this._tiles[key]) { return; } tile = this._tiles[key];
if (!tile) { return; }
this._loaded[key] = true; tile.loaded = +new Date();
this._pruneTiles(); if (this._map._fadeAnimated) {
L.DomUtil.setOpacity(tile.el, 0);
L.Util.cancelAnimFrame(this._fadeFrame);
this._fadeFrame = L.Util.requestAnimFrame(this._updateOpacity, this);
} else {
tile.active = true;
this._pruneTiles();
}
L.DomUtil.addClass(tile, 'leaflet-tile-loaded'); L.DomUtil.addClass(tile.el, 'leaflet-tile-loaded');
this.fire('tileload', { this.fire('tileload', {
tile: tile, tile: tile.el,
coords: coords coords: coords
}); });
this._tilesToLoad--; if (this._noTilesToLoad()) {
if (this._tilesToLoad === 0) {
this.fire('load'); this.fire('load');
} }
}, },
@ -591,8 +614,11 @@ L.GridLayer = L.Layer.extend({
bounds.max.divideBy(this._tileSize).ceil().subtract([1, 1])); bounds.max.divideBy(this._tileSize).ceil().subtract([1, 1]));
}, },
_animateZoom: function (e) { _noTilesToLoad: function () {
this._setZoomTransforms(e.center, e.zoom); for (var key in this._tiles) {
if (!this._tiles[key].loaded) { return false; }
}
return true;
} }
}); });

View File

@ -99,18 +99,17 @@ L.TileLayer = L.GridLayer.extend({
_getTileSize: function () { _getTileSize: function () {
var map = this._map, var map = this._map,
options = this.options, options = this.options,
zoom = map.getZoom() + options.zoomOffset, zoom = this._tileZoom + options.zoomOffset,
zoomN = options.maxNativeZoom; zoomN = options.maxNativeZoom;
// increase tile size when overscaling // increase tile size when overscaling
return zoomN !== null && zoom > zoomN ? return zoomN !== null && zoom > zoomN ?
Math.round(map.getZoomScale(zoomN, zoom) * options.tileSize) : Math.round(options.tileSize / map.getZoomScale(zoomN, zoom)) :
options.tileSize; options.tileSize;
}, },
_onTileRemove: function (e) { _onTileRemove: function (e) {
e.tile.onload = null; e.tile.onload = null;
e.tile.src = L.Util.emptyImageUrl;
}, },
_getZoomForUrl: function () { _getZoomForUrl: function () {
@ -136,7 +135,7 @@ L.TileLayer = L.GridLayer.extend({
_abortLoading: function () { _abortLoading: function () {
var i, tile; var i, tile;
for (i in this._tiles) { for (i in this._tiles) {
tile = this._tiles[i]; tile = this._tiles[i].el;
tile.onload = L.Util.falseFn; tile.onload = L.Util.falseFn;
tile.onerror = L.Util.falseFn; tile.onerror = L.Util.falseFn;

View File

@ -114,7 +114,7 @@ L.Canvas = L.Renderer.extend({
len = parts.length, len = parts.length,
ctx = this._ctx; ctx = this._ctx;
if (!len) { return; } if (!len) { return; }
ctx.beginPath(); ctx.beginPath();
@ -169,7 +169,7 @@ L.Canvas = L.Renderer.extend({
ctx.fill(options.fillRule || 'evenodd'); ctx.fill(options.fillRule || 'evenodd');
} }
if (options.stroke) { if (options.stroke && options.weight !== 0) {
ctx.globalAlpha = clear ? 1 : options.opacity; ctx.globalAlpha = clear ? 1 : options.opacity;
// if clearing shape, do it with the previously drawn line width // if clearing shape, do it with the previously drawn line width
@ -190,7 +190,8 @@ L.Canvas = L.Renderer.extend({
for (var id in this._layers) { for (var id in this._layers) {
if (this._layers[id]._containsPoint(point)) { if (this._layers[id]._containsPoint(point)) {
this._layers[id]._fireMouseEvent(e); L.DomEvent._fakeStop(e);
this._fireEvent(this._layers[id], e);
} }
} }
}, },
@ -213,20 +214,24 @@ L.Canvas = L.Renderer.extend({
// if we just got inside the layer, fire mouseover // if we just got inside the layer, fire mouseover
if (!layer._mouseInside) { if (!layer._mouseInside) {
L.DomUtil.addClass(this._container, 'leaflet-interactive'); // change cursor L.DomUtil.addClass(this._container, 'leaflet-interactive'); // change cursor
layer._fireMouseEvent(e, 'mouseover'); this._fireEvent(layer, e, 'mouseover');
layer._mouseInside = true; layer._mouseInside = true;
} }
// fire mousemove // fire mousemove
layer._fireMouseEvent(e); this._fireEvent(layer, e);
} else if (layer._mouseInside) { } else if (layer._mouseInside) {
// if we're leaving the layer, fire mouseout // if we're leaving the layer, fire mouseout
L.DomUtil.removeClass(this._container, 'leaflet-interactive'); L.DomUtil.removeClass(this._container, 'leaflet-interactive');
layer._fireMouseEvent(e, 'mouseout'); this._fireEvent(layer, e, 'mouseout');
layer._mouseInside = false; layer._mouseInside = false;
} }
}, },
_fireEvent: function (layer, e, type) {
this._map._fireDOMEvent(layer, e, type || e.type);
},
// TODO _bringToFront & _bringToBack, pretty tricky // TODO _bringToFront & _bringToBack, pretty tricky
_bringToFront: L.Util.falseFn, _bringToFront: L.Util.falseFn,

View File

@ -74,10 +74,6 @@ L.Path = L.Layer.extend({
return this; return this;
}, },
_fireMouseEvent: function (e, type) {
this._map._fireMouseEvent(this, e, type, true);
},
_clickTolerance: function () { _clickTolerance: function () {
// used when doing hit detection for Canvas layers // used when doing hit detection for Canvas layers
return (this.options.stroke ? this.options.weight / 2 : 0) + (L.Browser.touch ? 10 : 0); return (this.options.stroke ? this.options.weight / 2 : 0) + (L.Browser.touch ? 10 : 0);

View File

@ -58,7 +58,7 @@ L.Polygon = L.Polyline.extend({
this._parts = []; this._parts = [];
for (var i = 0, len = this._rings.length, clipped; i < len; i++) { for (var i = 0, len = this._rings.length, clipped; i < len; i++) {
clipped = L.PolyUtil.clipPolygon(this._rings[i], bounds); clipped = L.PolyUtil.clipPolygon(this._rings[i], bounds, true);
if (clipped.length) { if (clipped.length) {
this._parts.push(clipped); this._parts.push(clipped);
} }

View File

@ -179,7 +179,7 @@ L.Polyline = L.Path.extend({
points = this._rings[i]; points = this._rings[i];
for (j = 0, len2 = points.length; j < len2 - 1; j++) { for (j = 0, len2 = points.length; j < len2 - 1; j++) {
segment = L.LineUtil.clipSegment(points[j], points[j + 1], bounds, j); segment = L.LineUtil.clipSegment(points[j], points[j + 1], bounds, j, true);
if (!segment) { continue; } if (!segment) { continue; }

View File

@ -64,7 +64,7 @@ L.Renderer = L.Layer.extend({
L.Map.include({ L.Map.include({
// used by each vector layer to decide which renderer to use // used by each vector layer to decide which renderer to use
getRenderer: function (layer) { getRenderer: function (layer) {
var renderer = layer.options.renderer || this.options.renderer || this._renderer; var renderer = layer.options.renderer || this._getPaneRenderer(layer.options.pane) || this.options.renderer || this._renderer;
if (!renderer) { if (!renderer) {
renderer = this._renderer = (L.SVG && L.svg()) || (L.Canvas && L.canvas()); renderer = this._renderer = (L.SVG && L.svg()) || (L.Canvas && L.canvas());
@ -74,5 +74,18 @@ L.Map.include({
this.addLayer(renderer); this.addLayer(renderer);
} }
return renderer; return renderer;
},
_getPaneRenderer: function (name) {
if (name === 'overlayPane' || name === undefined) {
return false;
}
var renderer = this._paneRenderers[name];
if (renderer === undefined) {
renderer = (L.SVG && L.svg({pane: name})) || (L.Canvas && L.canvas({pane: name}));
this._paneRenderers[name] = renderer;
}
return renderer;
} }
}); });

View File

@ -7,9 +7,6 @@ L.SVG = L.Renderer.extend({
_initContainer: function () { _initContainer: function () {
this._container = L.SVG.create('svg'); this._container = L.SVG.create('svg');
this._paths = {};
this._initEvents();
// makes it possible to click through svg root; we'll reset it back in individual paths // makes it possible to click through svg root; we'll reset it back in individual paths
this._container.setAttribute('pointer-events', 'none'); this._container.setAttribute('pointer-events', 'none');
}, },
@ -54,15 +51,13 @@ L.SVG = L.Renderer.extend({
}, },
_addPath: function (layer) { _addPath: function (layer) {
var path = layer._path; this._container.appendChild(layer._path);
this._container.appendChild(path); layer.addInteractiveTarget(layer._path);
this._paths[L.stamp(path)] = layer;
}, },
_removePath: function (layer) { _removePath: function (layer) {
var path = layer._path; L.DomUtil.remove(layer._path);
L.DomUtil.remove(path); layer.removeInteractiveTarget(layer._path);
delete this._paths[L.stamp(path)];
}, },
_updatePath: function (layer) { _updatePath: function (layer) {
@ -122,7 +117,7 @@ L.SVG = L.Renderer.extend({
// drawing a circle with two half-arcs // drawing a circle with two half-arcs
var d = layer._empty() ? 'M0 0' : var d = layer._empty() ? 'M0 0' :
'M' + (p.x - r) + ',' + p.y + 'M' + (p.x - r) + ',' + p.y +
arc + (r * 2) + ',0 ' + arc + (r * 2) + ',0 ' +
arc + (-r * 2) + ',0 '; arc + (-r * 2) + ',0 ';
this._setPath(layer, d); this._setPath(layer, d);
@ -139,19 +134,6 @@ L.SVG = L.Renderer.extend({
_bringToBack: function (layer) { _bringToBack: function (layer) {
L.DomUtil.toBack(layer._path); L.DomUtil.toBack(layer._path);
},
// TODO remove duplication with L.Map
_initEvents: function () {
L.DomEvent.on(this._container, 'click dblclick mousedown mouseup mouseover mouseout mousemove contextmenu',
this._fireMouseEvent, this);
},
_fireMouseEvent: function (e) {
var path = this._paths[L.stamp(e.target || e.srcElement)];
if (path) {
path._fireMouseEvent(e);
}
} }
}); });

View File

@ -220,7 +220,7 @@ L.Map = L.Evented.extend({
remove: function () { remove: function () {
this._initEvents('off'); this._initEvents(true);
try { try {
// throws error in IE6-8 // throws error in IE6-8
@ -328,8 +328,8 @@ L.Map = L.Evented.extend({
return this._size.clone(); return this._size.clone();
}, },
getPixelBounds: function () { getPixelBounds: function (center, zoom) {
var topLeftPoint = this._getTopLeftPoint(); var topLeftPoint = this._getTopLeftPoint(center, zoom);
return new L.Bounds(topLeftPoint, topLeftPoint.add(this.getSize())); return new L.Bounds(topLeftPoint, topLeftPoint.add(this.getSize()));
}, },
@ -470,6 +470,7 @@ L.Map = L.Evented.extend({
_initPanes: function () { _initPanes: function () {
var panes = this._panes = {}; var panes = this._panes = {};
this._paneRenderers = {};
this._mapPane = this.createPane('mapPane', this._container); this._mapPane = this.createPane('mapPane', this._container);
@ -545,16 +546,17 @@ L.Map = L.Evented.extend({
} }
}, },
// map events // DOM event handling
_initEvents: function (onOff) { _initEvents: function (remove) {
if (!L.DomEvent) { return; } if (!L.DomEvent) { return; }
onOff = onOff || 'on'; this._targets = {};
L.DomEvent[onOff](this._container, var onOff = remove ? 'off' : 'on';
'click dblclick mousedown mouseup mouseenter mouseleave mousemove contextmenu',
this._handleMouseEvent, this); L.DomEvent[onOff](this._container, 'click dblclick mousedown mouseup ' +
'mouseover mouseout mousemove contextmenu keypress', this._handleDOMEvent, this);
if (this.options.trackResize) { if (this.options.trackResize) {
L.DomEvent[onOff](window, 'resize', this._onResize, this); L.DomEvent[onOff](window, 'resize', this._onResize, this);
@ -567,46 +569,53 @@ L.Map = L.Evented.extend({
function () { this.invalidateSize({debounceMoveend: true}); }, this, false, this._container); function () { this.invalidateSize({debounceMoveend: true}); }, this, false, this._container);
}, },
_handleMouseEvent: function (e) { _handleDOMEvent: function (e) {
if (!this._loaded) { return; } if (!this._loaded || L.DomEvent._skipped(e)) { return; }
this._fireMouseEvent(this, e, // find the layer the event is propagating from
e.type === 'mouseenter' ? 'mouseover' : var target = this._targets[L.stamp(e.target || e.srcElement)],
e.type === 'mouseleave' ? 'mouseout' : e.type); type = e.type === 'keypress' && e.keyCode === 13 ? 'click' : e.type;
},
_fireMouseEvent: function (obj, e, type, propagate, latlng) { // special case for map mouseover/mouseout events so that they're actually mouseenter/mouseleave
type = type || e.type; if (!target && (type === 'mouseover' || type === 'mouseout') &&
!L.DomEvent._checkMouse(this._container, e)) { return; }
if (L.DomEvent._skipped(e)) { return; } // prevents outline when clicking on keyboard-focusable element
if (type === 'click') { if (type === 'mousedown') {
var draggableObj = obj.options.draggable === true ? obj : this; L.DomUtil.preventOutline(e.target || e.srcElement);
if (!e._simulated && ((draggableObj.dragging && draggableObj.dragging.moved()) ||
(this.boxZoom && this.boxZoom.moved()))) {
L.DomEvent.stopPropagation(e);
return;
}
obj.fire('preclick');
} }
if (!obj.listens(type, propagate)) { return; } this._fireDOMEvent(target || this, e, type);
},
_fireDOMEvent: function (target, e, type) {
if (!target.listens(type, true) && (type !== 'click' || !target.listens('preclick', true))) { return; }
if (type === 'contextmenu') { if (type === 'contextmenu') {
L.DomEvent.preventDefault(e); L.DomEvent.preventDefault(e);
} }
if (type === 'click' || type === 'dblclick' || type === 'contextmenu') {
L.DomEvent.stopPropagation(e); // prevents firing click after you just dragged an object
} if (e.type === 'click' && !e._simulated && this._draggableMoved(target)) { return; }
var data = { var data = {
originalEvent: e, originalEvent: e
containerPoint: this.mouseEventToContainerPoint(e)
}; };
if (e.type !== 'keypress') {
data.containerPoint = target instanceof L.Marker ?
this.latLngToContainerPoint(target.getLatLng()) : this.mouseEventToContainerPoint(e);
data.layerPoint = this.containerPointToLayerPoint(data.containerPoint);
data.latlng = this.layerPointToLatLng(data.layerPoint);
}
if (type === 'click') {
target.fire('preclick', data, true);
}
target.fire(type, data, true);
},
data.layerPoint = this.containerPointToLayerPoint(data.containerPoint); _draggableMoved: function (obj) {
data.latlng = latlng || this.layerPointToLatLng(data.layerPoint); obj = obj.options.draggable ? obj : this;
return (obj.dragging && obj.dragging.moved()) || (this.boxZoom && this.boxZoom.moved());
obj.fire(type, data, propagate);
}, },
_clearHandlers: function () { _clearHandlers: function () {
@ -636,8 +645,11 @@ L.Map = L.Evented.extend({
return pos && !pos.equals([0, 0]); return pos && !pos.equals([0, 0]);
}, },
_getTopLeftPoint: function () { _getTopLeftPoint: function (center, zoom) {
return this.getPixelOrigin().subtract(this._getMapPanePos()); var pixelOrigin = center && zoom !== undefined ?
this._getNewPixelOrigin(center, zoom) :
this.getPixelOrigin();
return pixelOrigin.subtract(this._getMapPanePos());
}, },
_getNewPixelOrigin: function (center, zoom) { _getNewPixelOrigin: function (center, zoom) {

View File

@ -9,6 +9,7 @@ L.Map.include({
size = this.getSize(), size = this.getSize(),
startZoom = this._zoom; startZoom = this._zoom;
targetCenter = L.latLng(targetCenter);
targetZoom = targetZoom === undefined ? startZoom : targetZoom; targetZoom = targetZoom === undefined ? startZoom : targetZoom;
var w0 = Math.max(size.x, size.y), var w0 = Math.max(size.x, size.y),

View File

@ -96,6 +96,6 @@ L.Map.include({
this.panBy(offset, options); this.panBy(offset, options);
return true; return (options && options.animate) !== false;
} }
}); });

View File

@ -34,7 +34,15 @@ L.Map.include(!zoomAnimated ? {} : {
this._panes.mapPane.appendChild(proxy); this._panes.mapPane.appendChild(proxy);
this.on('zoomanim', function (e) { this.on('zoomanim', function (e) {
var prop = L.DomUtil.TRANSFORM,
transform = proxy.style[prop];
L.DomUtil.setTransform(proxy, this.project(e.center, e.zoom), this.getZoomScale(e.zoom, 1)); L.DomUtil.setTransform(proxy, this.project(e.center, e.zoom), this.getZoomScale(e.zoom, 1));
// workaround for case when transform is the same and so transitionend event is not fired
if (transform === proxy.style[prop] && this._animatingZoom) {
this._onZoomTransitionEnd();
}
}, this); }, this);
this.on('load moveend', function () { this.on('load moveend', function () {
@ -81,7 +89,7 @@ L.Map.include(!zoomAnimated ? {} : {
return true; return true;
}, },
_animateZoom: function (center, zoom, startAnim) { _animateZoom: function (center, zoom, startAnim, noUpdate) {
if (startAnim) { if (startAnim) {
this._animatingZoom = true; this._animatingZoom = true;
@ -97,7 +105,8 @@ L.Map.include(!zoomAnimated ? {} : {
zoom: zoom, zoom: zoom,
scale: this.getZoomScale(zoom), scale: this.getZoomScale(zoom),
origin: this.latLngToLayerPoint(center), origin: this.latLngToLayerPoint(center),
offset: this._getCenterOffset(center).multiplyBy(-1) offset: this._getCenterOffset(center).multiplyBy(-1),
noUpdate: noUpdate
}); });
}, },

View File

@ -14,7 +14,7 @@ L.Map.include({
locate: function (/*Object*/ options) { locate: function (/*Object*/ options) {
options = this._locateOptions = L.extend(this._defaultLocateOptions, options); options = this._locateOptions = L.extend({}, this._defaultLocateOptions, options);
if (!navigator.geolocation) { if (!navigator.geolocation) {
this._handleGeolocationError({ this._handleGeolocationError({

View File

@ -27,7 +27,7 @@ L.Map.BoxZoom = L.Handler.extend({
}, },
_onMouseDown: function (e) { _onMouseDown: function (e) {
if (!e.shiftKey || ((e.which !== 1) && (e.button !== 1))) { return false; } if (!e.shiftKey || ((e.which !== 1) && (e.button !== 0))) { return false; }
this._moved = false; this._moved = false;
@ -83,7 +83,7 @@ L.Map.BoxZoom = L.Handler.extend({
}, },
_onMouseUp: function (e) { _onMouseUp: function (e) {
if ((e.which !== 1) && (e.button !== 1)) { return false; } if ((e.which !== 1) && (e.button !== 0)) { return; }
this._finish(); this._finish();

View File

@ -8,7 +8,6 @@ L.Map.mergeOptions({
inertia: !L.Browser.android23, inertia: !L.Browser.android23,
inertiaDeceleration: 3400, // px/s^2 inertiaDeceleration: 3400, // px/s^2
inertiaMaxSpeed: Infinity, // px/s inertiaMaxSpeed: Infinity, // px/s
inertiaThreshold: L.Browser.touch ? 32 : 18, // ms
easeLinearity: 0.2, easeLinearity: 0.2,
// TODO refactor, move to CRS // TODO refactor, move to CRS
@ -64,7 +63,7 @@ L.Map.Drag = L.Handler.extend({
} }
}, },
_onDrag: function () { _onDrag: function (e) {
if (this._map.options.inertia) { if (this._map.options.inertia) {
var time = this._lastTime = +new Date(), var time = this._lastTime = +new Date(),
pos = this._lastPos = this._draggable._absPos || this._draggable._newPos; pos = this._lastPos = this._draggable._absPos || this._draggable._newPos;
@ -72,15 +71,15 @@ L.Map.Drag = L.Handler.extend({
this._positions.push(pos); this._positions.push(pos);
this._times.push(time); this._times.push(time);
if (time - this._times[0] > 100) { if (time - this._times[0] > 50) {
this._positions.shift(); this._positions.shift();
this._times.shift(); this._times.shift();
} }
} }
this._map this._map
.fire('move') .fire('move', e)
.fire('drag'); .fire('drag', e);
}, },
_onViewReset: function () { _onViewReset: function () {
@ -108,9 +107,8 @@ L.Map.Drag = L.Handler.extend({
_onDragEnd: function (e) { _onDragEnd: function (e) {
var map = this._map, var map = this._map,
options = map.options, options = map.options,
delay = +new Date() - this._lastTime,
noInertia = !options.inertia || delay > options.inertiaThreshold || !this._positions[0]; noInertia = !options.inertia || this._times.length < 2;
map.fire('dragend', e); map.fire('dragend', e);
@ -120,7 +118,7 @@ L.Map.Drag = L.Handler.extend({
} else { } else {
var direction = this._lastPos.subtract(this._positions[0]), var direction = this._lastPos.subtract(this._positions[0]),
duration = (this._lastTime + delay - this._times[0]) / 1000, duration = (this._lastTime - this._times[0]) / 1000,
ease = options.easeLinearity, ease = options.easeLinearity,
speedVector = direction.multiplyBy(ease / duration), speedVector = direction.multiplyBy(ease / duration),

View File

@ -35,14 +35,14 @@ L.Map.Keyboard = L.Handler.extend({
} }
L.DomEvent.on(container, { L.DomEvent.on(container, {
focus: this._onFocus, focus: this._onFocus,
blur: this._onBlur, blur: this._onBlur,
mousedown: this._onMouseDown mousedown: this._onMouseDown
}, this); }, this);
this._map.on({ this._map.on({
focus: this._addHooks, focus: this._addHooks,
blur: this._removeHooks blur: this._removeHooks
}, this); }, this);
}, },
@ -50,14 +50,14 @@ L.Map.Keyboard = L.Handler.extend({
this._removeHooks(); this._removeHooks();
L.DomEvent.off(this._map._container, { L.DomEvent.off(this._map._container, {
focus: this._onFocus, focus: this._onFocus,
blur: this._onBlur, blur: this._onBlur,
mousedown: this._onMouseDown mousedown: this._onMouseDown
}, this); }, this);
this._map.off({ this._map.off({
focus: this._addHooks, focus: this._addHooks,
blur: this._removeHooks blur: this._removeHooks
}, this); }, this);
}, },

View File

@ -48,7 +48,7 @@ L.Map.Tap = L.Handler.extend({
this._simulateEvent('contextmenu', first); this._simulateEvent('contextmenu', first);
} }
}, this), 1000); }, this), 1000);
this._simulateEvent('mousedown', first); this._simulateEvent('mousedown', first);
L.DomEvent.on(document, { L.DomEvent.on(document, {
@ -73,7 +73,7 @@ L.Map.Tap = L.Handler.extend({
if (el && el.tagName && el.tagName.toLowerCase() === 'a') { if (el && el.tagName && el.tagName.toLowerCase() === 'a') {
L.DomUtil.removeClass(el, 'leaflet-active'); L.DomUtil.removeClass(el, 'leaflet-active');
} }
this._simulateEvent('mouseup', first); this._simulateEvent('mouseup', first);
// simulate click if the touch didn't move too much // simulate click if the touch didn't move too much

View File

@ -80,9 +80,12 @@ L.Map.TouchZoom = L.Handler.extend({
} else { } else {
this._center = map.layerPointToLatLng(this._getTargetCenter()); this._center = map.layerPointToLatLng(this._getTargetCenter());
} }
this._zoom = map.getScaleZoom(this._scale); this._zoom = map.getScaleZoom(this._scale);
map._animateZoom(this._center, this._zoom); if (this._scale !== 1 || this._delta.x !== 0 || this._delta.y !== 0) {
map._animateZoom(this._center, this._zoom, false, true);
}
}, },
_onTouchEnd: function () { _onTouchEnd: function () {
@ -103,7 +106,7 @@ L.Map.TouchZoom = L.Handler.extend({
zoomDelta = this._zoom - oldZoom, zoomDelta = this._zoom - oldZoom,
finalZoom = map._limitZoom(zoomDelta > 0 ? Math.ceil(this._zoom) : Math.floor(this._zoom)); finalZoom = map._limitZoom(zoomDelta > 0 ? Math.ceil(this._zoom) : Math.floor(this._zoom));
map._animateZoom(this._center, finalZoom, true); map._animateZoom(this._center, finalZoom, true, true);
}, },
_getTargetCenter: function () { _getTargetCenter: function () {