merge last changes

This commit is contained in:
mourner 2012-02-12 15:15:03 +02:00
parent 5611745b1e
commit 839396ca78
21 changed files with 404 additions and 173 deletions

View File

@ -18,6 +18,7 @@ Leaflet Changelog
* Map now preserves its center after resize. * Map now preserves its center after resize.
* When panning to another copy of the world (that's infinite horizontally), map overlays now jump to corresponding positions. [#273](https://github.com/CloudMade/Leaflet/issues/273) * When panning to another copy of the world (that's infinite horizontally), map overlays now jump to corresponding positions. [#273](https://github.com/CloudMade/Leaflet/issues/273)
* Limited maximum zoom change on a single mouse wheel movement (so you won't zoom across the whole zoom range in one scroll). [#149](https://github.com/CloudMade/Leaflet/issues/149) * Limited maximum zoom change on a single mouse wheel movement (so you won't zoom across the whole zoom range in one scroll). [#149](https://github.com/CloudMade/Leaflet/issues/149)
* Significantly improved line simplification performance (noticeable when rendering polylines/polygons with tens of thousands of points)
* Improved circles performance by not drawing them if they're off the clip region. * Improved circles performance by not drawing them if they're off the clip region.
#### API improvements #### API improvements
@ -27,10 +28,13 @@ Leaflet Changelog
* Improved `Map` `locate` method, added ability to watch location continuously and more options. [#212](https://github.com/CloudMade/Leaflet/issues/212) * Improved `Map` `locate` method, added ability to watch location continuously and more options. [#212](https://github.com/CloudMade/Leaflet/issues/212)
* Added second argument `inside` to `Map` `getBoundsZoom` method that allows you to get appropriate zoom for the view to fit *inside* the given bounds. * Added second argument `inside` to `Map` `getBoundsZoom` method that allows you to get appropriate zoom for the view to fit *inside* the given bounds.
* Added `hasLayer` method to `Map`. * Added `hasLayer` method to `Map`.
* Added `Marker` `zIndexOffset` option to be able to set certain markers below/above others. [#65](https://github.com/CloudMade/Leaflet/issues/65)
* Added `urlParams` third optional argument to `TileLayer` constructor for convenience: an object with properties that will be evaluated in the URL template. * Added `urlParams` third optional argument to `TileLayer` constructor for convenience: an object with properties that will be evaluated in the URL template.
* Added `TileLayer` `continuousWorld` option to disable tile coordinates checking/wrapping. * Added `TileLayer` `continuousWorld` option to disable tile coordinates checking/wrapping.
* Added `TileLayer` `tileunload` event fired when tile gets removed after panning (by [@CodeJosch](https://github.com/CodeJosch)). [#256](https://github.com/CloudMade/Leaflet/pull/256) * Added `TileLayer` `tileunload` event fired when tile gets removed after panning (by [@CodeJosch](https://github.com/CodeJosch)). [#256](https://github.com/CloudMade/Leaflet/pull/256)
* Added `TileLayer` `zoomOffset` option useful for non-256px tiles (by [@msaspence](https://github.com/msaspence)). * Added `TileLayer` `zoomOffset` option useful for non-256px tiles (by [@msaspence](https://github.com/msaspence)).
* Added `TileLayer` `zoomReverse` option to reverse zoom numbering (by [@Majiir](https://github.com/Majiir)). [#406](https://github.com/CloudMade/Leaflet/pull/406)
* Added `TileLayer.Canvas` `redraw` method (by [@mortenbekditlevsen](https://github.com/mortenbekditlevsen)). [#459](https://github.com/CloudMade/Leaflet/pull/459)
* Added `Polyline` `closestLayerPoint` method that's can be useful for interaction features (by [@anru](https://github.com/anru)). [#186](https://github.com/CloudMade/Leaflet/pull/186) * Added `Polyline` `closestLayerPoint` method that's can be useful for interaction features (by [@anru](https://github.com/anru)). [#186](https://github.com/CloudMade/Leaflet/pull/186)
* Added `setLatLngs` method to `MultiPolyline` and `MultiPolygon` (by [@anru](https://github.com/anru)). [#194](https://github.com/CloudMade/Leaflet/pull/194) * Added `setLatLngs` method to `MultiPolyline` and `MultiPolygon` (by [@anru](https://github.com/anru)). [#194](https://github.com/CloudMade/Leaflet/pull/194)
* Added `getBounds` method to `Polyline` and `Polygon` (by [@JasonSanford](https://github.com/JasonSanford)). [#253](https://github.com/CloudMade/Leaflet/pull/253) * Added `getBounds` method to `Polyline` and `Polygon` (by [@JasonSanford](https://github.com/JasonSanford)). [#253](https://github.com/CloudMade/Leaflet/pull/253)
@ -39,18 +43,27 @@ Leaflet Changelog
* Added `ImageOverlay` `load` event. [#213](https://github.com/CloudMade/Leaflet/issues/213) * Added `ImageOverlay` `load` event. [#213](https://github.com/CloudMade/Leaflet/issues/213)
* Added `minWidth` option to `Popup` (by [@marphi](https://github.com/marphi)). [#214](https://github.com/CloudMade/Leaflet/pull/214) * Added `minWidth` option to `Popup` (by [@marphi](https://github.com/marphi)). [#214](https://github.com/CloudMade/Leaflet/pull/214)
* Improved `LatLng` constructor to be more tolerant (and throw descriptive error if latitude or longitude can't be interpreted as a number). [#136](https://github.com/CloudMade/Leaflet/issues/136) * Improved `LatLng` constructor to be more tolerant (and throw descriptive error if latitude or longitude can't be interpreted as a number). [#136](https://github.com/CloudMade/Leaflet/issues/136)
* Added `LatLng` `distanceTo` method (great circle distance) (by [@mortenbekditlevsen](https://github.com/mortenbekditlevsen)). [#462](https://github.com/CloudMade/Leaflet/pull/462)
* Added `LatLngBounds` `toBBoxString` method for convenience (by [@JasonSanford](https://github.com/JasonSanford)). [#263](https://github.com/CloudMade/Leaflet/pull/263) * Added `LatLngBounds` `toBBoxString` method for convenience (by [@JasonSanford](https://github.com/JasonSanford)). [#263](https://github.com/CloudMade/Leaflet/pull/263)
* Added `LatLngBounds` `intersects(otherBounds)` method (thanks to [@pagameba](https://github.com/pagameba)). [#350](https://github.com/CloudMade/Leaflet/pull/350) * Added `LatLngBounds` `intersects(otherBounds)` method (thanks to [@pagameba](https://github.com/pagameba)). [#350](https://github.com/CloudMade/Leaflet/pull/350)
* Added `Bounds` `intersects(otherBounds)` method. [#461](https://github.com/CloudMade/Leaflet/issues/461)
* Added `L.Util.template` method for simple string template evaluation. * Added `L.Util.template` method for simple string template evaluation.
* Added `DomUtil.removeClass` method (by [@anru](https://github.com/anru)). * Added `DomUtil.removeClass` method (by [@anru](https://github.com/anru)).
* Added ability to pass empty imageUrl to icons for creating transparent clickable regions (by [@mortenbekditlevsen](https://github.com/mortenbekditlevsen)). [#460](https://github.com/CloudMade/Leaflet/pull/460)
* Improved browser-specific code to rely more on feature detection rather than user agent string. * Improved browser-specific code to rely more on feature detection rather than user agent string.
* Improved superclass access mechanism to work with inheritance chains of 3 or more classes; now you should use `Klass.superclass` instead of `this.superclass` (by [@anru](https://github.com/anru)). [#179](https://github.com/CloudMade/Leaflet/pull/179) * Improved superclass access mechanism to work with inheritance chains of 3 or more classes; now you should use `Klass.superclass` instead of `this.superclass` (by [@anru](https://github.com/anru)). [#179](https://github.com/CloudMade/Leaflet/pull/179)
#### Development workflow improvements
* Build system completely overhauled to be based on Node.js, Jake, JSHint and UglifyJS.
* All code is now linted for errors and conformity with a strict code style (with JSHint), and wont build unless the check passes.
### Bugfixes ### Bugfixes
#### General bugfixes #### General bugfixes
* Fixed a bug where `Circle` was rendered with incorrect radius (didn't take projection exagerration into account). [#331](https://github.com/CloudMade/Leaflet/issues/331) * Fixed a bug where `Circle` was rendered with incorrect radius (didn't take projection exagerration into account). [#331](https://github.com/CloudMade/Leaflet/issues/331)
* Fixed a bug where `Map` `getBounds` would work incorrectly on a date line cross. [#295](https://github.com/CloudMade/Leaflet/issues/295)
* Fixed a bug where polygons and polylines sometimes rendered incorrectly on some zoom levels. [#381](https://github.com/CloudMade/Leaflet/issues/381) * Fixed a bug where polygons and polylines sometimes rendered incorrectly on some zoom levels. [#381](https://github.com/CloudMade/Leaflet/issues/381)
* Fixed a bug where fast mouse wheel zoom worked incorrectly when approaching min/max zoom values. * Fixed a bug where fast mouse wheel zoom worked incorrectly when approaching min/max zoom values.
* Fixed a bug where `GeoJSON` `pointToLayer` option wouldn't work in a `GeometryCollection`. [#391](https://github.com/CloudMade/Leaflet/issues/391) * Fixed a bug where `GeoJSON` `pointToLayer` option wouldn't work in a `GeometryCollection`. [#391](https://github.com/CloudMade/Leaflet/issues/391)
@ -66,6 +79,7 @@ Leaflet Changelog
* Fixed broken popup `closePopup` option (by [@jgerigmeyer](https://github.com/jgerigmeyer)). * Fixed broken popup `closePopup` option (by [@jgerigmeyer](https://github.com/jgerigmeyer)).
* Fixed a bug that caused en error when dragging marker with icon without shadow (by [@anru](https://github.com/anru)). [#178](https://github.com/CloudMade/Leaflet/issues/178) * Fixed a bug that caused en error when dragging marker with icon without shadow (by [@anru](https://github.com/anru)). [#178](https://github.com/CloudMade/Leaflet/issues/178)
* Fixed a typo in `Bounds` `contains` method (by [@anru](https://github.com/anru)). [#180](https://github.com/CloudMade/Leaflet/pull/180) * Fixed a typo in `Bounds` `contains` method (by [@anru](https://github.com/anru)). [#180](https://github.com/CloudMade/Leaflet/pull/180)
* Fixed a bug where creating an empty `Polygon` with `new L.Polygon()` would raise an error.
* Fixed a bug where drag event fired before the actual movement of layer (by [@anru](https://github.com/anru)). [#197](https://github.com/CloudMade/Leaflet/pull/197) * Fixed a bug where drag event fired before the actual movement of layer (by [@anru](https://github.com/anru)). [#197](https://github.com/CloudMade/Leaflet/pull/197)
* Fixed a bug where map click caused an error if dragging is initially disabled. [#196](https://github.com/CloudMade/Leaflet/issues/196) * Fixed a bug where map click caused an error if dragging is initially disabled. [#196](https://github.com/CloudMade/Leaflet/issues/196)
* Fixed a bug where map `movestart` event would fire after zoom animation. * Fixed a bug where map `movestart` event would fire after zoom animation.
@ -81,6 +95,7 @@ Leaflet Changelog
#### Browser bugfixes #### Browser bugfixes
* Fixed occasional crashes on Mac Safari (thanks to [@lapinos03](https://github.com/lapinos03)). [#191](https://github.com/CloudMade/Leaflet/issues/191) * Fixed occasional crashes on Mac Safari (thanks to [@lapinos03](https://github.com/lapinos03)). [#191](https://github.com/CloudMade/Leaflet/issues/191)
* Fixed a bug where resizing the map would sometimes make it blurry on WebKit (by [@mortenbekditlevsen](https://github.com/mortenbekditlevsen)). [#453](https://github.com/CloudMade/Leaflet/pull/453)
* Fixed a bug that raised error in IE6-8 when clicking on popup close button. [#235](https://github.com/CloudMade/Leaflet/issues/235) * Fixed a bug that raised error in IE6-8 when clicking on popup close button. [#235](https://github.com/CloudMade/Leaflet/issues/235)
* Fixed a bug with Safari not redrawing UI immediately after closing a popup. [#296](https://github.com/CloudMade/Leaflet/issues/296) * Fixed a bug with Safari not redrawing UI immediately after closing a popup. [#296](https://github.com/CloudMade/Leaflet/issues/296)
* Fixed a bug that caused performance drop and high CPU usage when calling `setView` or `panTo` to the current center. [#231](https://github.com/CloudMade/Leaflet/issues/231) * Fixed a bug that caused performance drop and high CPU usage when calling `setView` or `panTo` to the current center. [#231](https://github.com/CloudMade/Leaflet/issues/231)
@ -91,6 +106,7 @@ Leaflet Changelog
* Fixed a bug that caused an error when clicking vector layers under iOS. [#204](https://github.com/CloudMade/Leaflet/issues/204) * Fixed a bug that caused an error when clicking vector layers under iOS. [#204](https://github.com/CloudMade/Leaflet/issues/204)
* Fixed crash on Android 3+ when panning or zooming (by [@florian](https://github.com/florianf)). [#137](https://github.com/CloudMade/Leaflet/issues/137) * Fixed crash on Android 3+ when panning or zooming (by [@florian](https://github.com/florianf)). [#137](https://github.com/CloudMade/Leaflet/issues/137)
* Fixed a bug that caused the map to pan when touch-panning inside a popup. [#452](https://github.com/CloudMade/Leaflet/issues/452)
## 0.2.1 (2011-06-18) ## 0.2.1 (2011-06-18)

View File

@ -47,7 +47,7 @@ exports.uglify = function (code) {
ast = pro.ast_squeeze(ast, {keep_comps: false}); ast = pro.ast_squeeze(ast, {keep_comps: false});
ast = pro.ast_squeeze_more(ast); ast = pro.ast_squeeze_more(ast);
return pro.gen_code(ast); return pro.gen_code(ast) + ';';
}; };
exports.combineFiles = function (files) { exports.combineFiles = function (files) {

277
dist/leaflet-src.js vendored
View File

@ -451,7 +451,20 @@ L.Bounds = L.Class.extend({
(max.x <= this.max.x) && (max.x <= this.max.x) &&
(min.y >= this.min.y) && (min.y >= this.min.y) &&
(max.y <= this.max.y); (max.y <= this.max.y);
},
intersects: function (/*Bounds*/ bounds) {
var min = this.min,
max = this.max,
min2 = bounds.min,
max2 = bounds.max;
var xIntersects = (max2.x >= min.x) && (min2.x <= max.x),
yIntersects = (max2.y >= min.y) && (min2.y <= max.y);
return xIntersects && yIntersects;
} }
}); });
@ -659,7 +672,7 @@ L.LatLng = function (/*Number*/ rawLat, /*Number*/ rawLng, /*Boolean*/ noWrap) {
if (noWrap !== true) { if (noWrap !== true) {
lat = Math.max(Math.min(lat, 90), -90); // clamp latitude into -90..90 lat = Math.max(Math.min(lat, 90), -90); // clamp latitude into -90..90
lng = (lng + 180) % 360 + (lng < -180 ? 180 : -180); // wrap longtitude into -180..180 lng = (lng + 180) % 360 + ((lng < -180 || lng === 180) ? 180 : -180); // wrap longtitude into -180..180
} }
//TODO change to lat() & lng() //TODO change to lat() & lng()
@ -687,6 +700,22 @@ L.LatLng.prototype = {
return 'LatLng(' + return 'LatLng(' +
L.Util.formatNum(this.lat) + ', ' + L.Util.formatNum(this.lat) + ', ' +
L.Util.formatNum(this.lng) + ')'; L.Util.formatNum(this.lng) + ')';
},
// Haversine distance formula, see http://en.wikipedia.org/wiki/Haversine_formula
distanceTo: function (/*LatLng*/ other)/*->Double*/ {
var R = 6378137, // earth radius in meters
d2r = L.LatLng.DEG_TO_RAD,
dLat = (other.lat - this.lat) * d2r,
dLon = (other.lng - this.lng) * d2r,
lat1 = this.lat * d2r,
lat2 = other.lat * d2r,
sin1 = Math.sin(dLat / 2),
sin2 = Math.sin(dLon / 2);
var a = sin1 * sin1 + sin2 * sin2 * Math.cos(lat1) * Math.cos(lat2);
return R * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
} }
}; };
@ -709,8 +738,8 @@ L.LatLngBounds = L.Class.extend({
// extend the bounds to contain the given point // extend the bounds to contain the given point
extend: function (/*LatLng*/ latlng) { extend: function (/*LatLng*/ latlng) {
if (!this._southWest && !this._northEast) { if (!this._southWest && !this._northEast) {
this._southWest = new L.LatLng(latlng.lat, latlng.lng); this._southWest = new L.LatLng(latlng.lat, latlng.lng, true);
this._northEast = new L.LatLng(latlng.lat, latlng.lng); this._northEast = new L.LatLng(latlng.lat, latlng.lng, true);
} else { } else {
this._southWest.lat = Math.min(latlng.lat, this._southWest.lat); this._southWest.lat = Math.min(latlng.lat, this._southWest.lat);
this._southWest.lng = Math.min(latlng.lng, this._southWest.lng); this._southWest.lng = Math.min(latlng.lng, this._southWest.lng);
@ -734,11 +763,11 @@ L.LatLngBounds = L.Class.extend({
}, },
getNorthWest: function () { getNorthWest: function () {
return new L.LatLng(this._northEast.lat, this._southWest.lng); return new L.LatLng(this._northEast.lat, this._southWest.lng, true);
}, },
getSouthEast: function () { getSouthEast: function () {
return new L.LatLng(this._southWest.lat, this._northEast.lng); return new L.LatLng(this._southWest.lat, this._northEast.lng, true);
}, },
contains: function (/*LatLngBounds or LatLng*/ obj) /*-> Boolean*/ { contains: function (/*LatLngBounds or LatLng*/ obj) /*-> Boolean*/ {
@ -1125,7 +1154,7 @@ L.Map = L.Class.extend({
return this; return this;
} }
this._rawPanBy(oldSize.subtract(this.getSize()).divideBy(2)); this._rawPanBy(oldSize.subtract(this.getSize()).divideBy(2, true));
this.fire('move'); this.fire('move');
@ -1152,8 +1181,8 @@ L.Map = L.Class.extend({
getBounds: function () { getBounds: function () {
var bounds = this.getPixelBounds(), var bounds = this.getPixelBounds(),
sw = this.unproject(new L.Point(bounds.min.x, bounds.max.y)), sw = this.unproject(new L.Point(bounds.min.x, bounds.max.y), this._zoom, true),
ne = this.unproject(new L.Point(bounds.max.x, bounds.min.y)); ne = this.unproject(new L.Point(bounds.max.x, bounds.min.y), this._zoom, true);
return new L.LatLngBounds(sw, ne); return new L.LatLngBounds(sw, ne);
}, },
@ -1200,7 +1229,7 @@ L.Map = L.Class.extend({
} }
} while (zoomNotFound && (zoom <= maxZoom)); } while (zoomNotFound && (zoom <= maxZoom));
if (zoomNotFound) { if (zoomNotFound && inside) {
return null; return null;
} }
@ -1382,7 +1411,7 @@ L.Map = L.Class.extend({
_initEvents: function () { _initEvents: function () {
L.DomEvent.addListener(this._container, 'click', this._onMouseClick, this); L.DomEvent.addListener(this._container, 'click', this._onMouseClick, this);
var events = ['dblclick', 'mousedown', 'mouseenter', 'mouseleave', 'mousemove']; var events = ['dblclick', 'mousedown', 'mouseenter', 'mouseleave', 'mousemove', 'contextmenu'];
var i, len; var i, len;
@ -1420,6 +1449,10 @@ L.Map = L.Class.extend({
return; return;
} }
if (type === 'contextmenu') {
L.DomEvent.preventDefault(e);
}
this.fire(type, { this.fire(type, {
latlng: this.mouseEventToLatLng(e), latlng: this.mouseEventToLatLng(e),
layerPoint: this.mouseEventToLayerPoint(e) layerPoint: this.mouseEventToLayerPoint(e)
@ -1568,9 +1601,11 @@ L.TileLayer = L.Class.extend({
continuousWorld: false, continuousWorld: false,
noWrap: false, noWrap: false,
zoomOffset: 0, zoomOffset: 0,
zoomReverse: false,
unloadInvisibleTiles: L.Browser.mobile, unloadInvisibleTiles: L.Browser.mobile,
updateWhenIdle: L.Browser.mobile updateWhenIdle: L.Browser.mobile,
reuseTiles: false
}, },
initialize: function (url, options, urlParams) { initialize: function (url, options, urlParams) {
@ -1676,6 +1711,10 @@ L.TileLayer = L.Class.extend({
} }
this._tiles = {}; this._tiles = {};
if (this.options.reuseTiles) {
this._unusedTiles = [];
}
if (clearOldContainer && this._container) { if (clearOldContainer && this._container) {
this._container.innerHTML = ""; this._container.innerHTML = "";
} }
@ -1696,7 +1735,7 @@ L.TileLayer = L.Class.extend({
this._addTilesFromCenterOut(tileBounds); this._addTilesFromCenterOut(tileBounds);
if (this.options.unloadInvisibleTiles) { if (this.options.unloadInvisibleTiles || this.options.reuseTiles) {
this._removeOtherTiles(tileBounds); this._removeOtherTiles(tileBounds);
} }
}, },
@ -1747,9 +1786,14 @@ L.TileLayer = L.Class.extend({
// evil, don't do this! crashes Android 3, produces load errors, doesn't solve memory leaks // evil, don't do this! crashes Android 3, produces load errors, doesn't solve memory leaks
// this._tiles[key].src = ''; // this._tiles[key].src = '';
//tile.src = 'data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=';
if (tile.parentNode === this._container) { if (tile.parentNode === this._container) {
this._container.removeChild(tile); this._container.removeChild(tile);
} }
if (this.options.reuseTiles) {
this._unusedTiles.push(this._tiles[key]);
}
delete this._tiles[key]; delete this._tiles[key];
} }
} }
@ -1760,7 +1804,7 @@ L.TileLayer = L.Class.extend({
var tilePos = this._getTilePos(tilePoint), var tilePos = this._getTilePos(tilePoint),
zoom = this._map.getZoom(), zoom = this._map.getZoom(),
key = tilePoint.x + ':' + tilePoint.y, key = tilePoint.x + ':' + tilePoint.y,
tileLimit = Math.pow(2, (zoom + this.options.zoomOffset)); tileLimit = Math.pow(2, this._getOffsetZoom(zoom));
// wrap tile coordinates // wrap tile coordinates
if (!this.options.continuousWorld) { if (!this.options.continuousWorld) {
@ -1777,8 +1821,8 @@ L.TileLayer = L.Class.extend({
} }
} }
// create tile // get unused tile - or create a new tile
var tile = this._createTile(); var tile = this._getTile();
L.DomUtil.setPosition(tile, tilePos); L.DomUtil.setPosition(tile, tilePos);
this._tiles[key] = tile; this._tiles[key] = tile;
@ -1792,6 +1836,11 @@ L.TileLayer = L.Class.extend({
container.appendChild(tile); container.appendChild(tile);
}, },
_getOffsetZoom: function (zoom) {
zoom = this.options.zoomReverse ? this.options.maxZoom - zoom : zoom;
return zoom + this.options.zoomOffset;
},
_getTilePos: function (tilePoint) { _getTilePos: function (tilePoint) {
var origin = this._map.getPixelOrigin(), var origin = this._map.getPixelOrigin(),
tileSize = this.options.tileSize; tileSize = this.options.tileSize;
@ -1807,7 +1856,7 @@ L.TileLayer = L.Class.extend({
return L.Util.template(this._url, L.Util.extend({ return L.Util.template(this._url, L.Util.extend({
s: s, s: s,
z: zoom + this.options.zoomOffset, z: this._getOffsetZoom(zoom),
x: tilePoint.x, x: tilePoint.x,
y: tilePoint.y y: tilePoint.y
}, this._urlParams)); }, this._urlParams));
@ -1822,6 +1871,19 @@ L.TileLayer = L.Class.extend({
this._tileImg.style.height = tileSize + 'px'; this._tileImg.style.height = tileSize + 'px';
}, },
_getTile: function () {
if (this.options.reuseTiles && this._unusedTiles.length > 0) {
var tile = this._unusedTiles.pop();
this._resetTile(tile);
return tile;
}
return this._createTile();
},
_resetTile: function (tile) {
// Override if data stored on a tile needs to be cleaned up before reuse
},
_createTile: function () { _createTile: function () {
var tile = this._tileImg.cloneNode(false); var tile = this._tileImg.cloneNode(false);
tile.onselectstart = tile.onmousemove = L.Util.falseFn; tile.onselectstart = tile.onmousemove = L.Util.falseFn;
@ -1919,6 +1981,17 @@ L.TileLayer.Canvas = L.TileLayer.extend({
L.Util.setOptions(this, options); L.Util.setOptions(this, options);
}, },
redraw: function () {
for (var i in this._tiles) {
var tile = this._tiles[i];
this._redrawTile(tile);
}
},
_redrawTile: function (tile) {
this.drawTile(tile, tile._tilePoint, tile._zoom);
},
_createTileProto: function () { _createTileProto: function () {
this._canvasProto = L.DomUtil.create('canvas', 'leaflet-tile'); this._canvasProto = L.DomUtil.create('canvas', 'leaflet-tile');
@ -1935,6 +2008,8 @@ L.TileLayer.Canvas = L.TileLayer.extend({
_loadTile: function (tile, tilePoint, zoom) { _loadTile: function (tile, tilePoint, zoom) {
tile._layer = this; tile._layer = this;
tile._tilePoint = tilePoint;
tile._zoom = zoom;
this.drawTile(tile, tilePoint, zoom); this.drawTile(tile, tilePoint, zoom);
@ -2039,13 +2114,19 @@ L.Icon = L.Class.extend({
_createIcon: function (name) { _createIcon: function (name) {
var size = this[name + 'Size'], var size = this[name + 'Size'],
src = this[name + 'Url'], src = this[name + 'Url'];
img = this._createImg(src); if (!src && name === 'shadow') {
if (!src) {
return null; return null;
} }
var img;
if (!src) {
img = this._createDiv();
}
else {
img = this._createImg(src);
}
img.className = 'leaflet-marker-' + name; img.className = 'leaflet-marker-' + name;
img.style.marginLeft = (-this.iconAnchor.x) + 'px'; img.style.marginLeft = (-this.iconAnchor.x) + 'px';
@ -2069,6 +2150,10 @@ L.Icon = L.Class.extend({
el.style.filter = 'progid:DXImageTransform.Microsoft.AlphaImageLoader(src="' + src + '")'; el.style.filter = 'progid:DXImageTransform.Microsoft.AlphaImageLoader(src="' + src + '")';
} }
return el; return el;
},
_createDiv: function () {
return document.createElement('div');
} }
}); });
@ -2085,7 +2170,8 @@ L.Marker = L.Class.extend({
icon: new L.Icon(), icon: new L.Icon(),
title: '', title: '',
clickable: true, clickable: true,
draggable: false draggable: false,
zIndexOffset: 0
}, },
initialize: function (latlng, options) { initialize: function (latlng, options) {
@ -2179,8 +2265,7 @@ L.Marker = L.Class.extend({
L.DomUtil.setPosition(this._shadow, pos); L.DomUtil.setPosition(this._shadow, pos);
} }
this._icon.style.zIndex = pos.y; this._icon.style.zIndex = pos.y + this.options.zIndexOffset;
// TODO zIndex offset
}, },
_initInteraction: function () { _initInteraction: function () {
@ -2227,7 +2312,8 @@ L.Popup = L.Class.extend({
autoPan: true, autoPan: true,
closeButton: true, closeButton: true,
offset: new L.Point(0, 2), offset: new L.Point(0, 2),
autoPanPadding: new L.Point(5, 5) autoPanPadding: new L.Point(5, 5),
className: ''
}, },
initialize: function (options, source) { initialize: function (options, source) {
@ -2294,7 +2380,7 @@ L.Popup = L.Class.extend({
}, },
_initLayout: function () { _initLayout: function () {
this._container = L.DomUtil.create('div', 'leaflet-popup'); this._container = L.DomUtil.create('div', 'leaflet-popup ' + this.options.className);
if (this.options.closeButton) { if (this.options.closeButton) {
this._closeButton = L.DomUtil.create('a', 'leaflet-popup-close-button', this._container); this._closeButton = L.DomUtil.create('a', 'leaflet-popup-close-button', this._container);
@ -2404,7 +2490,9 @@ L.Popup = L.Class.extend({
L.Marker.include({ L.Marker.include({
openPopup: function () { openPopup: function () {
this._popup.setLatLng(this._latlng); this._popup.setLatLng(this._latlng);
this._map.openPopup(this._popup); if (this._map) {
this._map.openPopup(this._popup);
}
return this; return this;
}, },
@ -2816,7 +2904,7 @@ L.Map.include({
L.Path.include({ L.Path.include({
bindPopup: function (content, options) { bindPopup: function (content, options) {
if (!this._popup || this._popup.options !== options) { if (!this._popup || this._popup.options !== options) {
this._popup = new L.Popup(options); this._popup = new L.Popup(options, this);
} }
this._popup.setContent(content); this._popup.setContent(content);
@ -2945,72 +3033,80 @@ L.LineUtil = {
return points.slice(); return points.slice();
} }
var sqTolerance = tolerance * tolerance;
// stage 1: vertex reduction // stage 1: vertex reduction
points = this.reducePoints(points, tolerance); points = this._reducePoints(points, sqTolerance);
// stage 2: Douglas-Peucker simplification // stage 2: Douglas-Peucker simplification
points = this.simplifyDP(points, tolerance); points = this._simplifyDP(points, sqTolerance);
return points; return points;
}, },
// distance from a point to a segment between two points // distance from a point to a segment between two points
pointToSegmentDistance: function (/*Point*/ p, /*Point*/ p1, /*Point*/ p2) { pointToSegmentDistance: function (/*Point*/ p, /*Point*/ p1, /*Point*/ p2) {
return Math.sqrt(this._sqPointToSegmentDist(p, p1, p2)); return Math.sqrt(this._sqClosestPointOnSegment(p, p1, p2, true));
}, },
closestPointOnSegment: function (/*Point*/ p, /*Point*/ p1, /*Point*/ p2) { closestPointOnSegment: function (/*Point*/ p, /*Point*/ p1, /*Point*/ p2) {
var point = this._sqClosestPointOnSegment(p, p1, p2); return this._sqClosestPointOnSegment(p, p1, p2);
point.distance = Math.sqrt(point._sqDist);
return point;
}, },
// Douglas-Peucker simplification, see http://en.wikipedia.org/wiki/Douglas-Peucker_algorithm // Douglas-Peucker simplification, see http://en.wikipedia.org/wiki/Douglas-Peucker_algorithm
simplifyDP: function (points, tol) { _simplifyDP: function (points, sqTolerance) {
var maxDist2 = 0,
index = 0,
t2 = tol * tol,
len = points.length,
i, dist2;
if (len < 3) { var len = points.length,
return points; ArrayConstructor = typeof Uint8Array !== 'undefined' ? Uint8Array : Array,
} markers = new ArrayConstructor(len);
for (i = 0; i < len - 1; i++) { markers[0] = markers[len - 1] = 1;
dist2 = this._sqPointToSegmentDist(points[i], points[0], points[len - 1]);
if (dist2 > maxDist2) { this._simplifyDPStep(points, markers, sqTolerance, 0, len - 1);
index = i;
maxDist2 = dist2; var i,
newPoints = [];
for (i = 0; i < len; i++) {
if (markers[i]) {
newPoints.push(points[i]);
} }
} }
var part1, part2; return newPoints;
},
if (maxDist2 >= t2) { _simplifyDPStep: function (points, markers, sqTolerance, first, last) {
part1 = points.slice(0, index);
part2 = points.slice(index);
part1 = this.simplifyDP(part1, tol); var maxSqDist = 0,
part2 = this.simplifyDP(part2, tol); index, i, sqDist;
return part1.concat(part2); for (i = first + 1; i <= last - 1; i++) {
} else { sqDist = this._sqClosestPointOnSegment(points[i], points[first], points[last], true);
return [points[0], points[len - 1]];
if (sqDist > maxSqDist) {
index = i;
maxSqDist = sqDist;
}
}
if (maxSqDist > sqTolerance) {
markers[index] = 1;
this._simplifyDPStep(points, markers, sqTolerance, first, index);
this._simplifyDPStep(points, markers, sqTolerance, index, last);
} }
}, },
// reduce points that are too close to each other to a single point // reduce points that are too close to each other to a single point
reducePoints: function (points, tol) { _reducePoints: function (points, sqTolerance) {
var reducedPoints = [points[0]], var reducedPoints = [points[0]];
t2 = tol * tol;
for (var i = 1, prev = 0, len = points.length; i < len; i++) { for (var i = 1, prev = 0, len = points.length; i < len; i++) {
if (this._sqDist(points[i], points[prev]) < t2) { if (this._sqDist(points[i], points[prev]) > sqTolerance) {
continue; reducedPoints.push(points[i]);
prev = i;
} }
reducedPoints.push(points[i]);
prev = i;
} }
if (prev < len - 1) { if (prev < len - 1) {
reducedPoints.push(points[len - 1]); reducedPoints.push(points[len - 1]);
@ -3100,28 +3196,31 @@ L.LineUtil = {
return dx * dx + dy * dy; return dx * dx + dy * dy;
}, },
// return closest point on segment with attribute _sqDist - square distance to segment // return closest point on segment or distance to that point
_sqClosestPointOnSegment: function (p, p1, p2) { _sqClosestPointOnSegment: function (p, p1, p2, sqDist) {
var x2 = p2.x - p1.x, var x = p1.x,
y2 = p2.y - p1.y, y = p1.y,
apoint = p1; dx = p2.x - x,
if (x2 || y2) { dy = p2.y - y,
var dot = (p.x - p1.x) * x2 + (p.y - p1.y) * y2, dot = dx * dx + dy * dy,
t = dot / this._sqDist(p1, p2); t;
if (dot > 0) {
t = ((p.x - x) * dx + (p.y - y) * dy) / dot;
if (t > 1) { if (t > 1) {
apoint = p2; x = p2.x;
y = p2.y;
} else if (t > 0) { } else if (t > 0) {
apoint = new L.Point(p1.x + x2 * t, p1.y + y2 * t); x += dx * t;
y += dy * t;
} }
} }
apoint._sqDist = this._sqDist(p, apoint);
return apoint;
},
// distance from a point to a segment between two points dx = p.x - x;
_sqPointToSegmentDist: function (p, p1, p2) { dy = p.y - y;
return this._sqClosestPointOnSegment(p, p1, p2)._sqDist;
return sqDist ? dx * dx + dy * dy : new L.Point(x, y);
} }
}; };
@ -3347,7 +3446,7 @@ L.Polygon = L.Polyline.extend({
initialize: function (latlngs, options) { initialize: function (latlngs, options) {
L.Polyline.prototype.initialize.call(this, latlngs, options); L.Polyline.prototype.initialize.call(this, latlngs, options);
if (latlngs[0] instanceof Array) { if (latlngs && (latlngs[0] instanceof Array)) {
this._latlngs = latlngs[0]; this._latlngs = latlngs[0];
this._holes = latlngs.slice(1); this._holes = latlngs.slice(1);
} }
@ -3987,7 +4086,7 @@ L.DomEvent = {
}, },
disableClickPropagation: function (/*HTMLElement*/ el) { disableClickPropagation: function (/*HTMLElement*/ el) {
L.DomEvent.addListener(el, 'mousedown', L.DomEvent.stopPropagation); L.DomEvent.addListener(el, L.Draggable.START, L.DomEvent.stopPropagation);
L.DomEvent.addListener(el, 'click', L.DomEvent.stopPropagation); L.DomEvent.addListener(el, 'click', L.DomEvent.stopPropagation);
L.DomEvent.addListener(el, 'dblclick', L.DomEvent.stopPropagation); L.DomEvent.addListener(el, 'dblclick', L.DomEvent.stopPropagation);
}, },
@ -4745,12 +4844,15 @@ L.Control.Zoom = L.Class.extend({
L.Control.Attribution = L.Class.extend({ L.Control.Attribution = L.Class.extend({
initialize: function (prefix) {
this._prefix = prefix || 'Powered by <a href="http://leaflet.cloudmade.com">Leaflet</a>';
this._attributions = {};
},
onAdd: function (map) { onAdd: function (map) {
this._container = L.DomUtil.create('div', 'leaflet-control-attribution'); this._container = L.DomUtil.create('div', 'leaflet-control-attribution');
L.DomEvent.disableClickPropagation(this._container); L.DomEvent.disableClickPropagation(this._container);
this._map = map; this._map = map;
this._prefix = 'Powered by <a href="http://leaflet.cloudmade.com">Leaflet</a>';
this._attributions = {};
this._update(); this._update();
}, },
@ -5356,6 +5458,8 @@ L.Map.include(!L.DomUtil.TRANSITION ? {} : {
var transform = L.DomUtil.TRANSFORM; var transform = L.DomUtil.TRANSFORM;
clearTimeout(this._clearTileBgTimer);
//dumb FireFox hack, I have no idea why this magic zero translate fixes the scale transition problem //dumb FireFox hack, I have no idea why this magic zero translate fixes the scale transition problem
if (L.Browser.gecko || window.opera) { if (L.Browser.gecko || window.opera) {
this._tileBg.style[transform] += ' translate(0,0)'; this._tileBg.style[transform] += ' translate(0,0)';
@ -5415,7 +5519,10 @@ L.Map.include(!L.DomUtil.TRANSITION ? {} : {
for (var i = 0, len = tiles.length; i < len; i++) { for (var i = 0, len = tiles.length; i < len; i++) {
if (!tiles[i].complete) { if (!tiles[i].complete) {
// tiles[i].src = '' - evil, doesn't cancel the request! tiles[i].onload = L.Util.falseFn;
tiles[i].onerror = L.Util.falseFn;
tiles[i].src = 'data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=';
tiles[i].parentNode.removeChild(tiles[i]); tiles[i].parentNode.removeChild(tiles[i]);
tiles[i] = null; tiles[i] = null;
} }
@ -5491,7 +5598,7 @@ L.Map.include({
options = L.Util.extend({ options = L.Util.extend({
maxZoom: maxZoom || Infinity, maxZoom: maxZoom || Infinity,
setView: true setView: true
}); }, options);
return this.locate(options); return this.locate(options);
}, },

2
dist/leaflet.js vendored

File diff suppressed because one or more lines are too long

View File

@ -21,7 +21,7 @@ describe('LatLng', function() {
it("should clamp longtitude to lie between -180 and 180", function() { it("should clamp longtitude to lie between -180 and 180", function() {
var a = new L.LatLng(0, 190).lng; var a = new L.LatLng(0, 190).lng;
expect(a).toEqual(-170); expect(a).toEqual(-170);
var b = new L.LatLng(0, 360).lng; var b = new L.LatLng(0, 360).lng;
expect(b).toEqual(0); expect(b).toEqual(0);
@ -36,7 +36,13 @@ describe('LatLng', function() {
var f = new L.LatLng(0, -380).lng; var f = new L.LatLng(0, -380).lng;
expect(f).toEqual(-20); expect(f).toEqual(-20);
});
var g = new L.LatLng(0, 90).lng;
expect(g).toEqual(90);
var h = new L.LatLng(0, 180).lng;
expect(h).toEqual(180);
});
it("should not clamp latitude and longtitude if unbounded flag set to true", function() { it("should not clamp latitude and longtitude if unbounded flag set to true", function() {
var a = new L.LatLng(150, 0, true).lat; var a = new L.LatLng(150, 0, true).lat;

View File

@ -1,10 +1,13 @@
L.Control.Attribution = L.Class.extend({ L.Control.Attribution = L.Class.extend({
initialize: function (prefix) {
this._prefix = prefix || 'Powered by <a href="http://leaflet.cloudmade.com">Leaflet</a>';
this._attributions = {};
},
onAdd: function (map) { onAdd: function (map) {
this._container = L.DomUtil.create('div', 'leaflet-control-attribution'); this._container = L.DomUtil.create('div', 'leaflet-control-attribution');
L.DomEvent.disableClickPropagation(this._container); L.DomEvent.disableClickPropagation(this._container);
this._map = map; this._map = map;
this._prefix = 'Powered by <a href="http://leaflet.cloudmade.com">Leaflet</a>';
this._attributions = {};
this._update(); this._update();
}, },

View File

@ -112,7 +112,7 @@ L.DomEvent = {
}, },
disableClickPropagation: function (/*HTMLElement*/ el) { disableClickPropagation: function (/*HTMLElement*/ el) {
L.DomEvent.addListener(el, 'mousedown', L.DomEvent.stopPropagation); L.DomEvent.addListener(el, L.Draggable.START, L.DomEvent.stopPropagation);
L.DomEvent.addListener(el, 'click', L.DomEvent.stopPropagation); L.DomEvent.addListener(el, 'click', L.DomEvent.stopPropagation);
L.DomEvent.addListener(el, 'dblclick', L.DomEvent.stopPropagation); L.DomEvent.addListener(el, 'dblclick', L.DomEvent.stopPropagation);
}, },

View File

@ -12,7 +12,7 @@ L.LatLng = function (/*Number*/ rawLat, /*Number*/ rawLng, /*Boolean*/ noWrap) {
if (noWrap !== true) { if (noWrap !== true) {
lat = Math.max(Math.min(lat, 90), -90); // clamp latitude into -90..90 lat = Math.max(Math.min(lat, 90), -90); // clamp latitude into -90..90
lng = (lng + 180) % 360 + (lng < -180 ? 180 : -180); // wrap longtitude into -180..180 lng = (lng + 180) % 360 + ((lng < -180 || lng === 180) ? 180 : -180); // wrap longtitude into -180..180
} }
//TODO change to lat() & lng() //TODO change to lat() & lng()
@ -40,5 +40,21 @@ L.LatLng.prototype = {
return 'LatLng(' + return 'LatLng(' +
L.Util.formatNum(this.lat) + ', ' + L.Util.formatNum(this.lat) + ', ' +
L.Util.formatNum(this.lng) + ')'; L.Util.formatNum(this.lng) + ')';
},
// Haversine distance formula, see http://en.wikipedia.org/wiki/Haversine_formula
distanceTo: function (/*LatLng*/ other)/*->Double*/ {
var R = 6378137, // earth radius in meters
d2r = L.LatLng.DEG_TO_RAD,
dLat = (other.lat - this.lat) * d2r,
dLon = (other.lng - this.lng) * d2r,
lat1 = this.lat * d2r,
lat2 = other.lat * d2r,
sin1 = Math.sin(dLat / 2),
sin2 = Math.sin(dLon / 2);
var a = sin1 * sin1 + sin2 * sin2 * Math.cos(lat1) * Math.cos(lat2);
return R * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
} }
}; };

View File

@ -16,8 +16,8 @@ L.LatLngBounds = L.Class.extend({
// extend the bounds to contain the given point // extend the bounds to contain the given point
extend: function (/*LatLng*/ latlng) { extend: function (/*LatLng*/ latlng) {
if (!this._southWest && !this._northEast) { if (!this._southWest && !this._northEast) {
this._southWest = new L.LatLng(latlng.lat, latlng.lng); this._southWest = new L.LatLng(latlng.lat, latlng.lng, true);
this._northEast = new L.LatLng(latlng.lat, latlng.lng); this._northEast = new L.LatLng(latlng.lat, latlng.lng, true);
} else { } else {
this._southWest.lat = Math.min(latlng.lat, this._southWest.lat); this._southWest.lat = Math.min(latlng.lat, this._southWest.lat);
this._southWest.lng = Math.min(latlng.lng, this._southWest.lng); this._southWest.lng = Math.min(latlng.lng, this._southWest.lng);
@ -41,11 +41,11 @@ L.LatLngBounds = L.Class.extend({
}, },
getNorthWest: function () { getNorthWest: function () {
return new L.LatLng(this._northEast.lat, this._southWest.lng); return new L.LatLng(this._northEast.lat, this._southWest.lng, true);
}, },
getSouthEast: function () { getSouthEast: function () {
return new L.LatLng(this._southWest.lat, this._northEast.lng); return new L.LatLng(this._southWest.lat, this._northEast.lng, true);
}, },
contains: function (/*LatLngBounds or LatLng*/ obj) /*-> Boolean*/ { contains: function (/*LatLngBounds or LatLng*/ obj) /*-> Boolean*/ {

View File

@ -46,5 +46,18 @@ L.Bounds = L.Class.extend({
(max.x <= this.max.x) && (max.x <= this.max.x) &&
(min.y >= this.min.y) && (min.y >= this.min.y) &&
(max.y <= this.max.y); (max.y <= this.max.y);
},
intersects: function (/*Bounds*/ bounds) {
var min = this.min,
max = this.max,
min2 = bounds.min,
max2 = bounds.max;
var xIntersects = (max2.x >= min.x) && (min2.x <= max.x),
yIntersects = (max2.y >= min.y) && (min2.y <= max.y);
return xIntersects && yIntersects;
} }
}); });

View File

@ -13,72 +13,80 @@ L.LineUtil = {
return points.slice(); return points.slice();
} }
var sqTolerance = tolerance * tolerance;
// stage 1: vertex reduction // stage 1: vertex reduction
points = this.reducePoints(points, tolerance); points = this._reducePoints(points, sqTolerance);
// stage 2: Douglas-Peucker simplification // stage 2: Douglas-Peucker simplification
points = this.simplifyDP(points, tolerance); points = this._simplifyDP(points, sqTolerance);
return points; return points;
}, },
// distance from a point to a segment between two points // distance from a point to a segment between two points
pointToSegmentDistance: function (/*Point*/ p, /*Point*/ p1, /*Point*/ p2) { pointToSegmentDistance: function (/*Point*/ p, /*Point*/ p1, /*Point*/ p2) {
return Math.sqrt(this._sqPointToSegmentDist(p, p1, p2)); return Math.sqrt(this._sqClosestPointOnSegment(p, p1, p2, true));
}, },
closestPointOnSegment: function (/*Point*/ p, /*Point*/ p1, /*Point*/ p2) { closestPointOnSegment: function (/*Point*/ p, /*Point*/ p1, /*Point*/ p2) {
var point = this._sqClosestPointOnSegment(p, p1, p2); return this._sqClosestPointOnSegment(p, p1, p2);
point.distance = Math.sqrt(point._sqDist);
return point;
}, },
// Douglas-Peucker simplification, see http://en.wikipedia.org/wiki/Douglas-Peucker_algorithm // Douglas-Peucker simplification, see http://en.wikipedia.org/wiki/Douglas-Peucker_algorithm
simplifyDP: function (points, tol) { _simplifyDP: function (points, sqTolerance) {
var maxDist2 = 0,
index = 0,
t2 = tol * tol,
len = points.length,
i, dist2;
if (len < 3) { var len = points.length,
return points; ArrayConstructor = typeof Uint8Array !== 'undefined' ? Uint8Array : Array,
} markers = new ArrayConstructor(len);
for (i = 0; i < len - 1; i++) { markers[0] = markers[len - 1] = 1;
dist2 = this._sqPointToSegmentDist(points[i], points[0], points[len - 1]);
if (dist2 > maxDist2) { this._simplifyDPStep(points, markers, sqTolerance, 0, len - 1);
index = i;
maxDist2 = dist2; var i,
newPoints = [];
for (i = 0; i < len; i++) {
if (markers[i]) {
newPoints.push(points[i]);
} }
} }
var part1, part2; return newPoints;
},
if (maxDist2 >= t2) { _simplifyDPStep: function (points, markers, sqTolerance, first, last) {
part1 = points.slice(0, index);
part2 = points.slice(index);
part1 = this.simplifyDP(part1, tol); var maxSqDist = 0,
part2 = this.simplifyDP(part2, tol); index, i, sqDist;
return part1.concat(part2); for (i = first + 1; i <= last - 1; i++) {
} else { sqDist = this._sqClosestPointOnSegment(points[i], points[first], points[last], true);
return [points[0], points[len - 1]];
if (sqDist > maxSqDist) {
index = i;
maxSqDist = sqDist;
}
}
if (maxSqDist > sqTolerance) {
markers[index] = 1;
this._simplifyDPStep(points, markers, sqTolerance, first, index);
this._simplifyDPStep(points, markers, sqTolerance, index, last);
} }
}, },
// reduce points that are too close to each other to a single point // reduce points that are too close to each other to a single point
reducePoints: function (points, tol) { _reducePoints: function (points, sqTolerance) {
var reducedPoints = [points[0]], var reducedPoints = [points[0]];
t2 = tol * tol;
for (var i = 1, prev = 0, len = points.length; i < len; i++) { for (var i = 1, prev = 0, len = points.length; i < len; i++) {
if (this._sqDist(points[i], points[prev]) < t2) { if (this._sqDist(points[i], points[prev]) > sqTolerance) {
continue; reducedPoints.push(points[i]);
prev = i;
} }
reducedPoints.push(points[i]);
prev = i;
} }
if (prev < len - 1) { if (prev < len - 1) {
reducedPoints.push(points[len - 1]); reducedPoints.push(points[len - 1]);
@ -168,27 +176,30 @@ L.LineUtil = {
return dx * dx + dy * dy; return dx * dx + dy * dy;
}, },
// return closest point on segment with attribute _sqDist - square distance to segment // return closest point on segment or distance to that point
_sqClosestPointOnSegment: function (p, p1, p2) { _sqClosestPointOnSegment: function (p, p1, p2, sqDist) {
var x2 = p2.x - p1.x, var x = p1.x,
y2 = p2.y - p1.y, y = p1.y,
apoint = p1; dx = p2.x - x,
if (x2 || y2) { dy = p2.y - y,
var dot = (p.x - p1.x) * x2 + (p.y - p1.y) * y2, dot = dx * dx + dy * dy,
t = dot / this._sqDist(p1, p2); t;
if (dot > 0) {
t = ((p.x - x) * dx + (p.y - y) * dy) / dot;
if (t > 1) { if (t > 1) {
apoint = p2; x = p2.x;
y = p2.y;
} else if (t > 0) { } else if (t > 0) {
apoint = new L.Point(p1.x + x2 * t, p1.y + y2 * t); x += dx * t;
y += dy * t;
} }
} }
apoint._sqDist = this._sqDist(p, apoint);
return apoint;
},
// distance from a point to a segment between two points dx = p.x - x;
_sqPointToSegmentDist: function (p, p1, p2) { dy = p.y - y;
return this._sqClosestPointOnSegment(p, p1, p2)._sqDist;
return sqDist ? dx * dx + dy * dy : new L.Point(x, y);
} }
}; };

View File

@ -8,7 +8,8 @@ L.Popup = L.Class.extend({
autoPan: true, autoPan: true,
closeButton: true, closeButton: true,
offset: new L.Point(0, 2), offset: new L.Point(0, 2),
autoPanPadding: new L.Point(5, 5) autoPanPadding: new L.Point(5, 5),
className: ''
}, },
initialize: function (options, source) { initialize: function (options, source) {
@ -75,7 +76,7 @@ L.Popup = L.Class.extend({
}, },
_initLayout: function () { _initLayout: function () {
this._container = L.DomUtil.create('div', 'leaflet-popup'); this._container = L.DomUtil.create('div', 'leaflet-popup ' + this.options.className);
if (this.options.closeButton) { if (this.options.closeButton) {
this._closeButton = L.DomUtil.create('a', 'leaflet-popup-close-button', this._container); this._closeButton = L.DomUtil.create('a', 'leaflet-popup-close-button', this._container);

View File

@ -24,13 +24,19 @@ L.Icon = L.Class.extend({
_createIcon: function (name) { _createIcon: function (name) {
var size = this[name + 'Size'], var size = this[name + 'Size'],
src = this[name + 'Url'], src = this[name + 'Url'];
img = this._createImg(src); if (!src && name === 'shadow') {
if (!src) {
return null; return null;
} }
var img;
if (!src) {
img = this._createDiv();
}
else {
img = this._createImg(src);
}
img.className = 'leaflet-marker-' + name; img.className = 'leaflet-marker-' + name;
img.style.marginLeft = (-this.iconAnchor.x) + 'px'; img.style.marginLeft = (-this.iconAnchor.x) + 'px';
@ -54,5 +60,9 @@ L.Icon = L.Class.extend({
el.style.filter = 'progid:DXImageTransform.Microsoft.AlphaImageLoader(src="' + src + '")'; el.style.filter = 'progid:DXImageTransform.Microsoft.AlphaImageLoader(src="' + src + '")';
} }
return el; return el;
},
_createDiv: function () {
return document.createElement('div');
} }
}); });

View File

@ -5,7 +5,9 @@
L.Marker.include({ L.Marker.include({
openPopup: function () { openPopup: function () {
this._popup.setLatLng(this._latlng); this._popup.setLatLng(this._latlng);
this._map.openPopup(this._popup); if (this._map) {
this._map.openPopup(this._popup);
}
return this; return this;
}, },

View File

@ -10,7 +10,8 @@ L.Marker = L.Class.extend({
icon: new L.Icon(), icon: new L.Icon(),
title: '', title: '',
clickable: true, clickable: true,
draggable: false draggable: false,
zIndexOffset: 0
}, },
initialize: function (latlng, options) { initialize: function (latlng, options) {
@ -104,8 +105,7 @@ L.Marker = L.Class.extend({
L.DomUtil.setPosition(this._shadow, pos); L.DomUtil.setPosition(this._shadow, pos);
} }
this._icon.style.zIndex = pos.y; this._icon.style.zIndex = pos.y + this.options.zIndexOffset;
// TODO zIndex offset
}, },
_initInteraction: function () { _initInteraction: function () {

View File

@ -7,6 +7,17 @@ L.TileLayer.Canvas = L.TileLayer.extend({
L.Util.setOptions(this, options); L.Util.setOptions(this, options);
}, },
redraw: function () {
for (var i in this._tiles) {
var tile = this._tiles[i];
this._redrawTile(tile);
}
},
_redrawTile: function (tile) {
this.drawTile(tile, tile._tilePoint, tile._zoom);
},
_createTileProto: function () { _createTileProto: function () {
this._canvasProto = L.DomUtil.create('canvas', 'leaflet-tile'); this._canvasProto = L.DomUtil.create('canvas', 'leaflet-tile');
@ -23,6 +34,8 @@ L.TileLayer.Canvas = L.TileLayer.extend({
_loadTile: function (tile, tilePoint, zoom) { _loadTile: function (tile, tilePoint, zoom) {
tile._layer = this; tile._layer = this;
tile._tilePoint = tilePoint;
tile._zoom = zoom;
this.drawTile(tile, tilePoint, zoom); this.drawTile(tile, tilePoint, zoom);

View File

@ -17,9 +17,11 @@ L.TileLayer = L.Class.extend({
continuousWorld: false, continuousWorld: false,
noWrap: false, noWrap: false,
zoomOffset: 0, zoomOffset: 0,
zoomReverse: false,
unloadInvisibleTiles: L.Browser.mobile, unloadInvisibleTiles: L.Browser.mobile,
updateWhenIdle: L.Browser.mobile updateWhenIdle: L.Browser.mobile,
reuseTiles: false
}, },
initialize: function (url, options, urlParams) { initialize: function (url, options, urlParams) {
@ -125,6 +127,10 @@ L.TileLayer = L.Class.extend({
} }
this._tiles = {}; this._tiles = {};
if (this.options.reuseTiles) {
this._unusedTiles = [];
}
if (clearOldContainer && this._container) { if (clearOldContainer && this._container) {
this._container.innerHTML = ""; this._container.innerHTML = "";
} }
@ -145,7 +151,7 @@ L.TileLayer = L.Class.extend({
this._addTilesFromCenterOut(tileBounds); this._addTilesFromCenterOut(tileBounds);
if (this.options.unloadInvisibleTiles) { if (this.options.unloadInvisibleTiles || this.options.reuseTiles) {
this._removeOtherTiles(tileBounds); this._removeOtherTiles(tileBounds);
} }
}, },
@ -196,9 +202,14 @@ L.TileLayer = L.Class.extend({
// evil, don't do this! crashes Android 3, produces load errors, doesn't solve memory leaks // evil, don't do this! crashes Android 3, produces load errors, doesn't solve memory leaks
// this._tiles[key].src = ''; // this._tiles[key].src = '';
//tile.src = 'data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=';
if (tile.parentNode === this._container) { if (tile.parentNode === this._container) {
this._container.removeChild(tile); this._container.removeChild(tile);
} }
if (this.options.reuseTiles) {
this._unusedTiles.push(this._tiles[key]);
}
delete this._tiles[key]; delete this._tiles[key];
} }
} }
@ -209,7 +220,7 @@ L.TileLayer = L.Class.extend({
var tilePos = this._getTilePos(tilePoint), var tilePos = this._getTilePos(tilePoint),
zoom = this._map.getZoom(), zoom = this._map.getZoom(),
key = tilePoint.x + ':' + tilePoint.y, key = tilePoint.x + ':' + tilePoint.y,
tileLimit = Math.pow(2, (zoom + this.options.zoomOffset)); tileLimit = Math.pow(2, this._getOffsetZoom(zoom));
// wrap tile coordinates // wrap tile coordinates
if (!this.options.continuousWorld) { if (!this.options.continuousWorld) {
@ -226,8 +237,8 @@ L.TileLayer = L.Class.extend({
} }
} }
// create tile // get unused tile - or create a new tile
var tile = this._createTile(); var tile = this._getTile();
L.DomUtil.setPosition(tile, tilePos); L.DomUtil.setPosition(tile, tilePos);
this._tiles[key] = tile; this._tiles[key] = tile;
@ -241,6 +252,11 @@ L.TileLayer = L.Class.extend({
container.appendChild(tile); container.appendChild(tile);
}, },
_getOffsetZoom: function (zoom) {
zoom = this.options.zoomReverse ? this.options.maxZoom - zoom : zoom;
return zoom + this.options.zoomOffset;
},
_getTilePos: function (tilePoint) { _getTilePos: function (tilePoint) {
var origin = this._map.getPixelOrigin(), var origin = this._map.getPixelOrigin(),
tileSize = this.options.tileSize; tileSize = this.options.tileSize;
@ -256,7 +272,7 @@ L.TileLayer = L.Class.extend({
return L.Util.template(this._url, L.Util.extend({ return L.Util.template(this._url, L.Util.extend({
s: s, s: s,
z: zoom + this.options.zoomOffset, z: this._getOffsetZoom(zoom),
x: tilePoint.x, x: tilePoint.x,
y: tilePoint.y y: tilePoint.y
}, this._urlParams)); }, this._urlParams));
@ -271,6 +287,19 @@ L.TileLayer = L.Class.extend({
this._tileImg.style.height = tileSize + 'px'; this._tileImg.style.height = tileSize + 'px';
}, },
_getTile: function () {
if (this.options.reuseTiles && this._unusedTiles.length > 0) {
var tile = this._unusedTiles.pop();
this._resetTile(tile);
return tile;
}
return this._createTile();
},
_resetTile: function (tile) {
// Override if data stored on a tile needs to be cleaned up before reuse
},
_createTile: function () { _createTile: function () {
var tile = this._tileImg.cloneNode(false); var tile = this._tileImg.cloneNode(false);
tile.onselectstart = tile.onmousemove = L.Util.falseFn; tile.onselectstart = tile.onmousemove = L.Util.falseFn;

View File

@ -5,7 +5,7 @@
L.Path.include({ L.Path.include({
bindPopup: function (content, options) { bindPopup: function (content, options) {
if (!this._popup || this._popup.options !== options) { if (!this._popup || this._popup.options !== options) {
this._popup = new L.Popup(options); this._popup = new L.Popup(options, this);
} }
this._popup.setContent(content); this._popup.setContent(content);

View File

@ -10,7 +10,7 @@ L.Polygon = L.Polyline.extend({
initialize: function (latlngs, options) { initialize: function (latlngs, options) {
L.Polyline.prototype.initialize.call(this, latlngs, options); L.Polyline.prototype.initialize.call(this, latlngs, options);
if (latlngs[0] instanceof Array) { if (latlngs && (latlngs[0] instanceof Array)) {
this._latlngs = latlngs[0]; this._latlngs = latlngs[0];
this._holes = latlngs.slice(1); this._holes = latlngs.slice(1);
} }

View File

@ -253,7 +253,7 @@ L.Map = L.Class.extend({
return this; return this;
} }
this._rawPanBy(oldSize.subtract(this.getSize()).divideBy(2)); this._rawPanBy(oldSize.subtract(this.getSize()).divideBy(2, true));
this.fire('move'); this.fire('move');
@ -280,8 +280,8 @@ L.Map = L.Class.extend({
getBounds: function () { getBounds: function () {
var bounds = this.getPixelBounds(), var bounds = this.getPixelBounds(),
sw = this.unproject(new L.Point(bounds.min.x, bounds.max.y)), sw = this.unproject(new L.Point(bounds.min.x, bounds.max.y), this._zoom, true),
ne = this.unproject(new L.Point(bounds.max.x, bounds.min.y)); ne = this.unproject(new L.Point(bounds.max.x, bounds.min.y), this._zoom, true);
return new L.LatLngBounds(sw, ne); return new L.LatLngBounds(sw, ne);
}, },
@ -328,7 +328,7 @@ L.Map = L.Class.extend({
} }
} while (zoomNotFound && (zoom <= maxZoom)); } while (zoomNotFound && (zoom <= maxZoom));
if (zoomNotFound) { if (zoomNotFound && inside) {
return null; return null;
} }
@ -510,7 +510,7 @@ L.Map = L.Class.extend({
_initEvents: function () { _initEvents: function () {
L.DomEvent.addListener(this._container, 'click', this._onMouseClick, this); L.DomEvent.addListener(this._container, 'click', this._onMouseClick, this);
var events = ['dblclick', 'mousedown', 'mouseenter', 'mouseleave', 'mousemove']; var events = ['dblclick', 'mousedown', 'mouseenter', 'mouseleave', 'mousemove', 'contextmenu'];
var i, len; var i, len;
@ -548,6 +548,10 @@ L.Map = L.Class.extend({
return; return;
} }
if (type === 'contextmenu') {
L.DomEvent.preventDefault(e);
}
this.fire(type, { this.fire(type, {
latlng: this.mouseEventToLatLng(e), latlng: this.mouseEventToLatLng(e),
layerPoint: this.mouseEventToLayerPoint(e) layerPoint: this.mouseEventToLayerPoint(e)

View File

@ -42,7 +42,7 @@ L.Map.include({
options = L.Util.extend({ options = L.Util.extend({
maxZoom: maxZoom || Infinity, maxZoom: maxZoom || Infinity,
setView: true setView: true
}); }, options);
return this.locate(options); return this.locate(options);
}, },