Merge branch 'oldie-fixes' of http://github.com/danzel/Leaflet into oldie-fixes

This commit is contained in:
javi 2013-03-14 12:50:34 +01:00
commit eea61a6a56
70 changed files with 1775 additions and 1321 deletions

View File

@ -7,12 +7,21 @@ Leaflet Changelog
An in-progress version being developed on the master branch.
### Breaking changes
* Moved polyline editing code into [Leaflet.draw](https://github.com/Leaflet/Leaflet.draw) plugin (where it fits much better along with all other editing and drawing handlers). The API remains the same.
### Improvements
#### Usability and performance improvements
* **Improved zoom control design** once more - cleaner, simpler, more accessible (mostly by [@jacobtoye](https://github.com/jacobtoye)). [#1313](https://github.com/Leaflet/Leaflet/issues/1313)
* Improved `TileLayer` zoom animation to eliminate flickering in case one tile layer on top of another or when zooming several times quickly. [#1140](https://github.com/Leaflet/Leaflet/issues/1140) [#1437](https://github.com/Leaflet/Leaflet/issues/1437) [#52](https://github.com/Leaflet/Leaflet/issues/52)
* Significantly improved mass layer removal performance (by [@jfgirard](https://github.com/jfgirard)). [#1141](https://github.com/Leaflet/Leaflet/pull/1141)
* Improved paths with `clickable: false` to allow mouse events to pass through to objects underneath (by [@snkashis](https://github.com/snkashis)). [#1384](https://github.com/Leaflet/Leaflet/pull/1384) [#1281](https://github.com/Leaflet/Leaflet/issues/1281)
* Improved attribution control to be much less obtrusive (no "powered by", just a Leaflet link). You can still remove the prefix with `map.attributionControl.setPrefix('')` if you need.
* Improved zoom behavior so that there's no drift of coordinates when you change zoom back and forth without panning. [#426](https://github.com/Leaflet/Leaflet/issues/426)
* Improved box zoom to be cancelable by pressing Escape (by [@yohanboniface](https://github.com/yohanboniface)). [#1438](https://github.com/Leaflet/Leaflet/issues/1438)
#### API improvements
@ -24,6 +33,13 @@ An in-progress version being developed on the master branch.
* Added `Path` `pointerEvents` option for setting pointer-events on SVG-powered vector layers (by [@inpursuit](https://github.com/inpursuit)). [#1053](https://github.com/Leaflet/Leaflet/pull/1053)
* Added `LatLngBounds` `getNorth`, `getEast`, `getSouth`, `getWest` methods (by [@yohanboniface](https://github.com/yohanboniface)). [#1318](https://github.com/Leaflet/Leaflet/issues/1318)
* Updated `TileLayer.Canvas` `redraw` method to return `this` (by [@jieter](https://github.com/jieter)). [#1287](https://github.com/Leaflet/Leaflet/pull/1287)
* Improved `Marker` and `Path` `bindPopup` method to also accept `Popup` objects (by [@snkashis](https://github.com/snkashis)). [#1385](https://github.com/Leaflet/Leaflet/pull/1385) [#1208](https://github.com/Leaflet/Leaflet/issues/1208) [#1402](https://github.com/Leaflet/Leaflet/pull/1402)
* Added `Map` `zoomlevelschange` event that triggers when the current zoom range (min/max) changes (by [@moonlite](https://github.com/moonlite)). [#1376](https://github.com/Leaflet/Leaflet/pull/1376)
* Added `Marker` `setPopupContent` method (by [@snkashis](https://github.com/snkashis)). [#1373](https://github.com/Leaflet/Leaflet/pull/1373)
* Added `Control` `getContainer` method. [#1409](https://github.com/Leaflet/Leaflet/issues/1409)
* Added ability to pass coordinates as simple objects (`{lat: 50, lon: 30}` or `{lat: 50, lng: 30}`). [#1412](https://github.com/Leaflet/Leaflet/issues/1412)
* Added `Map` `remove` method to properly destroy the map and clean up all events (by [@jfirebaugh](https://github.com/jfirebaugh)). [#1434](https://github.com/Leaflet/Leaflet/issues/1434) [#1101](https://github.com/Leaflet/Leaflet/issues/1101)
* Added `TileLayer` `getContainer` method (by [@tmcw](https://github.com/tmcw)). [#1433](https://github.com/Leaflet/Leaflet/pull/1433)
#### Development workflow improvements
@ -32,15 +48,33 @@ An in-progress version being developed on the master branch.
### Bugfixes
#### General bugfixes
* Fixed a bug where clicking on a marker with an open popup caused the popup to faded in again (by [@snkashis](https://github.com/snkashis)). [#506](https://github.com/Leaflet/Leaflet/issues/560) [#1386](https://github.com/Leaflet/Leaflet/pull/1386)
* Fixed a bug where zoom buttons disabled state didn't update on min/max zoom change (by [@snkashis](https://github.com/snkashis)). [#1372](https://github.com/Leaflet/Leaflet/pull/1372) [#1328](https://github.com/Leaflet/Leaflet/issues/1328)
#### Browser bugfixes
* Fixed a bug in Android WebView where click was triggered twice on one tap (by [@jerel](https://github.com/jerel)). [#1227](https://github.com/Leaflet/Leaflet/pull/1227) [#1263](https://github.com/Leaflet/Leaflet/issues/1263)
* Fixed a bug where `TileLayer` opacity didn't work in IE 7-8 (by [@javisantana](https://github.com/javisantana) & [@danzel](https://gi
.com/danzel)). [#1084](https://github.com/Leaflet/Leaflet/issues/1084) [#1396](https://github.com/Leaflet/Leaflet/pull/1396) [#1371](https://github.com/Leaflet/Leaflet/issues/1371)
* Fixed a bug in Android WebView where click was triggered twice on one tap (by [@jerel](https://github.com/jerel)). [#1227](https://github.com/Leaflet/Leaflet/pull/1227) [#1263](https://github.com/Leaflet/Leaflet/issues/1263)
* Fixed a bug where mouse coordinates where shifted in Firefox if the map was inside a positioned block on a scrolled page (by [@joschka](https://github.com/joschka)). [#1365](https://github.com/Leaflet/Leaflet/pull/1365) [#1322](https://github.com/Leaflet/Leaflet/issues/1322)
* Fixed a bug where box zoom didn't work in some cases in Firefox 18+ (by [@fabriceds](https://github.com/fabriceds)). [#1405](https://github.com/Leaflet/Leaflet/pull/1405)
* Fixed a bug where tile layer z-index order sometimes broke after view reset. [#1422](https://github.com/Leaflet/Leaflet/issues/1422)
#### API bugfixes
* Fixed a bug where default marker icon path wasn't properly detected in some cases in IE6-7 (by [@calvinmetcalf](https://github.com/calvinmetcalf)). [#1294](https://github.com/Leaflet/Leaflet/pull/1294)
* Fixed a bug where `Map` `hasLayer` wasn't handling `null` objects (by [@rvlasveld](https://github.com/rvlasveld)). [#1282](https://github.com/Leaflet/Leaflet/issues/1282) [#1302](https://github.com/Leaflet/Leaflet/pull/1302)
* Fixed a bug where `TileLayer.WMS` param values weren't escaped in URLs (by [@yohanboniface](https://github.com/yohanboniface)). [#1317](https://github.com/Leaflet/Leaflet/issues/1317)
* Fixed a bug where `Map` `moveend` fired before `dragend` on drag (by [@oslek](https://github.com/oslek)). [#1374](https://github.com/Leaflet/Leaflet/pull/1374)
* Fixed a bug where panning with inertia produced an excessive `Map` `movestart` event on inertia start (by [@oslek](https://github.com/oslek)). [#1374](https://github.com/Leaflet/Leaflet/pull/1374)
* Fixed a bug where `Map` `moveend` fired repeatedly on window resize even if the actual map size didn't change (by [@oslek](https://github.com/oslek)). [#1374](https://github.com/Leaflet/Leaflet/pull/1374)
* Fixed a bug where `L.point` and `L.latLng` factories weren't passing `null` and `undefined` values through.
* Fixed a bug where layers that belong to multiple feature groups didn't propagate events correctly (by [@danzel](https://github.com/danzel)). [#1359](https://github.com/Leaflet/Leaflet/pull/1359)
* Fixed a bug where `Control.Attribution` `removeAttribution` of inexistant attribution corrupted the attribution text. [#1410](https://github.com/Leaflet/Leaflet/issues/1410)
* Fixed a bug where `TileLayer.WMS` `tileSize` option was ignored (by [@brianhatchl](https://github.com/brianhatchl)). [#1080](https://github.com/brianhatchl)
* Fixed a bug where `Polyline` constructor could overwrite the source array (by [@snkashis](https://github.com/snkashis) and [@danzel](https://github.com/danzel)). [#1439](https://github.com/Leaflet/Leaflet/pull/1439) [#1092](https://github.com/Leaflet/Leaflet/issues/1092) [#1246](https://github.com/Leaflet/Leaflet/issues/1246) [#1426](https://github.com/Leaflet/Leaflet/issues/1426)
## 0.5.1 (February 6, 2013)

View File

@ -86,6 +86,8 @@ Before commiting your changes, run `jake lint` to catch any JS errors in the cod
If you add any new files to the Leaflet source, make sure to also add them to `build/deps.js`
so that the build system knows about them.
Also, please make sure that you have [line endings configured properly](https://help.github.com/articles/dealing-with-line-endings) in Git! Otherwise the diff will show that all lines of a file were changed even if you touched only one.
Happy coding!
## Improving Documentation

View File

@ -22,7 +22,6 @@ var deps = {
desc: 'The core of the library, including OOP, events, DOM facilities, basic units, projections (EPSG:3857 and EPSG:4326) and the base Map class.'
},
EPSG3395: {
src: ['geo/projection/Projection.Mercator.js',
'geo/crs/CRS.EPSG3395.js'],
@ -85,7 +84,6 @@ var deps = {
desc: 'Extends LayerGroup with mouse events and bindPopup method shared between layers.'
},
Path: {
src: ['layer/vector/Path.js',
'layer/vector/Path.SVG.js',
@ -201,13 +199,6 @@ var deps = {
desc: 'Makes markers draggable (by mouse or touch).'
},
PolyEdit: {
src: ['layer/vector/Polyline.Edit.js'],
deps: ['Polyline', 'DivIcon'],
desc: 'Polyline and polygon editing.'
},
ControlZoom: {
src: ['control/Control.js',
'map/ext/Map.Control.js',
@ -237,7 +228,6 @@ var deps = {
desc: 'Layer Switcher control.'
},
AnimationPan: {
src: [
'dom/DomEvent.js',
@ -255,7 +245,7 @@ var deps = {
},
AnimationZoom: {
src: ['map/anim/Map.ZoomAnimation.js'],
src: ['map/anim/Map.ZoomAnimation.js', 'layer/tile/TileLayer.Anim.js'],
deps: ['AnimationPan'],
desc: 'Smooth zooming animation. Works only on browsers that support CSS3 Transitions.'
},

View File

@ -14,11 +14,16 @@
<script>
L_PREFER_CANVAS = true;
$(document).ready(function() {
var cloudmadeUrl = 'http://{s}.tile.cloudmade.com/BC9A493B41014CAABB98F0471D759707/997/256/{z}/{x}/{y}.png',
cloudmadeAttribution = 'Map data &copy; 2011 OpenStreetMap contributors, Imagery &copy; 2011 CloudMade',
cloudmade = new L.TileLayer(cloudmadeUrl, {maxZoom: 18, attribution: cloudmadeAttribution});
var map = L.map('map', {
minZoom: 1,
maxZoom: 19,
center: [51.505, -0.09],
zoom: 9
zoom: 9,
layers: [cloudmade]
});
var polygons = new L.FeatureGroup();
@ -27,10 +32,11 @@
polygons.addLayer(
new L.Polyline(
points, {
weight: 2,
weight: 10,
opacity: 1,
smoothFactor: 1,
color: 'red'
color: 'red',
clickable:true
}));
polygons.on('click', function(m) {
@ -43,6 +49,8 @@
});
</script>
<script type="text/javascript" src="../../build/deps.js"></script>
<script src="../leaflet-include.js"></script>
</head>
<body>

View File

@ -0,0 +1,29 @@
<!DOCTYPE html>
<html>
<head>
<title>Leaflet debug page</title>
<link rel="stylesheet" href="../../dist/leaflet.css" />
<!--[if lte IE 8]><link rel="stylesheet" href="../../dist/leaflet.ie.css" /><![endif]-->
<link rel="stylesheet" href="../css/screen.css" />
<script type="text/javascript" src="../../build/deps.js"></script>
<script src="../leaflet-include.js"></script>
</head>
<body>
<div id="map" style="width: 600px; height: 600px; border: 1px solid #ccc"></div>
<script type="text/javascript">
var map = L.map('map').setView( [50, 50], 10);
var marker = L.marker([50, 50], {draggable: true}).addTo(map);
setTimeout(function() {
map.removeLayer(marker);
}, 3000);
</script>
</body>
</html>

View File

@ -0,0 +1,41 @@
<!DOCTYPE html>
<html>
<head>
<title>Leaflet debug page</title>
<link rel="stylesheet" href="../../dist/leaflet.css" />
<!--[if lte IE 8]><link rel="stylesheet" href="../../dist/leaflet.ie.css" /><![endif]-->
<link rel="stylesheet" href="../css/screen.css" />
<script type="text/javascript" src="../../build/deps.js"></script>
<script src="../leaflet-include.js"></script>
</head>
<body>
<div id="map" style="width: 800px; height: 600px; border: 1px solid #ccc"></div>
<script>
var map = L.map('map');
var marker = L.marker([51.5, -0.09])
.bindPopup("<b>Hello world!</b><br />I am a popup.")
.addTo(map);
//.openPopup();
var marker2 = L.marker([51.525, -0.09])
.addTo(map);
map.setView([51.505, -0.09], 13);
var cloudmade = L.tileLayer('http://{s}.tile.cloudmade.com/{key}/{styleId}/256/{z}/{x}/{y}.png', {
attribution: 'Map data &copy; 2011 OpenStreetMap contributors, Imagery &copy; 2011 CloudMade',
maxZoom: 18,
key: 'BC9A493B41014CAABB98F0471D759707',
styleId: 997
}).addTo(map);
var a_popup = L.popup().setContent('Previously created')
marker2.bindPopup(a_popup);
</script>
</body>
</html>

View File

@ -0,0 +1,55 @@
<!DOCTYPE html>
<html>
<head>
<title>Leaflet debug page</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<link rel="stylesheet" href="../../dist/leaflet.css" />
<!--[if lte IE 8]><link rel="stylesheet" href="../../dist/leaflet.ie.css" /><![endif]-->
<link rel="stylesheet" href="../css/screen.css" />
<script type="text/javascript" src="../../build/deps.js"></script>
<script src="../leaflet-include.js"></script>
<script type='text/javascript' src='http://code.jquery.com/jquery-1.8.0.js'></script>
</head>
<body>
<div id="map"></div>
<script type="text/javascript">
var map;
var myLayerGroup = new L.LayerGroup();
initmap();
function initmap() {
// set up the map
map = new L.Map('map');
// create the tile layer with correct attribution
var osmUrl = 'http://a.tile.openstreetmap.org/{z}/{x}/{y}.png';
var osm = new L.TileLayer(osmUrl, { minZoom: 1, maxZoom: 17 });
map.addLayer(osm);
map.fitBounds(new L.LatLngBounds([51,7],[51,7]));
var route = L.polyline([
[51, 7.000],
[51.002, 7.004],
[51.004, 7.006]
]).addTo(map).on('click',function(e){console.log('bottom')})
var route2 = L.polyline([
[51, 7.000],
[51.002, 7.004]
],
{ clickable:false,color:'#f00' }
).addTo(map);
// when the mouse hovers over the red route2, you cannot click through the blue route1 beneath
};
</script>
</body>
</html>

View File

@ -1,53 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title>Leaflet debug page</title>
<link rel="stylesheet" href="../../dist/leaflet.css" />
<!--[if lte IE 8]><link rel="stylesheet" href="../../dist/leaflet.ie.css" /><![endif]-->
<link rel="stylesheet" href="../css/screen.css" />
<script type="text/javascript" src="../../build/deps.js"></script>
<script src="../leaflet-include.js"></script>
</head>
<body>
<div id="map" style="width: 800px; height: 600px; border: 1px solid #ccc"></div>
<script src="route.js"></script>
<script>
var cloudmadeUrl = 'http://{s}.tile.cloudmade.com/BC9A493B41014CAABB98F0471D759707/997/256/{z}/{x}/{y}.png',
cloudmade = new L.TileLayer(cloudmadeUrl, {maxZoom: 18}),
map = new L.Map('map', {layers: [cloudmade], center: new L.LatLng(51.505, -0.04), zoom: 13});
var polygon = new L.Polygon([
[51.51, -0.1],
[51.5, -0.06],
[51.52, -0.03]
]);
polygon.editing.enable();
map.addLayer(polygon);
var polyline = new L.Polyline([
[51.50, -0.04],
[51.49, -0.02],
[51.51, 0],
[51.52, -0.02]
]);
polyline.editing.enable();
map.addLayer(polyline);
polygon.on('edit', function() {
console.log('Polygon was edited!');
});
polyline.on('edit', function() {
console.log('Polyline was edited!');
});
</script>
</body>
</html>

898
dist/leaflet-src.js vendored

File diff suppressed because it is too large Load Diff

30
dist/leaflet.css vendored
View File

@ -5,6 +5,7 @@
.leaflet-marker-icon,
.leaflet-marker-shadow,
.leaflet-tile-pane,
.leaflet-tile-container,
.leaflet-overlay-pane,
.leaflet-shadow-pane,
.leaflet-marker-pane,
@ -27,6 +28,7 @@
-webkit-user-select: none;
-moz-user-select: none;
user-select: none;
-webkit-user-drag: none;
}
.leaflet-marker-icon,
.leaflet-marker-shadow {
@ -216,12 +218,16 @@
background-color: #f4f4f4;
}
.leaflet-bar a:first-child {
-webkit-border-radius: 4px 4px 0 0;
border-radius: 4px 4px 0 0;
-webkit-border-top-left-radius: 4px;
border-top-left-radius: 4px;
-webkit-border-top-right-radius: 4px;
border-top-right-radius: 4px;
}
.leaflet-bar a:last-child {
-webkit-border-radius: 0 0 4px 4px;
border-radius: 0 0 4px 4px;
-webkit-border-bottom-left-radius: 4px;
border-bottom-left-radius: 4px;
-webkit-border-bottom-right-radius: 4px;
border-bottom-right-radius: 4px;
border-bottom: none;
}
.leaflet-bar a.leaflet-disabled {
@ -239,22 +245,22 @@
height: 30px;
}
.leaflet-touch .leaflet-bar a:first-child {
-webkit-border-radius: 7px 7px 0 0;
border-radius: 7px 7px 0 0;
-webkit-border-top-left-radius: 7px;
border-top-left-radius: 7px;
-webkit-border-top-right-radius: 7px;
border-top-right-radius: 7px;
}
.leaflet-touch .leaflet-bar a:last-child {
-webkit-border-radius: 0 0 7px 7px;
border-radius: 0 0 7px 7px;
-webkit-border-bottom-left-radius: 7px;
border-bottom-left-radius: 7px;
-webkit-border-bottom-right-radius: 7px;
border-bottom-right-radius: 7px;
border-bottom: none;
}
/* zoom control */
.leaflet-container .leaflet-control-zoom {
margin-left: 13px;
margin-top: 12px;
}
.leaflet-control-zoom-in {
font: bold 18px 'Lucida Console', Monaco, monospace;
}

8
dist/leaflet.js vendored

File diff suppressed because one or more lines are too long

View File

@ -2,12 +2,13 @@
"name": "Leaflet",
"version": "0.6.0",
"description": "JavaScript library for mobile-friendly interactive maps",
"dependencies": {
"devDependencies": {
"jshint": "~0.9.1",
"testacular": "latest",
"uglify-js": "~2.2.3",
"jake": "latest"
},
"main": "dist/leaflet.js",
"scripts": {
"test": "node ./node_modules/jake/bin/cli test"
},

View File

@ -8,7 +8,7 @@
<script type="text/javascript" src="happen.js"></script>
<!-- source files -->
<script type="text/javascript" src="context.js"></script>
<script type="text/javascript" src="before.js"></script>
<script type="text/javascript" src="../build/deps.js"></script>
<script type="text/javascript" src="../debug/leaflet-include.js"></script>
@ -22,6 +22,7 @@
<!-- /control -->
<script type="text/javascript" src="suites/control/Control.LayersSpec.js"></script>
<script type="text/javascript" src="suites/control/Control.ScaleSpec.js"></script>
<script type="text/javascript" src="suites/control/Control.AttributionSpec.js"></script>
<!-- /core -->
<script type="text/javascript" src="suites/core/UtilSpec.js"></script>
@ -32,6 +33,8 @@
<script type="text/javascript" src="suites/geometry/PointSpec.js"></script>
<script type="text/javascript" src="suites/geometry/BoundsSpec.js"></script>
<script type="text/javascript" src="suites/geometry/TransformationSpec.js"></script>
<script type="text/javascript" src="suites/geometry/LineUtilSpec.js"></script>
<script type="text/javascript" src="suites/geometry/PolyUtilSpec.js"></script>
<!-- /geo -->
<script type="text/javascript" src="suites/geo/LatLngSpec.js"></script>
@ -44,11 +47,14 @@
<!-- /layer -->
<script type="text/javascript" src="suites/layer/FeatureGroupSpec.js"></script>
<script type="text/javascript" src="suites/layer/LayerGroupSpec.js"></script>
<script type="text/javascript" src="suites/layer/TileLayerSpec.js"></script>
<!-- /layer/vector/ -->
<script type="text/javascript" src="suites/layer/vector/CircleSpec.js"></script>
<script type="text/javascript" src="suites/layer/vector/CircleMarkerSpec.js"></script>
<script type="text/javascript" src="suites/layer/vector/PolygonSpec.js"></script>
<script type="text/javascript" src="suites/layer/vector/PolylineSpec.js"></script>
<script type="text/javascript" src="suites/layer/vector/PolylineGeometrySpec.js"></script>
<!-- /map -->

View File

@ -1,5 +1,5 @@
describe('L#noConflict', function() {
it('should restore the previous L value and return Leaflet namespace', function(){
it('restores the previous L value and returns Leaflet namespace', function(){
expect(L.version).toBeDefined();

View File

@ -1,5 +1,5 @@
function noSpecs() {
xit('should have specs');
xit('has no specs');
}
if (!Array.prototype.map) {

View File

@ -0,0 +1,68 @@
describe("Control.Attribution", function () {
var map, control, container;
beforeEach(function () {
map = L.map(document.createElement('div'));
control = new L.Control.Attribution({
prefix: 'prefix'
}).addTo(map);
container = control.getContainer();
});
it("contains just prefix if no attributions added", function () {
expect(container.innerHTML).toEqual('prefix');
});
describe('#addAttribution', function () {
it('adds one attribution correctly', function () {
control.addAttribution('foo');
expect(container.innerHTML).toEqual('prefix | foo');
});
it('adds no duplicate attributions', function () {
control.addAttribution('foo');
control.addAttribution('foo');
expect(container.innerHTML).toEqual('prefix | foo');
});
it('adds several attributions listed with comma', function () {
control.addAttribution('foo');
control.addAttribution('bar');
expect(container.innerHTML).toEqual('prefix | foo, bar');
});
});
describe('#removeAttribution', function () {
it('removes attribution correctly', function () {
control.addAttribution('foo');
control.addAttribution('bar');
control.removeAttribution('foo');
expect(container.innerHTML).toEqual('prefix | bar');
});
it('does nothing if removing attribution that was not present', function () {
control.addAttribution('foo');
control.addAttribution('baz');
control.removeAttribution('bar');
control.removeAttribution('baz');
control.removeAttribution('baz');
control.removeAttribution('');
expect(container.innerHTML).toEqual('prefix | foo');
});
});
describe('#setPrefix', function () {
it('changes prefix', function () {
control.setPrefix('bla');
expect(container.innerHTML).toEqual('bla');
});
});
describe('control.attribution factory', function () {
it('creates Control.Attribution instance', function () {
var options = {prefix: 'prefix'};
expect(L.control.attribution(options)).toEqual(new L.Control.Attribution(options));
});
});
});

View File

@ -2,5 +2,5 @@ describe("Control.Scale", function () {
it("can be added to an unloaded map", function () {
var map = L.map(document.createElement('div'));
new L.Control.Scale().addTo(map);
})
});
});

View File

@ -19,7 +19,7 @@ describe("Class", function() {
});
});
it("should create a class with the given constructor & properties", function() {
it("creates a class with the given constructor & properties", function() {
var a = new Klass();
expect(constructor).toHaveBeenCalled();
@ -30,7 +30,7 @@ describe("Class", function() {
expect(method).toHaveBeenCalled();
});
it("should inherit parent classes' constructor & properties", function() {
it("inherits parent classes' constructor & properties", function() {
var Klass2 = Klass.extend({baz: 2});
var b = new Klass2();
@ -46,28 +46,28 @@ describe("Class", function() {
expect(method).toHaveBeenCalled();
});
it("should support static properties", function() {
it("supports static properties", function() {
expect(Klass.bla).toEqual(1);
});
it("should inherit parent static properties", function() {
it("inherits parent static properties", function() {
var Klass2 = Klass.extend({});
expect(Klass2.bla).toEqual(1);
});
it("should override parent static properties", function() {
it("overrides parent static properties", function() {
var Klass2 = Klass.extend({statics: {bla: 2}});
expect(Klass2.bla).toEqual(2);
});
it("should include the given mixin", function() {
it("includes the given mixin", function() {
var a = new Klass();
expect(a.mixin).toBeTruthy();
});
it("should be able to include multiple mixins", function() {
it("includes multiple mixins", function() {
var Klass2 = L.Class.extend({
includes: [{mixin: true}, {mixin2: true}]
});
@ -77,14 +77,14 @@ describe("Class", function() {
expect(a.mixin2).toBeTruthy();
});
it("should grant the ability to include the given mixin", function() {
it("grants the ability to include the given mixin", function() {
Klass.include({mixin2: true});
var a = new Klass();
expect(a.mixin2).toBeTruthy();
});
it("should merge options instead of replacing them", function() {
it("merges options instead of replacing them", function() {
var KlassWithOptions1 = L.Class.extend({
options: {
foo1: 1,
@ -107,7 +107,7 @@ describe("Class", function() {
});
});
it("should add constructor hooks correctly", function () {
it("adds constructor hooks correctly", function () {
var spy1 = jasmine.createSpy("init hook 1");
Klass.addInitHook(spy1);
@ -119,7 +119,7 @@ describe("Class", function() {
expect(method).toHaveBeenCalledWith(1, 2, 3);
});
it("should inherit constructor hooks", function () {
it("inherits constructor hooks", function () {
var spy1 = jasmine.createSpy("init hook 1"),
spy2 = jasmine.createSpy("init hook 2");
@ -134,7 +134,7 @@ describe("Class", function() {
expect(spy2).toHaveBeenCalled();
});
it("should not call child constructor hooks", function () {
it("does not call child constructor hooks", function () {
var spy1 = jasmine.createSpy("init hook 1"),
spy2 = jasmine.createSpy("init hook 2");

View File

@ -9,7 +9,7 @@ describe('Events', function() {
describe('#fireEvent', function() {
it('should fire all listeners added through #addEventListener', function() {
it('fires all listeners added through #addEventListener', function() {
var obj = new Klass(),
spy = jasmine.createSpy(),
spy2 = jasmine.createSpy(),
@ -22,7 +22,7 @@ describe('Events', function() {
obj.addEventListener('test', spy2);
obj.addEventListener('other', spy3);
obj.addEventListener({ test: spy4, other: spy5 });
obj.addEventListener({'test other': spy6 })
obj.addEventListener({'test other': spy6 });
expect(spy).not.toHaveBeenCalled();
expect(spy2).not.toHaveBeenCalled();
@ -42,7 +42,7 @@ describe('Events', function() {
expect(spy6.calls.length).toEqual(1);
});
it('should provide event object to listeners and execute them in the right context', function() {
it('provides event object to listeners and executes them in the right context', function() {
var obj = new Klass(),
obj2 = new Klass(),
obj3 = new Klass(),
@ -88,7 +88,7 @@ describe('Events', function() {
obj4.fireEvent('test', {baz: 4});
});
it('should not call listeners removed through #removeEventListener', function() {
it('calls no listeners removed through #removeEventListener', function() {
var obj = new Klass(),
spy = jasmine.createSpy(),
spy2 = jasmine.createSpy(),
@ -127,7 +127,7 @@ describe('Events', function() {
});
// added due to context-sensitive removeListener optimization
it('should fire multiple listeners with the same context with id properly', function () {
it('fires multiple listeners with the same context with id', function () {
var obj = new Klass(),
spy = jasmine.createSpy(),
spy2 = jasmine.createSpy(),
@ -144,7 +144,7 @@ describe('Events', function() {
expect(spy2).toHaveBeenCalled();
});
it('should remove listeners with stamped contexts properly', function () {
it('removes listeners with stamped contexts', function () {
var obj = new Klass(),
spy = jasmine.createSpy(),
spy2 = jasmine.createSpy(),
@ -166,7 +166,7 @@ describe('Events', function() {
describe('#on, #off & #fire', function() {
it('should work like #addEventListener && #removeEventListener', function() {
it('works like #addEventListener && #removeEventListener', function() {
var obj = new Klass(),
spy = jasmine.createSpy();
@ -181,7 +181,7 @@ describe('Events', function() {
expect(spy.callCount).toBeLessThan(2);
});
it('should not override existing methods with the same name', function() {
it('does not override existing methods with the same name', function() {
var spy1 = jasmine.createSpy(),
spy2 = jasmine.createSpy(),
spy3 = jasmine.createSpy();

View File

@ -10,7 +10,7 @@ describe('Util', function() {
};
});
it('should extend the first argument with the properties of the second', function() {
it('extends the first argument with the properties of the second', function() {
L.Util.extend(a, {
bar: 7,
baz: 3
@ -23,7 +23,7 @@ describe('Util', function() {
});
});
it('should work with more than 2 arguments', function() {
it('accepts more than 2 arguments', function() {
L.Util.extend(a, {bar: 7}, {baz: 3});
expect(a).toEqual({
@ -35,7 +35,7 @@ describe('Util', function() {
});
describe('#bind', function() {
it('should return the given function with the given context', function() {
it('returns the given function with the given context', function() {
var fn = function() {
return this;
};
@ -45,7 +45,7 @@ describe('Util', function() {
expect(fn2()).toEqual(5);
});
it('should pass additional arguments to the bound function', function () {
it('passes additional arguments to the bound function', function () {
var fn = jasmine.createSpy(),
foo = {},
a = {},
@ -60,7 +60,7 @@ describe('Util', function() {
});
describe('#stamp', function() {
it('should set a unique id on the given object and return it', function() {
it('sets a unique id on the given object and returns it', function() {
var a = {},
id = L.Util.stamp(a);
@ -75,13 +75,13 @@ describe('Util', function() {
});
describe('#falseFn', function () {
it('should just return false', function () {
it('returns false', function () {
expect(L.Util.falseFn()).toBe(false);
});
});
describe('#formatNum', function () {
it('should format numbers with a given precision', function () {
it('formats numbers with a given precision', function () {
expect(L.Util.formatNum(13.12325555, 3)).toEqual(13.123);
expect(L.Util.formatNum(13.12325555)).toEqual(13.12326);
});
@ -89,7 +89,7 @@ describe('Util', function() {
describe('#getParamString', function() {
it('should create a valid query string for appending depending on url input', function() {
it('creates a valid query string for appending depending on url input', function() {
var a = {
url: "http://example.com/get",
obj: {bar: 7, baz: 3},
@ -117,7 +117,7 @@ describe('Util', function() {
});
describe('#requestAnimFrame', function () {
it('should call a function on next frame, unless canceled', function () {
it('calles a function on next frame, unless canceled', function () {
var spy = jasmine.createSpy(),
spy2 = jasmine.createSpy(),
called = false,
@ -147,7 +147,7 @@ describe('Util', function() {
});
describe('#limitExecByInterval', function() {
it('should limit execution to not more often than specified time interval', function () {
it('limits execution to not more often than specified time interval', function () {
var spy = jasmine.createSpy(),
check = false;
@ -176,15 +176,15 @@ describe('Util', function() {
});
describe('#splitWords', function () {
it('should split words into an array', function () {
it('splits words into an array', function () {
expect(L.Util.splitWords('foo bar baz')).toEqual(['foo', 'bar', 'baz']);
})
})
});
});
// TODO setOptions
describe('#template', function () {
it('should evaluate templates with a given data object', function () {
it('evaluates templates with a given data object', function () {
var tpl = 'Hello {foo} and {bar}!';
var str = L.Util.template(tpl, {
@ -193,7 +193,13 @@ describe('Util', function() {
});
expect(str).toEqual('Hello Vlad and Dave!');
});
it('does not modify text without a token variable', function () {
expect(L.Util.template('foo', {})).toEqual('foo');
});
it('throws when a template token is not given', function () {
expect(function () {
L.Util.template(tpl, {foo: 'bar'});
}).toThrow();

View File

@ -24,7 +24,7 @@ describe('DomEvent', function() {
});
describe('#addListener', function() {
it('should add a listener and call it on event', function() {
it('adds a listener and calls it on event', function() {
var listener1 = jasmine.createSpy('listener1'),
listener2 = jasmine.createSpy('listener2');
@ -37,7 +37,7 @@ describe('DomEvent', function() {
expect(listener2).toHaveBeenCalled();
});
it('should have "this" keyword point to the given context', function() {
it('binds "this" to the given context', function() {
var obj = {foo: 'bar'},
result;
@ -50,7 +50,7 @@ describe('DomEvent', function() {
expect(result).toEqual(obj);
});
it('should pass an event object to the listener', function() {
it('passes an event object to the listener', function() {
var type;
L.DomEvent.addListener(el, 'click', function(e) {
@ -63,7 +63,7 @@ describe('DomEvent', function() {
});
describe('#removeListener', function() {
it('should remove previously added listener', function() {
it('removes a previously added listener', function() {
var listener = jasmine.createSpy('listener');
L.DomEvent.addListener(el, 'click', listener);
@ -76,7 +76,7 @@ describe('DomEvent', function() {
});
describe('#stopPropagation', function() {
it('should stop propagation of the given event', function() {
it('stops propagation of the given event', function() {
var child = document.createElement('div'),
listener = jasmine.createSpy('listener');
@ -93,7 +93,7 @@ describe('DomEvent', function() {
});
});
describe('#preventDefault', function() {
it('should prevent the default action of event', function() {
it('prevents the default action of event', function() {
L.DomEvent.addListener(el, 'click', L.DomEvent.preventDefault);
expect(simulateClick(el)).toBe(false);

View File

@ -13,18 +13,18 @@ describe('DomUtil', function() {
});
describe('#get', function() {
it('should get element by id if the given argument is string', function() {
it('gets element by id if the given argument is string', function() {
el.id = 'testId';
expect(L.DomUtil.get(el.id)).toBe(el);
});
it('should return the element if it is given as an argument', function() {
it('returns the element if it is given as an argument', function() {
expect(L.DomUtil.get(el)).toBe(el);
});
});
describe('#addClass, #removeClass, #hasClass', function() {
it('should has defined class for test element', function() {
it('has defined class for test element', function() {
el.className = 'bar foo baz ';
expect(L.DomUtil.hasClass(el, 'foo')).toBeTruthy();
expect(L.DomUtil.hasClass(el, 'bar')).toBeTruthy();
@ -32,7 +32,7 @@ describe('DomUtil', function() {
expect(L.DomUtil.hasClass(el, 'boo')).toBeFalsy();
});
it('should properly addClass and removeClass for element', function() {
it('adds or removes the class', function() {
el.className = '';
L.DomUtil.addClass(el, 'foo');
@ -50,17 +50,18 @@ describe('DomUtil', function() {
el.className = 'foo bar barz';
L.DomUtil.removeClass(el, 'bar');
expect(el.className).toEqual('foo barz');
})
});
});
describe('#documentIsLtr', function () {
it('should return true if doc direction is ltr', function () {
it('returns true if doc direction is ltr', function () {
expect(L.DomUtil.documentIsLtr()).toBe(true);
expect(L.DomUtil.documentIsLtr()).toBe(true); // cached
});
});
describe('#getViewportOffset', function () {
it('calculates the viewport offset of an element', function () {
var div = document.createElement('div');
div.style.position = 'absolute';
div.style.top = '100px';
@ -80,6 +81,7 @@ describe('DomUtil', function() {
document.body.removeChild(div);
});
});
describe('#setPosition', noSpecs);

View File

@ -9,7 +9,7 @@ describe('LatLngBounds', function() {
});
describe('constructor', function () {
it('should instantiate properly either passing two latlngs or an array of latlngs', function () {
it('instantiates either passing two latlngs or an array of latlngs', function () {
var b = new L.LatLngBounds([
new L.LatLng(14, 12),
new L.LatLng(30, 40)
@ -20,12 +20,12 @@ describe('LatLngBounds', function() {
});
describe('#extend', function () {
it('should extend the bounds by a given point', function () {
it('extends the bounds by a given point', function () {
a.extend(new L.LatLng(20, 50));
expect(a.getNorthEast()).toEqual(new L.LatLng(30, 50));
});
it('should extend the bounds by given bounds', function () {
it('extends the bounds by given bounds', function () {
a.extend([[20, 50], [8, 40]]);
expect(a.getSouthEast()).toEqual(new L.LatLng(8, 50));
@ -33,13 +33,13 @@ describe('LatLngBounds', function() {
});
describe('#getCenter', function () {
it('should return the bounds center', function () {
it('returns the bounds center', function () {
expect(a.getCenter()).toEqual(new L.LatLng(22, 26));
});
});
describe('#pad', function () {
it('should pad the bounds by a given ratio', function () {
it('pads the bounds by a given ratio', function () {
var b = a.pad(0.5);
expect(b).toEqual(L.latLngBounds([[6, -2], [38, 54]]));
@ -47,7 +47,7 @@ describe('LatLngBounds', function() {
});
describe('#equals', function () {
it('should return true if bounds equal', function () {
it('returns true if bounds equal', function () {
expect(a.equals([[14, 12], [30, 40]])).toBe(true);
expect(a.equals([[14, 13], [30, 40]])).toBe(false);
expect(a.equals(null)).toBe(false);
@ -55,79 +55,79 @@ describe('LatLngBounds', function() {
});
describe('#isValid', function() {
it('should return true if properly set up', function() {
it('returns true if properly set up', function() {
expect(a.isValid()).toBeTruthy();
});
it('should return false if is invalid', function() {
it('returns false if is invalid', function() {
expect(c.isValid()).toBeFalsy();
});
it('should be valid if extended', function() {
it('returns true if extended', function() {
c.extend([0, 0]);
expect(c.isValid()).toBeTruthy();
});
});
describe('#getWest', function () {
it('should return a proper bbox west value', function() {
it('returns a proper bbox west value', function() {
expect(a.getWest()).toEqual(12);
});
});
describe('#getSouth', function () {
it('should return a proper bbox south value', function() {
it('returns a proper bbox south value', function() {
expect(a.getSouth()).toEqual(14);
});
});
describe('#getEast', function () {
it('should return a proper bbox east value', function() {
it('returns a proper bbox east value', function() {
expect(a.getEast()).toEqual(40);
});
});
describe('#getNorth', function () {
it('should return a proper bbox north value', function() {
it('returns a proper bbox north value', function() {
expect(a.getNorth()).toEqual(30);
});
});
describe('#toBBoxString', function () {
it('should return a proper left,bottom,right,top bbox', function() {
it('returns a proper left,bottom,right,top bbox', function() {
expect(a.toBBoxString()).toEqual("12,14,40,30");
});
});
describe('#getNorthWest', function () {
it('should return a proper north-west LatLng', function() {
it('returns a proper north-west LatLng', function() {
expect(a.getNorthWest()).toEqual(new L.LatLng(a.getNorth(), a.getWest()));
});
});
describe('#getSouthEast', function () {
it('should return a proper south-east LatLng', function() {
it('returns a proper south-east LatLng', function() {
expect(a.getSouthEast()).toEqual(new L.LatLng(a.getSouth(), a.getEast()));
});
});
describe('#contains', function () {
it('should return true if contains latlng point', function () {
it('returns true if contains latlng point', function () {
expect(a.contains([16, 20])).toBe(true);
expect(L.latLngBounds(a).contains([5, 20])).toBe(false);
});
it('should accept bounds', function () {
it('returns true if contains bounds', function () {
expect(a.contains([[16, 20], [20, 40]])).toBe(true);
expect(a.contains([[16, 50], [8, 40]])).toBe(false);
});
});
describe('#intersects', function () {
it('should return true if intersects the given bounds', function () {
it('returns true if intersects the given bounds', function () {
expect(a.intersects([[16, 20], [50, 60]])).toBe(true);
expect(a.contains([[40, 50], [50, 60]])).toBe(false);
});

View File

@ -1,16 +1,16 @@
describe('LatLng', function() {
describe('constructor', function() {
it("should set lat and lng", function() {
it("sets lat and lng", function() {
var a = new L.LatLng(25, 74);
expect(a.lat).toEqual(25);
expect(a.lng).toEqual(74);
var a = new L.LatLng(-25, -74);
expect(a.lat).toEqual(-25);
expect(a.lng).toEqual(-74);
var b = new L.LatLng(-25, -74);
expect(b.lat).toEqual(-25);
expect(b.lng).toEqual(-74);
});
it('should throw error if invalid lat or lng', function () {
it('throws an error if invalid lat or lng', function () {
expect(function () {
var a = new L.LatLng(NaN, NaN);
}).toThrow();
@ -18,26 +18,26 @@ describe('LatLng', function() {
});
describe('#equals', function() {
it("should return true if compared objects are equal within a certain margin", function() {
it("returns true if compared objects are equal within a certain margin", function() {
var a = new L.LatLng(10, 20);
var b = new L.LatLng(10 + 1.0E-10, 20 - 1.0E-10);
expect(a.equals(b)).toBe(true);
});
it("should return false if compared objects are not equal within a certain margin", function() {
it("returns false if compared objects are not equal within a certain margin", function() {
var a = new L.LatLng(10, 20);
var b = new L.LatLng(10, 23.3);
expect(a.equals(b)).toBe(false);
});
it('should return false if passed non-valid object', function () {
it('returns false if passed non-valid object', function () {
var a = new L.LatLng(10, 20);
expect(a.equals(null)).toBe(false);
});
});
describe('#wrap', function () {
it("#wrap should wrap longitude to lie between -180 and 180 by default", function() {
it("wraps longitude to lie between -180 and 180 by default", function() {
var a = new L.LatLng(0, 190).wrap().lng;
expect(a).toEqual(-170);
@ -63,7 +63,7 @@ describe('LatLng', function() {
expect(h).toEqual(180);
});
it("#wrap should wrap longitude within the given range", function() {
it("wraps longitude within the given range", function() {
var a = new L.LatLng(0, 190).wrap(-100, 100).lng;
expect(a).toEqual(-10);
});
@ -71,14 +71,14 @@ describe('LatLng', function() {
});
describe('#toString', function () {
it('should format to string', function () {
it('formats a string', function () {
var a = new L.LatLng(10.333333333, 20.2222222);
expect(a.toString(3)).toEqual('LatLng(10.333, 20.222)');
});
});
describe('#distanceTo', function () {
it('should calculate distance in meters', function () {
it('calculates distance in meters', function () {
var a = new L.LatLng(50.5, 30.5);
var b = new L.LatLng(50, 1);
@ -87,24 +87,32 @@ describe('LatLng', function() {
});
describe('L.latLng factory', function () {
it('should return LatLng instance as is', function () {
it('returns LatLng instance as is', function () {
var a = new L.LatLng(50, 30);
expect(L.latLng(a)).toBe(a);
});
it('should accept an array of coordinates', function () {
it('accepts an array of coordinates', function () {
expect(L.latLng([50, 30])).toEqual(new L.LatLng(50, 30));
});
it('should pass null or undefined as is', function () {
it('passes null or undefined as is', function () {
expect(L.latLng(undefined)).toBe(undefined);
expect(L.latLng(null)).toBe(null);
});
it('should create a LatLng object from two coordinates', function () {
it('creates a LatLng object from two coordinates', function () {
expect(L.latLng(50, 30)).toEqual(new L.LatLng(50, 30));
});
it('accepts an object with lat/lng', function () {
expect(L.latLng({lat: 50, lng: 30})).toEqual(new L.LatLng(50, 30));
});
it('accepts an object with lat/lon', function () {
expect(L.latLng({lat: 50, lon: 30})).toEqual(new L.LatLng(50, 30));
});
});
});

View File

@ -4,7 +4,7 @@ xdescribe("Projection.Mercator", function() {
beforeEach(function() {
function almostEqual(a, b, p) {
return Math.abs(a - b) <= (p || 1.0E-12);
};
}
this.addMatchers({
toAlmostEqual: function(expected, margin) {
var p1 = this.actual,
@ -16,7 +16,7 @@ xdescribe("Projection.Mercator", function() {
describe("#project", function() {
it("should do projection properly", function() {
it("projects", function() {
//edge cases
expect(p.project(new L.LatLng(0, 0))).toAlmostEqual(new L.Point(0, 0));
expect(p.project(new L.LatLng(90, 180))).toAlmostEqual(new L.Point(-Math.PI, Math.PI));
@ -27,7 +27,7 @@ xdescribe("Projection.Mercator", function() {
});
describe("#unproject", function() {
it("should do unprojection properly", function() {
it("unprojects", function() {
function pr(point) {
return p.project(p.unproject(point));
}

View File

@ -14,18 +14,18 @@ describe('Bounds', function() {
});
describe('constructor', function() {
it('should create bounds with proper min & max on (Point, Point)', function() {
it('creates bounds with proper min & max on (Point, Point)', function() {
expect(a.min).toEqual(new L.Point(14, 12));
expect(a.max).toEqual(new L.Point(30, 40));
});
it('should create bounds with proper min & max on (Point[])', function() {
it('creates bounds with proper min & max on (Point[])', function() {
expect(b.min).toEqual(new L.Point(14, 12));
expect(b.max).toEqual(new L.Point(30, 40));
});
});
describe('#extend', function() {
it('should extend the bounds to contain the given point', function() {
it('extends the bounds to contain the given point', function() {
a.extend(new L.Point(50, 20));
expect(a.min).toEqual(new L.Point(14, 12));
expect(a.max).toEqual(new L.Point(50, 40));
@ -37,13 +37,13 @@ describe('Bounds', function() {
});
describe('#getCenter', function() {
it('should return the center point', function() {
it('returns the center point', function() {
expect(a.getCenter()).toEqual(new L.Point(22, 26));
});
});
describe('#contains', function() {
it('should contains other bounds or point', function() {
it('contains other bounds or point', function() {
a.extend(new L.Point(50, 10));
expect(a.contains(b)).toBeTruthy();
expect(b.contains(a)).toBeFalsy();
@ -53,33 +53,33 @@ describe('Bounds', function() {
});
describe('#isValid', function() {
it('should return true if properly set up', function() {
it('returns true if properly set up', function() {
expect(a.isValid()).toBeTruthy();
});
it('should return false if is invalid', function() {
it('returns false if is invalid', function() {
expect(c.isValid()).toBeFalsy();
});
it('should be valid if extended', function() {
it('returns true if extended', function() {
c.extend([0, 0]);
expect(c.isValid()).toBeTruthy();
});
});
describe('#getSize', function () {
it('should return the size of the bounds as point', function () {
it('returns the size of the bounds as point', function () {
expect(a.getSize()).toEqual(new L.Point(16, 28));
});
});
describe('#intersects', function () {
it('should return true if bounds intersect', function () {
it('returns true if bounds intersect', function () {
expect(a.intersects(b)).toBe(true);
expect(a.intersects(new L.Bounds(new L.Point(100, 100), new L.Point(120, 120)))).toEqual(false);
});
});
describe('L.bounds factory', function () {
it('should create bounds from array of number arrays', function () {
it('creates bounds from array of number arrays', function () {
var bounds = L.bounds([[14, 12], [30, 40]]);
expect(bounds).toEqual(a);
});

View File

@ -8,7 +8,7 @@ describe('LineUtil', function () {
bounds = L.bounds([5, 0], [15, 10]);
});
it('should clip segment by bounds correctly', function () {
it('clips a segment by bounds', function () {
var a = new L.Point(0, 0);
var b = new L.Point(15, 15);
@ -26,7 +26,7 @@ describe('LineUtil', function () {
expect(segment2[1]).toEqual(new L.Point(15, 5));
});
it('should use last bit code and reject segments out of bounds', function () {
it('uses last bit code and reject segments out of bounds', function () {
var a = new L.Point(15, 15);
var b = new L.Point(25, 20);
var segment = L.LineUtil.clipSegment(a, b, bounds, true);
@ -41,17 +41,17 @@ describe('LineUtil', function () {
var p2 = new L.Point(10, 0);
var p = new L.Point(0, 0);
it('should calculate distance from point to segment', function () {
it('calculates distance from point to segment', function () {
expect(L.LineUtil.pointToSegmentDistance(p, p1, p2)).toEqual(Math.sqrt(200) / 2);
});
it('should get point closest to segment', function () {
it('calculates point closest to segment', function () {
expect(L.LineUtil.closestPointOnSegment(p, p1, p2)).toEqual(new L.Point(5, 5));
});
});
describe('#simplify', function () {
it('should simplify polylines according to tolerance', function () {
it('simplifies polylines according to tolerance', function () {
var points = [
new L.Point(0, 0),
new L.Point(0.01, 0),

View File

@ -2,13 +2,13 @@ describe("Point", function() {
describe('constructor', function() {
it("should create a point with the given x and y", function() {
it("creates a point with the given x and y", function() {
var p = new L.Point(1.5, 2.5);
expect(p.x).toEqual(1.5);
expect(p.y).toEqual(2.5);
});
it("should round the given x and y if the third argument is true", function() {
it("rounds the given x and y if the third argument is true", function() {
var p = new L.Point(1.3, 2.7, true);
expect(p.x).toEqual(1);
expect(p.y).toEqual(3);
@ -16,7 +16,7 @@ describe("Point", function() {
});
describe('#subtract', function() {
it('should subtract the given point from this one', function() {
it('subtracts the given point from this one', function() {
var a = new L.Point(50, 30),
b = new L.Point(20, 10);
expect(a.subtract(b)).toEqual(new L.Point(30, 20));
@ -24,31 +24,31 @@ describe("Point", function() {
});
describe('#add', function() {
it('should add the given point to this one', function() {
it('adds given point to this one', function() {
expect(new L.Point(50, 30).add(new L.Point(20, 10))).toEqual(new L.Point(70, 40));
});
});
describe('#divideBy', function() {
it('should divide this point by the given amount', function() {
it('divides this point by the given amount', function() {
expect(new L.Point(50, 30).divideBy(5)).toEqual(new L.Point(10, 6));
});
});
describe('#multiplyBy', function() {
it('should multiply this point by the given amount', function() {
it('multiplies this point by the given amount', function() {
expect(new L.Point(50, 30).multiplyBy(2)).toEqual(new L.Point(100, 60));
});
});
describe('#floor', function () {
it('should return a new point with floored coordinates', function () {
it('returns a new point with floored coordinates', function () {
expect(new L.Point(50.56, 30.123).floor()).toEqual(new L.Point(50, 30));
});
});
describe('#distanceTo', function () {
it('should calculate distance between two points', function () {
it('calculates distance between two points', function () {
var p1 = new L.Point(0, 30);
var p2 = new L.Point(40, 0);
expect(p1.distanceTo(p2)).toEqual(50.0);
@ -56,7 +56,7 @@ describe("Point", function() {
});
describe('#equals', function () {
it('should return true if points are equal', function () {
it('returns true if points are equal', function () {
var p1 = new L.Point(20.4, 50.12);
var p2 = new L.Point(20.4, 50.12);
var p3 = new L.Point(20.5, 50.13);
@ -66,24 +66,37 @@ describe("Point", function() {
});
});
describe('#contains', function () {
it('returns true if the point is bigger in absolute dimensions than the passed one', function () {
var p1 = new L.Point(50, 30),
p2 = new L.Point(-40, 20),
p3 = new L.Point(60, -20),
p4 = new L.Point(-40, -40);
expect(p1.contains(p2)).toBe(true);
expect(p1.contains(p3)).toBe(false);
expect(p1.contains(p4)).toBe(false);
});
});
describe('#toString', function () {
it('should format a string out of point coordinates', function () {
it('formats a string out of point coordinates', function () {
expect(new L.Point(50, 30) + '').toEqual('Point(50, 30)');
});
});
describe('L.point factory', function () {
it('should leave L.Point instances as is', function () {
it('leaves L.Point instances as is', function () {
var p = new L.Point(50, 30);
expect(L.point(p)).toBe(p);
});
it('should create a point out of three arguments', function () {
it('creates a point out of three arguments', function () {
expect(L.point(50.1, 30.1, true)).toEqual(new L.Point(50, 30));
});
it('should create a point from an array of coordinates', function () {
it('creates a point from an array of coordinates', function () {
expect(L.point([50, 30])).toEqual(new L.Point(50, 30));
});
it('should not fail on invalid arguments', function () {
it('does not fail on invalid arguments', function () {
expect(L.point(undefined)).toBe(undefined);
expect(L.point(null)).toBe(null);
});

View File

@ -1,7 +1,7 @@
describe('PolyUtil', function () {
describe('#clipPolygon', function () {
it('should clip polygon by bounds correctly', function () {
it('clips polygon by bounds', function () {
var bounds = L.bounds([0, 0], [10, 10]);
var points = [

View File

@ -7,23 +7,23 @@ describe("Transformation", function() {
});
describe('#transform', function () {
it("should perform a transformation", function() {
it("performs a transformation", function() {
var p2 = t.transform(p, 2);
expect(p2).toEqual(new L.Point(24, 128));
});
it('should assume scale of 1 if not specified', function () {
it('assumes a scale of 1 if not specified', function () {
var p2 = t.transform(p);
expect(p2).toEqual(new L.Point(12, 64));
});
});
describe('#untransform', function () {
it("should perform a reverse transformation", function() {
it("performs a reverse transformation", function() {
var p2 = t.transform(p, 2);
var p3 = t.untransform(p2, 2);
expect(p3).toEqual(p);
});
it('should assume scale of 1 if not specified', function () {
it('assumes a scale of 1 if not specified', function () {
var p2 = t.transform(p);
expect(t.untransform(new L.Point(12, 64))).toEqual(new L.Point(10, 20));
});

View File

@ -16,14 +16,14 @@
var wasClicked = 0;
fg2.on('click', function(e) {
expect(e.layer == marker).toBe(true);
expect(e.target === fg2).toBe(true);
expect(e.layer).toBe(marker);
expect(e.target).toBe(fg2);
wasClicked |= 1;
});
fg1.on('click', function (e) {
expect(e.layer == marker).toBe(true);
expect(e.target === fg1).toBe(true);
expect(e.layer).toBe(marker);
expect(e.target).toBe(fg1);
wasClicked |= 2;
});

View File

@ -0,0 +1,58 @@
describe('LayerGroup', function () {
describe("#addLayer", function () {
it('adds a layer', function() {
var lg = L.layerGroup(),
marker = L.marker([0, 0]);
expect(lg.addLayer(marker)).toEqual(lg);
expect(lg.hasLayer(marker)).toBe(true);
});
});
describe("#removeLayer", function () {
it('removes a layer', function() {
var lg = L.layerGroup(),
marker = L.marker([0, 0]);
lg.addLayer(marker);
expect(lg.removeLayer(marker)).toEqual(lg);
expect(lg.hasLayer(marker)).toBe(false);
});
});
describe("#clearLayers", function () {
it('removes all layers', function() {
var lg = L.layerGroup(),
marker = L.marker([0, 0]);
lg.addLayer(marker);
expect(lg.clearLayers()).toEqual(lg);
expect(lg.hasLayer(marker)).toBe(false);
});
});
describe("#getLayers", function () {
it('gets all layers', function() {
var lg = L.layerGroup(),
marker = L.marker([0, 0]);
lg.addLayer(marker);
expect(lg.getLayers()).toEqual([marker]);
});
});
describe("#eachLayer", function () {
it('iterates over all layers', function() {
var lg = L.layerGroup(),
marker = L.marker([0, 0]),
ctx = { foo: 'bar' };
lg.addLayer(marker);
lg.eachLayer(function(layer) {
expect(layer).toEqual(marker);
expect(this).toEqual(ctx);
}, ctx);
});
});
});

View File

@ -1,53 +1,65 @@
describe('TileLayer', function () {
var tileUrl = '';
describe("#getMaxZoom, #getMinZoom", function () {
var map;
beforeEach(function () {
map = L.map(document.createElement('div'));
});
describe("when a tilelayer is added to a map with no other layers", function () {
it("should have the same zoomlevels as the tilelayer", function () {
it("has the same zoomlevels as the tilelayer", function () {
var maxZoom = 10,
minZoom = 5;
map.setView([0, 0], 1);
L.tileLayer("{z}{x}{y}", {
L.tileLayer(tileUrl, {
maxZoom: maxZoom,
minZoom: minZoom
}).addTo(map);
expect(map.getMaxZoom() === maxZoom).toBeTruthy();
expect(map.getMinZoom() === minZoom).toBeTruthy();
expect(map.getMaxZoom()).toBe(maxZoom);
expect(map.getMinZoom()).toBe(minZoom);
});
});
describe("when a tilelayer is added to a map that already has a tilelayer", function () {
it("should have its zoomlevels updated to fit the new layer", function () {
describe("accessing a tilelayer's properties", function () {
it('provides a container', function () {
map.setView([0, 0], 1);
L.tileLayer("{z}{x}{y}", { minZoom:10, maxZoom: 15 }).addTo(map);
var layer = L.tileLayer(tileUrl).addTo(map);
expect(layer.getContainer()).toBeTruthy();
});
});
describe("when a tilelayer is added to a map that already has a tilelayer", function () {
it("has its zoomlevels updated to fit the new layer", function () {
map.setView([0, 0], 1);
L.tileLayer(tileUrl, { minZoom:10, maxZoom: 15 }).addTo(map);
expect(map.getMinZoom()).toBe(10);
expect(map.getMaxZoom()).toBe(15);
L.tileLayer("{z}{x}{y}", { minZoom:5, maxZoom: 10 }).addTo(map);
L.tileLayer(tileUrl, { minZoom:5, maxZoom: 10 }).addTo(map);
expect(map.getMinZoom()).toBe(5); // changed
expect(map.getMaxZoom()).toBe(15); // unchanged
L.tileLayer("{z}{x}{y}",{ minZoom:10, maxZoom: 20 }).addTo(map);
L.tileLayer(tileUrl,{ minZoom:10, maxZoom: 20 }).addTo(map);
expect(map.getMinZoom()).toBe(5); // unchanged
expect(map.getMaxZoom()).toBe(20); // changed
L.tileLayer("{z}{x}{y}", { minZoom:0, maxZoom: 25 }).addTo(map);
L.tileLayer(tileUrl, { minZoom:0, maxZoom: 25 }).addTo(map);
expect(map.getMinZoom()).toBe(0); // changed
expect(map.getMaxZoom()).toBe(25); // changed
});
});
describe("when a tilelayer is removed from a map", function () {
it("it should have its zoomlevels updated to only fit the layers it currently has", function () {
var tiles = [ L.tileLayer("{z}{x}{y}", { minZoom:10, maxZoom: 15 }).addTo(map),
L.tileLayer("{z}{x}{y}", { minZoom:5, maxZoom: 10 }).addTo(map),
L.tileLayer("{z}{x}{y}", { minZoom:10, maxZoom: 20 }).addTo(map),
L.tileLayer("{z}{x}{y}", { minZoom:0, maxZoom: 25 }).addTo(map)
it("has its zoomlevels updated to only fit the layers it currently has", function () {
var tiles = [ L.tileLayer(tileUrl, { minZoom:10, maxZoom: 15 }).addTo(map),
L.tileLayer(tileUrl, { minZoom:5, maxZoom: 10 }).addTo(map),
L.tileLayer(tileUrl, { minZoom:10, maxZoom: 20 }).addTo(map),
L.tileLayer(tileUrl, { minZoom:0, maxZoom: 25 }).addTo(map)
];
map.whenReady(function() {
expect(map.getMinZoom()).toBe(0);

View File

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

View File

@ -7,7 +7,7 @@ describe('Circle', function () {
circle = L.circle([50, 30], 200);
});
it('should return correct bounds', function () {
it('returns bounds', function () {
var bounds = circle.getBounds();
expect(bounds.getSouthWest().equals([49.998203369, 29.997204939])).toBeTruthy();

View File

@ -0,0 +1,55 @@
describe('Polygon', function() {
var c = document.createElement('div');
c.style.width = '400px';
c.style.height = '400px';
var map = new L.Map(c);
map.setView(new L.LatLng(55.8, 37.6), 6);
describe("#initialize", function() {
it("doesn't overwrite the given latlng array", function () {
var originalLatLngs = [
[1, 2],
[3, 4]
];
var sourceLatLngs = originalLatLngs.slice();
var polygon = new L.Polygon(sourceLatLngs);
expect(sourceLatLngs).toEqual(originalLatLngs);
expect(polygon._latlngs).toNotEqual(sourceLatLngs);
});
});
describe("#setLatLngs", function () {
it("doesn't overwrite the given latlng array", function () {
var originalLatLngs = [
[1, 2],
[3, 4]
];
var sourceLatLngs = originalLatLngs.slice();
var polygon = new L.Polygon(sourceLatLngs);
polygon.setLatLngs(sourceLatLngs);
expect(sourceLatLngs).toEqual(originalLatLngs);
});
});
describe("#spliceLatLngs", function () {
it("splices the internal latLngs", function () {
var latLngs = [
[1, 2],
[3, 4],
[5, 6]
];
var polygon = new L.Polygon(latLngs);
polygon.spliceLatLngs(1, 1, [7, 8]);
expect(polygon._latlngs).toEqual([L.latLng([1, 2]), L.latLng([7, 8]), L.latLng([5, 6])]);
});
});
});

View File

@ -7,7 +7,7 @@ describe('PolylineGeometry', function() {
map.setView(new L.LatLng(55.8, 37.6), 6);
describe("#distanceTo", function() {
it("should calculate correct distances to points", function() {
it("calculates distances to points", function() {
var p1 = map.latLngToLayerPoint(new L.LatLng(55.8, 37.6));
var p2 = map.latLngToLayerPoint(new L.LatLng(57.123076977278, 44.861962891635));
var latlngs = [[56.485503424111, 35.545556640339], [55.972522915346, 36.116845702918], [55.502459116923, 34.930322265253], [55.31534617509, 38.973291015816]]

View File

@ -0,0 +1,55 @@
describe('Polyline', function() {
var c = document.createElement('div');
c.style.width = '400px';
c.style.height = '400px';
var map = new L.Map(c);
map.setView(new L.LatLng(55.8, 37.6), 6);
describe("#initialize", function() {
it("doesn't overwrite the given latlng array", function () {
var originalLatLngs = [
[1, 2],
[3, 4]
];
var sourceLatLngs = originalLatLngs.slice();
var polyline = new L.Polyline(sourceLatLngs);
expect(sourceLatLngs).toEqual(originalLatLngs);
expect(polyline._latlngs).toNotEqual(sourceLatLngs);
});
});
describe("#setLatLngs", function () {
it("doesn't overwrite the given latlng array", function () {
var originalLatLngs = [
[1, 2],
[3, 4]
];
var sourceLatLngs = originalLatLngs.slice();
var polyline = new L.Polyline(sourceLatLngs);
polyline.setLatLngs(sourceLatLngs);
expect(sourceLatLngs).toEqual(originalLatLngs);
});
});
describe("#spliceLatLngs", function () {
it("splices the internal latLngs", function () {
var latLngs = [
[1, 2],
[3, 4],
[5, 6]
];
var polyline = new L.Polyline(latLngs);
polyline.spliceLatLngs(1, 1, [7, 8]);
expect(polyline._latlngs).toEqual([L.latLng([1, 2]), L.latLng([7, 8]), L.latLng([5, 6])]);
});
});
});

View File

@ -1,10 +1,60 @@
describe("Map", function () {
var map,
spy;
beforeEach(function () {
map = L.map(document.createElement('div'));
spy = jasmine.createSpy();
});
describe("#remove", function () {
it("fires an unload event if loaded", function () {
var container = document.createElement('div'),
map = new L.Map(container).setView([0, 0], 0);
map.on('unload', spy);
map.remove();
expect(spy).toHaveBeenCalled();
});
it("fires no unload event if not loaded", function () {
var container = document.createElement('div'),
map = new L.Map(container);
map.on('unload', spy);
map.remove();
expect(spy).not.toHaveBeenCalled();
});
it("undefines container._leaflet", function () {
var container = document.createElement('div'),
map = new L.Map(container);
map.remove();
expect(container._leaflet).toBeUndefined();
});
it("unbinds events", function () {
var container = document.createElement('div'),
map = new L.Map(container).setView([0, 0], 1);
map.on('click dblclick mousedown mouseup mousemove', spy);
map.remove();
happen.click(container);
happen.dblclick(container);
happen.mousedown(container);
happen.mouseup(container);
happen.mousemove(container);
expect(spy).not.toHaveBeenCalled();
});
});
describe('#getCenter', function () {
it ('throws if not set before', function () {
expect(function () {
map.getCenter();
}).toThrow();
});
});
describe("#whenReady", function () {
describe("when the map has not yet been loaded", function () {
it("calls the callback when the map is loaded", function () {
var map = L.map(document.createElement('div')),
spy = jasmine.createSpy();
map.whenReady(spy);
expect(spy).not.toHaveBeenCalled();
@ -15,9 +65,6 @@ describe("Map", function () {
describe("when the map has already been loaded", function () {
it("calls the callback immediately", function () {
var map = L.map(document.createElement('div')),
spy = jasmine.createSpy();
map.setView([0, 0], 1);
map.whenReady(spy);
@ -26,12 +73,16 @@ describe("Map", function () {
});
});
describe("#getBounds", function () {
it("is safe to call from within a moveend callback during initial "
+ "load (#1027)", function () {
var c = document.createElement('div'),
map = L.map(c);
describe("#setView", function () {
it("sets the view of the map", function () {
expect(map.setView([51.505, -0.09], 13)).toBe(map);
expect(map.getZoom()).toBe(13);
expect(map.getCenter().distanceTo([51.505, -0.09])).toBeLessThan(5);
});
});
describe("#getBounds", function () {
it("is safe to call from within a moveend callback during initial load (#1027)", function () {
map.on("moveend", function () {
map.getBounds();
});
@ -41,8 +92,7 @@ describe("Map", function () {
});
describe("#getMinZoom and #getMaxZoom", function () {
it("The minZoom and maxZoom options overrides any"
+ " minZoom and maxZoom set on layers", function () {
it("minZoom and maxZoom options overrides any minZoom and maxZoom set on layers", function () {
var c = document.createElement('div'),
map = L.map(c, { minZoom: 5, maxZoom: 10 });
L.tileLayer("{z}{x}{y}", { minZoom:0, maxZoom: 10 }).addTo(map);
@ -51,4 +101,108 @@ describe("Map", function () {
expect(map.getMaxZoom()).toBe(10);
});
});
describe("#addLayer", function () {
describe("When the first layer is added to a map", function () {
it("fires a zoomlevelschange event", function () {
map.on("zoomlevelschange", spy);
expect(spy).not.toHaveBeenCalled();
L.tileLayer("{z}{x}{y}", { minZoom:0, maxZoom: 10 }).addTo(map);
expect(spy).toHaveBeenCalled();
});
});
describe("when a new layer with greater zoomlevel coverage than the current layer is added to a map", function () {
it("fires a zoomlevelschange event", function () {
L.tileLayer("{z}{x}{y}", { minZoom:0, maxZoom: 10 }).addTo(map);
map.on("zoomlevelschange", spy);
expect(spy).not.toHaveBeenCalled();
L.tileLayer("{z}{x}{y}", { minZoom:0, maxZoom: 15 }).addTo(map);
expect(spy).toHaveBeenCalled();
});
});
describe("when a new layer with the same or lower zoomlevel coverage as the current layer is added to a map", function () {
it("fires no zoomlevelschange event", function () {
L.tileLayer("{z}{x}{y}", { minZoom:0, maxZoom: 10 }).addTo(map);
map.on("zoomlevelschange", spy);
expect(spy).not.toHaveBeenCalled();
L.tileLayer("{z}{x}{y}", { minZoom:0, maxZoom: 10 }).addTo(map);
expect(spy).not.toHaveBeenCalled();
L.tileLayer("{z}{x}{y}", { minZoom:0, maxZoom: 5 }).addTo(map);
expect(spy).not.toHaveBeenCalled();
});
});
});
describe("#removeLayer", function () {
describe("when the last tile layer on a map is removed", function () {
it("fires a zoomlevelschange event", function () {
map.whenReady(function(){
var tl = L.tileLayer("{z}{x}{y}", { minZoom:0, maxZoom: 10 }).addTo(map);
map.on("zoomlevelschange", spy);
expect(spy).not.toHaveBeenCalled();
map.removeLayer(tl);
expect(spy).toHaveBeenCalled();
});
});
});
describe("when a tile layer is removed from a map and it had greater zoom level coverage than the remainding layer", function () {
it("fires a zoomlevelschange event", function () {
map.whenReady(function(){
var tl = L.tileLayer("{z}{x}{y}", { minZoom:0, maxZoom: 10 }).addTo(map),
t2 = L.tileLayer("{z}{x}{y}", { minZoom:0, maxZoom: 15 }).addTo(map);
map.on("zoomlevelschange", spy);
expect(spy).not.toHaveBeenCalled();
map.removeLayer(t2);
expect(spy).toHaveBeenCalled();
});
});
});
describe("when a tile layer is removed from a map it and it had lesser or the sa,e zoom level coverage as the remainding layer(s)", function () {
it("fires no zoomlevelschange event", function () {
map.whenReady(function(){
var tl = L.tileLayer("{z}{x}{y}", { minZoom:0, maxZoom: 10 }).addTo(map),
t2 = L.tileLayer("{z}{x}{y}", { minZoom:0, maxZoom: 10 }).addTo(map),
t3 = L.tileLayer("{z}{x}{y}", { minZoom:0, maxZoom: 5 }).addTo(map);
map.on("zoomlevelschange", spy);
expect(spy).not.toHaveBeenCalled();
map.removeLayer(t2);
expect(spy).not.toHaveBeenCalled();
map.removeLayer(t3);
expect(spy).not.toHaveBeenCalled();
});
});
});
});
describe("#eachLayer", function () {
it("returns self", function () {
expect(map.eachLayer(function () {})).toBe(map);
});
it("calls the provided function for each layer", function () {
var t1 = L.tileLayer("{z}{x}{y}").addTo(map),
t2 = L.tileLayer("{z}{x}{y}").addTo(map);
map.eachLayer(spy);
expect(spy.calls.length).toEqual(2);
expect(spy.calls[0].args).toEqual([t1]);
expect(spy.calls[1].args).toEqual([t2]);
});
it("calls the provided function with the provided context", function () {
var t1 = L.tileLayer("{z}{x}{y}").addTo(map);
map.eachLayer(spy, map);
expect(spy.calls[0].object).toEqual(map);
});
});
});

View File

@ -12,7 +12,8 @@ for (var i=0; i < libSources.length; i++) {
files = [].concat([
JASMINE,
JASMINE_ADAPTER,
"before.js"
"before.js",
"testacular.js"
], libSources, [
"after.js",
"happen.js",

7
spec/testacular.js Normal file
View File

@ -0,0 +1,7 @@
// PhantomJS has `'ontouchstart' in document.documentElement`, but
// doesn't actually support touch. So force touch not to be used.
//
// http://code.google.com/p/phantomjs/issues/detail?id=375
// https://github.com/ariya/phantomjs/pull/408
// https://github.com/Leaflet/Leaflet/pull/1434#issuecomment-13843151
window.L_NO_TOUCH = true;

View File

@ -5,7 +5,7 @@
L.Control.Attribution = L.Control.extend({
options: {
position: 'bottomright',
prefix: 'Powered by <a href="http://leafletjs.com">Leaflet</a>'
prefix: '<a href="http://leafletjs.com" title="A JS library for interactive maps">Leaflet</a>'
},
initialize: function (options) {
@ -56,8 +56,10 @@ L.Control.Attribution = L.Control.extend({
removeAttribution: function (text) {
if (!text) { return; }
if (this._attributions[text]) {
this._attributions[text]--;
this._update();
}
return this;
},
@ -82,7 +84,7 @@ L.Control.Attribution = L.Control.extend({
prefixAndAttribs.push(attribs.join(', '));
}
this._container.innerHTML = prefixAndAttribs.join(' &#8212; ');
this._container.innerHTML = prefixAndAttribs.join(' | ');
},
_onLayerAdd: function (e) {

View File

@ -18,13 +18,13 @@ L.Control.Zoom = L.Control.extend({
this._zoomOutButton = this._createButton(
'-', 'Zoom out', zoomName + '-out', container, this._zoomOut, this);
map.on('zoomend', this._updateDisabled, this);
map.on('zoomend zoomlevelschange', this._updateDisabled, this);
return container;
},
onRemove: function (map) {
map.off('zoomend', this._updateDisabled, this);
map.off('zoomend zoomlevelschange', this._updateDisabled, this);
},
_zoomIn: function (e) {

View File

@ -32,6 +32,10 @@ L.Control = L.Class.extend({
return this;
},
getContainer: function () {
return this._container;
},
addTo: function (map) {
this._map = map;

View File

@ -116,10 +116,10 @@ L.Mixin.Events = {
return this;
}
var event = L.Util.extend({
var event = L.Util.extend({}, data, {
type: type,
target: this
}, data);
});
var listeners, i, len, eventsObj, contextId;

View File

@ -67,7 +67,7 @@ L.DomEvent = {
key = '_leaflet_' + type + id,
handler = obj[key];
if (!handler) { return; }
if (!handler) { return this; }
if (L.Browser.msTouch && type.indexOf('touch') === 0) {
this.removeMsTouchListener(obj, type, id);

View File

@ -29,6 +29,7 @@ L.DomUtil = {
left = 0,
el = element,
docBody = document.body,
docEl = document.documentElement,
pos,
ie7 = L.Browser.ie7;
@ -45,8 +46,8 @@ L.DomUtil = {
if (el.offsetParent === docBody && pos === 'absolute') { break; }
if (pos === 'fixed') {
top += docBody.scrollTop || 0;
left += docBody.scrollLeft || 0;
top += docBody.scrollTop || docEl.scrollTop || 0;
left += docBody.scrollLeft || docEl.scrollLeft || 0;
break;
}
el = el.offsetParent;
@ -151,7 +152,12 @@ L.DomUtil = {
filterName = 'DXImageTransform.Microsoft.Alpha';
// filters collection throws an error if we try to retrieve a filter that doesn't exist
try { filter = el.filters.item(filterName); } catch (e) {}
try {
filter = el.filters.item(filterName);
} catch (e) {
//Don't set opacity to 1 if we haven't already set an opacity, it isn't needed and breaks transparent pngs.
if (value === 1) { return; }
}
value = Math.round(value * 100);

View File

@ -46,7 +46,7 @@ L.Draggable = L.Class.extend({
},
_onDown: function (e) {
if ((!L.Browser.touch && e.shiftKey) ||
if (e.shiftKey ||
((e.which !== 1) && (e.button !== 1) && !e.touches)) { return; }
L.DomEvent.preventDefault(e);

View File

@ -80,6 +80,9 @@ L.latLng = function (a, b) { // (LatLng) or ([Number, Number]) or (Number, Numbe
if (a === undefined || a === null) {
return a;
}
if (typeof a === 'object' && 'lat' in a) {
return new L.LatLng(a.lat, 'lng' in a ? a.lng : a.lon);
}
return new L.LatLng(a, b);
};

View File

@ -89,6 +89,11 @@ L.Point.prototype = {
point.y === this.y;
},
contains: function (point) {
return Math.abs(point.x) <= Math.abs(this.x) &&
Math.abs(point.y) <= Math.abs(this.y);
},
toString: function () {
return 'Point(' +
L.Util.formatNum(this.x) + ', ' +

View File

@ -90,6 +90,17 @@ L.LayerGroup = L.Class.extend({
method.call(context, this._layers[i]);
}
}
return this;
},
getLayers: function () {
var layers = [];
for (var i in this._layers) {
if (this._layers.hasOwnProperty(i)) {
layers.push(this._layers[i]);
}
}
return layers;
},
setZIndex: function (zIndex) {

View File

@ -19,6 +19,11 @@ L.Handler.MarkerDrag = L.Handler.extend({
},
removeHooks: function () {
this._draggable
.off('dragstart', this._onDragStart)
.off('drag', this._onDrag)
.off('dragend', this._onDragEnd);
this._draggable.disable();
},

View File

@ -4,7 +4,7 @@
L.Marker.include({
openPopup: function () {
if (this._popup && this._map) {
if (this._popup && this._map && !this._map.hasLayer(this._popup)) {
this._popup.setLatLng(this._latlng);
this._map.openPopup(this._popup);
}
@ -37,12 +37,24 @@ L.Marker.include({
.on('move', this._movePopup, this);
}
if (content instanceof L.Popup) {
L.setOptions(content, options);
this._popup = content;
} else {
this._popup = new L.Popup(options, this)
.setContent(content);
}
return this;
},
setPopupContent: function (content) {
if (this._popup) {
this._popup.setContent(content);
}
return this;
},
unbindPopup: function () {
if (this._popup) {
this._popup = null;

View File

@ -41,6 +41,10 @@ L.Marker = L.Class.extend({
},
onRemove: function (map) {
if (this.dragging) {
this.dragging.disable();
}
this._removeIcon();
this.fire('remove');

View File

@ -0,0 +1,117 @@
/*
Zoom animation logic for L.TileLayer.
*/
L.TileLayer.include({
_animateZoom: function (e) {
var firstFrame = false;
if (!this._animating) {
this._animating = true;
firstFrame = true;
}
if (firstFrame) {
this._prepareBgBuffer();
}
var transform = L.DomUtil.TRANSFORM,
bg = this._bgBuffer;
if (firstFrame) {
//prevent bg buffer from clearing right after zoom
clearTimeout(this._clearBgBufferTimer);
// hack to make sure transform is updated before running animation
L.Util.falseFn(bg.offsetWidth);
}
var scaleStr = L.DomUtil.getScaleString(e.scale, e.origin),
oldTransform = bg.style[transform];
bg.style[transform] = e.backwards ?
(e.delta ? L.DomUtil.getTranslateString(e.delta) : oldTransform) + ' ' + scaleStr :
scaleStr + ' ' + oldTransform;
},
_endZoomAnim: function () {
var front = this._tileContainer,
bg = this._bgBuffer;
front.style.visibility = '';
front.style.zIndex = 2;
bg.style.zIndex = 1;
// force reflow
L.Util.falseFn(bg.offsetWidth);
this._animating = false;
},
_clearBgBuffer: function () {
var map = this._map;
if (!map._animatingZoom && !map.touchZoom._zooming) {
this._bgBuffer.innerHTML = '';
this._bgBuffer.style[L.DomUtil.TRANSFORM] = '';
}
},
_prepareBgBuffer: function () {
var front = this._tileContainer,
bg = this._bgBuffer;
// if foreground layer doesn't have many tiles but bg layer does,
// keep the existing bg layer and just zoom it some more
if (bg && this._getLoadedTilesPercentage(bg) > 0.5 &&
this._getLoadedTilesPercentage(front) < 0.5) {
front.style.visibility = 'hidden';
this._stopLoadingImages(front);
return;
}
// prepare the buffer to become the front tile pane
bg.style.visibility = 'hidden';
bg.style[L.DomUtil.TRANSFORM] = '';
// switch out the current layer to be the new bg layer (and vice-versa)
this._tileContainer = bg;
bg = this._bgBuffer = front;
this._stopLoadingImages(bg);
},
_getLoadedTilesPercentage: function (container) {
var tiles = container.getElementsByTagName('img'),
i, len, count = 0;
for (i = 0, len = tiles.length; i < len; i++) {
if (tiles[i].complete) {
count++;
}
}
return count / len;
},
// stops loading all tiles in the background layer
_stopLoadingImages: function (container) {
var tiles = Array.prototype.slice.call(container.getElementsByTagName('img')),
i, len, tile;
for (i = 0, len = tiles.length; i < len; i++) {
tile = tiles[i];
if (!tile.complete) {
tile.onload = L.Util.falseFn;
tile.onerror = L.Util.falseFn;
tile.src = L.Util.emptyImageUrl;
tile.parentNode.removeChild(tile);
}
}
}
});

View File

@ -18,12 +18,13 @@ L.TileLayer.WMS = L.TileLayer.extend({
this._url = url;
var wmsParams = L.extend({}, this.defaultWmsParams);
var wmsParams = L.extend({}, this.defaultWmsParams),
tileSize = options.tileSize || this.options.tileSize;
if (options.detectRetina && L.Browser.retina) {
wmsParams.width = wmsParams.height = this.options.tileSize * 2;
wmsParams.width = wmsParams.height = tileSize * 2;
} else {
wmsParams.width = wmsParams.height = this.options.tileSize;
wmsParams.width = wmsParams.height = tileSize;
}
for (var i in options) {
@ -48,8 +49,6 @@ L.TileLayer.WMS = L.TileLayer.extend({
getTileUrl: function (tilePoint, zoom) { // (Point, Number) -> String
this._adjustTilePoint(tilePoint);
var map = this._map,
crs = map.options.crs,
tileSize = this.options.tileSize,

View File

@ -22,6 +22,7 @@ L.TileLayer = L.Class.extend({
zoomReverse: false,
detectRetina: false,
reuseTiles: false,
bounds: false,
*/
unloadInvisibleTiles: L.Browser.mobile,
updateWhenIdle: L.Browser.mobile
@ -42,6 +43,10 @@ L.TileLayer = L.Class.extend({
this.options.maxZoom--;
}
if (options.bounds) {
options.bounds = L.latLngBounds(options.bounds);
}
this._url = url;
var subdomains = this.options.subdomains;
@ -53,6 +58,7 @@ L.TileLayer = L.Class.extend({
onAdd: function (map) {
this._map = map;
this._animated = map.options.zoomAnimation && L.Browser.any3d;
// create a container div for tiles
this._initContainer();
@ -62,10 +68,17 @@ L.TileLayer = L.Class.extend({
// set up events
map.on({
'viewreset': this._resetCallback,
'viewreset': this._reset,
'moveend': this._update
}, this);
if (this._animated) {
map.on({
'zoomanim': this._animateZoom,
'zoomend': this._endZoomAnim
}, this);
}
if (!this.options.updateWhenIdle) {
this._limitedUpdate = L.Util.limitExecByInterval(this._update, 150, this);
map.on('move', this._limitedUpdate, this);
@ -84,10 +97,17 @@ L.TileLayer = L.Class.extend({
this._container.parentNode.removeChild(this._container);
map.off({
'viewreset': this._resetCallback,
'viewreset': this._reset,
'moveend': this._update
}, this);
if (this._animated) {
map.off({
'zoomanim': this._animateZoom,
'zoomend': this._endZoomAnim
}, this);
}
if (!this.options.updateWhenIdle) {
map.off('move', this._limitedUpdate, this);
}
@ -122,6 +142,10 @@ L.TileLayer = L.Class.extend({
return this.options.attribution;
},
getContainer: function () {
return this._container;
},
setOpacity: function (opacity) {
this.options.opacity = opacity;
@ -151,8 +175,7 @@ L.TileLayer = L.Class.extend({
redraw: function () {
if (this._map) {
this._map._panes.tilePane.empty = false;
this._reset(true);
this._reset({hard: true});
this._update();
}
return this;
@ -212,11 +235,20 @@ L.TileLayer = L.Class.extend({
_initContainer: function () {
var tilePane = this._map._panes.tilePane;
if (!this._container || tilePane.empty) {
if (!this._container) {
this._container = L.DomUtil.create('div', 'leaflet-layer');
this._updateZIndex();
if (this._animated) {
var className = 'leaflet-tile-container leaflet-zoom-animated';
this._bgBuffer = L.DomUtil.create('div', className, this._container);
this._tileContainer = L.DomUtil.create('div', className, this._container);
} else {
this._tileContainer = this._container;
}
tilePane.appendChild(this._container);
if (this.options.opacity < 1) {
@ -225,11 +257,7 @@ L.TileLayer = L.Class.extend({
}
},
_resetCallback: function (e) {
this._reset(e.hard);
},
_reset: function (clearOldContainer) {
_reset: function (e) {
var tiles = this._tiles;
for (var key in tiles) {
@ -245,8 +273,10 @@ L.TileLayer = L.Class.extend({
this._unusedTiles = [];
}
if (clearOldContainer && this._container) {
this._container.innerHTML = "";
this._tileContainer.innerHTML = "";
if (this._animated && e && e.hard) {
this._clearBgBuffer();
}
this._initContainer();
@ -319,7 +349,7 @@ L.TileLayer = L.Class.extend({
this._addTile(queue[i], fragment);
}
this._container.appendChild(fragment);
this._tileContainer.appendChild(fragment);
},
_tileShouldBeLoaded: function (tilePoint) {
@ -336,6 +366,19 @@ L.TileLayer = L.Class.extend({
}
}
if (this.options.bounds) {
var tileSize = this.options.tileSize,
nwPoint = tilePoint.multiplyBy(tileSize),
sePoint = nwPoint.add(new L.Point(tileSize, tileSize)),
nw = this._map.unproject(nwPoint),
se = this._map.unproject(sePoint),
bounds = new L.LatLngBounds([nw, se]);
if (!this.options.bounds.intersects(bounds)) {
return false;
}
}
return true;
},
@ -365,8 +408,8 @@ L.TileLayer = L.Class.extend({
L.DomUtil.removeClass(tile, 'leaflet-tile-loaded');
this._unusedTiles.push(tile);
} else if (tile.parentNode === this._container) {
this._container.removeChild(tile);
} else if (tile.parentNode === this._tileContainer) {
this._tileContainer.removeChild(tile);
}
// for https://github.com/CloudMade/Leaflet/issues/137
@ -386,7 +429,6 @@ L.TileLayer = L.Class.extend({
/*
Chrome 20 layouts much faster with top/left (verify with timeline, frames)
Android 4 browser has display issues with top/left and requires transform instead
Android 3 browser not tested
Android 2 browser requires top/left or tiles disappear on load or first drag
(reappear after zoom) https://github.com/CloudMade/Leaflet/issues/866
(other browsers don't currently care) - see debug/hacks/jitter.html for an example
@ -397,7 +439,7 @@ L.TileLayer = L.Class.extend({
this._loadTile(tile, tilePoint);
if (tile.parentNode !== this._container) {
if (tile.parentNode !== this._tileContainer) {
container.appendChild(tile);
}
},
@ -424,11 +466,9 @@ L.TileLayer = L.Class.extend({
// image-specific code (override to implement e.g. Canvas or SVG tile layer)
getTileUrl: function (tilePoint) {
this._adjustTilePoint(tilePoint);
return L.Util.template(this._url, L.extend({
s: this._getSubdomain(tilePoint),
z: this._getZoomForUrl(),
z: tilePoint.z,
x: tilePoint.x,
y: tilePoint.y
}, this.options));
@ -451,6 +491,8 @@ L.TileLayer = L.Class.extend({
if (this.options.tms) {
tilePoint.y = limit - tilePoint.y - 1;
}
tilePoint.z = this._getZoomForUrl();
},
_getSubdomain: function (tilePoint) {
@ -491,6 +533,7 @@ L.TileLayer = L.Class.extend({
tile.onload = this._tileOnLoad;
tile.onerror = this._tileOnError;
this._adjustTilePoint(tilePoint);
tile.src = this.getTileUrl(tilePoint);
},
@ -498,6 +541,12 @@ L.TileLayer = L.Class.extend({
this._tilesToLoad--;
if (!this._tilesToLoad) {
this.fire('load');
if (this._animated) {
// clear scaled tiles after all new tiles are loaded (for performance)
clearTimeout(this._clearBgBufferTimer);
this._clearBgBufferTimer = setTimeout(L.bind(this._clearBgBuffer, this), 500);
}
}
},

View File

@ -6,11 +6,14 @@ L.Path.include({
bindPopup: function (content, options) {
if (content instanceof L.Popup) {
this._popup = content;
} else {
if (!this._popup || options) {
this._popup = new L.Popup(options, this);
}
this._popup.setContent(content);
}
if (!this._popupHandlersAdded) {
this

View File

@ -64,6 +64,9 @@ L.Path = L.Path.extend({
if (this.options.pointerEvents) {
this._path.setAttribute('pointer-events', this.options.pointerEvents);
}
if (!this.options.clickable && !this.options.pointerEvents) {
this._path.setAttribute('pointer-events', 'none');
}
this._updateStyle();
},

View File

@ -8,11 +8,20 @@ L.Polygon = L.Polyline.extend({
},
initialize: function (latlngs, options) {
var i, len, hole;
L.Polyline.prototype.initialize.call(this, latlngs, options);
if (latlngs && L.Util.isArray(latlngs[0]) && (typeof latlngs[0][0] !== 'number')) {
this._latlngs = this._convertLatLngs(latlngs[0]);
this._holes = latlngs.slice(1);
for (i = 0, len = this._holes.length; i < len; i++) {
hole = this._holes[i] = this._convertLatLngs(this._holes[i]);
if (hole[0].equals(hole[hole.length - 1])) {
hole.pop();
}
}
}
// filter out last point if its equal to the first one

View File

@ -1,249 +0,0 @@
/*
* L.Handler.PolyEdit is an editing handler for polylines and polygons.
*/
L.Handler.PolyEdit = L.Handler.extend({
options: {
icon: new L.DivIcon({
iconSize: new L.Point(8, 8),
className: 'leaflet-div-icon leaflet-editing-icon'
})
},
initialize: function (poly, options) {
this._poly = poly;
L.setOptions(this, options);
},
addHooks: function () {
if (this._poly._map) {
if (!this._markerGroup) {
this._initMarkers();
}
this._poly._map.addLayer(this._markerGroup);
}
},
removeHooks: function () {
if (this._poly._map) {
this._poly._map.removeLayer(this._markerGroup);
delete this._markerGroup;
delete this._markers;
}
},
updateMarkers: function () {
this._markerGroup.clearLayers();
this._initMarkers();
},
_initMarkers: function () {
if (!this._markerGroup) {
this._markerGroup = new L.LayerGroup();
}
this._markers = [];
var latlngs = this._poly._latlngs,
i, j, len, marker;
// TODO refactor holes implementation in Polygon to support it here
for (i = 0, len = latlngs.length; i < len; i++) {
marker = this._createMarker(latlngs[i], i);
marker.on('click', this._onMarkerClick, this);
this._markers.push(marker);
}
var markerLeft, markerRight;
for (i = 0, j = len - 1; i < len; j = i++) {
if (i === 0 && !(L.Polygon && (this._poly instanceof L.Polygon))) {
continue;
}
markerLeft = this._markers[j];
markerRight = this._markers[i];
this._createMiddleMarker(markerLeft, markerRight);
this._updatePrevNext(markerLeft, markerRight);
}
},
_createMarker: function (latlng, index) {
var marker = new L.Marker(latlng, {
draggable: true,
icon: this.options.icon
});
marker._origLatLng = latlng;
marker._index = index;
marker.on('drag', this._onMarkerDrag, this);
marker.on('dragend', this._fireEdit, this);
this._markerGroup.addLayer(marker);
return marker;
},
_fireEdit: function () {
this._poly.fire('edit');
},
_onMarkerDrag: function (e) {
var marker = e.target;
L.extend(marker._origLatLng, marker._latlng);
if (marker._middleLeft) {
marker._middleLeft.setLatLng(this._getMiddleLatLng(marker._prev, marker));
}
if (marker._middleRight) {
marker._middleRight.setLatLng(this._getMiddleLatLng(marker, marker._next));
}
this._poly.redraw();
},
_onMarkerClick: function (e) {
// we want to remove the marker on click, but if latlng count < 3, polyline would be invalid
if (this._poly._latlngs.length < 3) { return; }
var marker = e.target,
i = marker._index;
// remove the marker
this._markerGroup.removeLayer(marker);
this._markers.splice(i, 1);
this._poly.spliceLatLngs(i, 1);
this._updateIndexes(i, -1);
// update prev/next links of adjacent markers
this._updatePrevNext(marker._prev, marker._next);
// remove ghost markers near the removed marker
if (marker._middleLeft) {
this._markerGroup.removeLayer(marker._middleLeft);
}
if (marker._middleRight) {
this._markerGroup.removeLayer(marker._middleRight);
}
// create a ghost marker in place of the removed one
if (marker._prev && marker._next) {
this._createMiddleMarker(marker._prev, marker._next);
} else if (!marker._prev) {
marker._next._middleLeft = null;
} else if (!marker._next) {
marker._prev._middleRight = null;
}
this._poly.fire('edit');
},
_updateIndexes: function (index, delta) {
this._markerGroup.eachLayer(function (marker) {
if (marker._index > index) {
marker._index += delta;
}
});
},
_createMiddleMarker: function (marker1, marker2) {
var latlng = this._getMiddleLatLng(marker1, marker2),
marker = this._createMarker(latlng),
onClick,
onDragStart,
onDragEnd;
marker.setOpacity(0.6);
marker1._middleRight = marker2._middleLeft = marker;
onDragStart = function () {
var i = marker2._index;
marker._index = i;
marker
.off('click', onClick)
.on('click', this._onMarkerClick, this);
latlng.lat = marker.getLatLng().lat;
latlng.lng = marker.getLatLng().lng;
this._poly.spliceLatLngs(i, 0, latlng);
this._markers.splice(i, 0, marker);
marker.setOpacity(1);
this._updateIndexes(i, 1);
marker2._index++;
this._updatePrevNext(marker1, marker);
this._updatePrevNext(marker, marker2);
};
onDragEnd = function () {
marker.off('dragstart', onDragStart, this);
marker.off('dragend', onDragEnd, this);
this._createMiddleMarker(marker1, marker);
this._createMiddleMarker(marker, marker2);
};
onClick = function () {
onDragStart.call(this);
onDragEnd.call(this);
this._poly.fire('edit');
};
marker
.on('click', onClick, this)
.on('dragstart', onDragStart, this)
.on('dragend', onDragEnd, this);
this._markerGroup.addLayer(marker);
},
_updatePrevNext: function (marker1, marker2) {
if (marker1) {
marker1._next = marker2;
}
if (marker2) {
marker2._prev = marker1;
}
},
_getMiddleLatLng: function (marker1, marker2) {
var map = this._poly._map,
p1 = map.latLngToLayerPoint(marker1.getLatLng()),
p2 = map.latLngToLayerPoint(marker2.getLatLng());
return map.layerPointToLatLng(p1._add(p2)._divideBy(2));
}
});
L.Polyline.addInitHook(function () {
if (L.Handler.PolyEdit) {
this.editing = new L.Handler.PolyEdit(this);
if (this.options.editable) {
this.editing.enable();
}
}
this.on('add', function () {
if (this.editing && this.editing.enabled()) {
this.editing.addHooks();
}
});
this.on('remove', function () {
if (this.editing && this.editing.enabled()) {
this.editing.removeHooks();
}
});
});

View File

@ -47,7 +47,7 @@ L.Polyline = L.Path.extend({
spliceLatLngs: function () { // (Number index, Number howMany)
var removed = [].splice.apply(this._latlngs, arguments);
this._convertLatLngs(this._latlngs);
this._convertLatLngs(this._latlngs, true);
this.redraw();
return removed;
},
@ -85,15 +85,16 @@ L.Polyline = L.Path.extend({
return bounds;
},
_convertLatLngs: function (latlngs) {
var i, len;
_convertLatLngs: function (latlngs, overwrite) {
var i, len, target = overwrite ? latlngs : [];
for (i = 0, len = latlngs.length; i < len; i++) {
if (L.Util.isArray(latlngs[i]) && typeof latlngs[i][0] !== 'number') {
return;
}
latlngs[i] = L.latLng(latlngs[i]);
target[i] = L.latLng(latlngs[i]);
}
return latlngs;
return target;
},
_initEvents: function () {

View File

@ -123,20 +123,31 @@ L.Path = (L.Path.SVG && !window.L_PREFER_CANVAS) || !L.Browser.canvas ? L.Path :
_initEvents: function () {
if (this.options.clickable) {
// TODO hand cursor
// TODO mouseover, mouseout, dblclick
// TODO dblclick
this._map.on('mousemove', this._onMouseMove, this);
this._map.on('click', this._onClick, this);
}
},
_onClick: function (e) {
if (this._containsPoint(e.layerPoint)) {
this.fire('click', {
latlng: e.latlng,
layerPoint: e.layerPoint,
containerPoint: e.containerPoint,
originalEvent: e
});
this.fire('click', e);
}
},
_onMouseMove: function (e) {
if (this._map._animatingZoom) { return; }
// TODO don't do on each move
if (this._containsPoint(e.layerPoint)) {
this._ctx.canvas.style.cursor = 'pointer';
this._mouseInside = true;
this.fire('mouseover', e);
} else if (this._mouseInside) {
this._ctx.canvas.style.cursor = '';
this._mouseInside = false;
this.fire('mouseout', e);
}
}
});

View File

@ -198,6 +198,15 @@ L.Map = L.Class.extend({
return this._layers.hasOwnProperty(id);
},
eachLayer: function (method, context) {
for (var i in this._layers) {
if (this._layers.hasOwnProperty(i)) {
method.call(context, this._layers[i]);
}
}
return this;
},
invalidateSize: function (animate) {
var oldSize = this.getSize();
@ -211,6 +220,7 @@ L.Map = L.Class.extend({
var offset = oldSize._subtract(this.getSize())._divideBy(2)._round();
if ((offset.x !== 0) || (offset.y !== 0)) {
if (animate === true) {
this.panBy(offset);
} else {
@ -221,6 +231,7 @@ L.Map = L.Class.extend({
clearTimeout(this._sizeTimer);
this._sizeTimer = setTimeout(L.bind(this.fire, this, 'moveend'), 200);
}
}
return this;
},
@ -237,10 +248,24 @@ L.Map = L.Class.extend({
return this;
},
remove: function () {
if (this._loaded) {
this.fire('unload');
}
this._initEvents('off');
delete this._container._leaflet;
return this;
},
// public methods for getting map state
getCenter: function () { // (Boolean) -> LatLng
this._checkIfLoaded();
if (!this._moved()) {
return this._initialCenter;
}
return this.layerPointToLatLng(this._getCenterLayerPoint());
},
@ -328,6 +353,7 @@ L.Map = L.Class.extend({
},
getPixelOrigin: function () {
this._checkIfLoaded();
return this._initialTopLeftPoint;
},
@ -365,13 +391,13 @@ L.Map = L.Class.extend({
},
layerPointToLatLng: function (point) { // (Point)
var projectedPoint = L.point(point).add(this._initialTopLeftPoint);
var projectedPoint = L.point(point).add(this.getPixelOrigin());
return this.unproject(projectedPoint);
},
latLngToLayerPoint: function (latlng) { // (LatLng)
var projectedPoint = this.project(L.latLng(latlng))._round();
return projectedPoint._subtract(this._initialTopLeftPoint);
return projectedPoint._subtract(this.getPixelOrigin());
},
containerPointToLayerPoint: function (point) { // (Point)
@ -497,6 +523,7 @@ L.Map = L.Class.extend({
}
this._zoom = zoom;
this._initialCenter = center;
this._initialTopLeftPoint = this._getNewTopLeftPoint(center);
@ -530,10 +557,15 @@ L.Map = L.Class.extend({
L.DomUtil.setPosition(this._mapPane, this._getMapPanePos().subtract(offset));
},
_getZoomSpan: function () {
return this.getMaxZoom() - this.getMinZoom();
},
_updateZoomLevels: function () {
var i,
minZoom = Infinity,
maxZoom = -Infinity;
maxZoom = -Infinity,
oldZoomSpan = this._getZoomSpan();
for (i in this._zoomBoundLayers) {
if (this._zoomBoundLayers.hasOwnProperty(i)) {
@ -553,25 +585,37 @@ L.Map = L.Class.extend({
this._layersMaxZoom = maxZoom;
this._layersMinZoom = minZoom;
}
if (oldZoomSpan !== this._getZoomSpan()) {
this.fire("zoomlevelschange");
}
},
_checkIfLoaded: function () {
if (!this._loaded) {
throw new Error('Set map center and zoom first.');
}
},
// map events
_initEvents: function () {
_initEvents: function (onOff) {
if (!L.DomEvent) { return; }
L.DomEvent.on(this._container, 'click', this._onMouseClick, this);
onOff = onOff || 'on';
L.DomEvent[onOff](this._container, 'click', this._onMouseClick, this);
var events = ['dblclick', 'mousedown', 'mouseup', 'mouseenter',
'mouseleave', 'mousemove', 'contextmenu'],
i, len;
for (i = 0, len = events.length; i < len; i++) {
L.DomEvent.on(this._container, events[i], this._fireMouseEvent, this);
L.DomEvent[onOff](this._container, events[i], this._fireMouseEvent, this);
}
if (this.options.trackResize) {
L.DomEvent.on(window, 'resize', this._onResize, this);
L.DomEvent[onOff](window, 'resize', this._onResize, this);
}
},
@ -614,12 +658,9 @@ L.Map = L.Class.extend({
},
_onTileLayerLoad: function () {
// TODO super-ugly, refactor!!!
// clear scaled tiles after all new tiles are loaded (for performance)
this._tileLayersToLoad--;
if (this._tileLayersNum && !this._tileLayersToLoad && this._tileBg) {
clearTimeout(this._clearTileBgTimer);
this._clearTileBgTimer = setTimeout(L.bind(this._clearTileBg, this), 500);
if (this._tileLayersNum && !this._tileLayersToLoad) {
this.fire('tilelayersload');
}
},
@ -639,12 +680,13 @@ L.Map = L.Class.extend({
return L.DomUtil.getPosition(this._mapPane);
},
_getTopLeftPoint: function () {
if (!this._loaded) {
throw new Error('Set map center and zoom first.');
}
_moved: function () {
var pos = this._getMapPanePos();
return pos && !pos.equals(new L.Point(0, 0));
},
return this._initialTopLeftPoint.subtract(this._getMapPanePos());
_getTopLeftPoint: function () {
return this.getPixelOrigin().subtract(this._getMapPanePos());
},
_getNewTopLeftPoint: function (center, zoom) {
@ -658,12 +700,14 @@ L.Map = L.Class.extend({
return this.project(latlng, newZoom)._subtract(topLeft);
},
// layer point of the current center
_getCenterLayerPoint: function () {
return this.containerPointToLayerPoint(this.getSize()._divideBy(2));
},
_getCenterOffset: function (center) {
return this.latLngToLayerPoint(center).subtract(this._getCenterLayerPoint());
// offset of the specified place to the current center in pixels
_getCenterOffset: function (latlng) {
return this.latLngToLayerPoint(latlng).subtract(this._getCenterLayerPoint());
},
_limitZoom: function (zoom) {

View File

@ -5,37 +5,43 @@
L.Map.include({
setView: function (center, zoom, forceReset) {
zoom = this._limitZoom(zoom);
var zoomChanged = (this._zoom !== zoom);
if (this._loaded && !forceReset && this._layers) {
center = L.latLng(center);
if (this._panAnim) {
this._panAnim.stop();
}
var done = (zoomChanged ?
this._zoomToIfClose && this._zoomToIfClose(center, zoom) :
this._panByIfClose(center));
var zoomChanged = (this._zoom !== zoom),
canBeAnimated = this._loaded && !forceReset && !!this._layers;
// exit if animated pan or zoom started
if (done) {
if (canBeAnimated) {
// try animating pan or zoom
var animated = zoomChanged && this.options.zoomAnimation ?
this._animateZoomIfClose && this._animateZoomIfClose(center, zoom) :
this._animatePanIfClose(center);
if (animated) {
// prevent resize handler call, the view will refresh after animation anyway
clearTimeout(this._sizeTimer);
return this;
}
}
// reset the map view
// animation didn't start, just reset the map view
this._resetView(center, zoom);
return this;
},
panBy: function (offset, duration, easeLinearity) {
offset = L.point(offset);
panBy: function (offset, duration, easeLinearity, noMoveStart) {
offset = L.point(offset).round();
if (!(offset.x || offset.y)) {
// TODO add options instead of arguments to setView/panTo/panBy/etc.
if (!offset.x && !offset.y) {
return this;
}
@ -48,11 +54,14 @@ L.Map.include({
}, this);
}
// don't fire movestart if animating inertia
if (!noMoveStart) {
this.fire('movestart');
}
L.DomUtil.addClass(this._mapPane, 'leaflet-pan-anim');
var newPos = L.DomUtil.getPosition(this._mapPane).subtract(offset)._round();
var newPos = this._getMapPanePos().subtract(offset);
this._panAnim.run(this._mapPane, newPos, duration || 0.25, easeLinearity);
return this;
@ -67,22 +76,13 @@ L.Map.include({
this.fire('moveend');
},
_panByIfClose: function (center) {
_animatePanIfClose: function (center) {
// difference between the new and current centers in pixels
var offset = this._getCenterOffset(center)._floor();
if (this._offsetIsWithinView(offset)) {
if (!this.getSize().contains(offset)) { return false; }
this.panBy(offset);
return true;
}
return false;
},
_offsetIsWithinView: function (offset, multiplyFactor) {
var m = multiplyFactor || 1,
size = this.getSize();
return (Math.abs(offset.x) <= size.x * m) &&
(Math.abs(offset.y) <= size.y * m);
}
});

View File

@ -7,163 +7,80 @@ L.Map.mergeOptions({
});
if (L.DomUtil.TRANSITION) {
L.Map.addInitHook(function () {
// zoom transitions run with the same duration for all layers, so if one of transitionend events
// happens after starting zoom animation (propagating to the map pane), we know that it ended globally
L.DomEvent.on(this._mapPane, L.DomUtil.TRANSITION_END, this._catchTransitionEnd, this);
});
}
L.Map.include(!L.DomUtil.TRANSITION ? {} : {
_zoomToIfClose: function (center, zoom) {
if (this._animatingZoom) { return true; }
if (!this.options.zoomAnimation) { return false; }
var scale = this.getZoomScale(zoom),
offset = this._getCenterOffset(center)._divideBy(1 - 1 / scale);
// if offset does not exceed half of the view
if (!this._offsetIsWithinView(offset, 1)) { return false; }
L.DomUtil.addClass(this._mapPane, 'leaflet-zoom-anim');
this
.fire('movestart')
.fire('zoomstart');
this.fire('zoomanim', {
center: center,
zoom: zoom
});
var origin = this._getCenterLayerPoint().add(offset);
this._prepareTileBg();
this._runAnimation(center, zoom, scale, origin);
return true;
},
_catchTransitionEnd: function () {
if (this._animatingZoom) {
this._onZoomTransitionEnd();
}
},
_runAnimation: function (center, zoom, scale, origin, backwardsTransform) {
this._animateToCenter = center;
this._animateToZoom = zoom;
_animateZoomIfClose: function (center, zoom) {
if (this._animatingZoom) { return true; }
// offset is the pixel coords of the zoom origin relative to the current center
var scale = this.getZoomScale(zoom),
offset = this._getCenterOffset(center)._divideBy(1 - 1 / scale),
origin = this._getCenterLayerPoint()._add(offset);
// only animate if the zoom origin is within one screen from the current center
if (!this.getSize().contains(offset)) { return false; }
this
.fire('movestart')
.fire('zoomstart');
this._animateZoom(center, zoom, origin, scale);
return true;
},
_animateZoom: function (center, zoom, origin, scale, delta, backwards) {
this._animatingZoom = true;
// put transform transition on all layers with leaflet-zoom-animated class
L.DomUtil.addClass(this._mapPane, 'leaflet-zoom-anim');
// remember what center/zoom to set after animation
this._animateToCenter = center;
this._animateToZoom = zoom;
// disable any dragging during animation
if (L.Draggable) {
L.Draggable._disabled = true;
}
var transform = L.DomUtil.TRANSFORM,
tileBg = this._tileBg;
clearTimeout(this._clearTileBgTimer);
L.Util.falseFn(tileBg.offsetWidth); //hack to make sure transform is updated before running animation
var scaleStr = L.DomUtil.getScaleString(scale, origin),
oldTransform = tileBg.style[transform];
tileBg.style[transform] = backwardsTransform ?
oldTransform + ' ' + scaleStr :
scaleStr + ' ' + oldTransform;
},
_prepareTileBg: function () {
var tilePane = this._tilePane,
tileBg = this._tileBg;
// If foreground layer doesn't have many tiles but bg layer does, keep the existing bg layer and just zoom it some more
if (tileBg && this._getLoadedTilesPercentage(tileBg) > 0.5 &&
this._getLoadedTilesPercentage(tilePane) < 0.5) {
tilePane.style.visibility = 'hidden';
tilePane.empty = true;
this._stopLoadingImages(tilePane);
return;
}
if (!tileBg) {
tileBg = this._tileBg = this._createPane('leaflet-tile-pane', this._mapPane);
tileBg.style.zIndex = 1;
}
// prepare the background pane to become the main tile pane
tileBg.style[L.DomUtil.TRANSFORM] = '';
tileBg.style.visibility = 'hidden';
// tells tile layers to reinitialize their containers
tileBg.empty = true; //new FG
tilePane.empty = false; //new BG
//Switch out the current layer to be the new bg layer (And vice-versa)
this._tilePane = this._panes.tilePane = tileBg;
var newTileBg = this._tileBg = tilePane;
L.DomUtil.addClass(newTileBg, 'leaflet-zoom-animated');
this._stopLoadingImages(newTileBg);
},
_getLoadedTilesPercentage: function (container) {
var tiles = container.getElementsByTagName('img'),
i, len, count = 0;
for (i = 0, len = tiles.length; i < len; i++) {
if (tiles[i].complete) {
count++;
}
}
return count / len;
},
// stops loading all tiles in the background layer
_stopLoadingImages: function (container) {
var tiles = Array.prototype.slice.call(container.getElementsByTagName('img')),
i, len, tile;
for (i = 0, len = tiles.length; i < len; i++) {
tile = tiles[i];
if (!tile.complete) {
tile.onload = L.Util.falseFn;
tile.onerror = L.Util.falseFn;
tile.src = L.Util.emptyImageUrl;
tile.parentNode.removeChild(tile);
}
}
this.fire('zoomanim', {
center: center,
zoom: zoom,
origin: origin,
scale: scale,
delta: delta,
backwards: backwards
});
},
_onZoomTransitionEnd: function () {
this._restoreTileFront();
this._animatingZoom = false;
L.DomUtil.removeClass(this._mapPane, 'leaflet-zoom-anim');
L.Util.falseFn(this._tileBg.offsetWidth); // force reflow
this._animatingZoom = false;
this._resetView(this._animateToCenter, this._animateToZoom, true, true);
if (L.Draggable) {
L.Draggable._disabled = false;
}
},
_restoreTileFront: function () {
this._tilePane.innerHTML = '';
this._tilePane.style.visibility = '';
this._tilePane.style.zIndex = 2;
this._tileBg.style.zIndex = 1;
},
_clearTileBg: function () {
if (!this._animatingZoom && !this.touchZoom._zooming) {
this._tileBg.innerHTML = '';
}
}
});

View File

@ -60,13 +60,13 @@ L.Map.include({
},
_handleGeolocationResponse: function (pos) {
var latAccuracy = 180 * pos.coords.accuracy / 4e7,
lngAccuracy = latAccuracy * 2,
lat = pos.coords.latitude,
var lat = pos.coords.latitude,
lng = pos.coords.longitude,
latlng = new L.LatLng(lat, lng),
latAccuracy = 180 * pos.coords.accuracy / 40075017,
lngAccuracy = latAccuracy / Math.cos(L.LatLng.DEG_TO_RAD * lat),
sw = new L.LatLng(lat - latAccuracy, lng - lngAccuracy),
ne = new L.LatLng(lat + latAccuracy, lng + lngAccuracy),
bounds = new L.LatLngBounds(sw, ne),
@ -78,10 +78,11 @@ L.Map.include({
this.setView(latlng, zoom);
}
this.fire('locationfound', {
var event = L.extend({
latlng: latlng,
bounds: bounds,
accuracy: pos.coords.accuracy
});
bounds: bounds
}, pos.coords);
this.fire('locationfound', event);
}
});

View File

@ -38,6 +38,7 @@ L.Map.BoxZoom = L.Handler.extend({
L.DomEvent
.on(document, 'mousemove', this._onMouseMove, this)
.on(document, 'mouseup', this._onMouseUp, this)
.on(document, 'keydown', this._onKeyDown, this)
.preventDefault(e);
this._map.fire("boxzoomstart");
@ -61,7 +62,7 @@ L.Map.BoxZoom = L.Handler.extend({
box.style.height = (Math.max(0, Math.abs(offset.y) - 4)) + 'px';
},
_onMouseUp: function (e) {
_finish: function () {
this._pane.removeChild(this._box);
this._container.style.cursor = '';
@ -69,7 +70,13 @@ L.Map.BoxZoom = L.Handler.extend({
L.DomEvent
.off(document, 'mousemove', this._onMouseMove)
.off(document, 'mouseup', this._onMouseUp);
.off(document, 'mouseup', this._onMouseUp)
.off(document, 'keydown', this._onKeyDown);
},
_onMouseUp: function (e) {
this._finish();
var map = this._map,
layerPoint = map.mouseEventToLayerPoint(e);
@ -85,6 +92,12 @@ L.Map.BoxZoom = L.Handler.extend({
map.fire("boxzoomend", {
boxZoomBounds: bounds
});
},
_onKeyDown: function (e) {
if (e.keyCode === 27) {
this._finish();
}
}
});

View File

@ -111,6 +111,8 @@ L.Map.Drag = L.Handler.extend({
noInertia = !options.inertia || delay > options.inertiaThreshold || !this._positions[0];
map.fire('dragend');
if (noInertia) {
map.fire('moveend');
@ -130,12 +132,10 @@ L.Map.Drag = L.Handler.extend({
offset = limitedSpeedVector.multiplyBy(-decelerationDuration / 2).round();
L.Util.requestAnimFrame(function () {
map.panBy(offset, decelerationDuration, ease);
map.panBy(offset, decelerationDuration, ease, true);
});
}
map.fire('dragend');
if (options.maxBounds) {
// TODO predrag validation instead of animation
L.Util.requestAnimFrame(this._panInsideMaxBounds, map, true, map._container);

View File

@ -57,12 +57,11 @@ L.Map.TouchZoom = L.Handler.extend({
if (this._scale === 1) { return; }
if (!this._moved) {
L.DomUtil.addClass(map._mapPane, 'leaflet-zoom-anim leaflet-touching');
L.DomUtil.addClass(map._mapPane, 'leaflet-touching');
map
.fire('movestart')
.fire('zoomstart')
._prepareTileBg();
.fire('zoomstart');
this._moved = true;
}
@ -77,19 +76,10 @@ L.Map.TouchZoom = L.Handler.extend({
_updateOnMove: function () {
var map = this._map,
origin = this._getScaleOrigin(),
center = map.layerPointToLatLng(origin);
center = map.layerPointToLatLng(origin),
zoom = map.getScaleZoom(this._scale);
map.fire('zoomanim', {
center: center,
zoom: map.getScaleZoom(this._scale)
});
// Used 2 translates instead of transform-origin because of a very strange bug -
// it didn't count the origin on the first touch-zoom but worked correctly afterwards
map._tileBg.style[L.DomUtil.TRANSFORM] =
L.DomUtil.getTranslateString(this._delta) + ' ' +
L.DomUtil.getScaleString(this._scale, this._startCenter);
map._animateZoom(center, zoom, this._startCenter, this._scale, this._delta, true);
},
_onTouchEnd: function () {
@ -112,14 +102,10 @@ L.Map.TouchZoom = L.Handler.extend({
roundZoomDelta = (floatZoomDelta > 0 ?
Math.ceil(floatZoomDelta) : Math.floor(floatZoomDelta)),
zoom = map._limitZoom(oldZoom + roundZoomDelta);
zoom = map._limitZoom(oldZoom + roundZoomDelta),
scale = map.getZoomScale(zoom) / this._scale;
map.fire('zoomanim', {
center: center,
zoom: zoom
});
map._runAnimation(center, zoom, map.getZoomScale(zoom) / this._scale, origin, true);
map._animateZoom(center, zoom, origin, scale, null, true);
},
_getScaleOrigin: function () {