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} = stable &&
test ${TRAVIS_PULL_REQUEST} = false &&
gem install --no-rdoc --no-ri --version 0.8.9 faraday &&
gem install --no-rdoc --no-ri travis-artifacts || true
gem install --no-document --version 0.8.9 faraday &&
gem install --no-document travis-artifacts || true
after_success: >
test ${TRAVIS_BRANCH} = master ||
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*.
* 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 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,
@ -125,7 +125,7 @@ From there you can click through folders/files to get details on their individua
## Improving Documentation
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
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'),
version = require('./src/Leaflet.js').version;
function hint(msg, paths) {
function hint(msg, args) {
return function () {
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 () {
console.log('\tCheck passed.\n');
complete();
@ -26,11 +26,11 @@ function hint(msg, paths) {
};
}
desc('Check Leaflet source for errors with JSHint');
task('lint', {async: true}, hint('Checking for JS errors...', 'build/hintrc.js src'));
desc('Check Leaflet source for errors with ESLint');
task('lint', {async: true}, hint('Checking for JS errors...', 'src --config .eslintrc'));
desc('Check Leaflet specs source for errors with JSHint');
task('lintspec', {async: true}, hint('Checking for specs JS errors...', 'spec/spec.hintrc.js spec/suites'));
desc('Check Leaflet specs source for errors with ESLint');
task('lintspec', {async: true}, hint('Checking for specs JS errors...', 'spec/suites --config spec/.eslintrc'));
desc('Combine and compress Leaflet source files');
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
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`.
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
// bad
@ -168,11 +168,10 @@ You can add support for AMD/CommonJS loaders to your Leaflet plugin by following
// define a Common JS module that relies on 'leaflet'
} else if (typeof exports === 'object') {
module.exports = factory(require('leaflet'));
}
// 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);
}
}(function (L) {

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,
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
[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);
overlay.on('dblclick',function (e) {
console.log('Double click on image.')
})
console.log('Double click on image.');
});
</script>
</body>

11
dist/leaflet.css vendored
View File

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

View File

@ -3,20 +3,20 @@
"version": "0.8.0-dev",
"description": "JavaScript library for mobile-friendly interactive maps",
"devDependencies": {
"jake": "~8.0.10",
"jshint": "~2.5.7",
"uglify-js": "~2.4.15",
"mocha": "~2.0.1",
"copyfiles": "0.1.0",
"eslint": "^0.15.1",
"happen": "~0.1.3",
"karma": "~0.12.24",
"karma-mocha": "~0.1.9",
"karma-coverage": "~0.2.6",
"jake": "~8.0.10",
"karma": "~0.12.31",
"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-chrome-launcher": "^0.1.5",
"karma-firefox-launcher": "~0.1.3",
"karma-safari-launcher": "~0.1.1",
"mocha": "~2.1.0",
"tin": "^0.5.0",
"copyfiles": "0.1.0"
"uglify-js": "~2.4.16"
},
"main": "dist/leaflet-src.js",
"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
captureTimeout: 5000,
// Workaround for PhantomJS random DISCONNECTED error
browserDisconnectTimeout: 10000, // default 2000
browserDisconnectTolerance: 1, // default 0
// Continuous Integration mode
// if true, it capture browsers, run tests and exit
singleRun: true

View File

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

View File

@ -14,7 +14,7 @@ describe('Events', function () {
obj.addEventListener('test', spy1);
obj.addEventListener('test', spy2);
obj.addEventListener('other', spy3);
obj.addEventListener({ test: spy4, other: spy5 });
obj.addEventListener({test: spy4, other: spy5});
// obj.addEventListener({'test other': spy6 });
expect(spy1.called).to.be(false);
@ -72,8 +72,8 @@ describe('Events', function () {
obj.addEventListener('test', listener1);
obj2.addEventListener('test', listener2, foo);
obj3.addEventListener({ test: listener3 });
obj4.addEventListener({ test: listener4 }, foo);
obj3.addEventListener({test: listener3});
obj4.addEventListener({test: listener4}, foo);
obj.fireEvent('test', {baz: 1});
obj2.fireEvent('test', {baz: 2});

View File

@ -40,9 +40,9 @@ describe('Util', function () {
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 () {
@ -230,6 +230,7 @@ describe('Util', function () {
describe('#isArray', function () {
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('blabla')).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 () {
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, 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));
});
});
});

View File

@ -33,6 +33,21 @@ describe('LineUtil', function () {
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 () {

View File

@ -10,6 +10,7 @@ describe('PolyUtil', function () {
new L.Point(10, 15)
];
//check clip without rounding
var clipped = L.PolyUtil.clipPolygon(points, bounds);
for (var i = 0, len = clipped.length; i < len; i++) {
@ -17,6 +18,20 @@ describe('PolyUtil', function () {
}
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(5, 5),
new L.Point(10, 8),

View File

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

View File

@ -112,6 +112,41 @@ describe('Popup', function () {
marker1.closePopup();
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 () {

View File

@ -1,11 +1,16 @@
describe("Marker", function () {
var map,
spy,
div,
icon1,
icon2;
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();
icon2 = new L.Icon.Default({
iconUrl: icon1._getIconUrl('icon') + '?2',
@ -13,6 +18,10 @@ describe("Marker", function () {
});
});
afterEach(function () {
document.body.removeChild(div);
});
describe("#setIcon", function () {
it("changes the icon to another image", function () {
var marker = new L.Marker([0, 0], {icon: icon1});
@ -40,14 +49,25 @@ describe("Marker", function () {
marker.setIcon(icon1);
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 () {
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);
var beforeIcon = marker._icon;
marker.setIcon(new L.DivIcon({html: 'Inner2Text' }));
marker.setIcon(new L.DivIcon({html: 'Inner2Text'}));
var afterIcon = marker._icon;
expect(beforeIcon).to.be(afterIcon);
@ -55,7 +75,7 @@ describe("Marker", 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);
marker.setIcon(new L.DivIcon());
@ -65,7 +85,7 @@ describe("Marker", 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);
var oldIcon = marker._icon;
@ -87,7 +107,7 @@ describe("Marker", function () {
map.addLayer(marker);
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.parentNode).to.be(null);
@ -97,7 +117,7 @@ describe("Marker", 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);
var oldIcon = marker._icon;
var oldShadow = marker._shadow;
@ -115,7 +135,7 @@ describe("Marker", function () {
describe("#setLatLng", 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);
var beforeLatLng = marker._latlng;
@ -134,4 +154,17 @@ describe("Marker", function () {
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});
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 () {
@ -202,7 +176,8 @@ describe('GridLayer', 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 () {
var tiles = [ L.gridLayer({minZoom: 10, maxZoom: 15}).addTo(map),
var tiles = [
L.gridLayer({minZoom: 10, maxZoom: 15}).addTo(map),
L.gridLayer({minZoom: 5, maxZoom: 10}).addTo(map),
L.gridLayer({minZoom: 10, maxZoom: 20}).addTo(map),
L.gridLayer({minZoom: 0, maxZoom: 25}).addTo(map)

View File

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

View File

@ -570,4 +570,24 @@ describe("Map", function () {
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) {
if (!text) { return; }
if (!text) { return this; }
if (!this._attributions[text]) {
this._attributions[text] = 0;
@ -52,7 +52,7 @@ L.Control.Attribution = L.Control.extend({
},
removeAttribution: function (text) {
if (!text) { return; }
if (!text) { return this; }
if (this._attributions[text]) {
this._attributions[text]--;

View File

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

View File

@ -31,12 +31,28 @@ L.Control.Zoom = L.Control.extend({
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) {
if (!this._disabled) {
this._map.zoomIn(e.shiftKey ? 3 : 1);
}
},
_zoomOut: function (e) {
if (!this._disabled) {
this._map.zoomOut(e.shiftKey ? 3 : 1);
}
},
_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._zoomOutButton, className);
if (map._zoom === map.getMinZoom()) {
if (this._disabled || map._zoom === map.getMinZoom()) {
L.DomUtil.addClass(this._zoomOutButton, className);
}
if (map._zoom === map.getMaxZoom()) {
if (this._disabled || map._zoom === map.getMaxZoom()) {
L.DomUtil.addClass(this._zoomInButton, className);
}
}
@ -84,4 +100,3 @@ L.Map.addInitHook(function () {
L.control.zoom = function (options) {
return new L.Control.Zoom(options);
};

View File

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

View File

@ -1,4 +1,4 @@
/*
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();
};
// jshint camelcase: false
var parentProto = NewClass.__super__ = this.prototype;
var proto = L.Util.create(parentProto);
@ -27,7 +26,7 @@ L.Class.extend = function (props) {
NewClass.prototype = proto;
//inherit parent's statics
// inherit parent's statics
for (var i in this) {
if (this.hasOwnProperty(i) && i !== 'prototype') {
NewClass[i] = this[i];

View File

@ -42,9 +42,10 @@ L.Util = {
// return unique ID of an object
stamp: function (obj) {
// jshint camelcase: false
/*eslint-disable */
obj._leaflet_id = obj._leaflet_id || ++L.Util.lastId;
return obj._leaflet_id;
/*eslint-enable */
},
lastId: 0,
@ -116,7 +117,7 @@ L.Util = {
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) {
var params = [];
for (var i in obj) {

View File

@ -67,8 +67,9 @@ L.DomEvent = {
} else if ((type === 'mouseenter') || (type === 'mouseleave')) {
handler = function (e) {
e = e || window.event;
if (!L.DomEvent._checkMouse(obj, e)) { return; }
return originalHandler(e);
if (L.DomEvent._checkMouse(obj, e)) {
originalHandler(e);
}
};
obj.addEventListener(type === 'mouseenter' ? 'mouseover' : 'mouseout', handler, false);
@ -240,7 +241,7 @@ L.DomEvent = {
}
L.DomEvent._lastClick = timeStamp;
return handler(e);
handler(e);
}
};

View File

@ -100,7 +100,11 @@ L.DomUtil = {
el.style.opacity = value;
} else if ('filter' in el.style) {
L.DomUtil._setOpacityIE(el, value);
}
},
_setOpacityIE: function (el, value) {
var filter = false,
filterName = 'DXImageTransform.Microsoft.Alpha';
@ -121,7 +125,6 @@ L.DomUtil = {
} else {
el.style.filter += ' progid:' + filterName + '(opacity=' + value + ')';
}
}
},
testProp: function (props) {
@ -145,8 +148,9 @@ L.DomUtil = {
setPosition: function (el, point, no3d) { // (HTMLElement, Point[, Boolean])
// jshint camelcase: false
/*eslint-disable */
el._leaflet_pos = point;
/*eslint-enable */
if (L.Browser.any3d && !no3d) {
L.DomUtil.setTransform(el, point);
@ -160,7 +164,6 @@ L.DomUtil = {
// this method is only used for elements previously positioned using setPosition,
// so it's safe to cache the position for performance
// jshint camelcase: false
return el._leaflet_pos;
}
};
@ -216,4 +219,19 @@ L.DomUtil = {
L.DomUtil.enableImageDrag = function () {
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._dragStartTarget = dragStartTarget || element;
this._preventOutline = preventOutline;
},
enable: function () {
@ -45,10 +46,14 @@ L.Draggable = L.Evented.extend({
_onDown: function (e) {
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);
if (this._preventOutline) {
L.DomUtil.preventOutline(this._element);
}
if (L.DomUtil.hasClass(this._element, 'leaflet-zoom-anim')) { return; }
L.DomUtil.disableImageDrag();
@ -99,13 +104,15 @@ L.Draggable = L.Evented.extend({
this._moving = true;
L.Util.cancelAnimFrame(this._animRequest);
this._lastEvent = e;
this._animRequest = L.Util.requestAnimFrame(this._updatePosition, this, true, this._dragStartTarget);
},
_updatePosition: function () {
this.fire('predrag');
var e = {originalEvent: this._lastEvent};
this.fire('predrag', e);
L.DomUtil.setPosition(this._element, this._newPos);
this.fire('drag');
this.fire('drag', e);
},
_onUp: function () {

View File

@ -80,4 +80,3 @@ L.latLng = function (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,
// 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) {
var rad = Math.PI / 180,
lat1 = latlng1.lat * rad,

View File

@ -3,14 +3,12 @@
* and polylines (clipping, simplification, distances, etc.)
*/
/*jshint bitwise:false */ // allow bitwise operations for this file
L.LineUtil = {
// Simplify polyline with vertex reduction and Douglas-Peucker simplification.
// 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) {
return points.slice();
}
@ -27,11 +25,11 @@ L.LineUtil = {
},
// 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));
},
closestPointOnSegment: function (/*Point*/ p, /*Point*/ p1, /*Point*/ p2) {
closestPointOnSegment: function (p, p1, p2) {
return this._sqClosestPointOnSegment(p, p1, p2);
},
@ -99,7 +97,7 @@ L.LineUtil = {
// Cohen-Sutherland line clipping algorithm.
// 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),
codeB = this._getBitCode(b, bounds),
@ -118,7 +116,7 @@ L.LineUtil = {
// other cases
} else {
codeOut = codeA || codeB;
p = this._getEdgeIntersection(a, b, codeOut, bounds);
p = this._getEdgeIntersection(a, b, codeOut, bounds, round);
newCode = this._getBitCode(p, bounds);
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,
dy = b.y - a.y,
min = bounds.min,
@ -156,7 +154,7 @@ L.LineUtil = {
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) {

View File

@ -2,7 +2,7 @@
* 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.y = (round ? Math.round(y) : y);
};

View File

@ -2,15 +2,13 @@
* L.PolyUtil contains utility functions for polygons (clipping, etc.).
*/
/*jshint bitwise:false */ // allow bitwise operations here
L.PolyUtil = {};
/*
* Sutherland-Hodgeman polygon clipping algorithm.
* 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,
edges = [1, 4, 2, 8],
i, j, k,
@ -35,7 +33,7 @@ L.PolyUtil.clipPolygon = function (points, bounds) {
if (!(a._code & edge)) {
// if b is outside the clip window (a->b goes out of screen)
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);
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._code & edge)) {
p = lu._getEdgeIntersection(b, a, edge, bounds);
p = lu._getEdgeIntersection(b, a, edge, bounds, round);
p._code = lu._getBitCode(p, bounds);
clippedPoints.push(p);
}

View File

@ -31,7 +31,7 @@ L.GeoJSON = L.FeatureGroup.extend({
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);
layer.feature = L.GeoJSON.asFeature(geojson);
@ -146,7 +146,7 @@ L.extend(L.GeoJSON, {
for (var i = 0, len = latlngs.length; i < len; i++) {
coords.push(levelsDeep ?
L.GeoJSON.latLngsToCoords(latlngs[i], levelsDeep - 1, closed):
L.GeoJSON.latLngsToCoords(latlngs[i], levelsDeep - 1, closed) :
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._initInteraction();
this._reset();
},
onRemove: function () {
L.DomUtil.remove(this._image);
if (this.options.interactive) {
this.removeInteractiveTarget(this._image);
}
},
setOpacity: function (opacity) {
@ -65,19 +72,6 @@ L.ImageOverlay = L.Layer.extend({
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) {
this._url = url;

View File

@ -7,6 +7,7 @@ L.Layer.include({
bindPopup: function (content, options) {
if (content instanceof L.Popup) {
L.setOptions(content, options);
this._popup = content;
content._source = this;
} else {

View File

@ -25,6 +25,16 @@ L.Layer = L.Evented.extend({
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) {
var map = e.target;

View File

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

View File

@ -32,8 +32,8 @@ L.Marker = L.Layer.extend({
},
onRemove: function () {
if (this.dragging) {
this.dragging.disable();
if (this.dragging && this.dragging.enabled()) {
this.dragging.removeHooks();
}
this._removeIcon();
@ -58,7 +58,7 @@ L.Marker = L.Layer.extend({
var oldLatLng = this._latlng;
this._latlng = L.latLng(latlng);
this.update();
return this.fire('move', { oldLatLng: oldLatLng, latlng: this._latlng });
return this.fire('move', {oldLatLng: oldLatLng, latlng: this._latlng});
},
setZIndexOffset: function (offset) {
@ -123,11 +123,11 @@ L.Marker = L.Layer.extend({
this._icon = icon;
this._initInteraction();
if (L.DomEvent && options.riseOnHover) {
L.DomEvent.on(icon, {
if (options.riseOnHover) {
this.on({
mouseover: this._bringToFront,
mouseout: this._resetZIndex
}, this);
});
}
var newShadow = options.icon.createShadow(this._shadow),
@ -158,14 +158,15 @@ L.Marker = L.Layer.extend({
},
_removeIcon: function () {
if (L.DomEvent && this.options.riseOnHover) {
L.DomEvent.off(this._icon, {
if (this.options.riseOnHover) {
this.off({
mouseover: this._bringToFront,
mouseout: this._resetZIndex
}, this);
});
}
L.DomUtil.remove(this._icon);
this.removeInteractiveTarget(this._icon);
this._icon = null;
},
@ -205,11 +206,7 @@ L.Marker = L.Layer.extend({
L.DomUtil.addClass(this._icon, 'leaflet-interactive');
if (L.DomEvent) {
L.DomEvent.on(this._icon,
'click dblclick mousedown mouseup mouseover mousemove mouseout contextmenu keypress',
this._fireMouseEvent, this);
}
this.addInteractiveTarget(this._icon);
if (L.Handler.MarkerDrag) {
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) {
this.options.opacity = opacity;
if (this._map) {

View File

@ -10,7 +10,6 @@ L.GridLayer = L.Layer.extend({
tileSize: 256,
opacity: 1,
unloadInvisibleTiles: L.Browser.mobile,
updateWhenIdle: L.Browser.mobile,
updateInterval: 200,
@ -29,16 +28,10 @@ L.GridLayer = L.Layer.extend({
onAdd: function () {
this._initContainer();
this._pruneTiles = L.Util.throttle(this._pruneTiles, 200, this);
this._levels = {};
this._tiles = {};
this._loaded = {};
this._retain = {};
this._tilesToLoad = 0;
this._reset();
this._viewReset();
this._update();
},
@ -103,13 +96,13 @@ L.GridLayer = L.Layer.extend({
getEvents: function () {
var events = {
viewreset: this._reset,
moveend: this._update
viewreset: this._viewReset,
moveend: this._move
};
if (!this.options.updateWhenIdle) {
// 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) {
@ -153,13 +146,33 @@ L.GridLayer = L.Layer.extend({
_updateOpacity: function () {
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
for (var i in this._tiles) {
L.DomUtil.setOpacity(this._tiles[i], opacity);
}
} else {
if (!L.Browser.ielt9 && !this._map._fadeAnimated) {
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 () {
var zoom = this._tileZoom;
var zoom = this._tileZoom,
maxZoom = this.options.maxZoom;
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],
@ -190,10 +209,15 @@ L.GridLayer = L.Layer.extend({
level = this._levels[zoom] = {};
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.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;
@ -202,39 +226,26 @@ L.GridLayer = L.Layer.extend({
},
_pruneTiles: function () {
var key, tile;
if (!this._map) { return; }
for (key in this._tiles) {
tile = this._tiles[key];
tile.retain = tile.current;
}
this._retain = {};
var bounds = this._map.getBounds(),
z = this._tileZoom,
range = this._getTileRange(bounds, z),
i, j, key, found;
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) {
tile = this._tiles[key];
if (tile.current && !tile.active) {
var coords = tile.coords;
if (!this._retainParent(coords.x, coords.y, coords.z, coords.z - 5)) {
this._retainChildren(coords.x, coords.y, coords.z, coords.z + 2);
}
}
}
for (key in this._tiles) {
if (!this._retain[key]) {
if (!this._loaded[key]) {
if (!this._tiles[key].retain) {
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) {
this._removeTile(key);
}
this._tilesToLoad = 0;
},
_deferRemove: function (key) {
if (!this._retain[key]) {
this._removeTile(key);
}
},
_retainParent: function (x, y, z, minZoom) {
@ -257,13 +261,18 @@ L.GridLayer = L.Layer.extend({
y2 = Math.floor(y / 2),
z2 = z - 1;
var key = x2 + ':' + y2 + ':' + z2;
var key = x2 + ':' + y2 + ':' + z2,
tile = this._tiles[key];
if (this._loaded[key]) {
this._retain[key] = true;
if (tile && tile.active) {
tile.retain = 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);
}
@ -275,34 +284,56 @@ L.GridLayer = L.Layer.extend({
for (var i = 2 * x; i < 2 * x + 2; i++) {
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]) {
this._retain[key] = true;
if (tile && tile.active) {
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);
}
}
}
},
_reset: function (e) {
var map = this._map,
zoom = map.getZoom(),
tileZoom = Math.round(zoom),
_viewReset: function (e) {
this._reset(this._map.getCenter(), this._map.getZoom(), e && e.hard);
},
_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 (tileZoomChanged || (e && e.hard)) {
if (!noUpdate && (hard || tileZoomChanged)) {
if (this._abortLoading) {
this._abortLoading();
}
this._tileZoom = tileZoom;
this._updateLevels();
this._resetGrid();
if (!L.Browser.mobileWebkit) {
this._update(center, tileZoom);
}
this._setZoomTransforms(map.getCenter(), zoom);
if (!noPrune) {
this._pruneTiles();
}
}
this._setZoomTransforms(center, zoom);
},
_setZoomTransforms: function (center, zoom) {
@ -344,8 +375,14 @@ L.GridLayer = L.Layer.extend({
return this.options.tileSize;
},
_update: function () {
if (!this._map) { return; }
_move: function () {
this._update();
this._pruneTiles();
},
_update: function (center, zoom) {
var map = this._map;
if (!map) { return; }
// TODO move to reset
// var zoom = this._map.getZoom();
@ -353,38 +390,30 @@ L.GridLayer = L.Layer.extend({
// if (zoom > this.options.maxZoom ||
// 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) {
this._removeOtherTiles(bounds);
var pixelBounds = map.getPixelBounds(center, zoom),
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
for (j = tileRange.min.y; j <= tileRange.max.y; j++) {
for (i = tileRange.min.x; i <= tileRange.max.x; i++) {
for (var j = tileRange.min.y; j <= tileRange.max.y; j++) {
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);
coords.z = this._tileZoom;
if (!this._isValidTile(coords)) { continue; }
// add tile to queue if it's not in cache or out of bounds
if (!(this._tileCoordsToKey(coords) in this._tiles) && this._isValidTile(coords)) {
var tile = this._tiles[this._tileCoordsToKey(coords)];
if (tile) {
tile.current = true;
} else {
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
queue.sort(function (a, b) {
return a.distanceTo(center) - b.distanceTo(center);
return a.distanceTo(tileCenter) - b.distanceTo(tileCenter);
});
var tilesToLoad = queue.length;
if (tilesToLoad !== 0) {
if (queue.length !== 0) {
// if its the first batch of tiles to load
if (!this._tilesToLoad) {
if (this._noTilesToLoad()) {
this.fire('loading');
}
this._tilesToLoad += tilesToLoad;
// create DOM fragment to append tiles in one batch
var fragment = document.createDocumentFragment();
for (i = 0; i < tilesToLoad; i++) {
for (i = 0; i < queue.length; i++) {
this._addTile(queue[i], fragment);
}
this._level.el.appendChild(fragment);
}
this._pruneTiles();
},
_isValidTile: function (coords) {
@ -468,27 +490,16 @@ L.GridLayer = L.Layer.extend({
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) {
var tile = this._tiles[key];
if (!tile) { return; }
L.DomUtil.remove(tile);
L.DomUtil.remove(tile.el);
delete this._tiles[key];
delete this._loaded[key];
this.fire('tileunload', {
tile: tile,
tile: tile.el,
coords: this._keyToTileCoords(key)
});
},
@ -534,7 +545,11 @@ L.GridLayer = L.Layer.extend({
L.DomUtil.setPosition(tile, tilePos, true);
// save tile in cache
this._tiles[key] = tile;
this._tiles[key] = {
el: tile,
coords: coords,
current: true
};
container.appendChild(tile);
this.fire('tileloadstart', {
@ -544,6 +559,8 @@ L.GridLayer = L.Layer.extend({
},
_tileReady: function (coords, err, tile) {
if (!this._map) { return; }
if (err) {
this.fire('tileerror', {
error: err,
@ -554,21 +571,27 @@ L.GridLayer = L.Layer.extend({
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();
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', {
tile: tile,
tile: tile.el,
coords: coords
});
this._tilesToLoad--;
if (this._tilesToLoad === 0) {
if (this._noTilesToLoad()) {
this.fire('load');
}
},
@ -591,8 +614,11 @@ L.GridLayer = L.Layer.extend({
bounds.max.divideBy(this._tileSize).ceil().subtract([1, 1]));
},
_animateZoom: function (e) {
this._setZoomTransforms(e.center, e.zoom);
_noTilesToLoad: function () {
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 () {
var map = this._map,
options = this.options,
zoom = map.getZoom() + options.zoomOffset,
zoom = this._tileZoom + options.zoomOffset,
zoomN = options.maxNativeZoom;
// increase tile size when overscaling
return zoomN !== null && zoom > zoomN ?
Math.round(map.getZoomScale(zoomN, zoom) * options.tileSize) :
Math.round(options.tileSize / map.getZoomScale(zoomN, zoom)) :
options.tileSize;
},
_onTileRemove: function (e) {
e.tile.onload = null;
e.tile.src = L.Util.emptyImageUrl;
},
_getZoomForUrl: function () {
@ -136,7 +135,7 @@ L.TileLayer = L.GridLayer.extend({
_abortLoading: function () {
var i, tile;
for (i in this._tiles) {
tile = this._tiles[i];
tile = this._tiles[i].el;
tile.onload = L.Util.falseFn;
tile.onerror = L.Util.falseFn;

View File

@ -169,7 +169,7 @@ L.Canvas = L.Renderer.extend({
ctx.fill(options.fillRule || 'evenodd');
}
if (options.stroke) {
if (options.stroke && options.weight !== 0) {
ctx.globalAlpha = clear ? 1 : options.opacity;
// 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) {
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 (!layer._mouseInside) {
L.DomUtil.addClass(this._container, 'leaflet-interactive'); // change cursor
layer._fireMouseEvent(e, 'mouseover');
this._fireEvent(layer, e, 'mouseover');
layer._mouseInside = true;
}
// fire mousemove
layer._fireMouseEvent(e);
this._fireEvent(layer, e);
} else if (layer._mouseInside) {
// if we're leaving the layer, fire mouseout
L.DomUtil.removeClass(this._container, 'leaflet-interactive');
layer._fireMouseEvent(e, 'mouseout');
this._fireEvent(layer, e, 'mouseout');
layer._mouseInside = false;
}
},
_fireEvent: function (layer, e, type) {
this._map._fireDOMEvent(layer, e, type || e.type);
},
// TODO _bringToFront & _bringToBack, pretty tricky
_bringToFront: L.Util.falseFn,

View File

@ -74,10 +74,6 @@ L.Path = L.Layer.extend({
return this;
},
_fireMouseEvent: function (e, type) {
this._map._fireMouseEvent(this, e, type, true);
},
_clickTolerance: function () {
// used when doing hit detection for Canvas layers
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 = [];
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) {
this._parts.push(clipped);
}

View File

@ -179,7 +179,7 @@ L.Polyline = L.Path.extend({
points = this._rings[i];
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; }

View File

@ -64,7 +64,7 @@ L.Renderer = L.Layer.extend({
L.Map.include({
// used by each vector layer to decide which renderer to use
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) {
renderer = this._renderer = (L.SVG && L.svg()) || (L.Canvas && L.canvas());
@ -74,5 +74,18 @@ L.Map.include({
this.addLayer(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 () {
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
this._container.setAttribute('pointer-events', 'none');
},
@ -54,15 +51,13 @@ L.SVG = L.Renderer.extend({
},
_addPath: function (layer) {
var path = layer._path;
this._container.appendChild(path);
this._paths[L.stamp(path)] = layer;
this._container.appendChild(layer._path);
layer.addInteractiveTarget(layer._path);
},
_removePath: function (layer) {
var path = layer._path;
L.DomUtil.remove(path);
delete this._paths[L.stamp(path)];
L.DomUtil.remove(layer._path);
layer.removeInteractiveTarget(layer._path);
},
_updatePath: function (layer) {
@ -139,19 +134,6 @@ L.SVG = L.Renderer.extend({
_bringToBack: function (layer) {
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 () {
this._initEvents('off');
this._initEvents(true);
try {
// throws error in IE6-8
@ -328,8 +328,8 @@ L.Map = L.Evented.extend({
return this._size.clone();
},
getPixelBounds: function () {
var topLeftPoint = this._getTopLeftPoint();
getPixelBounds: function (center, zoom) {
var topLeftPoint = this._getTopLeftPoint(center, zoom);
return new L.Bounds(topLeftPoint, topLeftPoint.add(this.getSize()));
},
@ -470,6 +470,7 @@ L.Map = L.Evented.extend({
_initPanes: function () {
var panes = this._panes = {};
this._paneRenderers = {};
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; }
onOff = onOff || 'on';
this._targets = {};
L.DomEvent[onOff](this._container,
'click dblclick mousedown mouseup mouseenter mouseleave mousemove contextmenu',
this._handleMouseEvent, this);
var onOff = remove ? 'off' : 'on';
L.DomEvent[onOff](this._container, 'click dblclick mousedown mouseup ' +
'mouseover mouseout mousemove contextmenu keypress', this._handleDOMEvent, this);
if (this.options.trackResize) {
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);
},
_handleMouseEvent: function (e) {
if (!this._loaded) { return; }
_handleDOMEvent: function (e) {
if (!this._loaded || L.DomEvent._skipped(e)) { return; }
this._fireMouseEvent(this, e,
e.type === 'mouseenter' ? 'mouseover' :
e.type === 'mouseleave' ? 'mouseout' : e.type);
// find the layer the event is propagating from
var target = this._targets[L.stamp(e.target || e.srcElement)],
type = e.type === 'keypress' && e.keyCode === 13 ? 'click' : e.type;
// special case for map mouseover/mouseout events so that they're actually mouseenter/mouseleave
if (!target && (type === 'mouseover' || type === 'mouseout') &&
!L.DomEvent._checkMouse(this._container, e)) { return; }
// prevents outline when clicking on keyboard-focusable element
if (type === 'mousedown') {
L.DomUtil.preventOutline(e.target || e.srcElement);
}
this._fireDOMEvent(target || this, e, type);
},
_fireMouseEvent: function (obj, e, type, propagate, latlng) {
type = type || e.type;
if (L.DomEvent._skipped(e)) { return; }
if (type === 'click') {
var draggableObj = obj.options.draggable === true ? obj : this;
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; }
_fireDOMEvent: function (target, e, type) {
if (!target.listens(type, true) && (type !== 'click' || !target.listens('preclick', true))) { return; }
if (type === 'contextmenu') {
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 = {
originalEvent: e,
containerPoint: this.mouseEventToContainerPoint(e)
originalEvent: 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 = latlng || this.layerPointToLatLng(data.layerPoint);
data.latlng = this.layerPointToLatLng(data.layerPoint);
}
if (type === 'click') {
target.fire('preclick', data, true);
}
target.fire(type, data, true);
},
obj.fire(type, data, propagate);
_draggableMoved: function (obj) {
obj = obj.options.draggable ? obj : this;
return (obj.dragging && obj.dragging.moved()) || (this.boxZoom && this.boxZoom.moved());
},
_clearHandlers: function () {
@ -636,8 +645,11 @@ L.Map = L.Evented.extend({
return pos && !pos.equals([0, 0]);
},
_getTopLeftPoint: function () {
return this.getPixelOrigin().subtract(this._getMapPanePos());
_getTopLeftPoint: function (center, zoom) {
var pixelOrigin = center && zoom !== undefined ?
this._getNewPixelOrigin(center, zoom) :
this.getPixelOrigin();
return pixelOrigin.subtract(this._getMapPanePos());
},
_getNewPixelOrigin: function (center, zoom) {

View File

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

View File

@ -96,6 +96,6 @@ L.Map.include({
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.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));
// 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.on('load moveend', function () {
@ -81,7 +89,7 @@ L.Map.include(!zoomAnimated ? {} : {
return true;
},
_animateZoom: function (center, zoom, startAnim) {
_animateZoom: function (center, zoom, startAnim, noUpdate) {
if (startAnim) {
this._animatingZoom = true;
@ -97,7 +105,8 @@ L.Map.include(!zoomAnimated ? {} : {
zoom: zoom,
scale: this.getZoomScale(zoom),
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) {
options = this._locateOptions = L.extend(this._defaultLocateOptions, options);
options = this._locateOptions = L.extend({}, this._defaultLocateOptions, options);
if (!navigator.geolocation) {
this._handleGeolocationError({

View File

@ -27,7 +27,7 @@ L.Map.BoxZoom = L.Handler.extend({
},
_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;
@ -83,7 +83,7 @@ L.Map.BoxZoom = L.Handler.extend({
},
_onMouseUp: function (e) {
if ((e.which !== 1) && (e.button !== 1)) { return false; }
if ((e.which !== 1) && (e.button !== 0)) { return; }
this._finish();

View File

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

View File

@ -80,9 +80,12 @@ L.Map.TouchZoom = L.Handler.extend({
} else {
this._center = map.layerPointToLatLng(this._getTargetCenter());
}
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 () {
@ -103,7 +106,7 @@ L.Map.TouchZoom = L.Handler.extend({
zoomDelta = this._zoom - oldZoom,
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 () {