diff --git a/CHANGELOG.md b/CHANGELOG.md index ecf79317..68c06f39 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,72 +7,123 @@ Leaflet Changelog An in-progress version being developed on the master branch. +### Breaking changes + +Be sure to read through these changes to avoid any issues when upgrading from older versions: + + * Removed default `LatLng` wrapping/clamping of coordinates (`-180, -90` to `180, 90`), wrapping moved to an explicit method (`LatLng` `wrap`). + * Disabled `Map` `worldCopyJump` option by default (jumping back to the original world copy when panning out of it). Enable it explicitly if you need it. + * Changed styles for the zoom control (you may need to update your custom styles for it). + ### Improvements #### Usability improvements - * Added touch zoom, pan and double tap support for **IE10 touch devices and Metro apps** (by [@danzel](https://github.com/danzel) and [@veproza](https://github.com/veproza) with help from [@oliverheilig](https://github.com/oliverheilig)). [#1076](https://github.com/CloudMade/Leaflet/pull/1076) [#871](https://github.com/CloudMade/Leaflet/issues/871) +##### Interaction + + * Added touch zoom, pan and double tap support for **IE10 touch devices and Metro apps** (by [@danzel](https://github.com/danzel) and [@veproza](https://github.com/veproza) with help from [@oliverheilig](https://github.com/oliverheilig)). [#1076](https://github.com/Leaflet/Leaflet/pull/1076) [#871](https://github.com/Leaflet/Leaflet/issues/871) * **Improved panning inertia** to be much more natural and smooth. - * Improved **zoom control design** to look better, be more design-neutral and in line with other controls, making it easier to fit different website designs. Replaced +/- images with text. - * Improved **dragging cursors** in Chrome, Safari and Firefox (now grabbing hand cursors are used). - * Improved zoom control to zoom by 3 levels if you hold shift while clicking on a button. - * Improved scroll wheel zoom to be more responsive. + * **Improved dragging cursors** in Chrome, Safari and Firefox (now grabbing hand cursors are used). * Improved zoom animation curve for a better feel overall. - * Improved fallback control styles for IE6-8. + * Improved scroll wheel zoom to be more responsive. * Improved panning animation performance in IE6-8. - * Improved vectors updating/removing performance on Canvas backend even more (by [@danzel](https://github.com/danzel)). [#961](https://github.com/CloudMade/Leaflet/pull/961) + +##### Controls + + * **Improved zoom control design** to look better, more neutral and in line with other controls, making it easier to customize and fit different website designs. Replaced +/- images with text. + * Improved zoom control to zoom by 3 levels if you hold shift while clicking on a button. + * Improved zoom control buttons to become visually disabled when min/max zoom is reached. [#917](https://github.com/Leaflet/Leaflet/issues/917) + * Improved scale control styles. + * Improved fallback control styles for IE6-8. + +##### Other + + * Added **retina support for markers** (through `Icon` `iconRetinaUrl` and `shadowRetinaUrl` options) (by [@danzel](https://github.com/danzel)). [#1048](https://github.com/Leaflet/Leaflet/issues/1048) [#1174](https://github.com/Leaflet/Leaflet/pull/1174) + * Added retina-sized default marker icon in addition to standard one (along with its SVG source and with some subtle design improvements) (by [@danzel](https://github.com/danzel)). [#1048](https://github.com/Leaflet/Leaflet/issues/1048) [#1174](https://github.com/Leaflet/Leaflet/pull/1174) + * Improved vectors updating/removing performance on Canvas backend (by [@danzel](https://github.com/danzel)). [#961](https://github.com/Leaflet/Leaflet/pull/961) * Cut total images size from 10KB to 3.2KB with [Yahoo Smush.it](http://www.smushit.com/ysmush.it/). Thanks to Peter Rounce for suggestion. #### API improvements * Replaced `L.Transition` with a much better and simpler `L.PosAnimation`. + * Added `Class` `addInitHook` method for **adding constructor hooks to any classes** (great extension point for plugin authors). [#1123](https://github.com/Leaflet/Leaflet/issues/1123) + * Added `Map` `whenReady` method (by [@jfirebaugh](https://github.com/jfirebaugh)). [#1063](https://github.com/Leaflet/Leaflet/pull/1063) * Added optional `delta` argument to `Map` `zoomIn` and `zoomOut` (1 by default). - * Added `isValid` method to `LatLngBounds` and `Bounds` (by [@domoritz](https://github.com/domoritz)). [#972](https://github.com/CloudMade/Leaflet/pull/972) - * Improved markers and vectors click event so that it propagates to map if no one is listening to it (by [@danzel](https://github.com/danzel)). [#834](https://github.com/CloudMade/Leaflet/issues/834) [#1033](https://github.com/CloudMade/Leaflet/pull/1033) + * Added `isValid` method to `LatLngBounds` and `Bounds` (by [@domoritz](https://github.com/domoritz)). [#972](https://github.com/Leaflet/Leaflet/pull/972) + * Added `Point` `equals` method. + * Added `Bounds` `getSize` method. + * Improved markers and vectors click event so that it propagates to map if no one is listening to it (by [@danzel](https://github.com/danzel)). [#834](https://github.com/Leaflet/Leaflet/issues/834) [#1033](https://github.com/Leaflet/Leaflet/pull/1033) * Added `Path` `unbindPopup` and `closePopup` methods. - * Added `Path` `remove` event. - * Added `Marker` `riseOnHover` and `riseOffset` options (for bringing markers to front on hover, disabled by default) (by [jacobtoye](https://github.com/jacobtoye)). [#914](https://github.com/CloudMade/Leaflet/pull/914) [#920](https://github.com/CloudMade/Leaflet/issues/920) + * Added `Path` `add` and `remove` event. + * Added `Marker` `riseOnHover` and `riseOffset` options (for bringing markers to front on hover, disabled by default) (by [jacobtoye](https://github.com/jacobtoye)). [#914](https://github.com/Leaflet/Leaflet/pull/914) [#920](https://github.com/Leaflet/Leaflet/issues/920) * Added `Marker` `move` and `remove` events. - * Added `Map` `whenReady` method (by [@jfirebaugh](https://github.com/jfirebaugh)). [#1063](https://github.com/CloudMade/Leaflet/pull/1063) - * Added `FeatureGroup` `layeradd` and `layerremove` events (by [@jacobtoye](https://github.com/jacobtoye)). [#1122](https://github.com/CloudMade/Leaflet/issues/1122) - * Added `Control.Layers` `baselayerchange` event (by [@jfirebaugh](https://github.com/jfirebaugh)). [#1064](https://github.com/CloudMade/Leaflet/pull/1064) - * Improved `Control.Layers` to support HTML in layer names (by [@aparshin](https://github.com/aparshin)). [#1055](https://github.com/CloudMade/Leaflet/pull/1055) [#1099](https://github.com/CloudMade/Leaflet/issues/1099) - * Removed `Browser` `ua`, `ie`, `gecko`, `opera` properties (no longer needed). - * Added `CRS.Simple` to the list of built-in CRS. It was added earlier but not included in the build. + * Added `Marker` `contextmenu` event. [#223](https://github.com/Leaflet/Leaflet/issues/223) + * Added `Popup` `zoomAnimation` option (useful to disable when displaying flash content inside popups [#999](https://github.com/Leaflet/Leaflet/issues/999)). + * Added `FeatureGroup` `layeradd` and `layerremove` events (by [@jacobtoye](https://github.com/jacobtoye)). [#1122](https://github.com/Leaflet/Leaflet/issues/1122) + * Added `Control.Layers` `baselayerchange` event (by [@jfirebaugh](https://github.com/jfirebaugh)). [#1064](https://github.com/Leaflet/Leaflet/pull/1064) + * Improved `Control.Layers` to support HTML in layer names (by [@aparshin](https://github.com/aparshin)). [#1055](https://github.com/Leaflet/Leaflet/pull/1055) [#1099](https://github.com/Leaflet/Leaflet/issues/1099) + * Added `CRS.Simple` to the list of built-in CRS and improved it to be more usable out of the box (it has different default scaling and transformation now), see `debug/map/simple-proj.html` for an example. + * Removed `Browser` `ua`, `gecko`, `opera` properties (no longer needed). * Added `L.extend`, `L.bind`, `L.stamp`, `L.setOptions` shortcuts for corresponding `L.Util` methods. + * Disabled clearing of map container contents on map initialization (as a result of fixing [#278](https://github.com/Leaflet/Leaflet/issues/278)). + * Added `L.Util.isArray` function (by [@oslek](https://github.com/oslek)). [#1279](https://github.com/Leaflet/Leaflet/pull/1279) ### Bugfixes #### General bugfixes - * Fixed broken tiles and zooming in RTL layouts (by [@danzel](https://github.com/danzel)). [#1099](https://github.com/CloudMade/Leaflet/pull/1099) [#1095](https://github.com/CloudMade/Leaflet/issues/1095) + * Fixed broken tiles and zooming in RTL layouts (by [@danzel](https://github.com/danzel)). [#1099](https://github.com/Leaflet/Leaflet/pull/1099) [#1095](https://github.com/Leaflet/Leaflet/issues/1095) * Fixed a bug with pan animation where it jumped to its end position if you tried to drag the map. - * Fixed a bug with shift-clicking on a zoom button leading to unexpected result. + * Fixed a bug where shift-clicking on a map would zoom it to the max zoom level. * Fixed a glitch with zooming in while panning animation is running. * Fixed a glitch with dragging the map while zoom animation is running. + * Fixed a bug where slight touchpad scrolling or one-wheel scrolling wouln't always perform zooming. [#1039](https://github.com/Leaflet/Leaflet/issues/1039) + * Fixed a bug where `panBy` wouldn't round the offset values (so it was possible to make the map blurry with it). [#1085](https://github.com/Leaflet/Leaflet/issues/1085) + * Fixed a bug where you couldn't scroll the layers control with a mouse wheel. + * Fixed a regression where WMS tiles wouldn't wrap on date lines. [#970](https://github.com/Leaflet/Leaflet/issues/970) + * Fixed a bug where mouse interaction was affected by map container border width (by [@mohlendo](https://github.com/mohlendo)). [#1204](https://github.com/Leaflet/Leaflet/issues/1205) [#1205](https://github.com/Leaflet/Leaflet/pull/1205) + * Fixed a bug with weird vector zoom animation when using Canvas for rendering (by [@danzel](https://github.com/danzel)). [#1187](https://github.com/Leaflet/Leaflet/issues/1187) [#1188](https://github.com/Leaflet/Leaflet/pull/1188) + * Fixed a bug where max bounds limitation didn't work when navigating the map with a keyboard (by [@snkashis](https://github.com/snkashis)). [#989](https://github.com/Leaflet/Leaflet/issues/989) [#1221](https://github.com/Leaflet/Leaflet/pull/1221) #### API bugfixes - * Fixed a bug where `TileLayer` `bringToBack` didn't work properly in some cases (by [@danzel](https://github.com/danzel)). [#963](https://github.com/CloudMade/Leaflet/pull/963) [#959](https://github.com/CloudMade/Leaflet/issues/959) - * Fixed a bug where removing a tile layer while dragging would throw an error (by [@danzel](https://github.com/danzel)). [#965](https://github.com/CloudMade/Leaflet/issues/965) [#968](https://github.com/CloudMade/Leaflet/pull/968) - * Fixed a bug where middle marker wasn't removed after deleting 2 end nodes from a polyline (by [@Svad](https://github.com/Svad)). [#1022](https://github.com/CloudMade/Leaflet/issues/1022) [#1023](https://github.com/CloudMade/Leaflet/pull/1023) - * Fixed a bug where `Map` `load` event happened too late (after `moveend`, etc.) (by [@jfirebaugh](https://github.com/jfirebaugh)). [#1027](https://github.com/CloudMade/Leaflet/pull/1027) - * Fixed `Circle` `getBounds` to return correct bounds and work without adding the circle to a map. [#1068](https://github.com/CloudMade/Leaflet/issues/1068) - * Fixed a bug where removing `Popup` on `viewreset` throwed an error (by [fnicollet](https://github.com/fnicollet) and [@danzel](https://github.com/danzel)). [#1098](https://github.com/CloudMade/Leaflet/pull/1098) [#1094](https://github.com/CloudMade/Leaflet/issues/1094) + * Fixed a bug where `TileLayer` `bringToBack` didn't work properly in some cases (by [@danzel](https://github.com/danzel)). [#963](https://github.com/Leaflet/Leaflet/pull/963) [#959](https://github.com/Leaflet/Leaflet/issues/959) + * Fixed a bug where removing a tile layer while dragging would throw an error (by [@danzel](https://github.com/danzel)). [#965](https://github.com/Leaflet/Leaflet/issues/965) [#968](https://github.com/Leaflet/Leaflet/pull/968) + * Fixed a bug where middle marker wasn't removed after deleting 2 end nodes from a polyline (by [@Svad](https://github.com/Svad)). [#1022](https://github.com/Leaflet/Leaflet/issues/1022) [#1023](https://github.com/Leaflet/Leaflet/pull/1023) + * Fixed a bug where `Map` `load` event happened too late (after `moveend`, etc.) (by [@jfirebaugh](https://github.com/jfirebaugh)). [#1027](https://github.com/Leaflet/Leaflet/pull/1027) + * Fixed `Circle` `getBounds` to return correct bounds and work without adding the circle to a map. [#1068](https://github.com/Leaflet/Leaflet/issues/1068) + * Fixed a bug where removing `Popup` on `viewreset` throwed an error (by [fnicollet](https://github.com/fnicollet) and [@danzel](https://github.com/danzel)). [#1098](https://github.com/Leaflet/Leaflet/pull/1098) [#1094](https://github.com/Leaflet/Leaflet/issues/1094) * Fixed a bug where `TileLayer.Canvas` `drawTile` didn't receive tile zoom level in arguments. + * Fixed a bug where `GeoJSON` `resetStyle` would not fully reset a layer to its default style. [#1112](https://github.com/Leaflet/Leaflet/issues/1112) + * Fixed a bug that caused infinite recursion when using `latLngBounds` factory with coordinates as string values. [#933](https://github.com/Leaflet/Leaflet/issues/933) + * Fixed chaining on `Marker` `setIcon`, `setZIndexOffset`, `update` methods. [#1176](https://github.com/Leaflet/Leaflet/issues/1176) + * Fixed a bug with mouse interaction when the map container contained children with position other than absolute. [#278](https://github.com/Leaflet/Leaflet/issues/278) + * Fixed a bug with fill/stroke opacity conflicts when using Canvas for rendering (by [@danzel](https://github.com/danzel)). [#1186](https://github.com/Leaflet/Leaflet/issues/1186) [#1889](https://github.com/Leaflet/Leaflet/pull/1189) + * Fixed a bug where `FeatureGroup` `bindPopup` didn't take options into account. + * Fixed a bug where Canvas-based vector layers didn't cleanup click event on removal properly (by [@snkashis](https://github.com/snkashis)). [#1006](https://github.com/Leaflet/Leaflet/issues/1006) [#1273](https://github.com/Leaflet/Leaflet/pull/1273) + * Fixed a bug where `CircleMarker` `setStyle` didn't take `radius` into account (by [@fdlk](https://github.com/fdlk)). [#1012](https://github.com/Leaflet/Leaflet/issues/1012) [#1013](https://github.com/Leaflet/Leaflet/pull/1013) + * Fixed a bug where null GeoJSON geometries would throw an error instead of skipping (by [@brianherbert](https://github.com/brianherbert)). [#1240](https://github.com/Leaflet/Leaflet/pull/1240) + * Fixed a bug where Canvas-based vector layers passed incorrect `layer` event property on click (by [@snkashis](https://github.com/snkashis)). [#1215](https://github.com/Leaflet/Leaflet/issues/1215) [#1243](https://github.com/Leaflet/Leaflet/pull/1243) + * Fixed a bug where `TileLayer.WMS` didn't work correctly if the base URL contained query parameters (by [@snkashis](https://github.com/snkashis)). [#973](https://github.com/Leaflet/Leaflet/issues/973) [#1231](https://github.com/Leaflet/Leaflet/pull/1231) + * Fixed a bug where removing a polyline in editing state wouldn't clean up the editing handles (by [@mehmeta](https://github.com/mehmeta)). [#1233](https://github.com/Leaflet/Leaflet/pull/1233) + * Fixed a bug where removing a vector layer with a bound popup wouldn't clean up its click event properly (by [@yohanboniface](https://github.com/yohanboniface)). [#1229](https://github.com/Leaflet/Leaflet/pull/1229) + * Fixed a bug where `GeoJSON` features with `GeometryCollection` didn't pass properties to `pointToLayer` function (by [@calvinmetcalf](https://github.com/calvinmetcalf)). [#1097](https://github.com/Leaflet/Leaflet/pull/1097) #### Browser bugfixes - * Fixed a bug where "Not implemented" error sometimes appeared in IE6-8 (by [@bryguy](https://github.com/bryguy) and [@lookfirst](https://github.com/lookfirst)). [#892](https://github.com/CloudMade/Leaflet/issues/892) [#893](https://github.com/CloudMade/Leaflet/pull/893) - * Fixed compatibility with SmoothWheel extension for Firefox (by [@waldir](https://github.com/waldir)). [#1011](https://github.com/CloudMade/Leaflet/pull/1011) - * Fixed a bug with popup layout in IE6-7 (by [@danzel](https://github.com/danzel)). [#1117](https://github.com/CloudMade/Leaflet/issues/1117) - * Fixed a bug with incorrect box zoom opacity in IE6-7 (by [@jacobtoye](https://github.com/jacobtoye)). [#1072](https://githubcom/CloudMade/Leaflet/pull/1072) - * Fixed a bug with box zoom throwing a JS error in IE6-7 (by [@danzel](https://github.com/danzel)). [#1071](https://github.com/CloudMade/Leaflet/pull/1071) + * Fixed a bug with map **freezing after zoom on Android 4.1**. [#1182](https://github.com/Leaflet/Leaflet/issues/1182) + * Fixed a bug where "Not implemented" error sometimes appeared in IE6-8 (by [@bryguy](https://github.com/bryguy) and [@lookfirst](https://github.com/lookfirst)). [#892](https://github.com/Leaflet/Leaflet/issues/892) [#893](https://github.com/Leaflet/Leaflet/pull/893) + * Fixed compatibility with SmoothWheel extension for Firefox (by [@waldir](https://github.com/waldir)). [#1011](https://github.com/Leaflet/Leaflet/pull/1011) + * Fixed a bug with popup layout in IE6-7 (by [@danzel](https://github.com/danzel)). [#1117](https://github.com/Leaflet/Leaflet/issues/1117) + * Fixed a bug with incorrect box zoom opacity in IE6-7 (by [@jacobtoye](https://github.com/jacobtoye)). [#1072](https://githubcom/Leaflet/Leaflet/pull/1072) + * Fixed a bug with box zoom throwing a JS error in IE6-7 (by [@danzel](https://github.com/danzel)). [#1071](https://github.com/Leaflet/Leaflet/pull/1071) + * Fixed a bug where `TileLayer` `bringToFront/Back()` throwed an error in IE6-8. [#1168](https://github.com/Leaflet/Leaflet/issues/1168) + * Fixed array type checking in the code to be more consistent in a cross-frame environment (by [@oslek](https://github.com/oslek)). [#1279](https://github.com/Leaflet/Leaflet/pull/1279) ## 0.4.5 (October 25, 2012) - * Fixed a bug with **wonky zoom animation in IE10** (by [@danzel](https://github.com/danzel)). [#1007](https://github.com/CloudMade/Leaflet/pull/1007) - * Fixed a bug with **wonky zoom animation in Chrome 23+** (by [@danzel](https://github.com/danzel)). [#1060](https://github.com/CloudMade/Leaflet/pull/1060) [#1056](https://github.com/CloudMade/Leaflet/issues/1056) + * Fixed a bug with **wonky zoom animation in IE10** (by [@danzel](https://github.com/danzel)). [#1007](https://github.com/Leaflet/Leaflet/pull/1007) + * Fixed a bug with **wonky zoom animation in Chrome 23+** (by [@danzel](https://github.com/danzel)). [#1060](https://github.com/Leaflet/Leaflet/pull/1060) [#1056](https://github.com/Leaflet/Leaflet/issues/1056) ## 0.4.4 (August 7, 2012) @@ -82,26 +133,26 @@ An in-progress version being developed on the master branch. * Added `GeoJSON` `resetStyle(layer)`, useful for resetting hover state. * Added `feature` property to layers created with `GeoJSON` (containing the GeoJSON feature data). * Added `FeatureGroup` `bringToFront` and `bringToBack` methods (so that they would work for multipolys). - * Added optional `animate` argument to `Map` `invalidateSize` (by [@ajbeaven](https://github.com/ajbeaven)). [#857](https://github.com/CloudMade/Leaflet/pull/857) + * Added optional `animate` argument to `Map` `invalidateSize` (by [@ajbeaven](https://github.com/ajbeaven)). [#857](https://github.com/Leaflet/Leaflet/pull/857) ### Bugfixes - * Fixed a bug where tiles sometimes disappeared on initial map load on Android 2/3 (by [@danzel](https://github.com/danzel)). [#868](https://github.com/CloudMade/Leaflet/pull/868) + * Fixed a bug where tiles sometimes disappeared on initial map load on Android 2/3 (by [@danzel](https://github.com/danzel)). [#868](https://github.com/Leaflet/Leaflet/pull/868) * Fixed a bug where map would occasionally flicker near the border on zoom or pan on Chrome. * Fixed a bug where `Path` `bringToFront` and `bringToBack` didn't return `this`. - * Removed zoom out on Win/Meta key binding (since it interferes with global keyboard shortcuts). [#869](https://github.com/CloudMade/Leaflet/issues/869) + * Removed zoom out on Win/Meta key binding (since it interferes with global keyboard shortcuts). [#869](https://github.com/Leaflet/Leaflet/issues/869) ## 0.4.2 (August 1, 2012) - * Fixed a bug where layers control radio buttons would not work correctly in IE7 (by [@danzel](https://github.com/danzel)). [#862](https://github.com/CloudMade/Leaflet/pull/862) - * Fixed a bug where `FeatureGroup` `removeLayer` would unbind popups of removed layers even if the popups were not put by the group (affected [Leaflet.markercluster](https://github.com/danzel/Leaflet.markercluster) plugin) (by [@danzel](https://github.com/danzel)). [#861](https://github.com/CloudMade/Leaflet/pull/861) + * Fixed a bug where layers control radio buttons would not work correctly in IE7 (by [@danzel](https://github.com/danzel)). [#862](https://github.com/Leaflet/Leaflet/pull/862) + * Fixed a bug where `FeatureGroup` `removeLayer` would unbind popups of removed layers even if the popups were not put by the group (affected [Leaflet.markercluster](https://github.com/danzel/Leaflet.markercluster) plugin) (by [@danzel](https://github.com/danzel)). [#861](https://github.com/Leaflet/Leaflet/pull/861) ## 0.4.1 (July 31, 2012) - * Fixed a bug that caused marker shadows appear as opaque black in IE6-8. [#850](https://github.com/CloudMade/Leaflet/issues/850) - * Fixed a bug with incorrect calculation of scale by the scale control. [#852](https://github.com/CloudMade/Leaflet/issues/852) - * Fixed broken L.tileLayer.wms class factory (by [@mattcurrie](https://github.com/mattcurrie)). [#856](https://github.com/CloudMade/Leaflet/issues/856) - * Improved retina detection for `TileLayer` `detectRetina` option (by [@sxua](https://github.com/sxua)). [#854](https://github.com/CloudMade/Leaflet/issues/854) + * Fixed a bug that caused marker shadows appear as opaque black in IE6-8. [#850](https://github.com/Leaflet/Leaflet/issues/850) + * Fixed a bug with incorrect calculation of scale by the scale control. [#852](https://github.com/Leaflet/Leaflet/issues/852) + * Fixed broken L.tileLayer.wms class factory (by [@mattcurrie](https://github.com/mattcurrie)). [#856](https://github.com/Leaflet/Leaflet/issues/856) + * Improved retina detection for `TileLayer` `detectRetina` option (by [@sxua](https://github.com/sxua)). [#854](https://github.com/Leaflet/Leaflet/issues/854) ## 0.4 (July 30, 2012) @@ -116,26 +167,26 @@ Leaflet 0.4 contains several API improvements that allow simpler, jQuery-like sy ### Notable new features * Added configurable **panning inertia** - after a quick pan, the map slows down in the same direction. - * Added **keyboard navigation** for panning/zooming with keyboard arrows and +/- keys (by [@ericmmartinez](https://github.com/ericmmartinez)). [#663](https://github.com/CloudMade/Leaflet/pull/663) [#646](https://github.com/CloudMade/Leaflet/issues/646) - * Added smooth **zoom animation of markers, vector layers, image overlays and popups** (by [@danzel](https://github.com/danzel)). [#740](https://github.com/CloudMade/Leaflet/pull/740) [#758](https://github.com/CloudMade/Leaflet/issues/758) - * Added **Android 4+ pinch-zoom** support (by [@danzel](https://github.com/danzel)). [#774](https://github.com/CloudMade/Leaflet/pull/774) - * Added **polyline and polygon editing**. [#174](https://github.com/CloudMade/Leaflet/issues/174) + * Added **keyboard navigation** for panning/zooming with keyboard arrows and +/- keys (by [@ericmmartinez](https://github.com/ericmmartinez)). [#663](https://github.com/Leaflet/Leaflet/pull/663) [#646](https://github.com/Leaflet/Leaflet/issues/646) + * Added smooth **zoom animation of markers, vector layers, image overlays and popups** (by [@danzel](https://github.com/danzel)). [#740](https://github.com/Leaflet/Leaflet/pull/740) [#758](https://github.com/Leaflet/Leaflet/issues/758) + * Added **Android 4+ pinch-zoom** support (by [@danzel](https://github.com/danzel)). [#774](https://github.com/Leaflet/Leaflet/pull/774) + * Added **polyline and polygon editing**. [#174](https://github.com/Leaflet/Leaflet/issues/174) * Added an unobtrusive **scale control**. * Added **DivIcon** class that easily allows you to create lightweight div-based markers. - * Added **Rectangle** vector layer (by [@JasonSanford](https://github.com/JasonSanford)). [#504](https://github.com/CloudMade/Leaflet/pull/504) + * Added **Rectangle** vector layer (by [@JasonSanford](https://github.com/JasonSanford)). [#504](https://github.com/Leaflet/Leaflet/pull/504) ### Improvements #### Usability improvements - * Improved zooming so that you don't get a blank map when you zoom in or out twice quickly (by [@danzel](https://github.com/danzel)). [#7](https://github.com/CloudMade/Leaflet/issues/7) [#729](https://github.com/CloudMade/Leaflet/pull/729) - * Drag-panning now works even when there are markers in the starting point (helps on maps with lots of markers). [#506](https://github.com/CloudMade/Leaflet/issues/506) + * Improved zooming so that you don't get a blank map when you zoom in or out twice quickly (by [@danzel](https://github.com/danzel)). [#7](https://github.com/Leaflet/Leaflet/issues/7) [#729](https://github.com/Leaflet/Leaflet/pull/729) + * Drag-panning now works even when there are markers in the starting point (helps on maps with lots of markers). [#506](https://github.com/Leaflet/Leaflet/issues/506) * Improved panning performance even more (there are no wasted frames now). * Improved pinch-zoom performance in mobile Chrome and Firefox. * Improved map performance on window resize. * Replaced box-shadow with border on controls for mobile devices to improve performance. * Slightly improved default popup styling. - * Added `TileLayer` `detectRetina` option (`false` by default) that makes tiles show in a higher resolution on iOS retina displays (by [@Mithgol](https://github.com/Mithgol)). [#586](https://github.com/CloudMade/Leaflet/pull/586) + * Added `TileLayer` `detectRetina` option (`false` by default) that makes tiles show in a higher resolution on iOS retina displays (by [@Mithgol](https://github.com/Mithgol)). [#586](https://github.com/Leaflet/Leaflet/pull/586) #### GeoJSON API changes @@ -173,100 +224,100 @@ Icon API was improved to be more flexible, but one of the changes is backwards-i #### Other API improvements - * Improved `on` and `off` methods to also accept `(eventHash[, context])`, as well as multiple space-separated events (by [@Guiswa](https://github.com/Guiswa)). [#770](https://github.com/CloudMade/Leaflet/pull/770) - * Improved `off` to remove all listeners of the event if no function was specified (by [@Guiswa](https://github.com/Guiswa)). [#770](https://github.com/CloudMade/Leaflet/pull/770) [#691](https://github.com/CloudMade/Leaflet/issues/691) - * Added `TileLayer` `setZIndex` method for controlling the order of tile layers (thanks to [@mattcurrie](https://github.com/mattcurrie)). [#837](https://github.com/CloudMade/Leaflet/pull/837) + * Improved `on` and `off` methods to also accept `(eventHash[, context])`, as well as multiple space-separated events (by [@Guiswa](https://github.com/Guiswa)). [#770](https://github.com/Leaflet/Leaflet/pull/770) + * Improved `off` to remove all listeners of the event if no function was specified (by [@Guiswa](https://github.com/Guiswa)). [#770](https://github.com/Leaflet/Leaflet/pull/770) [#691](https://github.com/Leaflet/Leaflet/issues/691) + * Added `TileLayer` `setZIndex` method for controlling the order of tile layers (thanks to [@mattcurrie](https://github.com/mattcurrie)). [#837](https://github.com/Leaflet/Leaflet/pull/837) * Added `Control.Layers` `autoZIndex` option (on by default) to preserve the order of tile layers when switching. - * Added `TileLayer` `redraw` method for re-requesting tiles (by [@greeninfo](https://github.com/greeninfo)). [#719](https://github.com/CloudMade/Leaflet/issues/719) + * Added `TileLayer` `redraw` method for re-requesting tiles (by [@greeninfo](https://github.com/greeninfo)). [#719](https://github.com/Leaflet/Leaflet/issues/719) * Added `TileLayer` `setUrl` method for dynamically changing the tile URL template. - * Added `bringToFront` and `bringToBack` methods to `TileLayer`, `ImageOverlay` and vector layers. [#185](https://github.com/CloudMade/Leaflet/issues/185) [#505](https://github.com/CloudMade/Leaflet/issues/505) - * Added `TileLayer` `loading` event that fires when its tiles start to load (thanks to [@lapinos03](https://github.com/lapinos03)). [#177](https://github.com/CloudMade/Leaflet/issues/177) - * Added `TileLayer.WMS` `setParams` method for setting WMS parameters at runtime (by [@greeninfo](https://github.com/greeninfo)). [#719](https://github.com/CloudMade/Leaflet/issues/719) - * Added `TileLayer.WMS` subdomain support (`{s}` in the url) (by [@greeninfo](https://github.com/greeninfo)). [#735](https://github.com/CloudMade/Leaflet/issues/735) - * Added `originalEvent` property to `MouseEvent` (by [@k4](https://github.com/k4)). [#521](https://github.com/CloudMade/Leaflet/pull/521) - * Added `containerPoint` property to `MouseEvent`. [#413](https://github.com/CloudMade/Leaflet/issues/413) - * Added `contextmenu` event to vector layers (by [@ErrorProne](https://github.com/ErrorProne)). [#500](https://github.com/CloudMade/Leaflet/pull/500) + * Added `bringToFront` and `bringToBack` methods to `TileLayer`, `ImageOverlay` and vector layers. [#185](https://github.com/Leaflet/Leaflet/issues/185) [#505](https://github.com/Leaflet/Leaflet/issues/505) + * Added `TileLayer` `loading` event that fires when its tiles start to load (thanks to [@lapinos03](https://github.com/lapinos03)). [#177](https://github.com/Leaflet/Leaflet/issues/177) + * Added `TileLayer.WMS` `setParams` method for setting WMS parameters at runtime (by [@greeninfo](https://github.com/greeninfo)). [#719](https://github.com/Leaflet/Leaflet/issues/719) + * Added `TileLayer.WMS` subdomain support (`{s}` in the url) (by [@greeninfo](https://github.com/greeninfo)). [#735](https://github.com/Leaflet/Leaflet/issues/735) + * Added `originalEvent` property to `MouseEvent` (by [@k4](https://github.com/k4)). [#521](https://github.com/Leaflet/Leaflet/pull/521) + * Added `containerPoint` property to `MouseEvent`. [#413](https://github.com/Leaflet/Leaflet/issues/413) + * Added `contextmenu` event to vector layers (by [@ErrorProne](https://github.com/ErrorProne)). [#500](https://github.com/Leaflet/Leaflet/pull/500) * Added `LayerGroup` `eachLayer` method for iterating over its members. - * Added `FeatureGroup` `mousemove` and `contextmenu` events (by [@jacobtoye](https://github.com/jacobtoye)). [#816](https://github.com/CloudMade/Leaflet/pull/816) + * Added `FeatureGroup` `mousemove` and `contextmenu` events (by [@jacobtoye](https://github.com/jacobtoye)). [#816](https://github.com/Leaflet/Leaflet/pull/816) * Added chaining to `DomEvent` methods. * Added `on` and `off` aliases for `DomEvent` `addListener` and `removeListener` methods. - * Added `L_NO_TOUCH` global variable switch (set it before Leaflet inclusion) which disables touch detection, helpful for desktop apps built using QT. [#572](https://github.com/CloudMade/Leaflet/issues/572) - * Added `dashArray` option to vector layers for making dashed strokes (by [jacobtoye](https://github.com/jacobtoye)). [#821](https://github.com/CloudMade/Leaflet/pull/821) [#165](https://github.com/CloudMade/Leaflet/issues/165) - * Added `Circle` `getBounds` method. [#440](https://github.com/CloudMade/Leaflet/issues/440) - * Added `Circle` `getLatLng` and `getRadius` methods (by [@Guiswa](https://github.com/Guiswa)). [#655](https://github.com/CloudMade/Leaflet/pull/655) - * Added `openPopup` method to all vector layers. [#246](https://github.com/CloudMade/Leaflet/issues/246) + * Added `L_NO_TOUCH` global variable switch (set it before Leaflet inclusion) which disables touch detection, helpful for desktop apps built using QT. [#572](https://github.com/Leaflet/Leaflet/issues/572) + * Added `dashArray` option to vector layers for making dashed strokes (by [jacobtoye](https://github.com/jacobtoye)). [#821](https://github.com/Leaflet/Leaflet/pull/821) [#165](https://github.com/Leaflet/Leaflet/issues/165) + * Added `Circle` `getBounds` method. [#440](https://github.com/Leaflet/Leaflet/issues/440) + * Added `Circle` `getLatLng` and `getRadius` methods (by [@Guiswa](https://github.com/Guiswa)). [#655](https://github.com/Leaflet/Leaflet/pull/655) + * Added `openPopup` method to all vector layers. [#246](https://github.com/Leaflet/Leaflet/issues/246) * Added public `redraw` method to vector layers (useful if you manipulate their `LatLng` points directly). * Added `Marker` `opacity` option and `setOpacity` method. - * Added `Marker` `update` method. [#392](https://github.com/CloudMade/Leaflet/issues/392) - * Improved `Marker` `openPopup` not to raise an error if it doesn't have a popup. [#507](https://github.com/CloudMade/Leaflet/issues/507) - * Added `ImageOverlay` `opacity` option and `setOpacity` method. [#438](https://github.com/CloudMade/Leaflet/issues/438) + * Added `Marker` `update` method. [#392](https://github.com/Leaflet/Leaflet/issues/392) + * Improved `Marker` `openPopup` not to raise an error if it doesn't have a popup. [#507](https://github.com/Leaflet/Leaflet/issues/507) + * Added `ImageOverlay` `opacity` option and `setOpacity` method. [#438](https://github.com/Leaflet/Leaflet/issues/438) * Added `Popup` `maxHeight` option that makes content inside the popup scrolled if it doesn't fit the specified max height. * Added `Popup` `openOn(map)` method (similar to `Map` `openPopup`). - * Added `Map` `getContainer` method (by [@Guiswa](https://github.com/Guiswa)). [#654](https://github.com/CloudMade/Leaflet/pull/654) - * Added `Map` `containerPointToLatLng` and `latLngToContainerPoint` methods. [#474](https://github.com/CloudMade/Leaflet/issues/474) + * Added `Map` `getContainer` method (by [@Guiswa](https://github.com/Guiswa)). [#654](https://github.com/Leaflet/Leaflet/pull/654) + * Added `Map` `containerPointToLatLng` and `latLngToContainerPoint` methods. [#474](https://github.com/Leaflet/Leaflet/issues/474) * Added `Map` `addHandler` method. * Added `Map` `mouseup` and `autopanstart` events. - * Added `LatLngBounds` `pad` method that returns bounds extended by a percentage (by [@jacobtoye](https://github.com/jacobtoye)). [#492](https://github.com/CloudMade/Leaflet/pull/492) + * Added `LatLngBounds` `pad` method that returns bounds extended by a percentage (by [@jacobtoye](https://github.com/jacobtoye)). [#492](https://github.com/Leaflet/Leaflet/pull/492) * Moved dragging cursor styles from JS code to CSS. ### Bug fixes #### General bugfixes - * Fixed a bug where the map was zooming incorrectly inside a `position: fixed` container (by [@chx007](https://github.com/chx007)). [#602](https://github.com/CloudMade/Leaflet/pull/602) - * Fixed a bug where scaled tiles weren't cleared up after zoom in some cases (by [@cfis](https://github.com/cfis)) [#683](https://github.com/CloudMade/Leaflet/pull/683) - * Fixed a bug where map wouldn't drag over a polygon with a `mousedown` listener. [#834](https://github.com/CloudMade/Leaflet/issues/834) + * Fixed a bug where the map was zooming incorrectly inside a `position: fixed` container (by [@chx007](https://github.com/chx007)). [#602](https://github.com/Leaflet/Leaflet/pull/602) + * Fixed a bug where scaled tiles weren't cleared up after zoom in some cases (by [@cfis](https://github.com/cfis)) [#683](https://github.com/Leaflet/Leaflet/pull/683) + * Fixed a bug where map wouldn't drag over a polygon with a `mousedown` listener. [#834](https://github.com/Leaflet/Leaflet/issues/834) #### API bugfixes - * Fixed a regression where removeLayer would not remove corresponding attribution. [#488](https://github.com/CloudMade/Leaflet/issues/488) - * Fixed a bug where popup close button wouldn't work on manually added popups. [#423](https://github.com/CloudMade/Leaflet/issues/423) - * Fixed a bug where marker click event would stop working if you dragged it and then disabled dragging. [#434](https://github.com/CloudMade/Leaflet/issues/434) + * Fixed a regression where removeLayer would not remove corresponding attribution. [#488](https://github.com/Leaflet/Leaflet/issues/488) + * Fixed a bug where popup close button wouldn't work on manually added popups. [#423](https://github.com/Leaflet/Leaflet/issues/423) + * Fixed a bug where marker click event would stop working if you dragged it and then disabled dragging. [#434](https://github.com/Leaflet/Leaflet/issues/434) * Fixed a bug where `TileLayer` `setOpacity` wouldn't work when setting it back to 1. - * Fixed a bug where vector layer `setStyle({stroke: false})` wouldn't remove stroke and the same for fill. [#441](https://github.com/CloudMade/Leaflet/issues/441) + * Fixed a bug where vector layer `setStyle({stroke: false})` wouldn't remove stroke and the same for fill. [#441](https://github.com/Leaflet/Leaflet/issues/441) * Fixed a bug where `Marker` `bindPopup` method wouldn't take `offset` option into account. - * Fixed a bug where `TileLayer` `load` event wasn't fired if some tile didn't load (by [@lapinos03](https://github.com/lapinos03) and [@cfis](https://github.com/cfis)) [#682](https://github.com/CloudMade/Leaflet/pull/682) - * Fixed error when removing `GeoJSON` layer. [#685](https://github.com/CloudMade/Leaflet/issues/685) - * Fixed error when calling `GeoJSON` `clearLayer` (by [@runderwood](https://github.com/runderwood)). [#617](https://github.com/CloudMade/Leaflet/pull/617) - * Fixed a bug where `Control` `setPosition` wasn't always working correctly (by [@ericmmartinez](https://github.com/ericmmartinez)). [#657](https://github.com/CloudMade/Leaflet/pull/657) - * Fixed a bug with `Util.bind` sometimes losing arguments (by [@johtso](https://github.com/johtso)). [#588](https://github.com/CloudMade/Leaflet/pull/588) - * Fixed a bug where `drag` event was sometimes fired after `dragend`. [#555](https://github.com/CloudMade/Leaflet/issues/555) - * Fixed a bug where `TileLayer` `load` event was firing only once (by [@lapinos03](https://github.com/lapinos03) and [shintonik](https://github.com/shintonik)). [#742](https://github.com/CloudMade/Leaflet/pull/742) [#177](https://github.com/CloudMade/Leaflet/issues/177) - * Fixed a bug where `FeatureGroup` popup events where not cleaned up after removing a layer from it (by [@danzel](https://github.com/danzel)). [#775](https://github.com/CloudMade/Leaflet/pull/775) - * Fixed a bug where `DomUtil.removeClass` didn't remove trailing spaces (by [@jieter](https://github.com/jieter)). [#784](https://github.com/CloudMade/Leaflet/pull/784) + * Fixed a bug where `TileLayer` `load` event wasn't fired if some tile didn't load (by [@lapinos03](https://github.com/lapinos03) and [@cfis](https://github.com/cfis)) [#682](https://github.com/Leaflet/Leaflet/pull/682) + * Fixed error when removing `GeoJSON` layer. [#685](https://github.com/Leaflet/Leaflet/issues/685) + * Fixed error when calling `GeoJSON` `clearLayer` (by [@runderwood](https://github.com/runderwood)). [#617](https://github.com/Leaflet/Leaflet/pull/617) + * Fixed a bug where `Control` `setPosition` wasn't always working correctly (by [@ericmmartinez](https://github.com/ericmmartinez)). [#657](https://github.com/Leaflet/Leaflet/pull/657) + * Fixed a bug with `Util.bind` sometimes losing arguments (by [@johtso](https://github.com/johtso)). [#588](https://github.com/Leaflet/Leaflet/pull/588) + * Fixed a bug where `drag` event was sometimes fired after `dragend`. [#555](https://github.com/Leaflet/Leaflet/issues/555) + * Fixed a bug where `TileLayer` `load` event was firing only once (by [@lapinos03](https://github.com/lapinos03) and [shintonik](https://github.com/shintonik)). [#742](https://github.com/Leaflet/Leaflet/pull/742) [#177](https://github.com/Leaflet/Leaflet/issues/177) + * Fixed a bug where `FeatureGroup` popup events where not cleaned up after removing a layer from it (by [@danzel](https://github.com/danzel)). [#775](https://github.com/Leaflet/Leaflet/pull/775) + * Fixed a bug where `DomUtil.removeClass` didn't remove trailing spaces (by [@jieter](https://github.com/jieter)). [#784](https://github.com/Leaflet/Leaflet/pull/784) * Fixed a bug where marker popup didn't take popup offset into account. * Fixed a bug that led to an error when polyline was removed inside a `moveend` listener. - * Fixed a bug where `LayerGroup` `addLayer` wouldn't check if a layer has already been added (by [@danzel](https://github.com/danzel)). [798](https://github.com/CloudMade/Leaflet/pull/798) + * Fixed a bug where `LayerGroup` `addLayer` wouldn't check if a layer has already been added (by [@danzel](https://github.com/danzel)). [798](https://github.com/Leaflet/Leaflet/pull/798) #### Browser bugfixes - * Fixed broken zooming on IE10 beta (by [@danzel](https://github.com/danzel)). [#650](https://github.com/CloudMade/Leaflet/issues/650) [#751](https://github.com/CloudMade/Leaflet/pull/751) - * Fixed a bug that broke Leaflet for websites that had XHTML content-type header set (by [lars-sh](https://github.com/lars-sh)). [#801](https://github.com/CloudMade/Leaflet/pull/801) - * Fixed a bug that caused popups to be empty in IE when passing a DOM node as the content (by [@nrenner](https://github.com/nrenner)). [#472](https://github.com/CloudMade/Leaflet/pull/472) + * Fixed broken zooming on IE10 beta (by [@danzel](https://github.com/danzel)). [#650](https://github.com/Leaflet/Leaflet/issues/650) [#751](https://github.com/Leaflet/Leaflet/pull/751) + * Fixed a bug that broke Leaflet for websites that had XHTML content-type header set (by [lars-sh](https://github.com/lars-sh)). [#801](https://github.com/Leaflet/Leaflet/pull/801) + * Fixed a bug that caused popups to be empty in IE when passing a DOM node as the content (by [@nrenner](https://github.com/nrenner)). [#472](https://github.com/Leaflet/Leaflet/pull/472) * Fixed inability to use scrolled content inside popup due to mouse wheel propagation. * Fixed a bug that caused jumping/stuttering of panning animation in some cases. * Fixed a bug where popup size was calculated incorrectly in IE. * Fixed a bug where cursor would flicker when dragging a marker. - * Fixed a bug where clickable paths on IE9 didn't have a hand cursor (by [naehrstoff](https://github.com/naehrstoff)). [#671](https://github.com/CloudMade/Leaflet/pull/671) - * Fixed a bug in IE with disappearing icons when changing opacity (by [@tagliala](https://github.com/tagliala) and [DamonOehlman](https://github.com/DamonOehlman)). [#667](https://github.com/CloudMade/Leaflet/pull/667) [#600](https://github.com/CloudMade/Leaflet/pull/600) - * Fixed a bug with setting opacity on IE10 (by [@danzel](https://github.com/danzel)). [796](https://github.com/CloudMade/Leaflet/pull/796) - * Fixed a bug where `Control.Layers` didn't work on IE7. [#652](https://github.com/CloudMade/Leaflet/issues/652) - * Fixed a bug that could cause false `mousemove` events on click in Chrome (by [@stsydow](https://github.com/stsydow)). [#757](https://github.com/CloudMade/Leaflet/pull/757) - * Fixed a bug in IE6-8 where adding fill or stroke on vector layers after initialization with `setStyle` would break the map. [#641](https://github.com/CloudMade/Leaflet/issues/641) - * Fixed a bug with setOpacity in IE where it would not work correctly if used more than once on the same element (by [@ajbeaven](https://github.com/ajbeaven)). [#827](https://github.com/CloudMade/Leaflet/pull/827) - * Fixed a bug in Chrome where transparent control corners sometimes couldn't be clicked through (by [@danzel](https://github.com/danzel)). [#836](https://github.com/CloudMade/Leaflet/pull/836) [#575](https://github.com/CloudMade/Leaflet/issues/575) + * Fixed a bug where clickable paths on IE9 didn't have a hand cursor (by [naehrstoff](https://github.com/naehrstoff)). [#671](https://github.com/Leaflet/Leaflet/pull/671) + * Fixed a bug in IE with disappearing icons when changing opacity (by [@tagliala](https://github.com/tagliala) and [DamonOehlman](https://github.com/DamonOehlman)). [#667](https://github.com/Leaflet/Leaflet/pull/667) [#600](https://github.com/Leaflet/Leaflet/pull/600) + * Fixed a bug with setting opacity on IE10 (by [@danzel](https://github.com/danzel)). [796](https://github.com/Leaflet/Leaflet/pull/796) + * Fixed a bug where `Control.Layers` didn't work on IE7. [#652](https://github.com/Leaflet/Leaflet/issues/652) + * Fixed a bug that could cause false `mousemove` events on click in Chrome (by [@stsydow](https://github.com/stsydow)). [#757](https://github.com/Leaflet/Leaflet/pull/757) + * Fixed a bug in IE6-8 where adding fill or stroke on vector layers after initialization with `setStyle` would break the map. [#641](https://github.com/Leaflet/Leaflet/issues/641) + * Fixed a bug with setOpacity in IE where it would not work correctly if used more than once on the same element (by [@ajbeaven](https://github.com/ajbeaven)). [#827](https://github.com/Leaflet/Leaflet/pull/827) + * Fixed a bug in Chrome where transparent control corners sometimes couldn't be clicked through (by [@danzel](https://github.com/danzel)). [#836](https://github.com/Leaflet/Leaflet/pull/836) [#575](https://github.com/Leaflet/Leaflet/issues/575) #### Mobile browser bugfixes - * Fixed a bug that sometimes caused map to disappear completely after zoom on iOS (by [@fr1n63](https://github.com/fr1n63)). [#580](https://github.com/CloudMade/Leaflet/issues/580) [#777](https://github.com/CloudMade/Leaflet/pull/777) - * Fixed a bug that often caused vector layers to flicker on drag end on iOS (by [@krawaller](https://github.com/krawaller)). [#18](https://github.com/CloudMade/Leaflet/issues/18) - * Fixed a bug with false map click events on pinch-zoom and zoom/layers controls click. [#485](https://github.com/CloudMade/Leaflet/issues/485) + * Fixed a bug that sometimes caused map to disappear completely after zoom on iOS (by [@fr1n63](https://github.com/fr1n63)). [#580](https://github.com/Leaflet/Leaflet/issues/580) [#777](https://github.com/Leaflet/Leaflet/pull/777) + * Fixed a bug that often caused vector layers to flicker on drag end on iOS (by [@krawaller](https://github.com/krawaller)). [#18](https://github.com/Leaflet/Leaflet/issues/18) + * Fixed a bug with false map click events on pinch-zoom and zoom/layers controls click. [#485](https://github.com/Leaflet/Leaflet/issues/485) * Fixed a bug where touching the map with two or more fingers simultaneously would raise an error. - * Fixed a bug where zoom control wasn't always visible on Android 3. [#335](https://github.com/CloudMade/Leaflet/issues/335) - * Fixed a bug where opening the layers control would propagate a click to the map (by [@jacobtoye](https://github.com/jacobtoye)). [#638](https://github.com/CloudMade/Leaflet/pull/638) - * Fixed a bug where `ImageOverlay` wouldn't stretch properly on zoom on Android 2. [#651](https://github.com/CloudMade/Leaflet/issues/651) - * Fixed a bug where `clearLayers` for vector layers on a Canvas backend (e.g. on Android 2) would take unreasonable amount of time. [#785](https://github.com/CloudMade/Leaflet/issues/785) - * Fixed a bug where `setLatLngs` and similar methods on vector layers on a Canvas backend would not update the layers immediately. [#732](https://github.com/CloudMade/Leaflet/issues/732) + * Fixed a bug where zoom control wasn't always visible on Android 3. [#335](https://github.com/Leaflet/Leaflet/issues/335) + * Fixed a bug where opening the layers control would propagate a click to the map (by [@jacobtoye](https://github.com/jacobtoye)). [#638](https://github.com/Leaflet/Leaflet/pull/638) + * Fixed a bug where `ImageOverlay` wouldn't stretch properly on zoom on Android 2. [#651](https://github.com/Leaflet/Leaflet/issues/651) + * Fixed a bug where `clearLayers` for vector layers on a Canvas backend (e.g. on Android 2) would take unreasonable amount of time. [#785](https://github.com/Leaflet/Leaflet/issues/785) + * Fixed a bug where `setLatLngs` and similar methods on vector layers on a Canvas backend would not update the layers immediately. [#732](https://github.com/Leaflet/Leaflet/issues/732) ## 0.3.1 (February 14, 2012) @@ -279,15 +330,15 @@ Icon API was improved to be more flexible, but one of the changes is backwards-i * Added **Canvas backend** for vector layers (polylines, polygons, circles). This enables vector support on Android < 3, and it can also be optionally preferred over SVG for a performance gain in some cases. Thanks to [@florianf](https://github.com/florianf) for a big part of this work. * Added **layers control** (`Control.Layers`) for convenient layer switching. - * Added ability to set **max bounds** within which users can pan/zoom. [#93](https://github.com/CloudMade/Leaflet/issues/93) + * Added ability to set **max bounds** within which users can pan/zoom. [#93](https://github.com/Leaflet/Leaflet/issues/93) ### Improvements #### Usability improvements * 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) - * 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) + * When panning to another copy of the world (that's infinite horizontally), map overlays now jump to corresponding positions. [#273](https://github.com/Leaflet/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/Leaflet/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 stability of zoom animation (less flickering of tiles). @@ -295,37 +346,37 @@ Icon API was improved to be more flexible, but one of the changes is backwards-i #### API improvements * Added ability to add a tile layer below all others (`map.addLayer(layer, true)`) (useful for switching base tile layers). - * Added `Map` `zoomstart` event (thanks to [@Fabiz](https://github.com/Fabiz)). [#377](https://github.com/CloudMade/Leaflet/pull/377) - * Improved `Map` `locate` method, added ability to watch location continuously and more options. [#212](https://github.com/CloudMade/Leaflet/issues/212) + * Added `Map` `zoomstart` event (thanks to [@Fabiz](https://github.com/Fabiz)). [#377](https://github.com/Leaflet/Leaflet/pull/377) + * Improved `Map` `locate` method, added ability to watch location continuously and more options. [#212](https://github.com/Leaflet/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 `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 `Marker` `zIndexOffset` option to be able to set certain markers below/above others. [#65](https://github.com/Leaflet/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 `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/Leaflet/Leaflet/pull/256) * 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 `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 `FeatureGroup` (by [@JasonSanford](https://github.com/JasonSanford)). [#557](https://github.com/CloudMade/Leaflet/pull/557) - * Added `FeatureGroup` `setStyle` method (also inherited by `MultiPolyline` and `MultiPolygon`). [#353](https://github.com/CloudMade/Leaflet/issues/353) + * Added `TileLayer` `zoomReverse` option to reverse zoom numbering (by [@Majiir](https://github.com/Majiir)). [#406](https://github.com/Leaflet/Leaflet/pull/406) + * Added `TileLayer.Canvas` `redraw` method (by [@mortenbekditlevsen](https://github.com/mortenbekditlevsen)). [#459](https://github.com/Leaflet/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/Leaflet/Leaflet/pull/186) + * Added `setLatLngs` method to `MultiPolyline` and `MultiPolygon` (by [@anru](https://github.com/anru)). [#194](https://github.com/Leaflet/Leaflet/pull/194) + * Added `getBounds` method to `Polyline` and `Polygon` (by [@JasonSanford](https://github.com/JasonSanford)). [#253](https://github.com/Leaflet/Leaflet/pull/253) + * Added `getBounds` method to `FeatureGroup` (by [@JasonSanford](https://github.com/JasonSanford)). [#557](https://github.com/Leaflet/Leaflet/pull/557) + * Added `FeatureGroup` `setStyle` method (also inherited by `MultiPolyline` and `MultiPolygon`). [#353](https://github.com/Leaflet/Leaflet/issues/353) * Added `FeatureGroup` `invoke` method to call a particular method on all layers of the group with the given arguments. - * 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) - * 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` `intersects(otherBounds)` method (thanks to [@pagameba](https://github.com/pagameba)). [#350](https://github.com/CloudMade/Leaflet/pull/350) - * Made `LatLngBounds` `extend` method to accept other `LatLngBounds` in addition to `LatLng` (by [@JasonSanford](https://github.com/JasonSanford)). [#553](https://github.com/CloudMade/Leaflet/pull/553) - * Added `Bounds` `intersects(otherBounds)` method. [#461](https://github.com/CloudMade/Leaflet/issues/461) + * Added `ImageOverlay` `load` event. [#213](https://github.com/Leaflet/Leaflet/issues/213) + * Added `minWidth` option to `Popup` (by [@marphi](https://github.com/marphi)). [#214](https://github.com/Leaflet/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/Leaflet/Leaflet/issues/136) + * Added `LatLng` `distanceTo` method (great circle distance) (by [@mortenbekditlevsen](https://github.com/mortenbekditlevsen)). [#462](https://github.com/Leaflet/Leaflet/pull/462) + * Added `LatLngBounds` `toBBoxString` method for convenience (by [@JasonSanford](https://github.com/JasonSanford)). [#263](https://github.com/Leaflet/Leaflet/pull/263) + * Added `LatLngBounds` `intersects(otherBounds)` method (thanks to [@pagameba](https://github.com/pagameba)). [#350](https://github.com/Leaflet/Leaflet/pull/350) + * Made `LatLngBounds` `extend` method to accept other `LatLngBounds` in addition to `LatLng` (by [@JasonSanford](https://github.com/JasonSanford)). [#553](https://github.com/Leaflet/Leaflet/pull/553) + * Added `Bounds` `intersects(otherBounds)` method. [#461](https://github.com/Leaflet/Leaflet/issues/461) * Added `L.Util.template` method for simple string template evaluation. * Added `DomUtil.removeClass` method (by [@anru](https://github.com/anru)). * 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) - * Added `Map` `boxzoomstart` and `boxzoomend` events (by [@zedd45](https://github.com/zedd45)). [#554](https://github.com/CloudMade/Leaflet/pull/554) - * Added `Popup` `contentupdate` event (by [@mehmeta](https://github.com/mehmeta)). [#548](https://github.com/CloudMade/Leaflet/pull/548) + * 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/Leaflet/Leaflet/pull/179) + * Added `Map` `boxzoomstart` and `boxzoomend` events (by [@zedd45](https://github.com/zedd45)). [#554](https://github.com/Leaflet/Leaflet/pull/554) + * Added `Popup` `contentupdate` event (by [@mehmeta](https://github.com/mehmeta)). [#548](https://github.com/Leaflet/Leaflet/pull/548) #### Breaking API changes @@ -341,54 +392,54 @@ Icon API was improved to be more flexible, but one of the changes is backwards-i #### 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 `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 `Circle` was rendered with incorrect radius (didn't take projection exagerration into account). [#331](https://github.com/Leaflet/Leaflet/issues/331) + * Fixed a bug where `Map` `getBounds` would work incorrectly on a date line cross. [#295](https://github.com/Leaflet/Leaflet/issues/295) + * Fixed a bug where polygons and polylines sometimes rendered incorrectly on some zoom levels. [#381](https://github.com/Leaflet/Leaflet/issues/381) * 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 with incorrect rendering of GeoJSON on a date line cross. [#354](https://github.com/CloudMade/Leaflet/issues/354) - * Fixed a bug where map panning would stuck forever after releasing the mouse over an iframe or a flash object (thanks to [@sten82](https://github.com/sten82)). [#297](https://github.com/CloudMade/Leaflet/pull/297) [#64](https://github.com/CloudMade/Leaflet/issues/64) - * Fixed a bug where mouse wheel zoom worked incorrectly if map is inside scrolled container (partially by [@chrillo](https://github.com/chrillo)). [#206](https://github.com/CloudMade/Leaflet/issues/206) - * Fixed a bug where it was possible to add the same listener twice. [#281](https://github.com/CloudMade/Leaflet/issues/281) - * Fixed a bug where `Circle` was rendered with incorrect radius (didn't take projection exaggeration into account). [#331](https://github.com/CloudMade/Leaflet/issues/331) - * Fixed a bug where `Marker` `setIcon` was not working properly (by [@marphi](https://github.com/marphi)). [#218](https://github.com/CloudMade/Leaflet/pull/218) [#311](https://github.com/CloudMade/Leaflet/issues/311) - * Fixed a bug where `Marker` `setLatLng` was not working if it's set before adding the marker to a map. [#222](https://github.com/CloudMade/Leaflet/issues/222) - * Fixed a bug where marker popup would not move on `Marker` `setLatLng` (by [@tjarratt](https://github.com/tjarratt)). [#272](https://github.com/CloudMade/Leaflet/pull/272) + * Fixed a bug where `GeoJSON` `pointToLayer` option wouldn't work in a `GeometryCollection`. [#391](https://github.com/Leaflet/Leaflet/issues/391) + * Fixed a bug with incorrect rendering of GeoJSON on a date line cross. [#354](https://github.com/Leaflet/Leaflet/issues/354) + * Fixed a bug where map panning would stuck forever after releasing the mouse over an iframe or a flash object (thanks to [@sten82](https://github.com/sten82)). [#297](https://github.com/Leaflet/Leaflet/pull/297) [#64](https://github.com/Leaflet/Leaflet/issues/64) + * Fixed a bug where mouse wheel zoom worked incorrectly if map is inside scrolled container (partially by [@chrillo](https://github.com/chrillo)). [#206](https://github.com/Leaflet/Leaflet/issues/206) + * Fixed a bug where it was possible to add the same listener twice. [#281](https://github.com/Leaflet/Leaflet/issues/281) + * Fixed a bug where `Circle` was rendered with incorrect radius (didn't take projection exaggeration into account). [#331](https://github.com/Leaflet/Leaflet/issues/331) + * Fixed a bug where `Marker` `setIcon` was not working properly (by [@marphi](https://github.com/marphi)). [#218](https://github.com/Leaflet/Leaflet/pull/218) [#311](https://github.com/Leaflet/Leaflet/issues/311) + * Fixed a bug where `Marker` `setLatLng` was not working if it's set before adding the marker to a map. [#222](https://github.com/Leaflet/Leaflet/issues/222) + * Fixed a bug where marker popup would not move on `Marker` `setLatLng` (by [@tjarratt](https://github.com/tjarratt)). [#272](https://github.com/Leaflet/Leaflet/pull/272) * Fixed a bug where static properties of a child class would not override the parent ones. * 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 typo in `Bounds` `contains` method (by [@anru](https://github.com/anru)). [#180](https://github.com/CloudMade/Leaflet/pull/180) + * Fixed a bug that caused en error when dragging marker with icon without shadow (by [@anru](https://github.com/anru)). [#178](https://github.com/Leaflet/Leaflet/issues/178) + * Fixed a typo in `Bounds` `contains` method (by [@anru](https://github.com/anru)). [#180](https://github.com/Leaflet/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 map click caused an error if dragging is initially disabled. [#196](https://github.com/CloudMade/Leaflet/issues/196) + * Fixed a bug where drag event fired before the actual movement of layer (by [@anru](https://github.com/anru)). [#197](https://github.com/Leaflet/Leaflet/pull/197) + * Fixed a bug where map click caused an error if dragging is initially disabled. [#196](https://github.com/Leaflet/Leaflet/issues/196) * Fixed a bug where map `movestart` event would fire after zoom animation. - * Fixed a bug where attribution prefix would not update on `setPrefix`. [#195](https://github.com/CloudMade/Leaflet/issues/195) + * Fixed a bug where attribution prefix would not update on `setPrefix`. [#195](https://github.com/Leaflet/Leaflet/issues/195) * Fixed a bug where `TileLayer` `load` event wouldn't fire in some edge cases (by [@dravnic](https://github.com/dravnic)). * Fixed a bug related to clearing background tiles after zooming (by [@neno-giscloud](https://github.com/neno-giscloud) & [@dravnic](https://github.com/dravnic)). * Fixed a bug that sometimes caused map flickering after zoom animation. - * Fixed a bug related to cleaning up after removing tile layers (by [@dravnic](https://github.com/dravnic)). [#276](https://github.com/CloudMade/Leaflet/pull/276) - * Fixed a bug that made selecting text in the attribution control impossible. [#279](https://github.com/CloudMade/Leaflet/issues/279) - * Fixed a bug when initializing a map in a non-empty div. [#278](https://github.com/CloudMade/Leaflet/issues/278) + * Fixed a bug related to cleaning up after removing tile layers (by [@dravnic](https://github.com/dravnic)). [#276](https://github.com/Leaflet/Leaflet/pull/276) + * Fixed a bug that made selecting text in the attribution control impossible. [#279](https://github.com/Leaflet/Leaflet/issues/279) + * Fixed a bug when initializing a map in a non-empty div. [#278](https://github.com/Leaflet/Leaflet/issues/278) * Fixed a bug where `movestart` didn't fire on panning animation. - * Fixed a bug in Elliptical Mercator formula that affeted `EPSG:3395` CRS (by [@Savvkin](https://github.com/Savvkin)). [#358](https://github.com/CloudMade/Leaflet/pull/358) + * Fixed a bug in Elliptical Mercator formula that affeted `EPSG:3395` CRS (by [@Savvkin](https://github.com/Savvkin)). [#358](https://github.com/Leaflet/Leaflet/pull/358) #### Browser bugfixes - * 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 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 occasional crashes on Mac Safari (thanks to [@lapinos03](https://github.com/lapinos03)). [#191](https://github.com/Leaflet/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/Leaflet/Leaflet/pull/453) + * Fixed a bug that raised error in IE6-8 when clicking on popup close button. [#235](https://github.com/Leaflet/Leaflet/issues/235) + * Fixed a bug with Safari not redrawing UI immediately after closing a popup. [#296](https://github.com/Leaflet/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/Leaflet/Leaflet/issues/231) * Fixed a bug that caused map overlays to appear blurry in some cases under WebKit browsers. * Fixed a bug that was causing errors in some Webkit/Linux builds (requestAnimationFrame-related), thanks to Chris Martens. #### Mobile browser bugfixes - * 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 a bug on Android 2/3 that sometimes caused the map to disappear after zooming. [#69](https://github.com/CloudMade/Leaflet/issues/69) + * Fixed a bug that caused an error when clicking vector layers under iOS. [#204](https://github.com/Leaflet/Leaflet/issues/204) + * Fixed crash on Android 3+ when panning or zooming (by [@florian](https://github.com/florianf)). [#137](https://github.com/Leaflet/Leaflet/issues/137) + * Fixed a bug on Android 2/3 that sometimes caused the map to disappear after zooming. [#69](https://github.com/Leaflet/Leaflet/issues/69) * Fixed a bug on Android 3 that caused tiles to shift position on a big map. - * Fixed a bug that caused the map to pan when touch-panning inside a popup. [#452](https://github.com/CloudMade/Leaflet/issues/452) + * Fixed a bug that caused the map to pan when touch-panning inside a popup. [#452](https://github.com/Leaflet/Leaflet/issues/452) * Fixed a bug that caused click delays on zoom control. @@ -408,14 +459,14 @@ Icon API was improved to be more flexible, but one of the changes is backwards-i #### Usability improvements - * Improved panning performance in Chrome and FF considerably with the help of `requestAnimationFrame`. [#130](https://github.com/CloudMade/Leaflet/issues/130) - * Improved click responsiveness in mobile WebKit (now it happens without delay). [#26](https://github.com/CloudMade/Leaflet/issues/26) + * Improved panning performance in Chrome and FF considerably with the help of `requestAnimationFrame`. [#130](https://github.com/Leaflet/Leaflet/issues/130) + * Improved click responsiveness in mobile WebKit (now it happens without delay). [#26](https://github.com/Leaflet/Leaflet/issues/26) * Added tap tolerance (so click happens even if you moved your finger slighly when tapping). - * Improved geolocation error handling: better error messages, explicit timeout, set world view on locateAndSetView failure. [#61](https://github.com/CloudMade/Leaflet/issues/61) + * Improved geolocation error handling: better error messages, explicit timeout, set world view on locateAndSetView failure. [#61](https://github.com/Leaflet/Leaflet/issues/61) #### API improvements - * Added **MultiPolyline** and **MultiPolygon** layers. [#77](https://github.com/CloudMade/Leaflet/issues/77) + * Added **MultiPolyline** and **MultiPolygon** layers. [#77](https://github.com/Leaflet/Leaflet/issues/77) * Added **LayerGroup** and **FeatureGroup** layers for grouping other layers. * Added **TileLayer.Canvas** for easy creation of canvas-based tile layers. * Changed `Circle` to be zoom-dependent (with radius in meters); circle of a permanent size is now called `CircleMarker`. @@ -437,40 +488,40 @@ Icon API was improved to be more flexible, but one of the changes is backwards-i * Added `Makefile` for building `leaflet.js` on non-Windows machines (by [@tmcw](https://github.com/tmcw)). * Improved `debug/leaflet-include.js` script to allow using it outside of `debug` folder (by [@antonj](https://github.com/antonj)). - * Improved `L` definition to be compatible with CommonJS. [#122](https://github.com/CloudMade/Leaflet/issues/122) + * Improved `L` definition to be compatible with CommonJS. [#122](https://github.com/Leaflet/Leaflet/issues/122) ### Bug fixes #### General bugfixes - * Fixed a bug where zooming is broken if the map contains a polygon and you zoom to an area where it's not visible. [#47](https://github.com/CloudMade/Leaflet/issues/47) + * Fixed a bug where zooming is broken if the map contains a polygon and you zoom to an area where it's not visible. [#47](https://github.com/Leaflet/Leaflet/issues/47) * Fixed a bug where closed polylines would not appear on the map. - * Fixed a bug where marker that was added, removed and then added again would not appear on the map. [#66](https://github.com/CloudMade/Leaflet/issues/66) + * Fixed a bug where marker that was added, removed and then added again would not appear on the map. [#66](https://github.com/Leaflet/Leaflet/issues/66) * Fixed a bug where tile layer that was added, removed and then added again would not appear on the map. - * Fixed a bug where some tiles would not load when panning across the date line. [#97](https://github.com/CloudMade/Leaflet/issues/97) - * Fixed a bug where map div with `position: absolute` is reset to `relative`. [#100](https://github.com/CloudMade/Leaflet/issues/100) + * Fixed a bug where some tiles would not load when panning across the date line. [#97](https://github.com/Leaflet/Leaflet/issues/97) + * Fixed a bug where map div with `position: absolute` is reset to `relative`. [#100](https://github.com/Leaflet/Leaflet/issues/100) * Fixed a bug that caused an error when trying to add a marker without shadow in its icon. - * Fixed a bug where popup content would not update on `setContent` call. [#94](https://github.com/CloudMade/Leaflet/issues/94) + * Fixed a bug where popup content would not update on `setContent` call. [#94](https://github.com/Leaflet/Leaflet/issues/94) * Fixed a bug where double click zoom wouldn't work if popup is opened on map click - * Fixed a bug with click propagation on popup close button. [#99](https://github.com/CloudMade/Leaflet/issues/99) + * Fixed a bug with click propagation on popup close button. [#99](https://github.com/Leaflet/Leaflet/issues/99) * Fixed inability to remove ImageOverlay layer. #### Browser bugfixes * Fixed a bug where paths would not appear in IE8. - * Fixed a bug where there were occasional slowdowns before zoom animation in WebKit. [#123](https://github.com/CloudMade/Leaflet/issues/123) + * Fixed a bug where there were occasional slowdowns before zoom animation in WebKit. [#123](https://github.com/Leaflet/Leaflet/issues/123) * Fixed incorrect zoom animation & popup styling in Opera 11.11. * Fixed popup fade animation in Firefox and Opera. * Fixed a bug where map isn't displayed in Firefox when there's an `img { max-width: 100% }` rule. #### Mobile browsers bugfixes - * Fixed a bug that prevented panning on some Android 2.1 (and possibly older) devices. [#84](https://github.com/CloudMade/Leaflet/issues/84) - * Disabled zoom animation on Android by default because it's buggy on some devices (will be enabled back when it's stable enough). [#32](https://github.com/CloudMade/Leaflet/issues/32) - * Fixed a bug where map would occasionally break while multi-touch-zooming on iOS. [#32](https://github.com/CloudMade/Leaflet/issues/32) - * Fixed a bug that prevented panning/clicking on Android 3 tablets. [#121](https://github.com/CloudMade/Leaflet/issues/121) - * Fixed a bug that prevented panning/clicking on Opera Mobile. [#138](https://github.com/CloudMade/Leaflet/issues/138) - * Fixed potentional memory leak on WebKit when removing tiles, thanks to [@Scalar4eg](https://github.com/Scalar4eg). [#107](https://github.com/CloudMade/Leaflet/issues/107) + * Fixed a bug that prevented panning on some Android 2.1 (and possibly older) devices. [#84](https://github.com/Leaflet/Leaflet/issues/84) + * Disabled zoom animation on Android by default because it's buggy on some devices (will be enabled back when it's stable enough). [#32](https://github.com/Leaflet/Leaflet/issues/32) + * Fixed a bug where map would occasionally break while multi-touch-zooming on iOS. [#32](https://github.com/Leaflet/Leaflet/issues/32) + * Fixed a bug that prevented panning/clicking on Android 3 tablets. [#121](https://github.com/Leaflet/Leaflet/issues/121) + * Fixed a bug that prevented panning/clicking on Opera Mobile. [#138](https://github.com/Leaflet/Leaflet/issues/138) + * Fixed potentional memory leak on WebKit when removing tiles, thanks to [@Scalar4eg](https://github.com/Scalar4eg). [#107](https://github.com/Leaflet/Leaflet/issues/107) ## 0.1 (2011-05-13) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index bdce529f..4396f4c6 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -8,37 +8,59 @@ Contributing to Leaflet ## Getting Involved -Third-party patches are absolutely essential on our quest to create the best mapping library that will ever exist. However, they're not the only way to get involved with the development of Leaflet. You can help the project tremendously by discovering and [reporting bugs](#reporting-bugs), [improving documentation](#improving-documentation), helping others on the [Leaflet forum](https://groups.google.com/forum/#!forum/leaflet-js) and [GitHub issues](https://github.com/CloudMade/Leaflet/issues), showing your support for your favorite feature suggestions on [Leaflet UserVoice page](http://leaflet.uservoice.com), tweeting to [@LeafletJS](http://twitter.com/LeafletJS) and spreading the word about Leaflet among your colleagues and friends. +Third-party patches are absolutely essential on our quest to create the best mapping library that will ever exist. +However, they're not the only way to get involved with the development of Leaflet. +You can help the project tremendously by discovering and [reporting bugs](#reporting-bugs), +[improving documentation](#improving-documentation), +helping others on the [Leaflet forum](https://groups.google.com/forum/#!forum/leaflet-js) +and [GitHub issues](https://github.com/Leaflet/Leaflet/issues), +showing your support for your favorite feature suggestions on [Leaflet UserVoice page](http://leaflet.uservoice.com), +tweeting to [@LeafletJS](http://twitter.com/LeafletJS) +and spreading the word about Leaflet among your colleagues and friends. ## Reporting Bugs -Before reporting a bug on the project's [issues page](https://github.com/CloudMade/Leaflet/issues), first make sure that your issue is caused by Leaflet, not your application code (e.g. passing incorrect arguments to methods, etc.). Second, search the already reported issues for similar cases, and if it's already reported, just add any additional details in the comments. +Before reporting a bug on the project's [issues page](https://github.com/Leaflet/Leaflet/issues), +first make sure that your issue is caused by Leaflet, not your application code +(e.g. passing incorrect arguments to methods, etc.). +Second, search the already reported issues for similar cases, +and if it's already reported, just add any additional details in the comments. -After you made sure that you've found a new Leaflet bug, here are some tips for creating a helpful report that will make fixing it much easier and quicker: +After you made sure that you've found a new Leaflet bug, +here are some tips for creating a helpful report that will make fixing it much easier and quicker: * Write a **descriptive, specific title**. Bad: *Problem with polylines*. Good: *Doing X in IE9 causes Z*. * Include **browser, OS and Leaflet version** info in the description. * Create a **simple test case** that demonstrates the bug (e.g. using [JSFiddle](http://jsfiddle.net/)). * Check whether the bug can be reproduced in **other browsers**. * Check if the bug occurs in the stable version, master, or both. - * *Bonus tip:* if the bug only appears in the master version but the stable version is fine, use `git bisect` to find the exact commit that introduced the bug. + * *Bonus tip:* if the bug only appears in the master version but the stable version is fine, + use `git bisect` to find the exact commit that introduced the bug. ## Contributing Code ### Considerations for Accepting Patches -While we happily accept patches, we're also commited to keeping Leaflet simple, lightweight and blazingly fast. So bugfixes, performance optimizations and small improvements that don't add a lot of code are much more likely to get accepted quickly. +While we happily accept patches, we're also commited to keeping Leaflet simple, lightweight and blazingly fast. +So bugfixes, performance optimizations and small improvements that don't add a lot of code +are much more likely to get accepted quickly. -Before sending a pull request with a new feature, first check if it's been discussed before already (either on [GitHub issues](https://github.com/CloudMade/Leaflet/issues) or [Leaflet UserVoice](http://leaflet.uservoice.com/)), and then ask yourself two questions: +Before sending a pull request with a new feature, first check if it's been discussed before already +(either on [GitHub issues](https://github.com/Leaflet/Leaflet/issues) +or [Leaflet UserVoice](http://leaflet.uservoice.com/)), +and then ask yourself two questions: - 1. Are you sure that this new feature is important enough to justify its presense in the Leaflet core? Or will it look better as a plugin in a separate repository? + 1. Are you sure that this new feature is important enough to justify its presense in the Leaflet core? + Or will it look better as a plugin in a separate repository? 2. Is it written in a simple, concise way that doesn't add bulk to the codebase? -If your feature or API improvement did get merged into master, please consider submitting another pull request with the corresponding [documentation update](#improving-documentation). +If your feature or API improvement did get merged into master, +please consider submitting another pull request with the corresponding [documentation update](#improving-documentation). ### Setting up the Build System -To set up the Leaflet build system, install [Node](http://nodejs.org/), then run the following commands in the project root: +To set up the Leaflet build system, install [Node](http://nodejs.org/), +then run the following commands in the project root (with superuser permissions): ``` npm install -g jake @@ -50,23 +72,36 @@ You can build minified Leaflet by running `jake` (it will be built from source i ### Making Changes to Leaflet Source -If you're not yet familiar with the way GitHub works (forking, pull requests, etc.), be sure to check out the awesome [article about forking](https://help.github.com/articles/fork-a-repo) on the GitHub Help website — it will get you started quickly. +If you're not yet familiar with the way GitHub works (forking, pull requests, etc.), +be sure to check out the awesome [article about forking](https://help.github.com/articles/fork-a-repo) +on the GitHub Help website — it will get you started quickly. -You should always write each batch of changes (feature, bugfix, etc.) in **its own topic branch**. Please do not commit to the `master` branch, or your unrelated changes will go into the same pull request. +You should always write each batch of changes (feature, bugfix, etc.) in **its own topic branch**. +Please do not commit to the `master` branch, or your unrelated changes will go into the same pull request. You should also follow the code style and whitespace conventions of the original codebase. -Before commiting your changes, run `jake lint` to catch any JS errors in the code and fix them. If you add any new files to the Leaflet source, make sure to also add them to `build/deps.js` so that the build system knows about them. +Before commiting your changes, run `jake lint` to catch any JS errors in the code and fix them. +If you add any new files to the Leaflet source, make sure to also add them to `build/deps.js` +so that the build system knows about them. -But please **do not commit the built files** (`leaflet.js` and `leaflet-src.js`) along with your changes, otherwise there may problems merging the pull request. These files are only commited in the `master` branch of the main Leaflet repository. +But please **do not commit the built files** (`leaflet.js` and `leaflet-src.js`) along with your changes, +otherwise there may be problems merging the pull request. +These files are only commited in the `master` branch of the main Leaflet repository. Happy coding! ## Improving Documentation -The code of the live Leaflet website that contains all documentation and examples is located in the `gh-pages` branch and is automatically generated from a set of HTML and Markdown files by [Jekyll](https://github.com/mojombo/jekyll). +The code of the live Leaflet website that contains all documentation and examples is located in the `gh-pages` branch +and is automatically generated from a set of HTML and Markdown files by [Jekyll](https://github.com/mojombo/jekyll). -The easiest way to make little improvements such as fixing typos without even leaving the browser is by editing one of the files with the online GitHub editor: browse the [gh-pages branch](https://github.com/CloudMade/Leaflet/tree/gh-pages), choose a certain file for editing (e.g. `reference.html` for API reference), click the Edit button, make changes and follow instructions from there. Once it gets merged, the changes will immediately appear on the website. +The easiest way to make little improvements such as fixing typos without even leaving the browser +is by editing one of the files with the online GitHub editor: +browse the [gh-pages branch](https://github.com/Leaflet/Leaflet/tree/gh-pages), +choose a certain file for editing (e.g. `reference.html` for API reference), +click the Edit button, make changes and follow instructions from there. +Once it gets merged, the changes will immediately appear on the website. If you need to make edits in a local repository to see how it looks in the process, do the following: @@ -75,10 +110,15 @@ If you need to make edits in a local repository to see how it looks in the proce 3. Run `jekyll --auto` inside the `Leaflet` folder. 4. Open the website from the `_site` folder. -Now any file changes will be reflected on the generated pages automatically. After commiting the changes, just send a pull request. +Now any file changes will be reflected on the generated pages automatically. +After commiting the changes, just send a pull request. -If you need to update documentation according to a new feature that only appeared in the master version (not stable one), you need to make changes to `gh-pages-master` branch instead of `gh-pages`. It will get merged into the latter when released as stable. +If you need to update documentation according to a new feature that only appeared in the master version (not stable one), +you need to make changes to `gh-pages-master` branch instead of `gh-pages`. +It will get merged into the latter when released as stable. ## Thank You -Not only are we grateful for any contributions, — helping Leaflet and its community actually makes you AWESOME. Join [this approved list of awesome people](https://github.com/CloudMade/Leaflet/graphs/contributors) and help us push the limits of what's possible with online maps! +Not only are we grateful for any contributions, — helping Leaflet and its community actually makes you AWESOME. +Join [this approved list of awesome people](https://github.com/Leaflet/Leaflet/graphs/contributors) +and help us push the limits of what's possible with online maps! diff --git a/Jakefile.js b/Jakefile.js index baa72ba9..f184830d 100644 --- a/Jakefile.js +++ b/Jakefile.js @@ -1,67 +1,23 @@ -var build = require('./build/build.js'), - lint = require('./build/hint.js'); +/* +Leaflet building and linting scripts. -var COPYRIGHT = '/*\n Copyright (c) 2010-2012, CloudMade, Vladimir Agafonkin\n' + - ' Leaflet is an open-source JavaScript library for mobile-friendly interactive maps.\n' + - ' http://leafletjs.com\n*/\n'; +To use, install Node, then run the following commands in the project root: + + npm install -g jake + npm install uglify-js + npm install jshint + +To check the code and build Leaflet from source, run "jake" + +For a custom build, open build/build.html in the browser and follow the instructions. +*/ + +var build = require('./build/build.js'); desc('Check Leaflet source for errors with JSHint'); -task('lint', function () { - - var files = build.getFiles(); - - console.log('Checking for JS errors...'); - - var errorsFound = lint.jshint(files); - - if (errorsFound > 0) { - console.log(errorsFound + ' error(s) found.\n'); - fail(); - } else { - console.log('\tCheck passed'); - } -}); +task('lint', build.lint); desc('Combine and compress Leaflet source files'); -task('build', ['lint'], function (compsBase32, buildName) { - - var files = build.getFiles(compsBase32); - - console.log('Concatenating ' + files.length + ' files...'); - - var content = build.combineFiles(files), - newSrc = COPYRIGHT + content, - - pathPart = 'dist/leaflet' + (buildName ? '-' + buildName : ''), - srcPath = pathPart + '-src.js', - - oldSrc = build.load(srcPath), - srcDelta = build.getSizeDelta(newSrc, oldSrc); - - console.log('\tUncompressed size: ' + newSrc.length + ' bytes (' + srcDelta + ')'); - - if (newSrc === oldSrc) { - console.log('\tNo changes'); - } else { - build.save(srcPath, newSrc); - console.log('\tSaved to ' + srcPath); - } - - console.log('Compressing...'); - - var path = pathPart + '.js', - oldCompressed = build.load(path), - newCompressed = COPYRIGHT + build.uglify(content), - delta = build.getSizeDelta(newCompressed, oldCompressed); - - console.log('\tCompressed size: ' + newCompressed.length + ' bytes (' + delta + ')'); - - if (newCompressed === oldCompressed) { - console.log('\tNo changes'); - } else { - build.save(path, newCompressed); - console.log('\tSaved to ' + path); - } -}); +task('build', ['lint'], build.build); task('default', ['build']); diff --git a/LICENSE b/LICENSE index 02ef8e21..46cd121f 100644 --- a/LICENSE +++ b/LICENSE @@ -1,22 +1,23 @@ -Copyright (c) 2012, CloudMade, Vladimir Agafonkin -All rights reserved. +Copyright (c) 2010-2013, Vladimir Agafonkin +Copyright (c) 2010-2011, CloudMade +All rights reserved. -Redistribution and use in source and binary forms, with or without modification, are -permitted provided that the following conditions are met: +Redistribution and use in source and binary forms, with or without modification, are +permitted provided that the following conditions are met: - 1. Redistributions of source code must retain the above copyright notice, this list of - conditions and the following disclaimer. - - 2. Redistributions in binary form must reproduce the above copyright notice, this list + 1. Redistributions of source code must retain the above copyright notice, this list of + conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials - provided with the distribution. + provided with the distribution. -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY -EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE -COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR -TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR +TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/README.md b/README.md index e074ecb8..e9747126 100644 --- a/README.md +++ b/README.md @@ -1,20 +1,28 @@ Leaflet -Leaflet is a modern open-source JavaScript library for **mobile-friendly interactive maps**. It is developed by [Vladimir Agafonkin][] of [CloudMade][] with a team of dedicated [contributors][]. Weighing just about 27 KB of gzipped JS code, it has all the [features][] most developers ever need for online maps. +Leaflet is a modern open-source JavaScript library for **mobile-friendly interactive maps**. +It is developed by [Vladimir Agafonkin][] with a team of dedicated [contributors][]. +Weighing just about 27 KB of gzipped JS code, it has all the [features][] most developers ever need for online maps. -Leaflet is designed with *simplicity*, *performance* and *usability* in mind. It works efficiently across all major desktop and mobile platforms out of the box, taking advantage of HTML5 and CSS3 on modern browsers while being accessible on older ones too. It can also be extended with many [plugins][], has a beautiful, easy to use and [well-documented][] API and a simple, readable [source code][] that is a joy to [contribute][] to. +Leaflet is designed with *simplicity*, *performance* and *usability* in mind. +It works efficiently across all major desktop and mobile platforms out of the box, +taking advantage of HTML5 and CSS3 on modern browsers while being accessible on older ones too. +It can also be extended with many [plugins][], +has a beautiful, easy to use and [well-documented][] API +and a simple, readable [source code][] that is a joy to [contribute][] to. For more information, check out the [official website][]. -We're happy to meet new contributors. If you want to **get involved** with Leaflet development, check out the [contribution guide][contribute]. Let's make the best open-source library for maps that can possibly exist! +We're happy to meet new contributors. +If you want to **get involved** with Leaflet development, check out the [contribution guide][contribute]. +Let's make the best open-source library for maps that can possibly exist! [Vladimir Agafonkin]: http://agafonkin.com/en - [CloudMade]: http://cloudmade.com - [contributors]: https://github.com/CloudMade/Leaflet/graphs/contributors + [contributors]: https://github.com/Leaflet/Leaflet/graphs/contributors [features]: http://leafletjs.com/features.html [plugins]: http://leafletjs.com/plugins.html - [well-documented]: reference.html "Leaflet API reference" - [source code]: https://github.com/CloudMade/Leaflet "Leaflet GitHub repository" - [hosted on GitHub]: http://github.com/CloudMade/Leaflet - [contribute]: https://github.com/CloudMade/Leaflet/blob/master/CONTRIBUTING.md "A guide to contributing to Leaflet" + [well-documented]: http://leafletjs.com/reference.html "Leaflet API reference" + [source code]: https://github.com/Leaflet/Leaflet "Leaflet GitHub repository" + [hosted on GitHub]: http://github.com/Leaflet/Leaflet + [contribute]: https://github.com/Leaflet/Leaflet/blob/master/CONTRIBUTING.md "A guide to contributing to Leaflet" [official website]: http://leafletjs.com diff --git a/build/build.js b/build/build.js index a4da18ae..aba3471d 100644 --- a/build/build.js +++ b/build/build.js @@ -1,10 +1,35 @@ var fs = require('fs'), - uglifyjs = require('uglify-js'), - deps = require('./deps.js').deps; + jshint = require('jshint'), + UglifyJS = require('uglify-js'), -exports.getFiles = function (compsBase32) { + deps = require('./deps.js').deps, + hintrc = require('./hintrc.js').config; + + +function lintFiles(files) { + + var errorsFound = 0, + i, j, len, len2, src, errors, e; + + for (i = 0, len = files.length; i < len; i++) { + + jshint.JSHINT(fs.readFileSync(files[i], 'utf8'), hintrc); + errors = jshint.JSHINT.errors; + + for (j = 0, len2 = errors.length; j < len2; j++) { + e = errors[j]; + console.log(files[i] + '\tline ' + e.line + '\tcol ' + e.character + '\t ' + e.reason); + } + + errorsFound += len2; + } + + return errorsFound; +} + +function getFiles(compsBase32) { var memo = {}, - comps; + comps; if (compsBase32) { comps = parseInt(compsBase32, 32).toString(2).split(''); @@ -37,43 +62,94 @@ exports.getFiles = function (compsBase32) { } return files; -}; +} -exports.uglify = function (code) { - var pro = uglifyjs.uglify; +exports.lint = function () { - var ast = uglifyjs.parser.parse(code); - ast = pro.ast_mangle(ast, {mangle: true}); - ast = pro.ast_squeeze(ast); - ast = pro.ast_squeeze_more(ast); + var files = getFiles(); - return pro.gen_code(ast) + ';'; -}; + console.log('Checking for JS errors...'); -exports.combineFiles = function (files) { - var content = '(function (window, undefined) {\n\n'; - for (var i = 0, len = files.length; i < len; i++) { - content += fs.readFileSync(files[i], 'utf8') + '\n\n'; - } - return content + '\n\n}(this));'; -}; + var errorsFound = lintFiles(files); -exports.save = function (savePath, compressed) { - return fs.writeFileSync(savePath, compressed, 'utf8'); -}; - -exports.load = function (loadPath) { - try { - return fs.readFileSync(loadPath, 'utf8'); - } catch (e) { - return null; + if (errorsFound > 0) { + console.log(errorsFound + ' error(s) found.\n'); + fail(); + } else { + console.log('\tCheck passed'); } }; -exports.getSizeDelta = function (newContent, oldContent) { + +function getSizeDelta(newContent, oldContent) { if (!oldContent) { return 'new'; } - var delta = newContent.replace(/\r\n?/g, '\n').length - oldContent.replace(/\r\n?/g, '\n').length; + var newLen = newContent.replace(/\r\n?/g, '\n').length, + oldLen = oldContent.replace(/\r\n?/g, '\n').length, + delta = newLen - oldLen; + return (delta >= 0 ? '+' : '') + delta; -}; \ No newline at end of file +} + +function loadSilently(path) { + try { + return fs.readFileSync(path, 'utf8'); + } catch (e) { + return null; + } +} + +function combineFiles(files) { + var content = ''; + for (var i = 0, len = files.length; i < len; i++) { + content += fs.readFileSync(files[i], 'utf8') + '\n\n'; + } + return content; +} + +exports.build = function (compsBase32, buildName) { + + var files = getFiles(compsBase32); + + console.log('Concatenating ' + files.length + ' files...'); + + var copy = fs.readFileSync('src/copyright.js', 'utf8'), + intro = '(function (window, document, undefined) {', + outro = '}(this, document));', + newSrc = copy + intro + combineFiles(files) + outro, + + pathPart = 'dist/leaflet' + (buildName ? '-' + buildName : ''), + srcPath = pathPart + '-src.js', + + oldSrc = loadSilently(srcPath), + srcDelta = getSizeDelta(newSrc, oldSrc); + + console.log('\tUncompressed size: ' + newSrc.length + ' bytes (' + srcDelta + ')'); + + if (newSrc === oldSrc) { + console.log('\tNo changes'); + } else { + fs.writeFileSync(srcPath, newSrc); + console.log('\tSaved to ' + srcPath); + } + + console.log('Compressing...'); + + var path = pathPart + '.js', + oldCompressed = loadSilently(path), + newCompressed = copy + UglifyJS.minify(newSrc, { + warnings: true, + fromString: true + }).code, + delta = getSizeDelta(newCompressed, oldCompressed); + + console.log('\tCompressed size: ' + newCompressed.length + ' bytes (' + delta + ')'); + + if (newCompressed === oldCompressed) { + console.log('\tNo changes'); + } else { + fs.writeFileSync(path, newCompressed); + console.log('\tSaved to ' + path); + } +}; diff --git a/build/deps.js b/build/deps.js index 6f27b648..762bb748 100644 --- a/build/deps.js +++ b/build/deps.js @@ -54,7 +54,9 @@ var deps = { }, Marker: { - src: ['layer/marker/Icon.js', 'layer/marker/Icon.Default.js', 'layer/marker/Marker.js'], + src: ['layer/marker/Icon.js', + 'layer/marker/Icon.Default.js', + 'layer/marker/Marker.js'], desc: 'Markers to put on the map.' }, @@ -65,7 +67,9 @@ var deps = { }, Popup: { - src: ['layer/Popup.js', 'layer/marker/Marker.Popup.js', 'map/ext/Map.Popup.js'], + src: ['layer/Popup.js', + 'layer/marker/Marker.Popup.js', + 'map/ext/Map.Popup.js'], deps: ['Marker'], desc: 'Used to display the map popup (used mostly for binding HTML data to markers and paths on click).' }, @@ -83,7 +87,9 @@ var deps = { Path: { - src: ['layer/vector/Path.js', 'layer/vector/Path.SVG.js', 'layer/vector/Path.Popup.js'], + src: ['layer/vector/Path.js', + 'layer/vector/Path.SVG.js', + 'layer/vector/Path.Popup.js'], desc: 'Vector rendering core (SVG-powered), enables overlaying the map with SVG paths.', heading: 'Vector layers' }, @@ -100,13 +106,15 @@ var deps = { }, Polyline: { - src: ['geometry/LineUtil.js', 'layer/vector/Polyline.js'], + src: ['geometry/LineUtil.js', + 'layer/vector/Polyline.js'], deps: ['Path'], desc: 'Polyline overlays.' }, Polygon: { - src: ['geometry/PolyUtil.js', 'layer/vector/Polygon.js'], + src: ['geometry/PolyUtil.js', + 'layer/vector/Polygon.js'], deps: ['Polyline'], desc: 'Polygon overlays.' }, diff --git a/build/hint.js b/build/hint.js deleted file mode 100644 index 464bbe11..00000000 --- a/build/hint.js +++ /dev/null @@ -1,30 +0,0 @@ -var jshint = require('jshint').JSHINT, - fs = require('fs'), - config = require('./hintrc.js').config; - -function jshintSrc(path, src) { - jshint(src, config); - - var errors = jshint.errors, - i, len, e, line; - - for (i = 0, len = errors.length; i < len; i++) { - e = errors[i]; - //console.log(e.evidence); - console.log(path + '\tline ' + e.line + '\tcol ' + e.character + '\t ' + e.reason); - } - - return len; -} - -exports.jshint = function (files) { - var errorsFound = 0; - - for (var i = 0, len = files.length; i < len; i++) { - var src = fs.readFileSync(files[i], 'utf8'); - - errorsFound += jshintSrc(files[i], src); - } - - return errorsFound; -}; \ No newline at end of file diff --git a/debug/leaflet-include.js b/debug/leaflet-include.js index 6833ff10..1a9610a3 100644 --- a/debug/leaflet-include.js +++ b/debug/leaflet-include.js @@ -38,6 +38,7 @@ 'geo/crs/CRS.EPSG3857.js', 'geo/crs/CRS.EPSG4326.js', 'geo/crs/CRS.EPSG3395.js', + 'geo/crs/CRS.Simple.js', 'map/Map.js', @@ -128,3 +129,7 @@ function getRandomLatLng(map) { southWest.lat + latSpan * Math.random(), southWest.lng + lngSpan * Math.random()); } + +function logEvent(e) { + console.log(e.type); +} diff --git a/debug/map/simple-proj.html b/debug/map/simple-proj.html new file mode 100644 index 00000000..d2d11e9a --- /dev/null +++ b/debug/map/simple-proj.html @@ -0,0 +1,44 @@ + + + + Leaflet debug page + + + + + + + + + + + + +
+ + + + diff --git a/debug/map/zoomlevels.html b/debug/map/zoomlevels.html new file mode 100644 index 00000000..d5b8747e --- /dev/null +++ b/debug/map/zoomlevels.html @@ -0,0 +1,45 @@ + + + + Leaflet debug page + + + + + + + + + + +
+ + + + diff --git a/debug/tests/add_remove_layers.html b/debug/tests/add_remove_layers.html new file mode 100644 index 00000000..24c07aeb --- /dev/null +++ b/debug/tests/add_remove_layers.html @@ -0,0 +1,88 @@ + + + + Leaflet debug page + + + + + + + + + + + + + + +
+
+ + +
+ + diff --git a/debug/tests/click_on_canvas.html b/debug/tests/click_on_canvas.html new file mode 100644 index 00000000..2209748e --- /dev/null +++ b/debug/tests/click_on_canvas.html @@ -0,0 +1,51 @@ + + + + Leaflet debug page + + + + + + + + + + + + + + +
+ + diff --git a/debug/tests/click_on_canvas_broken.html b/debug/tests/click_on_canvas_broken.html new file mode 100644 index 00000000..dbce2bed --- /dev/null +++ b/debug/tests/click_on_canvas_broken.html @@ -0,0 +1,49 @@ + + + + Leaflet debug page + + + + + + + + + + + + +
+ + diff --git a/dist/images/marker-icon.png b/dist/images/marker-icon.png index 5b1e7da9..e2e9f757 100644 Binary files a/dist/images/marker-icon.png and b/dist/images/marker-icon.png differ diff --git a/dist/images/marker-icon@2x.png b/dist/images/marker-icon@2x.png new file mode 100644 index 00000000..0015b649 Binary files /dev/null and b/dist/images/marker-icon@2x.png differ diff --git a/dist/leaflet-src.js b/dist/leaflet-src.js index 6c057233..948377ea 100644 --- a/dist/leaflet-src.js +++ b/dist/leaflet-src.js @@ -1,9 +1,11 @@ /* - Copyright (c) 2010-2012, CloudMade, Vladimir Agafonkin - Leaflet is an open-source JavaScript library for mobile-friendly interactive maps. - http://leafletjs.com + Leaflet, a JavaScript library for mobile-friendly interactive maps. http://leafletjs.com + (c) 2010-2013, Vladimir Agafonkin, CloudMade */ -(function (window, undefined) { +(function (window, document, undefined) {/* + * The L namespace contains all Leaflet classes and functions. + * This code allows you to handle any possible namespace conflicts. + */ var L, originalL; @@ -25,7 +27,7 @@ L.version = '0.5'; /* - * L.Util is a namespace for various utility functions. + * L.Util contains various utility functions used throughout Leaflet code. */ L.Util = { @@ -103,14 +105,14 @@ L.Util = { return obj.options; }, - getParamString: function (obj) { + getParamString: function (obj, existingUrl) { var params = []; for (var i in obj) { if (obj.hasOwnProperty(i)) { params.push(i + '=' + obj[i]); } } - return '?' + params.join('&'); + return ((!existingUrl || existingUrl.indexOf('?') === -1) ? '?' : '&') + params.join('&'); }, template: function (str, data) { @@ -123,6 +125,10 @@ L.Util = { }); }, + isArray: function (obj) { + return (Object.prototype.toString.call(obj) === '[object Array]'); + }, + emptyImageUrl: 'data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=' }; @@ -186,18 +192,26 @@ L.setOptions = L.Util.setOptions; /* - * Class powers the OOP facilities of the library. Thanks to John Resig and Dean Edwards for inspiration! + * L.Class powers the OOP facilities of the library. + * Thanks to John Resig and Dean Edwards for inspiration! */ L.Class = function () {}; -L.Class.extend = function (/*Object*/ props) /*-> Class*/ { +L.Class.extend = function (props) { // extended class with the new prototype var NewClass = function () { + + // call the constructor if (this.initialize) { this.initialize.apply(this, arguments); } + + // call all constructor hooks + if (this._initHooks) { + this.callInitHooks(); + } }; // instantiate class without calling constructor @@ -236,6 +250,25 @@ L.Class.extend = function (/*Object*/ props) /*-> Class*/ { // mix given properties into the prototype L.extend(proto, props); + proto._initHooks = []; + + var parent = this; + // add method for calling all hooks + proto.callInitHooks = function () { + + if (this._initHooksCalled) { return; } + + if (parent.prototype.callInitHooks) { + parent.prototype.callInitHooks.call(this); + } + + this._initHooksCalled = true; + + for (var i = 0, len = proto._initHooks.length; i < len; i++) { + proto._initHooks[i].call(this); + } + }; + return NewClass; }; @@ -245,13 +278,26 @@ L.Class.include = function (props) { L.extend(this.prototype, props); }; +// merge new default options to the Class L.Class.mergeOptions = function (options) { L.extend(this.prototype.options, options); }; +// add a constructor hook +L.Class.addInitHook = function (fn) { // (Function) || (String, args...) + var args = Array.prototype.slice.call(arguments, 1); + + var init = typeof fn === 'function' ? fn : function () { + this[fn].apply(this, args); + }; + + this.prototype._initHooks = this.prototype._initHooks || []; + this.prototype._initHooks.push(init); +}; + /* - * L.Mixin.Events adds custom events functionality to Leaflet classes + * L.Mixin.Events is used to add custom events functionality to Leaflet classes. */ var key = '_leaflet_events'; @@ -352,26 +398,29 @@ L.Mixin.Events.off = L.Mixin.Events.removeEventListener; L.Mixin.Events.fire = L.Mixin.Events.fireEvent; +/* + * L.Browser handles different browser and feature detections for internal Leaflet use. + */ + (function () { var ie = !!window.ActiveXObject, - // http://tanalin.com/en/articles/ie-version-js/ ie6 = ie && !window.XMLHttpRequest, ie7 = ie && !document.querySelector, // terrible browser detection to work around Safari / iOS / Android browser bugs - // see TileLayer._addTile and debug/hacks/jitter.html - ua = navigator.userAgent.toLowerCase(), - webkit = ua.indexOf("webkit") !== -1, - chrome = ua.indexOf("chrome") !== -1, - android = ua.indexOf("android") !== -1, - android23 = ua.search("android [23]") !== -1, + webkit = ua.indexOf('webkit') !== -1, + chrome = ua.indexOf('chrome') !== -1, + android = ua.indexOf('android') !== -1, + android23 = ua.search('android [23]') !== -1, mobile = typeof orientation !== undefined + '', - msTouch = (window.navigator && window.navigator.msPointerEnabled && window.navigator.msMaxTouchPoints), - retina = (('devicePixelRatio' in window && window.devicePixelRatio > 1) || - ('matchMedia' in window && window.matchMedia("(min-resolution:144dpi)").matches)), + msTouch = window.navigator && window.navigator.msPointerEnabled && + window.navigator.msMaxTouchPoints, + retina = ('devicePixelRatio' in window && window.devicePixelRatio > 1) || + ('matchMedia' in window && window.matchMedia('(min-resolution:144dpi)') && + window.matchMedia('(min-resolution:144dpi)').matches), doc = document.documentElement, ie3d = ie && ('transition' in doc.style), @@ -411,6 +460,7 @@ L.Mixin.Events.fire = L.Mixin.Events.fireEvent; L.Browser = { + ie: ie, ie6: ie6, ie7: ie7, webkit: webkit, @@ -526,6 +576,11 @@ L.Point.prototype = { return Math.sqrt(x * x + y * y); }, + equals: function (point) { + return point.x === this.x && + point.y === this.y; + }, + toString: function () { return 'Point(' + L.Util.formatNum(this.x) + ', ' + @@ -537,7 +592,7 @@ L.point = function (x, y, round) { if (x instanceof L.Point) { return x; } - if (x instanceof Array) { + if (L.Util.isArray(x)) { return new L.Point(x[0], x[1]); } if (isNaN(x)) { @@ -551,18 +606,17 @@ L.point = function (x, y, round) { * L.Bounds represents a rectangular area on the screen in pixel coordinates. */ -L.Bounds = L.Class.extend({ +L.Bounds = function (a, b) { //(Point, Point) or Point[] + if (!a) { return; } - initialize: function (a, b) { //(Point, Point) or Point[] - if (!a) { return; } + var points = b ? [a, b] : a; - var points = b ? [a, b] : a; - - for (var i = 0, len = points.length; i < len; i++) { - this.extend(points[i]); - } - }, + for (var i = 0, len = points.length; i < len; i++) { + this.extend(points[i]); + } +}; +L.Bounds.prototype = { // extend the bounds to contain the given point extend: function (point) { // (Point) point = L.point(point); @@ -593,6 +647,10 @@ L.Bounds = L.Class.extend({ return new L.Point(this.max.x, this.min.y); }, + getSize: function () { + return this.max.subtract(this.min); + }, + contains: function (obj) { // (Bounds) or (Point) -> Boolean var min, max; @@ -631,7 +689,7 @@ L.Bounds = L.Class.extend({ isValid: function () { return !!(this.min && this.max); } -}); +}; L.bounds = function (a, b) { // (Bounds) or (Point, Point) or (Point[]) if (!a || a instanceof L.Bounds) { @@ -645,33 +703,33 @@ L.bounds = function (a, b) { // (Bounds) or (Point, Point) or (Point[]) * L.Transformation is an utility class to perform simple point transformations through a 2d-matrix. */ -L.Transformation = L.Class.extend({ - initialize: function (/*Number*/ a, /*Number*/ b, /*Number*/ c, /*Number*/ d) { - this._a = a; - this._b = b; - this._c = c; - this._d = d; - }, +L.Transformation = function (a, b, c, d) { + this._a = a; + this._b = b; + this._c = c; + this._d = d; +}; - transform: function (point, scale) { +L.Transformation.prototype = { + transform: function (point, scale) { // (Point, Number) -> Point return this._transform(point.clone(), scale); }, // destructive transform (faster) - _transform: function (/*Point*/ point, /*Number*/ scale) /*-> Point*/ { + _transform: function (point, scale) { scale = scale || 1; point.x = scale * (this._a * point.x + this._b); point.y = scale * (this._c * point.y + this._d); return point; }, - untransform: function (/*Point*/ point, /*Number*/ scale) /*-> Point*/ { + untransform: function (point, scale) { scale = scale || 1; return new L.Point( (point.x / scale - this._b) / this._a, (point.y / scale - this._d) / this._c); } -}); +}; /* @@ -711,6 +769,11 @@ L.DomUtil = { do { top += el.offsetTop || 0; left += el.offsetLeft || 0; + + //add borders + top += parseInt(L.DomUtil.getStyle(el, "borderTopWidth"), 10) || 0; + left += parseInt(L.DomUtil.getStyle(el, "borderLeftWidth"), 10) || 0; + pos = L.DomUtil.getStyle(el, 'position'); if (el.offsetParent === docBody && pos === 'absolute') { break; } @@ -776,7 +839,7 @@ L.DomUtil = { document.selection.empty(); } if (!this._onselectstart) { - this._onselectstart = document.onselectstart; + this._onselectstart = document.onselectstart || null; document.onselectstart = L.Util.falseFn; } }, @@ -897,8 +960,11 @@ L.DomUtil = { L.DomUtil.TRANSFORM = L.DomUtil.testProp( ['transform', 'WebkitTransform', 'OTransform', 'MozTransform', 'msTransform']); +// webkitTransition comes first because some browser versions that drop vendor prefix don't do +// the same for the transitionend event, in particular the Android 4.1 stock browser + L.DomUtil.TRANSITION = L.DomUtil.testProp( - ['transition', 'webkitTransition', 'OTransition', 'MozTransition', 'msTransition']); + ['webkitTransition', 'transition', 'OTransition', 'MozTransition', 'msTransition']); L.DomUtil.TRANSITION_END = L.DomUtil.TRANSITION === 'webkitTransition' || L.DomUtil.TRANSITION === 'OTransition' ? @@ -906,10 +972,10 @@ L.DomUtil.TRANSITION_END = /* - L.LatLng represents a geographical point with latitude and longitude coordinates. -*/ + * L.LatLng represents a geographical point with latitude and longitude coordinates. + */ -L.LatLng = function (rawLat, rawLng, noWrap) { // (Number, Number[, Boolean]) +L.LatLng = function (rawLat, rawLng) { // (Number, Number) var lat = parseFloat(rawLat), lng = parseFloat(rawLng); @@ -917,11 +983,6 @@ L.LatLng = function (rawLat, rawLng, noWrap) { // (Number, Number[, Boolean]) throw new Error('Invalid LatLng object: (' + rawLat + ', ' + rawLng + ')'); } - if (noWrap !== true) { - lat = Math.max(Math.min(lat, 90), -90); // clamp latitude into -90..90 - lng = (lng + 180) % 360 + ((lng < -180 || lng === 180) ? 180 : -180); // wrap longitude into -180..180 - } - this.lat = lat; this.lng = lng; }; @@ -938,17 +999,21 @@ L.LatLng.prototype = { obj = L.latLng(obj); - var margin = Math.max(Math.abs(this.lat - obj.lat), Math.abs(this.lng - obj.lng)); + var margin = Math.max( + Math.abs(this.lat - obj.lat), + Math.abs(this.lng - obj.lng)); + return margin <= L.LatLng.MAX_MARGIN; }, - toString: function (precision) { // -> String + toString: function (precision) { // (Number) -> String return 'LatLng(' + L.Util.formatNum(this.lat, precision) + ', ' + L.Util.formatNum(this.lng, precision) + ')'; }, // Haversine distance formula, see http://en.wikipedia.org/wiki/Haversine_formula + // TODO move to projection code, LatLng shouldn't know about Earth distanceTo: function (other) { // (LatLng) -> Number other = L.latLng(other); @@ -964,20 +1029,31 @@ L.LatLng.prototype = { var a = sin1 * sin1 + sin2 * sin2 * Math.cos(lat1) * Math.cos(lat2); return R * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); + }, + + wrap: function (a, b) { // (Number, Number) -> LatLng + var lng = this.lng; + + a = a || -180; + b = b || 180; + + lng = (lng + b) % (b - a) + (lng < a || lng === b ? b : a); + + return new L.LatLng(this.lat, lng); } }; -L.latLng = function (a, b, c) { // (LatLng) or ([Number, Number]) or (Number, Number, Boolean) +L.latLng = function (a, b) { // (LatLng) or ([Number, Number]) or (Number, Number) if (a instanceof L.LatLng) { return a; } - if (a instanceof Array) { + if (L.Util.isArray(a)) { return new L.LatLng(a[0], a[1]); } if (isNaN(a)) { return a; } - return new L.LatLng(a, b, c); + return new L.LatLng(a, b); }; @@ -986,20 +1062,20 @@ L.latLng = function (a, b, c) { // (LatLng) or ([Number, Number]) or (Number, Nu * L.LatLngBounds represents a rectangular area on the map in geographical coordinates. */ -L.LatLngBounds = L.Class.extend({ - initialize: function (southWest, northEast) { // (LatLng, LatLng) or (LatLng[]) - if (!southWest) { return; } +L.LatLngBounds = function (southWest, northEast) { // (LatLng, LatLng) or (LatLng[]) + if (!southWest) { return; } - var latlngs = northEast ? [southWest, northEast] : southWest; + var latlngs = northEast ? [southWest, northEast] : southWest; - for (var i = 0, len = latlngs.length; i < len; i++) { - this.extend(latlngs[i]); - } - }, + for (var i = 0, len = latlngs.length; i < len; i++) { + this.extend(latlngs[i]); + } +}; +L.LatLngBounds.prototype = { // extend the bounds to contain the given point or bounds extend: function (obj) { // (LatLng) or (LatLngBounds) - if (typeof obj[0] === 'number' || obj instanceof L.LatLng) { + if (typeof obj[0] === 'number' || typeof obj[0] === 'string' || obj instanceof L.LatLng) { obj = L.latLng(obj); } else { obj = L.latLngBounds(obj); @@ -1007,8 +1083,8 @@ L.LatLngBounds = L.Class.extend({ if (obj instanceof L.LatLng) { if (!this._southWest && !this._northEast) { - this._southWest = new L.LatLng(obj.lat, obj.lng, true); - this._northEast = new L.LatLng(obj.lat, obj.lng, true); + this._southWest = new L.LatLng(obj.lat, obj.lng); + this._northEast = new L.LatLng(obj.lat, obj.lng); } else { this._southWest.lat = Math.min(obj.lat, this._southWest.lat); this._southWest.lng = Math.min(obj.lng, this._southWest.lng); @@ -1050,11 +1126,11 @@ L.LatLngBounds = L.Class.extend({ }, getNorthWest: function () { - return new L.LatLng(this._northEast.lat, this._southWest.lng, true); + return new L.LatLng(this._northEast.lat, this._southWest.lng); }, getSouthEast: function () { - return new L.LatLng(this._southWest.lat, this._northEast.lng, true); + return new L.LatLng(this._southWest.lat, this._northEast.lng); }, contains: function (obj) { // (LatLngBounds) or (LatLng) -> Boolean @@ -1112,7 +1188,7 @@ L.LatLngBounds = L.Class.extend({ isValid: function () { return !!(this._southWest && this._northEast); } -}); +}; //TODO International date line? @@ -1131,6 +1207,9 @@ L.latLngBounds = function (a, b) { // (LatLngBounds) or (LatLng, LatLng) L.Projection = {}; +/* + * Spherical Mercator is the most popular map projection, used by EPSG:3857 CRS used by default. + */ L.Projection.SphericalMercator = { MAX_LATITUDE: 85.0511287798, @@ -1152,12 +1231,14 @@ L.Projection.SphericalMercator = { lng = point.x * d, lat = (2 * Math.atan(Math.exp(point.y)) - (Math.PI / 2)) * d; - // TODO refactor LatLng wrapping - return new L.LatLng(lat, lng, true); + return new L.LatLng(lat, lng); } }; +/* + * Simple equirectangular (Plate Carree) projection, used by CRS like EPSG:4326 and Simple. + */ L.Projection.LonLat = { project: function (latlng) { @@ -1165,11 +1246,14 @@ L.Projection.LonLat = { }, unproject: function (point) { - return new L.LatLng(point.y, point.x, true); + return new L.LatLng(point.y, point.x); } }; +/* + * L.CRS is a base object for all defined CRS (Coordinate Reference Systems) in Leaflet. + */ L.CRS = { latLngToPoint: function (latlng, zoom) { // (LatLng, Number) -> Point @@ -1196,13 +1280,24 @@ L.CRS = { }; +/* + * A simple CRS that can be used for flat non-Earth maps like panoramas or game maps. + */ L.CRS.Simple = L.extend({}, L.CRS, { projection: L.Projection.LonLat, - transformation: new L.Transformation(1, 0, 1, 0) + transformation: new L.Transformation(1, 0, -1, 0), + + scale: function (zoom) { + return Math.pow(2, zoom); + } }); +/* + * L.CRS.EPSG3857 (Spherical Mercator) is the most common CRS for web mapping + * and is used by Leaflet by default. + */ L.CRS.EPSG3857 = L.extend({}, L.CRS, { code: 'EPSG:3857', @@ -1222,6 +1317,9 @@ L.CRS.EPSG900913 = L.extend({}, L.CRS.EPSG3857, { }); +/* + * L.CRS.EPSG4326 is a CRS popular among advanced GIS specialists. + */ L.CRS.EPSG4326 = L.extend({}, L.CRS, { code: 'EPSG:4326', @@ -1258,7 +1356,7 @@ L.Map = L.Class.extend({ this._initContainer(id); this._initLayout(); - this._initHooks(); + this.callInitHooks(); this._initEvents(); if (options.maxBounds) { @@ -1381,11 +1479,9 @@ L.Map = L.Class.extend({ this._layers[id] = layer; // TODO getMaxZoom, getMinZoom in ILayer (instead of options) - if (layer.options && !isNaN(layer.options.maxZoom)) { - this._layersMaxZoom = Math.max(this._layersMaxZoom || 0, layer.options.maxZoom); - } - if (layer.options && !isNaN(layer.options.minZoom)) { - this._layersMinZoom = Math.min(this._layersMinZoom || Infinity, layer.options.minZoom); + if (layer.options && (!isNaN(layer.options.maxZoom) || !isNaN(layer.options.minZoom))) { + this._zoomBoundLayers[id] = layer; + this._updateZoomLevels(); } // TODO looks ugly, refactor!!! @@ -1411,6 +1507,10 @@ L.Map = L.Class.extend({ layer.onRemove(this); delete this._layers[id]; + if (this._zoomBoundLayers[id]) { + delete this._zoomBoundLayers[id]; + this._updateZoomLevels(); + } // TODO looks ugly, refactor if (this.options.zoomAnimation && L.TileLayer && (layer instanceof L.TileLayer)) { @@ -1648,7 +1748,6 @@ L.Map = L.Class.extend({ _initLayout: function () { var container = this._container; - container.innerHTML = ''; L.DomUtil.addClass(container, 'leaflet-container'); if (L.Browser.touch) { @@ -1697,19 +1796,11 @@ L.Map = L.Class.extend({ return L.DomUtil.create('div', className, container || this._panes.objectsPane); }, - _initializers: [], - - _initHooks: function () { - var i, len; - for (i = 0, len = this._initializers.length; i < len; i++) { - this._initializers[i].call(this); - } - }, - _initLayers: function (layers) { - layers = layers ? (layers instanceof Array ? layers : [layers]) : []; + layers = layers ? (L.Util.isArray(layers) ? layers : [layers]) : []; this._layers = {}; + this._zoomBoundLayers = {}; this._tileLayersNum = 0; var i, len; @@ -1768,6 +1859,30 @@ L.Map = L.Class.extend({ L.DomUtil.setPosition(this._mapPane, this._getMapPanePos().subtract(offset)); }, + _updateZoomLevels: function () { + var i, + minZoom = Infinity, + maxZoom = -Infinity; + + for (i in this._zoomBoundLayers) { + if (this._zoomBoundLayers.hasOwnProperty(i)) { + var layer = this._zoomBoundLayers[i]; + if (!isNaN(layer.options.minZoom)) { + minZoom = Math.min(minZoom, layer.options.minZoom); + } + if (!isNaN(layer.options.maxZoom)) { + maxZoom = Math.max(maxZoom, layer.options.maxZoom); + } + } + } + + if (i === undefined) { // we have no tilelayers + this._layersMaxZoom = this._layersMinZoom = undefined; + } else { + this._layersMaxZoom = maxZoom; + this._layersMinZoom = minZoom; + } + }, // map events @@ -1888,21 +2003,15 @@ L.Map = L.Class.extend({ } }); -L.Map.addInitHook = function (fn) { - var args = Array.prototype.slice.call(arguments, 1); - - var init = typeof fn === 'function' ? fn : function () { - this[fn].apply(this, args); - }; - - this.prototype._initializers.push(init); -}; - L.map = function (id, options) { return new L.Map(id, options); }; +/* + * Mercator projection that takes into account that the Earth is not a perfect sphere. + * Less popular than spherical mercator; used by projections like EPSG:3395. + */ L.Projection.Mercator = { MAX_LATITUDE: 85.0840591556, @@ -1952,7 +2061,7 @@ L.Projection.Mercator = { phi += dphi; } - return new L.LatLng(phi * d, lng, true); + return new L.LatLng(phi * d, lng); } }; @@ -2056,7 +2165,7 @@ L.TileLayer = L.Class.extend({ }, onRemove: function (map) { - map._panes.tilePane.removeChild(this._container); + this._container.parentNode.removeChild(this._container); map.off({ 'viewreset': this._resetCallback, @@ -2141,7 +2250,7 @@ L.TileLayer = L.Class.extend({ _setAutoZIndex: function (pane, compare) { - var layers = pane.getElementsByClassName('leaflet-layer'), + var layers = pane.children, edgeZIndex = -compare(Infinity, -Infinity), // -Infinity for max, Infinity for min zIndex, i, len; @@ -2219,7 +2328,7 @@ L.TileLayer = L.Class.extend({ this._initContainer(); }, - _update: function (e) { + _update: function () { if (!this._map) { return; } @@ -2440,9 +2549,8 @@ L.TileLayer = L.Class.extend({ return this._createTile(); }, - _resetTile: function (tile) { - // Override if data stored on a tile needs to be cleaned up before reuse - }, + // Override if data stored on a tile needs to be cleaned up before reuse + _resetTile: function (/*tile*/) {}, _createTile: function () { var tile = this._tileImg.cloneNode(false); @@ -2465,7 +2573,7 @@ L.TileLayer = L.Class.extend({ } }, - _tileOnLoad: function (e) { + _tileOnLoad: function () { var layer = this._layer; //Only if we are loading an actual image @@ -2481,7 +2589,7 @@ L.TileLayer = L.Class.extend({ layer._tileLoaded(); }, - _tileOnError: function (e) { + _tileOnError: function () { var layer = this._layer; layer.fire('tileerror', { @@ -2503,6 +2611,10 @@ L.tileLayer = function (url, options) { }; +/* + * L.TileLayer.WMS is used for putting WMS tile layers on the map. + */ + L.TileLayer.WMS = L.TileLayer.extend({ defaultWmsParams: { @@ -2549,6 +2661,8 @@ 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, @@ -2563,7 +2677,7 @@ L.TileLayer.WMS = L.TileLayer.extend({ url = L.Util.template(this._url, {s: this._getSubdomain(tilePoint)}); - return url + L.Util.getParamString(this.wmsParams) + "&bbox=" + bbox; + return url + L.Util.getParamString(this.wmsParams, url) + "&bbox=" + bbox; }, setParams: function (params, noRedraw) { @@ -2583,6 +2697,11 @@ L.tileLayer.wms = function (url, options) { }; +/* + * L.TileLayer.Canvas is a class that you can use as a base for creating + * dynamically drawn Canvas-based tile layers. + */ + L.TileLayer.Canvas = L.TileLayer.extend({ options: { async: false @@ -2628,7 +2747,7 @@ L.TileLayer.Canvas = L.TileLayer.extend({ } }, - drawTile: function (tile, tilePoint) { + drawTile: function (/*tile, tilePoint*/) { // override with rendering code }, @@ -2643,6 +2762,10 @@ L.tileLayer.canvas = function (options) { }; +/* + * L.ImageOverlay is used to overlay images over the map (to specific geographical bounds). + */ + L.ImageOverlay = L.Class.extend({ includes: L.Mixin.Events, @@ -2742,8 +2865,7 @@ L.ImageOverlay = L.Class.extend({ topLeft = map._latLngToNewLayerPoint(nw, e.zoom, e.center), size = map._latLngToNewLayerPoint(se, e.zoom, e.center)._subtract(topLeft), - currentSize = map.latLngToLayerPoint(se)._subtract(map.latLngToLayerPoint(nw)), - origin = topLeft._add(size._subtract(currentSize)._divideBy(2)); + origin = topLeft._add(size._multiplyBy((1 / 2) * (1 - 1 / scale))); image.style[L.DomUtil.TRANSFORM] = L.DomUtil.getTranslateString(origin) + ' scale(' + scale + ') '; @@ -2752,7 +2874,7 @@ L.ImageOverlay = L.Class.extend({ _reset: function () { var image = this._image, topLeft = this._map.latLngToLayerPoint(this._bounds.getNorthWest()), - size = this._map.latLngToLayerPoint(this._bounds.getSouthEast())._subtract(topLeft); + size = this._map.latLngToLayerPoint(this._bounds.getSouthEast())._subtract(topLeft); L.DomUtil.setPosition(image, topLeft); @@ -2774,14 +2896,20 @@ L.imageOverlay = function (url, bounds, options) { }; +/* + * L.Icon is an image-based icon class that you can use with L.Marker for custom markers. + */ + L.Icon = L.Class.extend({ options: { /* iconUrl: (String) (required) + iconRetinaUrl: (String) (optional, used for retina devices if detected) iconSize: (Point) (can be set through CSS) iconAnchor: (Point) (centered by default, can be set in CSS with negative margins) popupAnchor: (Point) (if not specified, popup opens in the anchor point) shadowUrl: (Point) (no shadow by default) + shadowRetinaUrl: (String) (optional, used for retina devices if detected) shadowSize: (Point) shadowAnchor: (Point) */ @@ -2859,6 +2987,9 @@ L.Icon = L.Class.extend({ }, _getIconUrl: function (name) { + if (L.Browser.retina && this.options[name + 'RetinaUrl']) { + return this.options[name + 'RetinaUrl']; + } return this.options[name + 'Url']; } }); @@ -2868,6 +2999,9 @@ L.icon = function (options) { }; +/* + * L.Icon.Default is the blue marker icon used by default in Leaflet. + */ L.Icon.Default = L.Icon.extend({ @@ -2886,6 +3020,10 @@ L.Icon.Default = L.Icon.extend({ return this.options[key]; } + if (L.Browser.retina && name === 'icon') { + name += '@2x'; + } + var path = L.Icon.Default.imagePath; if (!path) { @@ -2977,12 +3115,14 @@ L.Marker = L.Class.extend({ this.update(); - this.fire('move', { latlng: this._latlng }); + return this.fire('move', { latlng: this._latlng }); }, setZIndexOffset: function (offset) { this.options.zIndexOffset = offset; this.update(); + + return this; }, setIcon: function (icon) { @@ -2996,13 +3136,17 @@ L.Marker = L.Class.extend({ this._initIcon(); this.update(); } + + return this; }, update: function () { - if (!this._icon) { return; } + if (this._icon) { + var pos = this._map.latLngToLayerPoint(this._latlng).round(); + this._setPos(pos); + } - var pos = this._map.latLngToLayerPoint(this._latlng).round(); - this._setPos(pos); + return this; }, _initIcon: function () { @@ -3094,12 +3238,13 @@ L.Marker = L.Class.extend({ }, _initInteraction: function () { - if (!this.options.clickable) { - return; - } + + if (!this.options.clickable) { return; } + + // TODO refactor into something shared with Map/Path/etc. to DRY it up var icon = this._icon, - events = ['dblclick', 'mousedown', 'mouseover', 'mouseout']; + events = ['dblclick', 'mousedown', 'mouseover', 'mouseout', 'contextmenu']; L.DomUtil.addClass(icon, 'leaflet-clickable'); L.DomEvent.on(icon, 'click', this._onMouseClick, this); @@ -3119,20 +3264,31 @@ L.Marker = L.Class.extend({ _onMouseClick: function (e) { var wasDragged = this.dragging && this.dragging.moved(); + if (this.hasEventListeners(e.type) || wasDragged) { L.DomEvent.stopPropagation(e); } + if (wasDragged) { return; } - if (this._map.dragging && this._map.dragging.moved()) { return; } + + if ((!this.dragging || !this.dragging._enabled) && this._map.dragging && this._map.dragging.moved()) { return; } + this.fire(e.type, { originalEvent: e }); }, _fireMouseEvent: function (e) { + this.fire(e.type, { originalEvent: e }); + + // TODO proper custom event propagation + // this line will always be called if marker is in a FeatureGroup + if (e.type === 'contextmenu' && this.hasEventListeners(e.type)) { + L.DomEvent.preventDefault(e); + } if (e.type !== 'mousedown') { L.DomEvent.stopPropagation(e); } @@ -3166,6 +3322,11 @@ L.marker = function (latlng, options) { }; +/* + * L.DivIcon is a lightweight HTML-based icon class (as opposed to the image-based L.Icon) + * to use with L.Marker. + */ + L.DivIcon = L.Icon.extend({ options: { iconSize: new L.Point(12, 12), // also can be set through CSS @@ -3205,6 +3366,9 @@ L.divIcon = function (options) { }; +/* + * L.Popup is used for displaying popups on the map. + */ L.Map.mergeOptions({ closePopupOnClick: true @@ -3221,13 +3385,15 @@ L.Popup = L.Class.extend({ closeButton: true, offset: new L.Point(0, 6), autoPanPadding: new L.Point(5, 5), - className: '' + className: '', + zoomAnimation: true }, initialize: function (options, source) { L.setOptions(this, options); this._source = source; + this._animated = L.Browser.any3d && this.options.zoomAnimation; }, onAdd: function (map) { @@ -3247,7 +3413,7 @@ L.Popup = L.Class.extend({ map.on('viewreset', this._updatePosition, this); - if (L.Browser.any3d) { + if (this._animated) { map.on('zoomanim', this._zoomAnimation, this); } @@ -3316,7 +3482,8 @@ L.Popup = L.Class.extend({ _initLayout: function () { var prefix = 'leaflet-popup', - containerClass = prefix + ' ' + this.options.className + ' leaflet-zoom-animated', + containerClass = prefix + ' ' + this.options.className + ' leaflet-zoom-' + + (this._animated ? 'animated' : 'hide'), container = this._container = L.DomUtil.create('div', containerClass), closeButton; @@ -3402,15 +3569,15 @@ L.Popup = L.Class.extend({ if (!this._map) { return; } var pos = this._map.latLngToLayerPoint(this._latlng), - is3d = L.Browser.any3d, + animated = this._animated, offset = this.options.offset; - if (is3d) { + if (animated) { L.DomUtil.setPosition(this._container, pos); } - this._containerBottom = -offset.y - (is3d ? 0 : pos.y); - this._containerLeft = -Math.round(this._containerWidth / 2) + offset.x + (is3d ? 0 : pos.x); + this._containerBottom = -offset.y - (animated ? 0 : pos.y); + this._containerLeft = -Math.round(this._containerWidth / 2) + offset.x + (animated ? 0 : pos.x); //Bottom position the popup in case the height of the popup changes (images loading etc) this._container.style.bottom = this._containerBottom + 'px'; @@ -3432,7 +3599,7 @@ L.Popup = L.Class.extend({ layerPos = new L.Point(this._containerLeft, -containerHeight - this._containerBottom); - if (L.Browser.any3d) { + if (this._animated) { layerPos._add(L.DomUtil.getPosition(this._container)); } @@ -3472,7 +3639,7 @@ L.popup = function (options, source) { /* - * Popup extension to L.Marker, adding openPopup & bindPopup methods. + * Popup extension to L.Marker, adding popup-related methods. */ L.Marker.include({ @@ -3533,6 +3700,9 @@ L.Marker.include({ }); +/* + * Adds popup-related methods to L.Map. + */ L.Map.include({ openPopup: function (popup) { @@ -3555,7 +3725,8 @@ L.Map.include({ /* - * L.LayerGroup is a class to combine several layers so you can manipulate the group (e.g. add/remove it) as one layer. + * L.LayerGroup is a class to combine several layers into one so that + * you can manipulate the group (e.g. add/remove it) as one layer. */ L.LayerGroup = L.Class.extend({ @@ -3638,6 +3809,10 @@ L.LayerGroup = L.Class.extend({ method.call(context, this._layers[i]); } } + }, + + setZIndex: function (zIndex) { + return this.invoke('setZIndex', zIndex); } }); @@ -3647,7 +3822,8 @@ L.layerGroup = function (layers) { /* - * L.FeatureGroup extends L.LayerGroup by introducing mouse events and bindPopup method shared between a group of layers. + * L.FeatureGroup extends L.LayerGroup by introducing mouse events and additional methods + * shared between a group of interactive layers (like vectors or markers). */ L.FeatureGroup = L.LayerGroup.extend({ @@ -3667,7 +3843,7 @@ L.FeatureGroup = L.LayerGroup.extend({ L.LayerGroup.prototype.addLayer.call(this, layer); if (this._popupContent && layer.bindPopup) { - layer.bindPopup(this._popupContent); + layer.bindPopup(this._popupContent, this._popupOptions); } return this.fire('layeradd', {layer: layer}); @@ -3686,9 +3862,10 @@ L.FeatureGroup = L.LayerGroup.extend({ return this.fire('layerremove', {layer: layer}); }, - bindPopup: function (content) { + bindPopup: function (content, options) { this._popupContent = content; - return this.invoke('bindPopup', content); + this._popupOptions = options; + return this.invoke('bindPopup', content, options); }, setStyle: function (style) { @@ -3775,6 +3952,8 @@ L.Path = L.Class.extend({ this._map._pathRoot.appendChild(this._container); } + this.fire('add'); + map.on({ 'viewreset': this.projectLatlngs, 'moveend': this._updatePath @@ -3789,6 +3968,8 @@ L.Path = L.Class.extend({ onRemove: function (map) { map._pathRoot.removeChild(this._container); + // Need to fire remove event before we set _map to null as the event hooks might need the object + this.fire('remove'); this._map = null; if (L.Browser.vml) { @@ -3797,8 +3978,6 @@ L.Path = L.Class.extend({ this._fill = null; } - this.fire('remove'); - map.off({ 'viewreset': this.projectLatlngs, 'moveend': this._updatePath @@ -3841,6 +4020,10 @@ L.Map.include({ }); +/* + * Extends L.Path with SVG-specific rendering code. + */ + L.Path.SVG_NS = 'http://www.w3.org/2000/svg'; L.Browser.svg = !!(document.createElementNS && document.createElementNS(L.Path.SVG_NS, 'svg').createSVGRect); @@ -4002,13 +4185,12 @@ L.Map.include({ } }, - _animatePathZoom: function (opt) { - var scale = this.getZoomScale(opt.zoom), - offset = this._getCenterOffset(opt.center), - translate = offset.multiplyBy(-scale)._add(this._pathViewport.min); + _animatePathZoom: function (e) { + var scale = this.getZoomScale(e.zoom), + offset = this._getCenterOffset(e.center)._multiplyBy(-scale)._add(this._pathViewport.min); this._pathRoot.style[L.DomUtil.TRANSFORM] = - L.DomUtil.getTranslateString(translate) + ' scale(' + scale + ') '; + L.DomUtil.getTranslateString(offset) + ' scale(' + scale + ') '; this._pathZooming = true; }, @@ -4054,7 +4236,7 @@ L.Map.include({ /* - * Popup extension to L.Path (polylines, polygons, circles), adding bindPopup method. + * Popup extension to L.Path (polylines, polygons, circles), adding popup-related methods. */ L.Path.include({ @@ -4082,7 +4264,7 @@ L.Path.include({ if (this._popup) { this._popup = null; this - .off('click', this.openPopup) + .off('click', this._openPopup) .off('remove', this.closePopup); this._popupHandlersAdded = false; @@ -4193,11 +4375,15 @@ L.Path = L.Browser.svg || !L.Browser.vml ? L.Path : L.Path.extend({ stroke.weight = options.weight + 'px'; stroke.color = options.color; stroke.opacity = options.opacity; + if (options.dashArray) { - stroke.dashStyle = options.dashArray.replace(/ *, */g, ' '); + stroke.dashStyle = options.dashArray instanceof Array ? + options.dashArray.join(' ') : + options.dashArray.replace(/ *, */g, ' '); } else { stroke.dashStyle = ''; } + } else if (stroke) { container.removeChild(stroke); this._stroke = null; @@ -4210,6 +4396,7 @@ L.Path = L.Browser.svg || !L.Browser.vml ? L.Path : L.Path.extend({ } fill.color = options.fillColor || options.color; fill.opacity = options.fillOpacity; + } else if (fill) { container.removeChild(fill); this._fill = null; @@ -4277,6 +4464,10 @@ L.Path = (L.Path.SVG && !window.L_PREFER_CANVAS) || !L.Browser.canvas ? L.Path : .off('viewreset', this.projectLatlngs, this) .off('moveend', this._updatePath, this); + if (this.options.clickable) { + this._map.off('click', this._onClick, this); + } + this._requestUpdate(); this._map = null; @@ -4344,16 +4535,12 @@ L.Path = (L.Path.SVG && !window.L_PREFER_CANVAS) || !L.Browser.canvas ? L.Path : this._updateStyle(); if (options.fill) { - if (options.fillOpacity < 1) { - ctx.globalAlpha = options.fillOpacity; - } + ctx.globalAlpha = options.fillOpacity; ctx.fill(); } if (options.stroke) { - if (options.opacity < 1) { - ctx.globalAlpha = options.opacity; - } + ctx.globalAlpha = options.opacity; ctx.stroke(); } @@ -4372,7 +4559,12 @@ L.Path = (L.Path.SVG && !window.L_PREFER_CANVAS) || !L.Browser.canvas ? L.Path : _onClick: function (e) { if (this._containsPoint(e.layerPoint)) { - this.fire('click', e); + this.fire('click', { + latlng: e.latlng, + layerPoint: e.layerPoint, + containerPoint: e.containerPoint, + originalEvent: e + }); } } }); @@ -4426,6 +4618,8 @@ L.Map.include((L.Path.SVG && !window.L_PREFER_CANVAS) || !L.Browser.canvas ? {} * and polylines (clipping, simplification, distances, etc.) */ +/*jshint bitwise:false */ // allow bitwise oprations for this file + L.LineUtil = { // Simplify polyline with vertex reduction and Douglas-Peucker simplification. @@ -4517,16 +4711,11 @@ L.LineUtil = { return reducedPoints; }, - /*jshint bitwise:false */ // temporarily allow bitwise oprations - // Cohen-Sutherland line clipping algorithm. // Used to avoid rendering parts of a polyline that are not currently visible. clipSegment: function (a, b, bounds, useLastCode) { - var min = bounds.min, - max = bounds.max, - - codeA = useLastCode ? this._lastCode : this._getBitCode(a, bounds), + var codeA = useLastCode ? this._lastCode : this._getBitCode(a, bounds), codeB = this._getBitCode(b, bounds), codeOut, p, newCode; @@ -4592,8 +4781,6 @@ L.LineUtil = { return code; }, - /*jshint bitwise:true */ - // square distance (to avoid unnecessary Math.sqrt calls) _sqDist: function (p1, p2) { var dx = p2.x - p1.x, @@ -4630,20 +4817,15 @@ L.LineUtil = { }; +/* + * L.Polygon is used to display polylines on a map. + */ + L.Polyline = L.Path.extend({ initialize: function (latlngs, options) { L.Path.prototype.initialize.call(this, options); this._latlngs = this._convertLatLngs(latlngs); - - // TODO refactor: move to Polyline.Edit.js - if (L.Handler.PolyEdit) { - this.editing = new L.Handler.PolyEdit(this); - - if (this.options.editable) { - this.editing.enable(); - } - } }, options: { @@ -4682,7 +4864,7 @@ L.Polyline = L.Path.extend({ return this.redraw(); }, - spliceLatLngs: function (index, howMany) { + spliceLatLngs: function () { // (Number index, Number howMany) var removed = [].splice.apply(this._latlngs, arguments); this._convertLatLngs(this._latlngs); this.redraw(); @@ -4722,27 +4904,10 @@ L.Polyline = L.Path.extend({ return bounds; }, - // TODO refactor: move to Polyline.Edit.js - onAdd: function (map) { - L.Path.prototype.onAdd.call(this, map); - - if (this.editing && this.editing.enabled()) { - this.editing.addHooks(); - } - }, - - onRemove: function (map) { - if (this.editing && this.editing.enabled()) { - this.editing.removeHooks(); - } - - L.Path.prototype.onRemove.call(this, map); - }, - _convertLatLngs: function (latlngs) { var i, len; for (i = 0, len = latlngs.length; i < len; i++) { - if (latlngs[i] instanceof Array && typeof latlngs[i][0] !== 'number') { + if (L.Util.isArray(latlngs[i]) && typeof latlngs[i][0] !== 'number') { return; } latlngs[i] = L.latLng(latlngs[i]); @@ -4838,9 +5003,7 @@ L.PolyUtil = {}; * Used to avoid rendering parts of a polygon that are not currently visible. */ L.PolyUtil.clipPolygon = function (points, bounds) { - var min = bounds.min, - max = bounds.max, - clippedPoints, + var clippedPoints, edges = [1, 4, 2, 8], i, j, k, a, b, @@ -4883,8 +5046,6 @@ L.PolyUtil.clipPolygon = function (points, bounds) { return points; }; -/*jshint bitwise:true */ - /* * L.Polygon is used to display polygons on a map. @@ -4898,7 +5059,7 @@ L.Polygon = L.Polyline.extend({ initialize: function (latlngs, options) { L.Polyline.prototype.initialize.call(this, latlngs, options); - if (latlngs && (latlngs[0] instanceof Array) && (typeof latlngs[0][0] !== 'number')) { + if (latlngs && L.Util.isArray(latlngs[0]) && (typeof latlngs[0][0] !== 'number')) { this._latlngs = this._convertLatLngs(latlngs[0]); this._holes = latlngs.slice(1); } @@ -4913,7 +5074,7 @@ L.Polygon = L.Polyline.extend({ if (!this._holes) { return; } - var i, j, len, len2, hole; + var i, j, len, len2; for (i = 0, len = this._holes.length; i < len; i++) { this._holePoints[i] = []; @@ -5003,7 +5164,7 @@ L.polygon = function (latlngs, options) { /* - * L.Rectangle extends Polygon and creates a rectangle when passed a LatLngBounds + * L.Rectangle extends Polygon and creates a rectangle when passed a LatLngBounds object. */ L.Rectangle = L.Polygon.extend({ @@ -5021,8 +5182,7 @@ L.Rectangle = L.Polygon.extend({ latLngBounds.getSouthWest(), latLngBounds.getNorthWest(), latLngBounds.getNorthEast(), - latLngBounds.getSouthEast(), - latLngBounds.getSouthWest() + latLngBounds.getSouthEast() ]; } }); @@ -5060,7 +5220,7 @@ L.Circle = L.Path.extend({ projectLatlngs: function () { var lngRadius = this._getLngRadius(), - latlng2 = new L.LatLng(this._latlng.lat, this._latlng.lng - lngRadius, true), + latlng2 = new L.LatLng(this._latlng.lat, this._latlng.lng - lngRadius), point2 = this._map.latLngToLayerPoint(latlng2); this._point = this._map.latLngToLayerPoint(this._latlng); @@ -5150,6 +5310,11 @@ L.CircleMarker = L.Circle.extend({ projectLatlngs: function () { this._point = this._map.latLngToLayerPoint(this._latlng); }, + + _updateStyle : function () { + L.Circle.prototype._updateStyle.call(this); + this.setRadius(this.options.radius); + }, setRadius: function (radius) { this._radius = radius; @@ -5162,6 +5327,9 @@ L.circleMarker = function (latlng, options) { }; +/* + * Extends L.Polyline to be able to manually detect clicks on Canvas-rendered polylines. + */ L.Polyline.include(!L.Path.CANVAS ? {} : { _containsPoint: function (p, closed) { @@ -5191,6 +5359,9 @@ L.Polyline.include(!L.Path.CANVAS ? {} : { }); +/* + * Extends L.Polygon to be able to manually detect clicks on Canvas-rendered polygons. + */ L.Polygon.include(!L.Path.CANVAS ? {} : { _containsPoint: function (p) { @@ -5228,7 +5399,7 @@ L.Polygon.include(!L.Path.CANVAS ? {} : { /* - * Circle canvas specific drawing parts. + * Extends L.Circle with Canvas-specific code. */ L.Circle.include(!L.Path.CANVAS ? {} : { @@ -5247,7 +5418,12 @@ L.Circle.include(!L.Path.CANVAS ? {} : { }); +/* + * L.GeoJSON turns any GeoJSON data into a Leaflet layer. + */ + L.GeoJSON = L.FeatureGroup.extend({ + initialize: function (geojson, options) { L.setOptions(this, options); @@ -5259,12 +5435,15 @@ L.GeoJSON = L.FeatureGroup.extend({ }, addData: function (geojson) { - var features = geojson instanceof Array ? geojson : geojson.features, + var features = L.Util.isArray(geojson) ? geojson : geojson.features, i, len; if (features) { for (i = 0, len = features.length; i < len; i++) { - this.addData(features[i]); + // Only add this if geometry or geometries are set and not null + if (features[i].geometries || features[i].geometry) { + this.addData(features[i]); + } } return this; } @@ -5276,6 +5455,7 @@ L.GeoJSON = L.FeatureGroup.extend({ var layer = L.GeoJSON.geometryToLayer(geojson, options.pointToLayer); layer.feature = geojson; + layer.defaultOptions = layer.options; this.resetStyle(layer); if (options.onEachFeature) { @@ -5288,6 +5468,9 @@ L.GeoJSON = L.FeatureGroup.extend({ resetStyle: function (layer) { var style = this.options.style; if (style) { + // reset any custom styles + L.Util.extend(layer.options, layer.defaultOptions); + this._setLayerStyle(layer, style); } }, @@ -5340,13 +5523,17 @@ L.extend(L.GeoJSON, { latlngs = this.coordsToLatLngs(coords, 1); return new L.MultiPolyline(latlngs); - case "MultiPolygon": + case 'MultiPolygon': latlngs = this.coordsToLatLngs(coords, 2); return new L.MultiPolygon(latlngs); - case "GeometryCollection": + case 'GeometryCollection': for (i = 0, len = geometry.geometries.length; i < len; i++) { - layer = this.geometryToLayer(geometry.geometries[i], pointToLayer); + layer = this.geometryToLayer({ + geometry: geometry.geometries[i], + type: 'Feature', + properties: geojson.properties + }, pointToLayer); layers.push(layer); } return new L.FeatureGroup(layers); @@ -5360,7 +5547,7 @@ L.extend(L.GeoJSON, { var lat = parseFloat(coords[reverse ? 0 : 1]), lng = parseFloat(coords[reverse ? 1 : 0]); - return new L.LatLng(lat, lng, true); + return new L.LatLng(lat, lng); }, coordsToLatLngs: function (coords, levelsDeep, reverse) { // (Array, Number, Boolean) -> Array @@ -5405,10 +5592,12 @@ L.DomEvent = { if (L.Browser.msTouch && type.indexOf('touch') === 0) { return this.addMsTouchListener(obj, type, handler, id); - } else if (L.Browser.touch && (type === 'dblclick') && this.addDoubleTapListener) { - return this.addDoubleTapListener(obj, handler, id); + } + if (L.Browser.touch && (type === 'dblclick') && this.addDoubleTapListener) { + this.addDoubleTapListener(obj, handler, id); + } - } else if ('addEventListener' in obj) { + if ('addEventListener' in obj) { if (type === 'mousewheel') { obj.addEventListener('DOMMouseScroll', handler, false); @@ -5486,8 +5675,11 @@ L.DomEvent = { var stop = L.DomEvent.stopPropagation; + for (var i = L.Draggable.START.length - 1; i >= 0; i--) { + L.DomEvent.addListener(el, L.Draggable.START[i], stop); + } + return L.DomEvent - .addListener(el, L.Draggable.START, stop) .addListener(el, 'click', stop) .addListener(el, 'dblclick', stop); }, @@ -5547,9 +5739,8 @@ L.DomEvent = { return (related !== el); }, - /*jshint noarg:false */ _getEvent: function () { // evil magic for IE - + /*jshint noarg:false */ var e = window.event; if (!e) { var caller = arguments.callee.caller; @@ -5563,7 +5754,6 @@ L.DomEvent = { } return e; } - /*jshint noarg:false */ }; L.DomEvent.on = L.DomEvent.addListener; @@ -5578,9 +5768,17 @@ L.Draggable = L.Class.extend({ includes: L.Mixin.Events, statics: { - START: L.Browser.touch ? 'touchstart' : 'mousedown', - END: L.Browser.touch ? 'touchend' : 'mouseup', - MOVE: L.Browser.touch ? 'touchmove' : 'mousemove', + START: L.Browser.touch ? ['touchstart', 'mousedown'] : ['mousedown'], + END: { + mousedown: 'mouseup', + touchstart: 'touchend', + MSPointerDown: 'touchend' + }, + MOVE: { + mousedown: 'mousemove', + touchstart: 'touchmove', + MSPointerDown: 'touchmove' + }, TAP_TOLERANCE: 15 }, @@ -5593,14 +5791,18 @@ L.Draggable = L.Class.extend({ enable: function () { if (this._enabled) { return; } - L.DomEvent.on(this._dragStartTarget, L.Draggable.START, this._onDown, this); + for (var i = L.Draggable.START.length - 1; i >= 0; i--) { + L.DomEvent.on(this._dragStartTarget, L.Draggable.START[i], this._onDown, this); + } this._enabled = true; }, disable: function () { if (!this._enabled) { return; } - L.DomEvent.off(this._dragStartTarget, L.Draggable.START, this._onDown); + for (var i = L.Draggable.START.length - 1; i >= 0; i--) { + L.DomEvent.off(this._dragStartTarget, L.Draggable.START[i], this._onDown, this); + } this._enabled = false; this._moved = false; }, @@ -5648,8 +5850,8 @@ L.Draggable = L.Class.extend({ }, this), 1000); } - L.DomEvent.on(document, L.Draggable.MOVE, this._onMove, this); - L.DomEvent.on(document, L.Draggable.END, this._onUp, this); + L.DomEvent.on(document, L.Draggable.MOVE[e.type], this._onMove, this); + L.DomEvent.on(document, L.Draggable.END[e.type], this._onUp, this); }, _onMove: function (e) { @@ -5710,8 +5912,12 @@ L.Draggable = L.Class.extend({ this._restoreCursor(); } - L.DomEvent.off(document, L.Draggable.MOVE, this._onMove); - L.DomEvent.off(document, L.Draggable.END, this._onUp); + for (var i in L.Draggable.MOVE) { + if (L.Draggable.MOVE.hasOwnProperty(i)) { + L.DomEvent.off(document, L.Draggable.MOVE[i], this._onMove); + L.DomEvent.off(document, L.Draggable.END[i], this._onUp); + } + } if (this._moved) { // ensure drag is not fired after dragend @@ -5750,8 +5956,9 @@ L.Draggable = L.Class.extend({ /* - * L.Handler classes are used internally to inject interaction features to classes like Map and Marker. - */ + L.Handler is a base class for handler classes that are used internally to inject + interaction features like dragging to classes like Map and Marker. +*/ L.Handler = L.Class.extend({ initialize: function (map) { @@ -5779,7 +5986,7 @@ L.Handler = L.Class.extend({ /* - * L.Handler.MapDrag is used internally by L.Map to make the map draggable. + * L.Handler.MapDrag is used to make the map draggable (with panning inertia), enabled by default. */ L.Map.mergeOptions({ @@ -5794,7 +6001,7 @@ L.Map.mergeOptions({ longPress: true, // TODO refactor, move to CRS - worldCopyJump: true + worldCopyJump: false }); L.Map.Drag = L.Handler.extend({ @@ -5863,6 +6070,7 @@ L.Map.Drag = L.Handler.extend({ }, _onViewReset: function () { + // TODO fix hardcoded Earth values var pxCenter = this._map.getSize()._divideBy(2), pxWorldCenter = this._map.latLngToLayerPoint(new L.LatLng(0, 0)); @@ -5872,8 +6080,7 @@ L.Map.Drag = L.Handler.extend({ _onPreDrag: function () { // TODO refactor to be able to adjust map pane position after zoom - var map = this._map, - worldWidth = this._worldWidth, + var worldWidth = this._worldWidth, halfWidth = Math.round(worldWidth / 2), dx = this._initialWorldOffset, x = this._draggable._newPos.x, @@ -5889,9 +6096,7 @@ L.Map.Drag = L.Handler.extend({ options = map.options, delay = +new Date() - this._lastTime, - noInertia = !options.inertia || - delay > options.inertiaThreshold || - !this._positions[0]; + noInertia = !options.inertia || delay > options.inertiaThreshold || !this._positions[0]; if (noInertia) { map.fire('moveend'); @@ -5900,18 +6105,19 @@ L.Map.Drag = L.Handler.extend({ var direction = this._lastPos.subtract(this._positions[0]), duration = (this._lastTime + delay - this._times[0]) / 1000, + ease = options.easeLinearity, - speedVector = direction.multiplyBy(options.easeLinearity / duration), + speedVector = direction.multiplyBy(ease / duration), speed = speedVector.distanceTo(new L.Point(0, 0)), limitedSpeed = Math.min(options.inertiaMaxSpeed, speed), limitedSpeedVector = speedVector.multiplyBy(limitedSpeed / speed), - decelerationDuration = limitedSpeed / (options.inertiaDeceleration * options.easeLinearity), + decelerationDuration = limitedSpeed / (options.inertiaDeceleration * ease), offset = limitedSpeedVector.multiplyBy(-decelerationDuration / 2).round(); L.Util.requestAnimFrame(function () { - map.panBy(offset, decelerationDuration, options.easeLinearity); + map.panBy(offset, decelerationDuration, ease); }); } @@ -5932,7 +6138,7 @@ L.Map.addInitHook('addHandler', 'dragging', L.Map.Drag); /* - * L.Handler.DoubleClickZoom is used internally by L.Map to add double-click zooming. + * L.Handler.DoubleClickZoom is used to handle double-click zoom on the map, enabled by default. */ L.Map.mergeOptions({ @@ -5955,12 +6161,13 @@ L.Map.DoubleClickZoom = L.Handler.extend({ L.Map.addInitHook('addHandler', 'doubleClickZoom', L.Map.DoubleClickZoom); + /* * L.Handler.ScrollWheelZoom is used by L.Map to enable mouse scroll wheel zoom on the map. */ L.Map.mergeOptions({ - scrollWheelZoom: !L.Browser.touch || L.Browser.msTouch + scrollWheelZoom: true }); L.Map.ScrollWheelZoom = L.Handler.extend({ @@ -5994,9 +6201,10 @@ L.Map.ScrollWheelZoom = L.Handler.extend({ _performZoom: function () { var map = this._map, - delta = Math.round(this._delta), + delta = this._delta, zoom = map.getZoom(); + delta = delta > 0 ? Math.ceil(delta) : Math.round(delta); delta = Math.max(Math.min(delta, 4), -4); delta = map._limitZoom(zoom + delta) - zoom; @@ -6026,6 +6234,10 @@ L.Map.ScrollWheelZoom = L.Handler.extend({ L.Map.addInitHook('addHandler', 'scrollWheelZoom', L.Map.ScrollWheelZoom); +/* + * Extends the event handling code with double tap support for mobile browsers. + */ + L.extend(L.DomEvent, { _touchstart: L.Browser.msTouch ? 'MSPointerDown' : 'touchstart', @@ -6061,7 +6273,9 @@ L.extend(L.DomEvent, { doubleTap = (delta > 0 && delta <= delay); last = now; } + function onTouchEnd(e) { + /*jshint forin:false */ if (L.Browser.msTouch) { var idx = trackedTouches.indexOf(e.pointerId); if (idx === -1) { @@ -6075,14 +6289,13 @@ L.extend(L.DomEvent, { //Work around .type being readonly with MSPointer* events var newTouch = { }, prop; + for (var i in touch) { - if (true) { //Make JSHint happy, we want to copy all properties - prop = touch[i]; - if (typeof prop === 'function') { - newTouch[i] = prop.bind(touch); - } else { - newTouch[i] = prop; - } + prop = touch[i]; + if (typeof prop === 'function') { + newTouch[i] = prop.bind(touch); + } else { + newTouch[i] = prop; } } touch = newTouch; @@ -6119,6 +6332,10 @@ L.extend(L.DomEvent, { }); +/* + * Extends L.DomEvent to provide touch support for Internet Explorer and Windows-based devices. + */ + L.extend(L.DomEvent, { _msTouches: [], @@ -6357,7 +6574,7 @@ L.Map.TouchZoom = L.Handler.extend({ L.DomUtil.getScaleString(this._scale, this._startCenter); }, - _onTouchEnd: function (e) { + _onTouchEnd: function () { if (!this._moved || !this._zooming) { return; } var map = this._map; @@ -6397,7 +6614,8 @@ L.Map.addInitHook('addHandler', 'touchZoom', L.Map.TouchZoom); /* - * L.Handler.ShiftDragZoom is used internally by L.Map to add shift-drag zoom (zoom to a selected bounding box). + * L.Handler.ShiftDragZoom is used to add shift-drag zoom interaction to the map + * (zoom to a selected bounding box), enabled by default. */ L.Map.mergeOptions({ @@ -6469,9 +6687,11 @@ L.Map.BoxZoom = L.Handler.extend({ .off(document, 'mouseup', this._onMouseUp); var map = this._map, - layerPoint = map.mouseEventToLayerPoint(e), + layerPoint = map.mouseEventToLayerPoint(e); - bounds = new L.LatLngBounds( + if (this._startLayerPoint.equals(layerPoint)) { return; } + + var bounds = new L.LatLngBounds( map.layerPointToLatLng(this._startLayerPoint), map.layerPointToLatLng(layerPoint)); @@ -6486,6 +6706,10 @@ L.Map.BoxZoom = L.Handler.extend({ L.Map.addInitHook('addHandler', 'boxZoom', L.Map.BoxZoom); +/* + * L.Map.Keyboard is handling keyboard interaction with the map, enabled by default. + */ + L.Map.mergeOptions({ keyboard: true, keyboardPanOffset: 80, @@ -6494,7 +6718,6 @@ L.Map.mergeOptions({ L.Map.Keyboard = L.Handler.extend({ - // list of e.keyCode values for particular actions keyCodes: { left: [37], right: [39], @@ -6520,9 +6743,9 @@ L.Map.Keyboard = L.Handler.extend({ } L.DomEvent - .addListener(container, 'focus', this._onFocus, this) - .addListener(container, 'blur', this._onBlur, this) - .addListener(container, 'mousedown', this._onMouseDown, this); + .on(container, 'focus', this._onFocus, this) + .on(container, 'blur', this._onBlur, this) + .on(container, 'mousedown', this._onMouseDown, this); this._map .on('focus', this._addHooks, this) @@ -6535,9 +6758,9 @@ L.Map.Keyboard = L.Handler.extend({ var container = this._map._container; L.DomEvent - .removeListener(container, 'focus', this._onFocus, this) - .removeListener(container, 'blur', this._onBlur, this) - .removeListener(container, 'mousedown', this._onMouseDown, this); + .off(container, 'focus', this._onFocus, this) + .off(container, 'blur', this._onBlur, this) + .off(container, 'mousedown', this._onMouseDown, this); this._map .off('focus', this._addHooks, this) @@ -6593,21 +6816,26 @@ L.Map.Keyboard = L.Handler.extend({ }, _addHooks: function () { - L.DomEvent.addListener(document, 'keydown', this._onKeyDown, this); + L.DomEvent.on(document, 'keydown', this._onKeyDown, this); }, _removeHooks: function () { - L.DomEvent.removeListener(document, 'keydown', this._onKeyDown, this); + L.DomEvent.off(document, 'keydown', this._onKeyDown, this); }, _onKeyDown: function (e) { - var key = e.keyCode; + var key = e.keyCode, + map = this._map; if (this._panKeys.hasOwnProperty(key)) { - this._map.panBy(this._panKeys[key]); + map.panBy(this._panKeys[key]); + + if (map.options.maxBounds) { + map.panInsideBounds(map.options.maxBounds); + } } else if (this._zoomKeys.hasOwnProperty(key)) { - this._map.setZoom(this._map.getZoom() + this._zoomKeys[key]); + map.setZoom(map.getZoom() + this._zoomKeys[key]); } else { return; @@ -6648,14 +6876,14 @@ L.Handler.MarkerDrag = L.Handler.extend({ return this._draggable && this._draggable._moved; }, - _onDragStart: function (e) { + _onDragStart: function () { this._marker .closePopup() .fire('movestart') .fire('dragstart'); }, - _onDrag: function (e) { + _onDrag: function () { var marker = this._marker, shadow = marker._shadow, iconPos = L.DomUtil.getPosition(marker._icon), @@ -6681,6 +6909,10 @@ L.Handler.MarkerDrag = L.Handler.extend({ }); +/* + * L.Handler.PolyEdit is an editing handler for polylines and polygons. + */ + L.Handler.PolyEdit = L.Handler.extend({ options: { icon: new L.DivIcon({ @@ -6904,7 +7136,34 @@ L.Handler.PolyEdit = L.Handler.extend({ } }); +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(); + } + }); +}); + + +/* + * L.Control is a base class for implementing map controls. Handles positioning. + * All other controls extend from this class. + */ L.Control = L.Class.extend({ options: { @@ -6973,6 +7232,10 @@ L.control = function (options) { }; +/* + * Adds control-related methods to L.Map. + */ + L.Map.include({ addControl: function (control) { control.addTo(this); @@ -7004,23 +7267,44 @@ L.Map.include({ }); +/* + * L.Control.Zoom is used for the default zoom buttons on the map. + */ + L.Control.Zoom = L.Control.extend({ options: { position: 'topleft' }, onAdd: function (map) { - var className = 'leaflet-control-zoom', - container = L.DomUtil.create('div', className); + var zoomName = 'leaflet-control-zoom', + barName = 'leaflet-bar', + partName = barName + '-part', + container = L.DomUtil.create('div', zoomName + ' ' + barName); this._map = map; - this._createButton('+', 'Zoom in', className + '-in', container, this._zoomIn, this); - this._createButton('-', 'Zoom out', className + '-out', container, this._zoomOut, this); + this._zoomInButton = this._createButton('+', 'Zoom in', + zoomName + '-in ' + + partName + ' ' + + partName + '-top', + container, this._zoomIn, this); + + this._zoomOutButton = this._createButton('-', 'Zoom out', + zoomName + '-out ' + + partName + ' ' + + partName + '-bottom', + container, this._zoomOut, this); + + map.on('zoomend', this._updateDisabled, this); return container; }, + onRemove: function (map) { + map.off('zoomend', this._updateDisabled, this); + }, + _zoomIn: function (e) { this._map.zoomIn(e.shiftKey ? 3 : 1); }, @@ -7035,14 +7319,31 @@ L.Control.Zoom = L.Control.extend({ link.href = '#'; link.title = title; + var stop = L.DomEvent.stopPropagation; + L.DomEvent - .on(link, 'click', L.DomEvent.stopPropagation) - .on(link, 'mousedown', L.DomEvent.stopPropagation) - .on(link, 'dblclick', L.DomEvent.stopPropagation) + .on(link, 'click', stop) + .on(link, 'mousedown', stop) + .on(link, 'dblclick', stop) .on(link, 'click', L.DomEvent.preventDefault) .on(link, 'click', fn, context); return link; + }, + + _updateDisabled: function () { + var map = this._map, + className = 'leaflet-control-zoom-disabled'; + + L.DomUtil.removeClass(this._zoomInButton, className); + L.DomUtil.removeClass(this._zoomOutButton, className); + + if (map._zoom === map.getMinZoom()) { + L.DomUtil.addClass(this._zoomOutButton, className); + } + if (map._zoom === map.getMaxZoom()) { + L.DomUtil.addClass(this._zoomInButton, className); + } } }); @@ -7063,6 +7364,10 @@ L.control.zoom = function (options) { +/* + * L.Control.Attribution is used for displaying attribution on the map (added by default). + */ + L.Control.Attribution = L.Control.extend({ options: { position: 'bottomright', @@ -7174,6 +7479,10 @@ L.control.attribution = function (options) { }; +/* + * L.Control.Scale is used for displaying metric/imperial scale on the map. + */ + L.Control.Scale = L.Control.extend({ options: { position: 'bottomleft', @@ -7284,6 +7593,10 @@ L.control.scale = function (options) { }; +/* + * L.Control.Layers is a control to allow users to switch between different layers on the map. + */ + L.Control.Layers = L.Control.extend({ options: { collapsed: true, @@ -7296,6 +7609,7 @@ L.Control.Layers = L.Control.extend({ this._layers = {}; this._lastZIndex = 0; + this._handlingClick = false; for (var i in baseLayers) { if (baseLayers.hasOwnProperty(i)) { @@ -7314,9 +7628,19 @@ L.Control.Layers = L.Control.extend({ this._initLayout(); this._update(); + map + .on('layeradd', this._onLayerChange, this) + .on('layerremove', this._onLayerChange, this); + return this._container; }, + onRemove: function (map) { + map + .off('layeradd', this._onLayerChange) + .off('layerremove', this._onLayerChange); + }, + addBaseLayer: function (layer, name) { this._addLayer(layer, name); this._update(); @@ -7342,6 +7666,7 @@ L.Control.Layers = L.Control.extend({ if (!L.Browser.touch) { L.DomEvent.disableClickPropagation(container); + L.DomEvent.on(container, 'mousewheel', L.DomEvent.stopPropagation); } else { L.DomEvent.on(container, 'click', L.DomEvent.stopPropagation); } @@ -7418,6 +7743,14 @@ L.Control.Layers = L.Control.extend({ this._separator.style.display = (overlaysPresent && baseLayersPresent ? '' : 'none'); }, + _onLayerChange: function (e) { + var id = L.stamp(e.layer); + + if (this._layers[id] && !this._handlingClick) { + this._update(); + } + }, + // IE7 bugs out if you create a radio dynamically, so you have to do it this hacky way (see http://bit.ly/PqYLBe) _createRadioElement: function (name, checked) { @@ -7459,6 +7792,8 @@ L.Control.Layers = L.Control.extend({ var container = obj.overlay ? this._overlaysList : this._baseLayersList; container.appendChild(label); + + return label; }, _onInputClick: function () { @@ -7467,6 +7802,8 @@ L.Control.Layers = L.Control.extend({ inputsLen = inputs.length, baseLayer; + this._handlingClick = true; + for (i = 0; i < inputsLen; i++) { input = inputs[i]; obj = this._layers[input.layerId]; @@ -7482,8 +7819,11 @@ L.Control.Layers = L.Control.extend({ } if (baseLayer) { + this._map.setZoom(this._map.getZoom()); this._map.fire('baselayerchange', {layer: baseLayer}); } + + this._handlingClick = false; }, _expand: function () { @@ -7577,6 +7917,9 @@ L.PosAnimation = L.Class.extend({ }); +/* + * Extends L.Map to handle panning animations. + */ L.Map.include({ @@ -7628,7 +7971,7 @@ L.Map.include({ L.DomUtil.addClass(this._mapPane, 'leaflet-pan-anim'); - var newPos = L.DomUtil.getPosition(this._mapPane).subtract(offset); + var newPos = L.DomUtil.getPosition(this._mapPane).subtract(offset)._round(); this._panAnim.run(this._mapPane, newPos, duration || 0.25, easeLinearity); return this; @@ -7733,6 +8076,10 @@ L.PosAnimation = L.DomUtil.TRANSITION ? L.PosAnimation : L.PosAnimation.extend({ }); +/* + * Extends L.Map to handle zoom animations. + */ + L.Map.mergeOptions({ zoomAnimation: L.DomUtil.TRANSITION && !L.Browser.android23 && !L.Browser.mobileOpera }); @@ -7776,7 +8123,7 @@ L.Map.include(!L.DomUtil.TRANSITION ? {} : { return true; }, - _catchTransitionEnd: function (e) { + _catchTransitionEnd: function () { if (this._animatingZoom) { this._onZoomTransitionEnd(); } @@ -7874,11 +8221,11 @@ L.Map.include(!L.DomUtil.TRANSITION ? {} : { _onZoomTransitionEnd: function () { this._restoreTileFront(); - L.Util.falseFn(this._tileBg.offsetWidth); // force reflow - this._resetView(this._animateToCenter, this._animateToZoom, true, true); 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; @@ -7901,7 +8248,7 @@ L.Map.include(!L.DomUtil.TRANSITION ? {} : { /* - * Provides L.Map with convenient shortcuts for W3C geolocation. + * Provides L.Map with convenient shortcuts for using browser geolocation features. */ L.Map.include({ @@ -7989,6 +8336,4 @@ L.Map.include({ }); - - -}(this)); \ No newline at end of file +}(this, document)); \ No newline at end of file diff --git a/dist/leaflet.css b/dist/leaflet.css index 59c49236..ea3da390 100644 --- a/dist/leaflet.css +++ b/dist/leaflet.css @@ -15,6 +15,7 @@ .leaflet-layer { position: absolute; left: 0; + top: 0; } .leaflet-container { overflow: hidden; @@ -184,23 +185,58 @@ /* general typography */ .leaflet-container { font: 12px/1.5 "Helvetica Neue", Arial, Helvetica, sans-serif; -} + } + + +/* general toolbar styles */ + +.leaflet-bar { + box-shadow: 0 0 8px rgba(0,0,0,0.4); + border: 1px solid #888; + -webkit-border-radius: 5px; + border-radius: 5px; + } +.leaflet-bar-part { + background-color: rgba(255, 255, 255, 0.8); + border-bottom: 1px solid #aaa; + } +.leaflet-bar-part-top { + -webkit-border-radius: 4px 4px 0 0; + border-radius: 4px 4px 0 0; + } +.leaflet-bar-part-bottom { + -webkit-border-radius: 0 0 4px 4px; + border-radius: 0 0 4px 4px; + border-bottom: none; + } + +.leaflet-touch .leaflet-bar { + -webkit-border-radius: 10px; + border-radius: 10px; + } +.leaflet-touch .leaflet-bar-part { + border-bottom: 4px solid rgba(0,0,0,0.3); + } +.leaflet-touch .leaflet-bar-part-top { + -webkit-border-radius: 7px 7px 0 0; + border-radius: 7px 7px 0 0; + } +.leaflet-touch .leaflet-bar-part-bottom { + -webkit-border-radius: 0 0 7px 7px; + border-radius: 0 0 7px 7px; + border-bottom: none; + } /* zoom control */ -.leaflet-control-zoom { +.leaflet-container .leaflet-control-zoom { margin-left: 13px; margin-top: 12px; - box-shadow: 0 0 8px #666; - border: 1px solid #777; - -webkit-border-radius: 5px; - border-radius: 5px; } .leaflet-control-zoom a { - width: 23px; - height: 23px; - background-color: rgba(255, 255, 255, 0.8); + width: 22px; + height: 22px; text-align: center; text-decoration: none; color: black; @@ -216,15 +252,15 @@ color: #777; } .leaflet-control-zoom-in { - border-bottom: 1px solid #aaa; - font: bold 18px/23px Arial, Helvetica, sans-serif; - -webkit-border-radius: 5px 5px 0 0; - border-radius: 5px 5px 0 0; + font: bold 18px/24px Arial, Helvetica, sans-serif; } .leaflet-control-zoom-out { - font: bold 25px/20px Tahoma, Verdana, sans-serif; - -webkit-border-radius: 0 0 5px 5px; - border-radius: 0 0 5px 5px; + font: bold 23px/20px Tahoma, Verdana, sans-serif; + } +.leaflet-control-zoom a.leaflet-control-zoom-disabled { + cursor: default; + background-color: rgba(255, 255, 255, 0.8); + color: #bbb; } .leaflet-touch .leaflet-control-zoom a { @@ -243,7 +279,7 @@ /* layers control */ .leaflet-control-layers { - box-shadow: 0 1px 7px #999; + box-shadow: 0 1px 7px rgba(0,0,0,0.4); background: #f8f8f9; -webkit-border-radius: 8px; border-radius: 8px; @@ -311,16 +347,20 @@ border: 2px solid #777; border-top: none; color: black; - line-height: 1; + line-height: 1.1; padding: 2px 5px 1px; - font-size: 10px; + font-size: 11px; text-shadow: 1px 1px 1px #fff; background-color: rgba(255, 255, 255, 0.5); + box-shadow: 0 -1px 5px rgba(0, 0, 0, 0.2); + white-space: nowrap; + overflow: hidden; } .leaflet-control-scale-line:not(:first-child) { border-top: 2px solid #777; border-bottom: none; margin-top: -2px; + box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2); } .leaflet-control-scale-line:not(:first-child):not(:last-child) { border-bottom: 2px solid #777; @@ -333,7 +373,7 @@ } .leaflet-touch .leaflet-control-layers, .leaflet-touch .leaflet-control-zoom { - border: 3px solid #999; + border: 4px solid rgba(0,0,0,0.3); } @@ -393,6 +433,7 @@ color: #c3c3c3; text-decoration: none; font-weight: bold; + background: transparent; } .leaflet-container a.leaflet-popup-close-button:hover { color: #999; diff --git a/dist/leaflet.js b/dist/leaflet.js index 37ceddb7..9936cba5 100644 --- a/dist/leaflet.js +++ b/dist/leaflet.js @@ -1,6 +1,8 @@ /* - Copyright (c) 2010-2012, CloudMade, Vladimir Agafonkin - Leaflet is an open-source JavaScript library for mobile-friendly interactive maps. - http://leafletjs.com + Leaflet, a JavaScript library for mobile-friendly interactive maps. http://leafletjs.com + (c) 2010-2013, Vladimir Agafonkin, CloudMade */ -(function(e,t){var n,r;typeof exports!=t+""?n=exports:(r=e.L,n={},n.noConflict=function(){return e.L=r,this},e.L=n),n.version="0.5",n.Util={extend:function(e){var t=Array.prototype.slice.call(arguments,1),n,r,i,s;for(r=0,i=t.length;r2?Array.prototype.slice.call(arguments,2):null;return function(){return e.apply(t,n||arguments)}},stamp:function(){var e=0,t="_leaflet_id";return function(n){return n[t]=n[t]||++e,n[t]}}(),limitExecByInterval:function(e,t,n){var r,i;return function s(){var o=arguments;if(r){i=!0;return}r=!0,setTimeout(function(){r=!1,i&&(s.apply(n,o),i=!1)},t),e.apply(n,o)}},falseFn:function(){return!1},formatNum:function(e,t){var n=Math.pow(10,t||5);return Math.round(e*n)/n},splitWords:function(e){return e.replace(/^\s+|\s+$/g,"").split(/\s+/)},setOptions:function(e,t){return e.options=n.extend({},e.options,t),e.options},getParamString:function(e){var t=[];for(var n in e)e.hasOwnProperty(n)&&t.push(n+"="+e[n]);return"?"+t.join("&")},template:function(e,t){return e.replace(/\{ *([\w_]+) *\}/g,function(e,n){var r=t[n];if(!t.hasOwnProperty(n))throw Error("No value provided for variable "+e);return r})},emptyImageUrl:"data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs="},function(){function t(t){var n,r,i=["webkit","moz","o","ms"];for(n=0;n0},removeEventListener:function(e,t,r){var s=this[i],o,u,a,f,l;if(typeof e=="object"){for(o in e)e.hasOwnProperty(o)&&this.removeEventListener(o,e[o],t);return this}e=n.Util.splitWords(e);for(u=0,a=e.length;u=0;l--)(!t||f[l].action===t)&&(!r||f[l].context===r)&&f.splice(l,1)}return this},fireEvent:function(e,t){if(!this.hasEventListeners(e))return this;var r=n.extend({type:e,target:this},t),s=this[i][e].slice();for(var o=0,u=s.length;o1||"matchMedia"in e&&e.matchMedia("(min-resolution:144dpi)").matches,d=document.documentElement,v=r&&"transition"in d.style,m="WebKitCSSMatrix"in e&&"m11"in new e.WebKitCSSMatrix,g="MozPerspective"in d.style,y="OTransition"in d.style,b=!e.L_DISABLE_3D&&(v||m||g||y),w=!e.L_NO_TOUCH&&function(){var e="ontouchstart";if(h||e in d)return!0;var t=document.createElement("div"),n=!1;return t.setAttribute?(t.setAttribute(e,"return;"),typeof t[e]=="function"&&(n=!0),t.removeAttribute(e),t=null,n):!1}();n.Browser={ie6:i,ie7:s,webkit:u,android:f,android23:l,chrome:a,ie3d:v,webkit3d:m,gecko3d:g,opera3d:y,any3d:b,mobile:c,mobileWebkit:c&&u,mobileWebkit3d:c&&m,mobileOpera:c&&e.opera,touch:w,msTouch:h,retina:p}}(),n.Point=function(e,t,n){this.x=n?Math.round(e):e,this.y=n?Math.round(t):t},n.Point.prototype={clone:function(){return new n.Point(this.x,this.y)},add:function(e){return this.clone()._add(n.point(e))},_add:function(e){return this.x+=e.x,this.y+=e.y,this},subtract:function(e){return this.clone()._subtract(n.point(e))},_subtract:function(e){return this.x-=e.x,this.y-=e.y,this},divideBy:function(e){return this.clone()._divideBy(e)},_divideBy:function(e){return this.x/=e,this.y/=e,this},multiplyBy:function(e){return this.clone()._multiplyBy(e)},_multiplyBy:function(e){return this.x*=e,this.y*=e,this},round:function(){return this.clone()._round()},_round:function(){return this.x=Math.round(this.x),this.y=Math.round(this.y),this},floor:function(){return this.clone()._floor()},_floor:function(){return this.x=Math.floor(this.x),this.y=Math.floor(this.y),this},distanceTo:function(e){e=n.point(e);var t=e.x-this.x,r=e.y-this.y;return Math.sqrt(t*t+r*r)},toString:function(){return"Point("+n.Util.formatNum(this.x)+", "+n.Util.formatNum(this.y)+")"}},n.point=function(e,t,r){return e instanceof n.Point?e:e instanceof Array?new n.Point(e[0],e[1]):isNaN(e)?e:new n.Point(e,t,r)},n.Bounds=n.Class.extend({initialize:function(e,t){if(!e)return;var n=t?[e,t]:e;for(var r=0,i=n.length;r=this.min.x&&r.x<=this.max.x&&t.y>=this.min.y&&r.y<=this.max.y},intersects:function(e){e=n.bounds(e);var t=this.min,r=this.max,i=e.min,s=e.max,o=s.x>=t.x&&i.x<=r.x,u=s.y>=t.y&&i.y<=r.y;return o&&u},isValid:function(){return!!this.min&&!!this.max}}),n.bounds=function(e,t){return!e||e instanceof n.Bounds?e:new n.Bounds(e,t)},n.Transformation=n.Class.extend({initialize:function(e,t,n,r){this._a=e,this._b=t,this._c=n,this._d=r},transform:function(e,t){return this._transform(e.clone(),t)},_transform:function(e,t){return t=t||1,e.x=t*(this._a*e.x+this._b),e.y=t*(this._c*e.y+this._d),e},untransform:function(e,t){return t=t||1,new n.Point((e.x/t-this._b)/this._a,(e.y/t-this._d)/this._c)}}),n.DomUtil={get:function(e){return typeof e=="string"?document.getElementById(e):e},getStyle:function(e,t){var n=e.style[t];!n&&e.currentStyle&&(n=e.currentStyle[t]);if((!n||n==="auto")&&document.defaultView){var r=document.defaultView.getComputedStyle(e,null);n=r?r[t]:null}return n==="auto"?null:n},getViewportOffset:function(e){var t=0,r=0,i=e,s=document.body,o,u=n.Browser.ie7;do{t+=i.offsetTop||0,r+=i.offsetLeft||0,o=n.DomUtil.getStyle(i,"position");if(i.offsetParent===s&&o==="absolute")break;if(o==="fixed"){t+=s.scrollTop||0,r+=s.scrollLeft||0;break}i=i.offsetParent}while(i);i=e;do{if(i===s)break;t-=i.scrollTop||0,r-=i.scrollLeft||0,!n.DomUtil.documentIsLtr()&&(n.Browser.webkit||u)&&(r+=i.scrollWidth-i.clientWidth,u&&n.DomUtil.getStyle(i,"overflow-y")!=="hidden"&&n.DomUtil.getStyle(i,"overflow")!=="hidden"&&(r+=17)),i=i.parentNode}while(i);return new n.Point(r,t)},documentIsLtr:function(){return n.DomUtil._docIsLtrCached||(n.DomUtil._docIsLtrCached=!0,n.DomUtil._docIsLtr=n.DomUtil.getStyle(document.body,"direction")==="ltr"),n.DomUtil._docIsLtr},create:function(e,t,n){var r=document.createElement(e);return r.className=t,n&&n.appendChild(r),r},disableTextSelection:function(){document.selection&&document.selection.empty&&document.selection.empty(),this._onselectstart||(this._onselectstart=document.onselectstart,document.onselectstart=n.Util.falseFn)},enableTextSelection:function(){document.onselectstart===n.Util.falseFn&&(document.onselectstart=this._onselectstart,this._onselectstart=null)},hasClass:function(e,t){return e.className.length>0&&RegExp("(^|\\s)"+t+"(\\s|$)").test(e.className)},addClass:function(e,t){n.DomUtil.hasClass(e,t)||(e.className+=(e.className?" ":"")+t)},removeClass:function(e,t){function n(e,n){return n===t?"":e}e.className=e.className.replace(/(\S+)\s*/g,n).replace(/(^\s+|\s+$)/,"")},setOpacity:function(e,t){if("opacity"in e.style)e.style.opacity=t;else if("filter"in e.style){var n=!1,r="DXImageTransform.Microsoft.Alpha";try{n=e.filters.item(r)}catch(i){}t=Math.round(t*100),n?(n.Enabled=t!==100,n.Opacity=t):e.style.filter+=" progid:"+r+"(opacity="+t+")"}},testProp:function(e){var t=document.documentElement.style;for(var n=0;n=t.lat&&s.lat<=r.lat&&i.lng>=t.lng&&s.lng<=r.lng},intersects:function(e){e=n.latLngBounds(e);var t=this._southWest,r=this._northEast,i=e.getSouthWest(),s=e.getNorthEast(),o=s.lat>=t.lat&&i.lat<=r.lat,u=s.lng>=t.lng&&i.lng<=r.lng;return o&&u},toBBoxString:function(){var e=this._southWest,t=this._northEast;return[e.lng,e.lat,t.lng,t.lat].join(",")},equals:function(e){return e?(e=n.latLngBounds(e),this._southWest.equals(e.getSouthWest())&&this._northEast.equals(e.getNorthEast())):!1},isValid:function(){return!!this._southWest&&!!this._northEast}}),n.latLngBounds=function(e,t){return!e||e instanceof n.LatLngBounds?e:new n.LatLngBounds(e,t)},n.Projection={},n.Projection.SphericalMercator={MAX_LATITUDE:85.0511287798,project:function(e){var t=n.LatLng.DEG_TO_RAD,r=this.MAX_LATITUDE,i=Math.max(Math.min(r,e.lat),-r),s=e.lng*t,o=i*t;return o=Math.log(Math.tan(Math.PI/4+o/2)),new n.Point(s,o)},unproject:function(e){var t=n.LatLng.RAD_TO_DEG,r=e.x*t,i=(2*Math.atan(Math.exp(e.y))-Math.PI/2)*t;return new n.LatLng(i,r,!0)}},n.Projection.LonLat={project:function(e){return new n.Point(e.lng,e.lat)},unproject:function(e){return new n.LatLng(e.y,e.x,!0)}},n.CRS={latLngToPoint:function(e,t){var n=this.projection.project(e),r=this.scale(t);return this.transformation._transform(n,r)},pointToLatLng:function(e,t){var n=this.scale(t),r=this.transformation.untransform(e,n);return this.projection.unproject(r)},project:function(e){return this.projection.project(e)},scale:function(e){return 256*Math.pow(2,e)}},n.CRS.Simple=n.extend({},n.CRS,{projection:n.Projection.LonLat,transformation:new n.Transformation(1,0,1,0)}),n.CRS.EPSG3857=n.extend({},n.CRS,{code:"EPSG:3857",projection:n.Projection.SphericalMercator,transformation:new n.Transformation(.5/Math.PI,.5,-0.5/Math.PI,.5),project:function(e){var t=this.projection.project(e),n=6378137;return t.multiplyBy(n)}}),n.CRS.EPSG900913=n.extend({},n.CRS.EPSG3857,{code:"EPSG:900913"}),n.CRS.EPSG4326=n.extend({},n.CRS,{code:"EPSG:4326",projection:n.Projection.LonLat,transformation:new n.Transformation(1/360,.5,-1/360,.5)}),n.Map=n.Class.extend({includes:n.Mixin.Events,options:{crs:n.CRS.EPSG3857,fadeAnimation:n.DomUtil.TRANSITION&&!n.Browser.android23,trackResize:!0,markerZoomAnimation:n.DomUtil.TRANSITION&&n.Browser.any3d},initialize:function(e,r){r=n.setOptions(this,r),this._initContainer(e),this._initLayout(),this._initHooks(),this._initEvents(),r.maxBounds&&this.setMaxBounds(r.maxBounds),r.center&&r.zoom!==t&&this.setView(n.latLng(r.center),r.zoom,!0),this._initLayers(r.layers)},setView:function(e,t){return this._resetView(n.latLng(e),this._limitZoom(t)),this},setZoom:function(e){return this.setView(this.getCenter(),e)},zoomIn:function(e){return this.setZoom(this._zoom+(e||1))},zoomOut:function(e){return this.setZoom(this._zoom-(e||1))},fitBounds:function(e){var t=this.getBoundsZoom(e);return this.setView(n.latLngBounds(e).getCenter(),t)},fitWorld:function(){var e=new n.LatLng(-60,-170),t=new n.LatLng(85,179);return this.fitBounds(new n.LatLngBounds(e,t))},panTo:function(e){return this.setView(e,this._zoom)},panBy:function(e){return this.fire("movestart"),this._rawPanBy(n.point(e)),this.fire("move"),this.fire("moveend")},setMaxBounds:function(e){e=n.latLngBounds(e),this.options.maxBounds=e;if(!e)return this._boundsMinZoom=null,this;var t=this.getBoundsZoom(e,!0);return this._boundsMinZoom=t,this._loaded&&(this._zoomo.x&&(u=o.x-i.x),r.y>s.y&&(a=s.y-r.y),r.xc&&--h>0)d=u*Math.sin(f),p=Math.PI/2-2*Math.atan(a*Math.pow((1-d)/(1+d),.5*u))-f,f+=p;return new n.LatLng(f*t,s,!0)}},n.CRS.EPSG3395=n.extend({},n.CRS,{code:"EPSG:3395",projection:n.Projection.Mercator,transformation:function(){var e=n.Projection.Mercator,t=e.R_MAJOR,r=e.R_MINOR;return new n.Transformation(.5/(Math.PI*t),.5,-0.5/(Math.PI*r),.5)}()}),n.TileLayer=n.Class.extend({includes:n.Mixin.Events,options:{minZoom:0,maxZoom:18,tileSize:256,subdomains:"abc",errorTileUrl:"",attribution:"",zoomOffset:0,opacity:1,unloadInvisibleTiles:n.Browser.mobile,updateWhenIdle:n.Browser.mobile},initialize:function(e,t){t=n.setOptions(this,t),t.detectRetina&&n.Browser.retina&&t.maxZoom>0&&(t.tileSize=Math.floor(t.tileSize/2),t.zoomOffset++,t.minZoom>0&&t.minZoom--,this.options.maxZoom--),this._url=e;var r=this.options.subdomains;typeof r=="string"&&(this.options.subdomains=r.split(""))},onAdd:function(e){this._map=e,this._initContainer(),this._createTileProto(),e.on({viewreset:this._resetCallback,moveend:this._update},this),this.options.updateWhenIdle||(this._limitedUpdate=n.Util.limitExecByInterval(this._update,150,this),e.on("move",this._limitedUpdate,this)),this._reset(),this._update()},addTo:function(e){return e.addLayer(this),this},onRemove:function(e){e._panes.tilePane.removeChild(this._container),e.off({viewreset:this._resetCallback,moveend:this._update},this),this.options.updateWhenIdle||e.off("move",this._limitedUpdate,this),this._container=null,this._map=null},bringToFront:function(){var e=this._map._panes.tilePane;return this._container&&(e.appendChild(this._container),this._setAutoZIndex(e,Math.max)),this},bringToBack:function(){var e=this._map._panes.tilePane;return this._container&&(e.insertBefore(this._container,e.firstChild),this._setAutoZIndex(e,Math.min)),this},getAttribution:function(){return this.options.attribution},setOpacity:function(e){return this.options.opacity=e,this._map&&this._updateOpacity(),this},setZIndex:function(e){return this.options.zIndex=e,this._updateZIndex(),this},setUrl:function(e,t){return this._url=e,t||this.redraw(),this},redraw:function(){return this._map&&(this._map._panes.tilePane.empty=!1,this._reset(!0),this._update()),this},_updateZIndex:function(){this._container&&this.options.zIndex!==t&&(this._container.style.zIndex=this.options.zIndex)},_setAutoZIndex:function(e,t){var n=e.getElementsByClassName("leaflet-layer"),r=-t(Infinity,-Infinity),i,s,o;for(s=0,o=n.length;sthis.options.maxZoom||r=t)||e.y<0||e.y>=t)return!1}return!0},_removeOtherTiles:function(e){var t,n,r,i;for(i in this._tiles)this._tiles.hasOwnProperty(i)&&(t=i.split(":"),n=parseInt(t[0],10),r=parseInt(t[1],10),(ne.max.x||re.max.y)&&this._removeTile(i))},_removeTile:function(e){var t=this._tiles[e];this.fire("tileunload",{tile:t,url:t.src}),this.options.reuseTiles?(n.DomUtil.removeClass(t,"leaflet-tile-loaded"),this._unusedTiles.push(t)):t.parentNode===this._container&&this._container.removeChild(t),n.Browser.android||(t.src=n.Util.emptyImageUrl),delete this._tiles[e]},_addTile:function(e,t){var r=this._getTilePos(e),i=this._getTile();n.DomUtil.setPosition(i,r,n.Browser.chrome||n.Browser.android23),this._tiles[e.x+":"+e.y]=i,this._loadTile(i,e),i.parentNode!==this._container&&t.appendChild(i)},_getZoomForUrl:function(){var e=this.options,t=this._map.getZoom();return e.zoomReverse&&(t=e.maxZoom-t),t+e.zoomOffset},_getTilePos:function(e){var t=this._map.getPixelOrigin(),n=this.options.tileSize;return e.multiplyBy(n).subtract(t)},getTileUrl:function(e){return this._adjustTilePoint(e),n.Util.template(this._url,n.extend({s:this._getSubdomain(e),z:this._getZoomForUrl(),x:e.x,y:e.y},this.options))},_getWrapTileNum:function(){return Math.pow(2,this._getZoomForUrl())},_adjustTilePoint:function(e){var t=this._getWrapTileNum();!this.options.continuousWorld&&!this.options.noWrap&&(e.x=(e.x%t+t)%t),this.options.tms&&(e.y=t-e.y-1)},_getSubdomain:function(e){var t=(e.x+e.y)%this.options.subdomains.length;return this.options.subdomains[t]},_createTileProto:function(){var e=this._tileImg=n.DomUtil.create("img","leaflet-tile");e.style.width=e.style.height=this.options.tileSize+"px",e.galleryimg="no"},_getTile:function(){if(this.options.reuseTiles&&this._unusedTiles.length>0){var e=this._unusedTiles.pop();return this._resetTile(e),e}return this._createTile()},_resetTile:function(e){},_createTile:function(){var e=this._tileImg.cloneNode(!1);return e.onselectstart=e.onmousemove=n.Util.falseFn,e},_loadTile:function(e,t){e._layer=this,e.onload=this._tileOnLoad,e.onerror=this._tileOnError,e.src=this.getTileUrl(t)},_tileLoaded:function(){this._tilesToLoad--,this._tilesToLoad||this.fire("load")},_tileOnLoad:function(e){var t=this._layer;this.src!==n.Util.emptyImageUrl&&(n.DomUtil.addClass(this,"leaflet-tile-loaded"),t.fire("tileload",{tile:this,url:this.src})),t._tileLoaded()},_tileOnError:function(e){var t=this._layer;t.fire("tileerror",{tile:this,url:this.src});var n=t.options.errorTileUrl;n&&(this.src=n),t._tileLoaded()}}),n.tileLayer=function(e,t){return new n.TileLayer(e,t)},n.TileLayer.WMS=n.TileLayer.extend({defaultWmsParams:{service:"WMS",request:"GetMap",version:"1.1.1",layers:"",styles:"",format:"image/jpeg",transparent:!1},initialize:function(e,t){this._url=e;var r=n.extend({},this.defaultWmsParams);t.detectRetina&&n.Browser.retina?r.width=r.height=this.options.tileSize*2:r.width=r.height=this.options.tileSize;for(var i in t)this.options.hasOwnProperty(i)||(r[i]=t[i]);this.wmsParams=r,n.setOptions(this,t)},onAdd:function(e){var t=parseFloat(this.wmsParams.version)>=1.3?"crs":"srs";this.wmsParams[t]=e.options.crs.code,n.TileLayer.prototype.onAdd.call(this,e)},getTileUrl:function(e,t){var r=this._map,i=r.options.crs,s=this.options.tileSize,o=e.multiplyBy(s),u=o.add(new n.Point(s,s)),a=i.project(r.unproject(o,t)),f=i.project(r.unproject(u,t)),l=[a.x,f.y,f.x,a.y].join(","),c=n.Util.template(this._url,{s:this._getSubdomain(e)});return c+n.Util.getParamString(this.wmsParams)+"&bbox="+l},setParams:function(e,t){return n.extend(this.wmsParams,e),t||this.redraw(),this}}),n.tileLayer.wms=function(e,t){return new n.TileLayer.WMS(e,t)},n.TileLayer.Canvas=n.TileLayer.extend({options:{async:!1},initialize:function(e){n.setOptions(this,e)},redraw:function(){var e=this._tiles;for(var t in e)e.hasOwnProperty(t)&&this._redrawTile(e[t])},_redrawTile:function(e){this.drawTile(e,e._tilePoint,this._map._zoom)},_createTileProto:function(){var e=this._canvasProto=n.DomUtil.create("canvas","leaflet-tile");e.width=e.height=this.options.tileSize},_createTile:function(){var e=this._canvasProto.cloneNode(!1);return e.onselectstart=e.onmousemove=n.Util.falseFn,e},_loadTile:function(e,t){e._layer=this,e._tilePoint=t,this._redrawTile(e),this.options.async||this.tileDrawn(e)},drawTile:function(e,t){},tileDrawn:function(e){this._tileOnLoad.call(e)}}),n.tileLayer.canvas=function(e){return new n.TileLayer.Canvas(e)},n.ImageOverlay=n.Class.extend({includes:n.Mixin.Events,options:{opacity:1},initialize:function(e,t,r){this._url=e,this._bounds=n.latLngBounds(t),n.setOptions(this,r)},onAdd:function(e){this._map=e,this._image||this._initImage(),e._panes.overlayPane.appendChild(this._image),e.on("viewreset",this._reset,this),e.options.zoomAnimation&&n.Browser.any3d&&e.on("zoomanim",this._animateZoom,this),this._reset()},onRemove:function(e){e.getPanes().overlayPane.removeChild(this._image),e.off("viewreset",this._reset,this),e.options.zoomAnimation&&e.off("zoomanim",this._animateZoom,this)},addTo:function(e){return e.addLayer(this),this},setOpacity:function(e){return this.options.opacity=e,this._updateOpacity(),this},bringToFront:function(){return this._image&&this._map._panes.overlayPane.appendChild(this._image),this},bringToBack:function(){var e=this._map._panes.overlayPane;return this._image&&e.insertBefore(this._image,e.firstChild),this},_initImage:function(){this._image=n.DomUtil.create("img","leaflet-image-layer"),this._map.options.zoomAnimation&&n.Browser.any3d?n.DomUtil.addClass(this._image,"leaflet-zoom-animated"):n.DomUtil.addClass(this._image,"leaflet-zoom-hide"),this._updateOpacity(),n.extend(this._image,{galleryimg:"no",onselectstart:n.Util.falseFn,onmousemove:n.Util.falseFn,onload:n.bind(this._onImageLoad,this),src:this._url})},_animateZoom:function(e){var t=this._map,r=this._image,i=t.getZoomScale(e.zoom),s=this._bounds.getNorthWest(),o=this._bounds.getSouthEast(),u=t._latLngToNewLayerPoint(s,e.zoom,e.center),a=t._latLngToNewLayerPoint(o,e.zoom,e.center)._subtract(u),f=t.latLngToLayerPoint(o)._subtract(t.latLngToLayerPoint(s)),l=u._add(a._subtract(f)._divideBy(2));r.style[n.DomUtil.TRANSFORM]=n.DomUtil.getTranslateString(l)+" scale("+i+") "},_reset:function(){var e=this._image,t=this._map.latLngToLayerPoint(this._bounds.getNorthWest()),r=this._map.latLngToLayerPoint(this._bounds.getSouthEast())._subtract(t);n.DomUtil.setPosition(e,t),e.style.width=r.x+"px",e.style.height=r.y+"px"},_onImageLoad:function(){this.fire("load")},_updateOpacity:function(){n.DomUtil.setOpacity(this._image,this.options.opacity)}}),n.imageOverlay=function(e,t,r){return new n.ImageOverlay(e,t,r)},n.Icon=n.Class.extend({options:{className:""},initialize:function(e){n.setOptions(this,e)},createIcon:function(){return this._createIcon("icon")},createShadow:function(){return this._createIcon("shadow")},_createIcon:function(e){var t=this._getIconUrl(e);if(!t){if(e==="icon")throw Error("iconUrl not set in Icon options (see the docs).");return null}var n=this._createImg(t);return this._setIconStyles(n,e),n},_setIconStyles:function(e,t){var r=this.options,i=n.point(r[t+"Size"]),s;t==="shadow"?s=n.point(r.shadowAnchor||r.iconAnchor):s=n.point(r.iconAnchor),!s&&i&&(s=i.divideBy(2,!0)),e.className="leaflet-marker-"+t+" "+r.className,s&&(e.style.marginLeft=-s.x+"px",e.style.marginTop=-s.y+"px"),i&&(e.style.width=i.x+"px",e.style.height=i.y+"px")},_createImg:function(e){var t;return n.Browser.ie6?(t=document.createElement("div"),t.style.filter='progid:DXImageTransform.Microsoft.AlphaImageLoader(src="'+e+'")'):(t=document.createElement("img"),t.src=e),t},_getIconUrl:function(e){return this.options[e+"Url"]}}),n.icon=function(e){return new n.Icon(e)},n.Icon.Default=n.Icon.extend({options:{iconSize:new n.Point(25,41),iconAnchor:new n.Point(12,41),popupAnchor:new n.Point(1,-34),shadowSize:new n.Point(41,41)},_getIconUrl:function(e){var t=e+"Url";if(this.options[t])return this.options[t];var r=n.Icon.Default.imagePath;if(!r)throw Error("Couldn't autodetect L.Icon.Default.imagePath, set it manually.");return r+"/marker-"+e+".png"}}),n.Icon.Default.imagePath=function(){var e=document.getElementsByTagName("script"),t=/\/?leaflet[\-\._]?([\w\-\._]*)\.js\??/,n,r,i,s;for(n=0,r=e.length;ns?(t.height=s+"px",n.DomUtil.addClass(e,o)):n.DomUtil.removeClass(e,o),this._containerWidth=this._container.offsetWidth},_updatePosition:function(){if(!this._map)return;var e=this._map.latLngToLayerPoint(this._latlng),t=n.Browser.any3d,r=this.options.offset;t&&n.DomUtil.setPosition(this._container,e),this._containerBottom=-r.y-(t?0:e.y),this._containerLeft=-Math.round(this._containerWidth/2)+r.x+(t?0:e.x),this._container.style.bottom=this._containerBottom+"px",this._container.style.left=this._containerLeft+"px"},_zoomAnimation:function(e){var t=this._map._latLngToNewLayerPoint(this._latlng,e.zoom,e.center);n.DomUtil.setPosition(this._container,t)},_adjustPan:function(){if(!this.options.autoPan)return;var e=this._map,t=this._container.offsetHeight,r=this._containerWidth,i=new n.Point(this._containerLeft,-t-this._containerBottom);n.Browser.any3d&&i._add(n.DomUtil.getPosition(this._container));var s=e.layerPointToContainerPoint(i),o=this.options.autoPanPadding,u=e.getSize(),a=0,f=0;s.x<0&&(a=s.x-o.x),s.x+r>u.x&&(a=s.x+r-u.x+o.x),s.y<0&&(f=s.y-o.y),s.y+t>u.y&&(f=s.y+t-u.y+o.y),(a||f)&&e.panBy(new n.Point(a,f))},_onCloseButtonClick:function(e){this._close(),n.DomEvent.stop(e)}}),n.popup=function(e,t){return new n.Popup(e,t)},n.Marker.include({openPopup:function(){return this._popup&&this._map&&(this._popup.setLatLng(this._latlng),this._map.openPopup(this._popup)),this},closePopup:function(){return this._popup&&this._popup._close(),this},bindPopup:function(e,t){var r=n.point(this.options.icon.options.popupAnchor)||new n.Point(0,0);return r=r.add(n.Popup.prototype.options.offset),t&&t.offset&&(r=r.add(t.offset)),t=n.extend({offset:r},t),this._popup||this.on("click",this.openPopup,this).on("remove",this.closePopup,this).on("move",this._movePopup,this),this._popup=(new n.Popup(t,this)).setContent(e),this},unbindPopup:function(){return this._popup&&(this._popup=null,this.off("click",this.openPopup).off("remove",this.closePopup).off("move",this._movePopup)),this},_movePopup:function(e){this._popup.setLatLng(e.latlng)}}),n.Map.include({openPopup:function(e){return this.closePopup(),this._popup=e,this.addLayer(e).fire("popupopen",{popup:this._popup})},closePopup:function(){return this._popup&&this._popup._close(),this}}),n.LayerGroup=n.Class.extend({initialize:function(e){this._layers={};var t,n;if(e)for(t=0,n=e.length;t';var t=e.firstChild;return t.style.behavior="url(#default#VML)",t&&typeof t.adj=="object"}catch(n){return!1}}(),n.Path=n.Browser.svg||!n.Browser.vml?n.Path:n.Path.extend({statics:{VML:!0,CLIP_PADDING:.02},_createElement:function(){try{return document.namespaces.add("lvml","urn:schemas-microsoft-com:vml"),function(e){return document.createElement("')}}catch(e){return function(e){return document.createElement("<"+e+' xmlns="urn:schemas-microsoft.com:vml" class="lvml">')}}}(),_initPath:function(){var e=this._container=this._createElement("shape");n.DomUtil.addClass(e,"leaflet-vml-shape"),this.options.clickable&&n.DomUtil.addClass(e,"leaflet-clickable"),e.coordsize="1 1",this._path=this._createElement("path"),e.appendChild(this._path),this._map._pathRoot.appendChild(e)},_initStyle:function(){this._updateStyle()},_updateStyle:function(){var e=this._stroke,t=this._fill,n=this.options,r=this._container;r.stroked=n.stroke,r.filled=n.fill,n.stroke?(e||(e=this._stroke=this._createElement("stroke"),e.endcap="round",r.appendChild(e)),e.weight=n.weight+"px",e.color=n.color,e.opacity=n.opacity,n.dashArray?e.dashStyle=n.dashArray.replace(/ *, */g," "):e.dashStyle=""):e&&(r.removeChild(e),this._stroke=null),n.fill?(t||(t=this._fill=this._createElement("fill"),r.appendChild(t)),t.color=n.fillColor||n.color,t.opacity=n.fillOpacity):t&&(r.removeChild(t),this._fill=null)},_updatePath:function(){var e=this._container.style;e.display="none",this._path.v=this.getPathString()+" ",e.display=""}}),n.Map.include(n.Browser.svg||!n.Browser.vml?{}:{_initPathRoot:function(){if(this._pathRoot)return;var e=this._pathRoot=document.createElement("div");e.className="leaflet-vml-container",this._panes.overlayPane.appendChild(e),this.on("moveend",this._updatePathViewport),this._updatePathViewport()}}),n.Browser.canvas=function(){return!!document.createElement("canvas").getContext}(),n.Path=n.Path.SVG&&!e.L_PREFER_CANVAS||!n.Browser.canvas?n.Path:n.Path.extend({statics:{CANVAS:!0,SVG:!1},redraw:function(){return this._map&&(this.projectLatlngs(),this._requestUpdate()),this},setStyle:function(e){return n.setOptions(this,e),this._map&&(this._updateStyle(),this._requestUpdate()),this},onRemove:function(e){e.off("viewreset",this.projectLatlngs,this).off("moveend",this._updatePath,this),this._requestUpdate(),this._map=null},_requestUpdate:function(){this._map&&!n.Path._updateRequest&&(n.Path._updateRequest=n.Util.requestAnimFrame(this._fireMapMoveEnd,this._map))},_fireMapMoveEnd:function(){n.Path._updateRequest=null,this.fire("moveend")},_initElements:function(){this._map._initPathRoot(),this._ctx=this._map._canvasCtx},_updateStyle:function(){var e=this.options;e.stroke&&(this._ctx.lineWidth=e.weight,this._ctx.strokeStyle=e.color),e.fill&&(this._ctx.fillStyle=e.fillColor||e.color)},_drawPath:function(){var e,t,r,i,s,o;this._ctx.beginPath();for(e=0,r=this._parts.length;es&&(o=u,s=a);s>n&&(t[o]=1,this._simplifyDPStep(e,t,n,r,o),this._simplifyDPStep(e,t,n,o,i))},_reducePoints:function(e,t){var n=[e[0]];for(var r=1,i=0,s=e.length;rt&&(n.push(e[r]),i=r);return it.max.x&&(n|=2),e.yt.max.y&&(n|=8),n},_sqDist:function(e,t){var n=t.x-e.x,r=t.y-e.y;return n*n+r*r},_sqClosestPointOnSegment:function(e,t,r,i){var s=t.x,o=t.y,u=r.x-s,a=r.y-o,f=u*u+a*a,l;return f>0&&(l=((e.x-s)*u+(e.y-o)*a)/f,l>1?(s=r.x,o=r.y):l>0&&(s+=u*l,o+=a*l)),u=e.x-s,a=e.y-o,i?u*u+a*a:new n.Point(s,o)}},n.Polyline=n.Path.extend({initialize:function(e,t){n.Path.prototype.initialize.call(this,t),this._latlngs=this._convertLatLngs(e),n.Handler.PolyEdit&&(this.editing=new n.Handler.PolyEdit(this),this.options.editable&&this.editing.enable())},options:{smoothFactor:1,noClip:!1},projectLatlngs:function(){this._originalPoints=[];for(var e=0,t=this._latlngs.length;ee.max.x||n.y-t>e.max.y||n.x+te.y!=s.y>e.y&&e.x<(s.x-i.x)*(e.y-i.y)/(s.y-i.y)+i.x&&(t=!t)}return t}}:{}),n.Circle.include(n.Path.CANVAS?{_drawPath:function(){var e=this._point;this._ctx.beginPath(),this._ctx.arc(e.x,e.y,this._radius,0,Math.PI*2,!1)},_containsPoint:function(e){var t=this._point,n=this.options.stroke?this.options.weight/2:0;return e.distanceTo(t)<=this._radius+n}}:{}),n.GeoJSON=n.FeatureGroup.extend({initialize:function(e,t){n.setOptions(this,t),this._layers={},e&&this.addData(e)},addData:function(e){var t=e instanceof Array?e:e.features,r,i;if(t){for(r=0,i=t.length;r1){this._simulateClick=!1,clearTimeout(this._longPressTimeout);return}var t=e.touches&&e.touches.length===1?e.touches[0]:e,r=t.target;n.Browser.touch&&r.tagName.toLowerCase()==="a"&&n.DomUtil.addClass(r,"leaflet-active"),this._moved=!1;if(this._moving)return;this._startPoint=new n.Point(t.clientX,t.clientY),this._startPos=this._newPos=n.DomUtil.getPosition(this._element),e.touches&&e.touches.length===1&&n.Browser.touch&&this._longPress&&(this._longPressTimeout=setTimeout(n.bind(function(){var e=this._newPos&&this._newPos.distanceTo(this._startPos)||0;e1)return;var t=e.touches&&e.touches.length===1?e.touches[0]:e,r=new n.Point(t.clientX,t.clientY),i=r.subtract(this._startPoint);if(!i.x&&!i.y)return;n.DomEvent.preventDefault(e),this._moved||(this.fire("dragstart"),this._moved=!0,this._startPos=n.DomUtil.getPosition(this._element).subtract(i),n.Browser.touch||(n.DomUtil.disableTextSelection(),this._setMovingCursor())),this._newPos=this._startPos.add(i),this._moving=!0,n.Util.cancelAnimFrame(this._animRequest),this._animRequest=n.Util.requestAnimFrame(this._updatePosition,this,!0,this._dragStartTarget)},_updatePosition:function(){this.fire("predrag"),n.DomUtil.setPosition(this._element,this._newPos),this.fire("drag")},_onUp:function(e){var t;clearTimeout(this._longPressTimeout);if(this._simulateClick&&e.changedTouches){var r=e.changedTouches[0],i=r.target,s=this._newPos&&this._newPos.distanceTo(this._startPos)||0;i.tagName.toLowerCase()==="a"&&n.DomUtil.removeClass(i,"leaflet-active"),s200&&(this._positions.shift(),this._times.shift())}this._map.fire("move").fire("drag")},_onViewReset:function(){var e=this._map.getSize()._divideBy(2),t=this._map.latLngToLayerPoint(new n.LatLng(0,0));this._initialWorldOffset=t.subtract(e).x,this._worldWidth=this._map.project(new n.LatLng(0,180)).x},_onPreDrag:function(){var e=this._map,t=this._worldWidth,n=Math.round(t/2),r=this._initialWorldOffset,i=this._draggable._newPos.x,s=(i-n+r)%t+n-r,o=(i+n+r)%t-n-r,u=Math.abs(s+r)t.inertiaThreshold||!this._positions[0];if(i)e.fire("moveend");else{var s=this._lastPos.subtract(this._positions[0]),o=(this._lastTime+r-this._times[0])/1e3,u=s.multiplyBy(t.easeLinearity/o),a=u.distanceTo(new n.Point(0,0)),f=Math.min(t.inertiaMaxSpeed,a),l=u.multiplyBy(f/a),c=f/(t.inertiaDeceleration*t.easeLinearity),h=l.multiplyBy(-c/2).round();n.Util.requestAnimFrame(function(){e.panBy(h,c,t.easeLinearity)})}e.fire("dragend"),t.maxBounds&&n.Util.requestAnimFrame(this._panInsideMaxBounds,e,!0,e._container)},_panInsideMaxBounds:function(){this.panInsideBounds(this.options.maxBounds)}}),n.Map.addInitHook("addHandler","dragging",n.Map.Drag),n.Map.mergeOptions({doubleClickZoom:!0}),n.Map.DoubleClickZoom=n.Handler.extend({addHooks:function(){this._map.on("dblclick",this._onDoubleClick)},removeHooks:function(){this._map.off("dblclick",this._onDoubleClick)},_onDoubleClick:function(e){this.setView(e.latlng,this._zoom+1)}}),n.Map.addInitHook("addHandler","doubleClickZoom",n.Map.DoubleClickZoom),n.Map.mergeOptions({scrollWheelZoom:!n.Browser.touch||n.Browser.msTouch}),n.Map.ScrollWheelZoom=n.Handler.extend({addHooks:function(){n.DomEvent.on(this._map._container,"mousewheel",this._onWheelScroll,this),this._delta=0},removeHooks:function(){n.DomEvent.off(this._map._container,"mousewheel",this._onWheelScroll)},_onWheelScroll:function(e){var t=n.DomEvent.getWheelDelta(e);this._delta+=t,this._lastMousePos=this._map.mouseEventToContainerPoint(e),this._startTime||(this._startTime=+(new Date));var r=Math.max(40-(+(new Date)-this._startTime),0);clearTimeout(this._timer),this._timer=setTimeout(n.bind(this._performZoom,this),r),n.DomEvent.preventDefault(e),n.DomEvent.stopPropagation(e)},_performZoom:function(){var e=this._map,t=Math.round(this._delta),n=e.getZoom();t=Math.max(Math.min(t,4),-4),t=e._limitZoom(n+t)-n,this._delta=0,this._startTime=null;if(!t)return;var r=n+t,i=this._getCenterForScrollWheelZoom(r);e.setView(i,r)},_getCenterForScrollWheelZoom:function(e){var t=this._map,n=t.getZoomScale(e),r=t.getSize()._divideBy(2),i=this._lastMousePos._subtract(r)._multiplyBy(1-1/n),s=t._getTopLeftPoint()._add(r)._add(i);return t.unproject(s)}}),n.Map.addInitHook("addHandler","scrollWheelZoom",n.Map.ScrollWheelZoom),n.extend(n.DomEvent,{_touchstart:n.Browser.msTouch?"MSPointerDown":"touchstart",_touchend:n.Browser.msTouch?"MSPointerUp":"touchend",addDoubleTapListener:function(e,t,r){function h(e){var t;n.Browser.msTouch?(c.push(e.pointerId),t=c.length):t=e.touches.length;if(t>1)return;var r=Date.now(),a=r-(i||r);u=e.touches?e.touches[0]:e,s=a>0&&a<=o,i=r}function p(e){if(n.Browser.msTouch){var r=c.indexOf(e.pointerId);if(r===-1)return;c.splice(r,1)}if(s){if(n.Browser.msTouch){var o={},a;for(var f in u)a=u[f],typeof a=="function"?o[f]=a.bind(u):o[f]=a;u=o}u.type="dblclick",t(u),i=null}}var i,s=!1,o=250,u,a="_leaflet_",f=this._touchstart,l=this._touchend,c=[];e[a+f+r]=h,e[a+l+r]=p;var d=n.Browser.msTouch?document.documentElement:e;return e.addEventListener(f,h,!1),d.addEventListener(l,p,!1),n.Browser.msTouch&&d.addEventListener("MSPointerCancel",p,!1),this},removeDoubleTapListener:function(e,t){var r="_leaflet_";return e.removeEventListener(this._touchstart,e[r+this._touchstart+t],!1),(n.Browser.msTouch?document.documentElement:e).removeEventListener(this._touchend,e[r+this._touchend+t],!1),n.Browser.msTouch&&document.documentElement.removeEventListener("MSPointerCancel",e[r+this._touchend+t],!1),this}}),n.extend(n.DomEvent,{_msTouches:[],_msDocumentListener:!1,addMsTouchListener:function(e,t,n,r){switch(t){case"touchstart":return this.addMsTouchListenerStart(e,t,n,r);case"touchend":return this.addMsTouchListenerEnd(e,t,n,r);case"touchmove":return this.addMsTouchListenerMove(e,t,n,r);default:throw"Unknown touch event type"}},addMsTouchListenerStart:function(e,t,n,r){var i="_leaflet_",s=this._msTouches,o=function(e){var t=!1;for(var r=0;r0?Math.ceil(o):Math.floor(o),a=t._limitZoom(s+u);t.fire("zoomanim",{center:i,zoom:a}),t._runAnimation(i,a,t.getZoomScale(a)/this._scale,r,!0)},_getScaleOrigin:function(){var e=this._centerOffset.subtract(this._delta).divideBy(this._scale);return this._startCenter.add(e)}}),n.Map.addInitHook("addHandler","touchZoom",n.Map.TouchZoom),n.Map.mergeOptions({boxZoom:!0}),n.Map.BoxZoom=n.Handler.extend({initialize:function(e){this._map=e,this._container=e._container,this._pane=e._panes.overlayPane},addHooks:function(){n.DomEvent.on(this._container,"mousedown",this._onMouseDown,this)},removeHooks:function(){n.DomEvent.off(this._container,"mousedown",this._onMouseDown)},_onMouseDown:function(e){if(!e.shiftKey||e.which!==1&&e.button!==1)return!1;n.DomUtil.disableTextSelection(),this._startLayerPoint=this._map.mouseEventToLayerPoint(e),this._box=n.DomUtil.create("div","leaflet-zoom-box",this._pane),n.DomUtil.setPosition(this._box,this._startLayerPoint),this._container.style.cursor="crosshair",n.DomEvent.on(document,"mousemove",this._onMouseMove,this).on(document,"mouseup",this._onMouseUp,this).preventDefault(e),this._map.fire("boxzoomstart")},_onMouseMove:function(e){var t=this._startLayerPoint,r=this._box,i=this._map.mouseEventToLayerPoint(e),s=i.subtract(t),o=new n.Point(Math.min(i.x,t.x),Math.min(i.y,t.y));n.DomUtil.setPosition(r,o),r.style.width=Math.max(0,Math.abs(s.x)-4)+"px",r.style.height=Math.max(0,Math.abs(s.y)-4)+"px"},_onMouseUp:function(e){this._pane.removeChild(this._box),this._container.style.cursor="",n.DomUtil.enableTextSelection(),n.DomEvent.off(document,"mousemove",this._onMouseMove).off(document,"mouseup",this._onMouseUp);var t=this._map,r=t.mouseEventToLayerPoint(e),i=new n.LatLngBounds(t.layerPointToLatLng(this._startLayerPoint),t.layerPointToLatLng(r));t.fitBounds(i),t.fire("boxzoomend",{boxZoomBounds:i})}}),n.Map.addInitHook("addHandler","boxZoom",n.Map.BoxZoom),n.Map.mergeOptions({keyboard:!0,keyboardPanOffset:80,keyboardZoomOffset:1}),n.Map.Keyboard=n.Handler.extend({keyCodes:{left:[37],right:[39],down:[40],up:[38],zoomIn:[187,107,61],zoomOut:[189,109]},initialize:function(e){this._map=e,this._setPanOffset(e.options.keyboardPanOffset),this._setZoomOffset(e.options.keyboardZoomOffset)},addHooks:function(){var e=this._map._container;e.tabIndex===-1&&(e.tabIndex="0"),n.DomEvent.addListener(e,"focus",this._onFocus,this).addListener(e,"blur",this._onBlur,this).addListener(e,"mousedown",this._onMouseDown,this),this._map.on("focus",this._addHooks,this).on("blur",this._removeHooks,this)},removeHooks:function(){this._removeHooks();var e=this._map._container;n.DomEvent.removeListener(e,"focus",this._onFocus,this).removeListener(e,"blur",this._onBlur,this).removeListener(e,"mousedown",this._onMouseDown,this),this._map.off("focus",this._addHooks,this).off("blur",this._removeHooks,this)},_onMouseDown:function(){this._focused||this._map._container.focus()},_onFocus:function(){this._focused=!0,this._map.fire("focus")},_onBlur:function(){this._focused=!1,this._map.fire("blur")},_setPanOffset:function(e){var t=this._panKeys={},n=this.keyCodes,r,i;for(r=0,i=n.left.length;re&&(n._index+=t)})},_createMiddleMarker:function(e,t){var n=this._getMiddleLatLng(e,t),r=this._createMarker(n),i,s,o;r.setOpacity(.6),e._middleRight=t._middleLeft=r,s=function(){var s=t._index;r._index=s,r.off("click",i).on("click",this._onMarkerClick,this),n.lat=r.getLatLng().lat,n.lng=r.getLatLng().lng,this._poly.spliceLatLngs(s,0,n),this._markers.splice(s,0,r),r.setOpacity(1),this._updateIndexes(s,1),t._index++,this._updatePrevNext(e,r),this._updatePrevNext(r,t)},o=function(){r.off("dragstart",s,this),r.off("dragend",o,this),this._createMiddleMarker(e,r),this._createMiddleMarker(r,t)},i=function(){s.call(this),o.call(this),this._poly.fire("edit")},r.on("click",i,this).on("dragstart",s,this).on("dragend",o,this),this._markerGroup.addLayer(r)},_updatePrevNext:function(e,t){e&&(e._next=t),t&&(t._prev=e)},_getMiddleLatLng:function(e,t){var n=this._poly._map,r=n.latLngToLayerPoint(e.getLatLng()),i=n.latLngToLayerPoint(t.getLatLng());return n.layerPointToLatLng(r._add(i)._divideBy(2))}}),n.Control=n.Class.extend({options:{position:"topright"},initialize:function(e){n.setOptions(this,e)},getPosition:function(){return this.options.position},setPosition:function(e){var t=this._map;return t&&t.removeControl(this),this.options.position=e,t&&t.addControl(this),this},addTo:function(e){this._map=e;var t=this._container=this.onAdd(e),r=this.getPosition(),i=e._controlCorners[r];return n.DomUtil.addClass(t,"leaflet-control"),r.indexOf("bottom")!==-1?i.insertBefore(t,i.firstChild):i.appendChild(t),this},removeFrom:function(e){var t=this.getPosition(),n=e._controlCorners[t];return n.removeChild(this._container),this._map=null,this.onRemove&&this.onRemove(e),this}}),n.control=function(e){return new n.Control(e)},n.Map.include({addControl:function(e){return e.addTo(this),this},removeControl:function(e){return e.removeFrom(this),this},_initControlPos:function(){function i(i,s){var o=t+i+" "+t+s;e[i+s]=n.DomUtil.create("div",o,r)}var e=this._controlCorners={},t="leaflet-",r=this._controlContainer=n.DomUtil.create("div",t+"control-container",this._container);i("top","left"),i("top","right"),i("bottom","left"),i("bottom","right")}}),n.Control.Zoom=n.Control.extend({options:{position:"topleft"},onAdd:function(e){var t="leaflet-control-zoom",r=n.DomUtil.create("div",t);return this._map=e,this._createButton("+","Zoom in",t+"-in",r,this._zoomIn,this),this._createButton("-","Zoom out",t+"-out",r,this._zoomOut,this),r},_zoomIn:function(e){this._map.zoomIn(e.shiftKey?3:1)},_zoomOut:function(e){this._map.zoomOut(e.shiftKey?3:1)},_createButton:function(e,t,r,i,s,o){var u=n.DomUtil.create("a",r,i);return u.innerHTML=e,u.href="#",u.title=t,n.DomEvent.on(u,"click",n.DomEvent.stopPropagation).on(u,"mousedown",n.DomEvent.stopPropagation).on(u,"dblclick",n.DomEvent.stopPropagation).on(u,"click",n.DomEvent.preventDefault).on(u,"click",s,o),u}}),n.Map.mergeOptions({zoomControl:!0}),n.Map.addInitHook(function(){this.options.zoomControl&&(this.zoomControl=new n.Control.Zoom,this.addControl(this.zoomControl))}),n.control.zoom=function(e){return new n.Control.Zoom(e)},n.Control.Attribution=n.Control.extend({options:{position:"bottomright",prefix:'Powered by Leaflet'},initialize:function(e){n.setOptions(this,e),this._attributions={}},onAdd:function(e){return this._container=n.DomUtil.create("div","leaflet-control-attribution"),n.DomEvent.disableClickPropagation(this._container),e.on("layeradd",this._onLayerAdd,this).on("layerremove",this._onLayerRemove,this),this._update(),this._container},onRemove:function(e){e.off("layeradd",this._onLayerAdd).off("layerremove",this._onLayerRemove)},setPrefix:function(e){return this.options.prefix=e,this._update(),this},addAttribution:function(e){if(!e)return;return this._attributions[e]||(this._attributions[e]=0),this._attributions[e]++,this._update(),this},removeAttribution:function(e){if(!e)return;return this._attributions[e]--,this._update(),this},_update:function(){if(!this._map)return;var e=[];for(var t in this._attributions)this._attributions.hasOwnProperty(t)&&this._attributions[t]&&e.push(t);var n=[];this.options.prefix&&n.push(this.options.prefix),e.length&&n.push(e.join(", ")),this._container.innerHTML=n.join(" — ")},_onLayerAdd:function(e){e.layer.getAttribution&&this.addAttribution(e.layer.getAttribution())},_onLayerRemove:function(e){e.layer.getAttribution&&this.removeAttribution(e.layer.getAttribution())}}),n.Map.mergeOptions({attributionControl:!0}),n.Map.addInitHook(function(){this.options.attributionControl&&(this.attributionControl=(new n.Control.Attribution).addTo(this))}),n.control.attribution=function(e){return new n.Control.Attribution(e)},n.Control.Scale=n.Control.extend({options:{position:"bottomleft",maxWidth:100,metric:!0,imperial:!0,updateWhenIdle:!1},onAdd:function(e){this._map=e;var t="leaflet-control-scale",r=n.DomUtil.create("div",t),i=this.options;return this._addScales(i,t,r),e.on(i.updateWhenIdle?"moveend":"move",this._update,this),e.whenReady(this._update,this),r},onRemove:function(e){e.off(this.options.updateWhenIdle?"moveend":"move",this._update,this)},_addScales:function(e,t,r){e.metric&&(this._mScale=n.DomUtil.create("div",t+"-line",r)),e.imperial&&(this._iScale=n.DomUtil.create("div",t+"-line",r))},_update:function(){var e=this._map.getBounds(),t=e.getCenter().lat,n=6378137*Math.PI*Math.cos(t*Math.PI/180),r=n*(e.getNorthEast().lng-e.getSouthWest().lng)/180,i=this._map.getSize(),s=this.options,o=0;i.x>0&&(o=r*(s.maxWidth/i.x)),this._updateScales(s,o)},_updateScales:function(e,t){e.metric&&t&&this._updateMetric(t),e.imperial&&t&&this._updateImperial(t)},_updateMetric:function(e){var t=this._getRoundNum(e);this._mScale.style.width=this._getScaleWidth(t/e)+"px",this._mScale.innerHTML=t<1e3?t+" m":t/1e3+" km"},_updateImperial:function(e){var t=e*3.2808399,n=this._iScale,r,i,s;t>5280?(r=t/5280,i=this._getRoundNum(r),n.style.width=this._getScaleWidth(i/r)+"px",n.innerHTML=i+" mi"):(s=this._getRoundNum(t),n.style.width=this._getScaleWidth(s/t)+"px",n.innerHTML=s+" ft")},_getScaleWidth:function(e){return Math.round(this.options.maxWidth*e)-10},_getRoundNum:function(e){var t=Math.pow(10,(Math.floor(e)+"").length-1),n=e/t;return n=n>=10?10:n>=5?5:n>=3?3:n>=2?2:1,t*n}}),n.control.scale=function(e){return new n.Control.Scale(e)},n.Control.Layers=n.Control.extend({options:{collapsed:!0,position:"topright",autoZIndex:!0},initialize:function(e,t,r){n.setOptions(this,r),this._layers={},this._lastZIndex=0;for(var i in e)e.hasOwnProperty(i)&&this._addLayer(e[i],i);for(i in t)t.hasOwnProperty(i)&&this._addLayer(t[i],i,!0)},onAdd:function(e){return this._initLayout(),this._update(),this._container},addBaseLayer:function(e,t){return this._addLayer(e,t),this._update(),this},addOverlay:function(e,t){return this._addLayer(e,t,!0),this._update(),this},removeLayer:function(e){var t=n.stamp(e);return delete this._layers[t],this._update(),this},_initLayout:function(){var e="leaflet-control-layers",t=this._container=n.DomUtil.create("div",e);n.Browser.touch?n.DomEvent.on(t,"click",n.DomEvent.stopPropagation):n.DomEvent.disableClickPropagation(t);var r=this._form=n.DomUtil.create("form",e+"-list");if(this.options.collapsed){n.DomEvent.on(t,"mouseover",this._expand,this).on(t,"mouseout",this._collapse,this);var i=this._layersLink=n.DomUtil.create("a",e+"-toggle",t);i.href="#",i.title="Layers",n.Browser.touch?n.DomEvent.on(i,"click",n.DomEvent.stopPropagation).on(i,"click",n.DomEvent.preventDefault).on(i,"click",this._expand,this):n.DomEvent.on(i,"focus",this._expand,this),this._map.on("movestart",this._collapse,this)}else this._expand();this._baseLayersList=n.DomUtil.create("div",e+"-base",r),this._separator=n.DomUtil.create("div",e+"-separator",r),this._overlaysList=n.DomUtil.create("div",e+"-overlays",r),t.appendChild(r)},_addLayer:function(e,t,r){var i=n.stamp(e);this._layers[i]={layer:e,name:t,overlay:r},this.options.autoZIndex&&e.setZIndex&&(this._lastZIndex++,e.setZIndex(this._lastZIndex))},_update:function(){if(!this._container)return;this._baseLayersList.innerHTML="",this._overlaysList.innerHTML="";var e=!1,t=!1;for(var n in this._layers)if(this._layers.hasOwnProperty(n)){var r=this._layers[n];this._addItem(r),t=t||r.overlay,e=e||!r.overlay}this._separator.style.display=t&&e?"":"none"},_createRadioElement:function(e,t){var n='.5&&this._getLoadedTilesPercentage(e)<.5){e.style.visibility="hidden",e.empty=!0,this._stopLoadingImages(e);return}t||(t=this._tileBg=this._createPane("leaflet-tile-pane",this._mapPane),t.style.zIndex=1),t.style[n.DomUtil.TRANSFORM]="",t.style.visibility="hidden",t.empty=!0,e.empty=!1,this._tilePane=this._panes.tilePane=t;var r=this._tileBg=e;n.DomUtil.addClass(r,"leaflet-zoom-animated"),this._stopLoadingImages(r)},_getLoadedTilesPercentage:function(e){var t=e.getElementsByTagName("img"),n,r,i=0;for(n=0,r=t.length;ni;i++){o=s[i]||{};for(e in o)o.hasOwnProperty(e)&&(t[e]=o[e])}return t},bind:function(t,e){var i=arguments.length>2?Array.prototype.slice.call(arguments,2):null;return function(){return t.apply(e,i||arguments)}},stamp:function(){var t=0,e="_leaflet_id";return function(i){return i[e]=i[e]||++t,i[e]}}(),limitExecByInterval:function(t,e,n){var o,s;return function a(){var r=arguments;return o?(s=!0,i):(o=!0,setTimeout(function(){o=!1,s&&(a.apply(n,r),s=!1)},e),t.apply(n,r),i)}},falseFn:function(){return!1},formatNum:function(t,e){var i=Math.pow(10,e||5);return Math.round(t*i)/i},splitWords:function(t){return t.replace(/^\s+|\s+$/g,"").split(/\s+/)},setOptions:function(t,e){return t.options=n.extend({},t.options,e),t.options},getParamString:function(t,e){var i=[];for(var n in t)t.hasOwnProperty(n)&&i.push(n+"="+t[n]);return(e&&-1!==e.indexOf("?")?"&":"?")+i.join("&")},template:function(t,e){return t.replace(/\{ *([\w_]+) *\}/g,function(t,i){var n=e[i];if(!e.hasOwnProperty(i))throw Error("No value provided for variable "+t);return n})},isArray:function(t){return"[object Array]"===Object.prototype.toString.call(t)},emptyImageUrl:"data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs="},function(){function e(e){var i,n,o=["webkit","moz","o","ms"];for(i=0;o.length>i&&!n;i++)n=t[o[i]+e];return n}function o(e){var i=+new Date,n=Math.max(0,16-(i-s));return s=i+n,t.setTimeout(e,n)}var s=0,a=t.requestAnimationFrame||e("RequestAnimationFrame")||o,r=t.cancelAnimationFrame||e("CancelAnimationFrame")||e("CancelRequestAnimationFrame")||function(e){t.clearTimeout(e)};n.Util.requestAnimFrame=function(e,s,r,h){return e=n.bind(e,s),r&&a===o?(e(),i):a.call(t,e,h)},n.Util.cancelAnimFrame=function(e){e&&r.call(t,e)}}(),n.extend=n.Util.extend,n.bind=n.Util.bind,n.stamp=n.Util.stamp,n.setOptions=n.Util.setOptions,n.Class=function(){},n.Class.extend=function(t){var e=function(){this.initialize&&this.initialize.apply(this,arguments),this._initHooks&&this.callInitHooks()},i=function(){};i.prototype=this.prototype;var o=new i;o.constructor=e,e.prototype=o;for(var s in this)this.hasOwnProperty(s)&&"prototype"!==s&&(e[s]=this[s]);t.statics&&(n.extend(e,t.statics),delete t.statics),t.includes&&(n.Util.extend.apply(null,[o].concat(t.includes)),delete t.includes),t.options&&o.options&&(t.options=n.extend({},o.options,t.options)),n.extend(o,t),o._initHooks=[];var a=this;return o.callInitHooks=function(){if(!this._initHooksCalled){a.prototype.callInitHooks&&a.prototype.callInitHooks.call(this),this._initHooksCalled=!0;for(var t=0,e=o._initHooks.length;e>t;t++)o._initHooks[t].call(this)}},e},n.Class.include=function(t){n.extend(this.prototype,t)},n.Class.mergeOptions=function(t){n.extend(this.prototype.options,t)},n.Class.addInitHook=function(t){var e=Array.prototype.slice.call(arguments,1),i="function"==typeof t?t:function(){this[t].apply(this,e)};this.prototype._initHooks=this.prototype._initHooks||[],this.prototype._initHooks.push(i)};var s="_leaflet_events";n.Mixin={},n.Mixin.Events={addEventListener:function(t,e,i){var o,a,r,h=this[s]=this[s]||{};if("object"==typeof t){for(o in t)t.hasOwnProperty(o)&&this.addEventListener(o,t[o],e);return this}for(t=n.Util.splitWords(t),a=0,r=t.length;r>a;a++)h[t[a]]=h[t[a]]||[],h[t[a]].push({action:e,context:i||this});return this},hasEventListeners:function(t){return s in this&&t in this[s]&&this[s][t].length>0},removeEventListener:function(t,e,i){var o,a,r,h,l,u=this[s];if("object"==typeof t){for(o in t)t.hasOwnProperty(o)&&this.removeEventListener(o,t[o],e);return this}for(t=n.Util.splitWords(t),a=0,r=t.length;r>a;a++)if(this.hasEventListeners(t[a]))for(h=u[t[a]],l=h.length-1;l>=0;l--)e&&h[l].action!==e||i&&h[l].context!==i||h.splice(l,1);return this},fireEvent:function(t,e){if(!this.hasEventListeners(t))return this;for(var i=n.extend({type:t,target:this},e),o=this[s][t].slice(),a=0,r=o.length;r>a;a++)o[a].action.call(o[a].context||this,i);return this}},n.Mixin.Events.on=n.Mixin.Events.addEventListener,n.Mixin.Events.off=n.Mixin.Events.removeEventListener,n.Mixin.Events.fire=n.Mixin.Events.fireEvent,function(){var o=!!t.ActiveXObject,s=o&&!t.XMLHttpRequest,a=o&&!e.querySelector,r=navigator.userAgent.toLowerCase(),h=-1!==r.indexOf("webkit"),l=-1!==r.indexOf("chrome"),u=-1!==r.indexOf("android"),c=-1!==r.search("android [23]"),_=typeof orientation!=i+"",d=t.navigator&&t.navigator.msPointerEnabled&&t.navigator.msMaxTouchPoints,p="devicePixelRatio"in t&&t.devicePixelRatio>1||"matchMedia"in t&&t.matchMedia("(min-resolution:144dpi)")&&t.matchMedia("(min-resolution:144dpi)").matches,m=e.documentElement,f=o&&"transition"in m.style,g="WebKitCSSMatrix"in t&&"m11"in new t.WebKitCSSMatrix,v="MozPerspective"in m.style,y="OTransition"in m.style,L=!t.L_DISABLE_3D&&(f||g||v||y),P=!t.L_NO_TOUCH&&function(){var t="ontouchstart";if(d||t in m)return!0;var i=e.createElement("div"),n=!1;return i.setAttribute?(i.setAttribute(t,"return;"),"function"==typeof i[t]&&(n=!0),i.removeAttribute(t),i=null,n):!1}();n.Browser={ie:o,ie6:s,ie7:a,webkit:h,android:u,android23:c,chrome:l,ie3d:f,webkit3d:g,gecko3d:v,opera3d:y,any3d:L,mobile:_,mobileWebkit:_&&h,mobileWebkit3d:_&&g,mobileOpera:_&&t.opera,touch:P,msTouch:d,retina:p}}(),n.Point=function(t,e,i){this.x=i?Math.round(t):t,this.y=i?Math.round(e):e},n.Point.prototype={clone:function(){return new n.Point(this.x,this.y)},add:function(t){return this.clone()._add(n.point(t))},_add:function(t){return this.x+=t.x,this.y+=t.y,this},subtract:function(t){return this.clone()._subtract(n.point(t))},_subtract:function(t){return this.x-=t.x,this.y-=t.y,this},divideBy:function(t){return this.clone()._divideBy(t)},_divideBy:function(t){return this.x/=t,this.y/=t,this},multiplyBy:function(t){return this.clone()._multiplyBy(t)},_multiplyBy:function(t){return this.x*=t,this.y*=t,this},round:function(){return this.clone()._round()},_round:function(){return this.x=Math.round(this.x),this.y=Math.round(this.y),this},floor:function(){return this.clone()._floor()},_floor:function(){return this.x=Math.floor(this.x),this.y=Math.floor(this.y),this},distanceTo:function(t){t=n.point(t);var e=t.x-this.x,i=t.y-this.y;return Math.sqrt(e*e+i*i)},equals:function(t){return t.x===this.x&&t.y===this.y},toString:function(){return"Point("+n.Util.formatNum(this.x)+", "+n.Util.formatNum(this.y)+")"}},n.point=function(t,e,i){return t instanceof n.Point?t:n.Util.isArray(t)?new n.Point(t[0],t[1]):isNaN(t)?t:new n.Point(t,e,i)},n.Bounds=function(t,e){if(t)for(var i=e?[t,e]:t,n=0,o=i.length;o>n;n++)this.extend(i[n])},n.Bounds.prototype={extend:function(t){return t=n.point(t),this.min||this.max?(this.min.x=Math.min(t.x,this.min.x),this.max.x=Math.max(t.x,this.max.x),this.min.y=Math.min(t.y,this.min.y),this.max.y=Math.max(t.y,this.max.y)):(this.min=t.clone(),this.max=t.clone()),this},getCenter:function(t){return new n.Point((this.min.x+this.max.x)/2,(this.min.y+this.max.y)/2,t)},getBottomLeft:function(){return new n.Point(this.min.x,this.max.y)},getTopRight:function(){return new n.Point(this.max.x,this.min.y)},getSize:function(){return this.max.subtract(this.min)},contains:function(t){var e,i;return t="number"==typeof t[0]||t instanceof n.Point?n.point(t):n.bounds(t),t instanceof n.Bounds?(e=t.min,i=t.max):e=i=t,e.x>=this.min.x&&i.x<=this.max.x&&e.y>=this.min.y&&i.y<=this.max.y},intersects:function(t){t=n.bounds(t);var e=this.min,i=this.max,o=t.min,s=t.max,a=s.x>=e.x&&o.x<=i.x,r=s.y>=e.y&&o.y<=i.y;return a&&r},isValid:function(){return!(!this.min||!this.max)}},n.bounds=function(t,e){return!t||t instanceof n.Bounds?t:new n.Bounds(t,e)},n.Transformation=function(t,e,i,n){this._a=t,this._b=e,this._c=i,this._d=n},n.Transformation.prototype={transform:function(t,e){return this._transform(t.clone(),e)},_transform:function(t,e){return e=e||1,t.x=e*(this._a*t.x+this._b),t.y=e*(this._c*t.y+this._d),t},untransform:function(t,e){return e=e||1,new n.Point((t.x/e-this._b)/this._a,(t.y/e-this._d)/this._c)}},n.DomUtil={get:function(t){return"string"==typeof t?e.getElementById(t):t},getStyle:function(t,i){var n=t.style[i];if(!n&&t.currentStyle&&(n=t.currentStyle[i]),(!n||"auto"===n)&&e.defaultView){var o=e.defaultView.getComputedStyle(t,null);n=o?o[i]:null}return"auto"===n?null:n},getViewportOffset:function(t){var i,o=0,s=0,a=t,r=e.body,h=n.Browser.ie7;do{if(o+=a.offsetTop||0,s+=a.offsetLeft||0,o+=parseInt(n.DomUtil.getStyle(a,"borderTopWidth"),10)||0,s+=parseInt(n.DomUtil.getStyle(a,"borderLeftWidth"),10)||0,i=n.DomUtil.getStyle(a,"position"),a.offsetParent===r&&"absolute"===i)break;if("fixed"===i){o+=r.scrollTop||0,s+=r.scrollLeft||0;break}a=a.offsetParent}while(a);a=t;do{if(a===r)break;o-=a.scrollTop||0,s-=a.scrollLeft||0,n.DomUtil.documentIsLtr()||!n.Browser.webkit&&!h||(s+=a.scrollWidth-a.clientWidth,h&&"hidden"!==n.DomUtil.getStyle(a,"overflow-y")&&"hidden"!==n.DomUtil.getStyle(a,"overflow")&&(s+=17)),a=a.parentNode}while(a);return new n.Point(s,o)},documentIsLtr:function(){return n.DomUtil._docIsLtrCached||(n.DomUtil._docIsLtrCached=!0,n.DomUtil._docIsLtr="ltr"===n.DomUtil.getStyle(e.body,"direction")),n.DomUtil._docIsLtr},create:function(t,i,n){var o=e.createElement(t);return o.className=i,n&&n.appendChild(o),o},disableTextSelection:function(){e.selection&&e.selection.empty&&e.selection.empty(),this._onselectstart||(this._onselectstart=e.onselectstart||null,e.onselectstart=n.Util.falseFn)},enableTextSelection:function(){e.onselectstart===n.Util.falseFn&&(e.onselectstart=this._onselectstart,this._onselectstart=null)},hasClass:function(t,e){return t.className.length>0&&RegExp("(^|\\s)"+e+"(\\s|$)").test(t.className)},addClass:function(t,e){n.DomUtil.hasClass(t,e)||(t.className+=(t.className?" ":"")+e)},removeClass:function(t,e){function i(t,i){return i===e?"":t}t.className=t.className.replace(/(\S+)\s*/g,i).replace(/(^\s+|\s+$)/,"")},setOpacity:function(t,e){if("opacity"in t.style)t.style.opacity=e;else if("filter"in t.style){var i=!1,n="DXImageTransform.Microsoft.Alpha";try{i=t.filters.item(n)}catch(o){}e=Math.round(100*e),i?(i.Enabled=100!==e,i.Opacity=e):t.style.filter+=" progid:"+n+"(opacity="+e+")"}},testProp:function(t){for(var i=e.documentElement.style,n=0;t.length>n;n++)if(t[n]in i)return t[n];return!1},getTranslateString:function(t){var e=n.Browser.webkit3d,i="translate"+(e?"3d":"")+"(",o=(e?",0":"")+")";return i+t.x+"px,"+t.y+"px"+o},getScaleString:function(t,e){var i=n.DomUtil.getTranslateString(e.add(e.multiplyBy(-1*t))),o=" scale("+t+") ";return i+o},setPosition:function(t,e,i){t._leaflet_pos=e,!i&&n.Browser.any3d?(t.style[n.DomUtil.TRANSFORM]=n.DomUtil.getTranslateString(e),n.Browser.mobileWebkit3d&&(t.style.WebkitBackfaceVisibility="hidden")):(t.style.left=e.x+"px",t.style.top=e.y+"px")},getPosition:function(t){return t._leaflet_pos}},n.DomUtil.TRANSFORM=n.DomUtil.testProp(["transform","WebkitTransform","OTransform","MozTransform","msTransform"]),n.DomUtil.TRANSITION=n.DomUtil.testProp(["webkitTransition","transition","OTransition","MozTransition","msTransition"]),n.DomUtil.TRANSITION_END="webkitTransition"===n.DomUtil.TRANSITION||"OTransition"===n.DomUtil.TRANSITION?n.DomUtil.TRANSITION+"End":"transitionend",n.LatLng=function(t,e){var i=parseFloat(t),n=parseFloat(e);if(isNaN(i)||isNaN(n))throw Error("Invalid LatLng object: ("+t+", "+e+")");this.lat=i,this.lng=n},n.extend(n.LatLng,{DEG_TO_RAD:Math.PI/180,RAD_TO_DEG:180/Math.PI,MAX_MARGIN:1e-9}),n.LatLng.prototype={equals:function(t){if(!t)return!1;t=n.latLng(t);var e=Math.max(Math.abs(this.lat-t.lat),Math.abs(this.lng-t.lng));return n.LatLng.MAX_MARGIN>=e},toString:function(t){return"LatLng("+n.Util.formatNum(this.lat,t)+", "+n.Util.formatNum(this.lng,t)+")"},distanceTo:function(t){t=n.latLng(t);var e=6378137,i=n.LatLng.DEG_TO_RAD,o=(t.lat-this.lat)*i,s=(t.lng-this.lng)*i,a=this.lat*i,r=t.lat*i,h=Math.sin(o/2),l=Math.sin(s/2),u=h*h+l*l*Math.cos(a)*Math.cos(r);return 2*e*Math.atan2(Math.sqrt(u),Math.sqrt(1-u))},wrap:function(t,e){var i=this.lng;return t=t||-180,e=e||180,i=(i+e)%(e-t)+(t>i||i===e?e:t),new n.LatLng(this.lat,i)}},n.latLng=function(t,e){return t instanceof n.LatLng?t:n.Util.isArray(t)?new n.LatLng(t[0],t[1]):isNaN(t)?t:new n.LatLng(t,e)},n.LatLngBounds=function(t,e){if(t)for(var i=e?[t,e]:t,n=0,o=i.length;o>n;n++)this.extend(i[n])},n.LatLngBounds.prototype={extend:function(t){return t="number"==typeof t[0]||"string"==typeof t[0]||t instanceof n.LatLng?n.latLng(t):n.latLngBounds(t),t instanceof n.LatLng?this._southWest||this._northEast?(this._southWest.lat=Math.min(t.lat,this._southWest.lat),this._southWest.lng=Math.min(t.lng,this._southWest.lng),this._northEast.lat=Math.max(t.lat,this._northEast.lat),this._northEast.lng=Math.max(t.lng,this._northEast.lng)):(this._southWest=new n.LatLng(t.lat,t.lng),this._northEast=new n.LatLng(t.lat,t.lng)):t instanceof n.LatLngBounds&&(this.extend(t._southWest),this.extend(t._northEast)),this},pad:function(t){var e=this._southWest,i=this._northEast,o=Math.abs(e.lat-i.lat)*t,s=Math.abs(e.lng-i.lng)*t;return new n.LatLngBounds(new n.LatLng(e.lat-o,e.lng-s),new n.LatLng(i.lat+o,i.lng+s))},getCenter:function(){return new n.LatLng((this._southWest.lat+this._northEast.lat)/2,(this._southWest.lng+this._northEast.lng)/2)},getSouthWest:function(){return this._southWest},getNorthEast:function(){return this._northEast},getNorthWest:function(){return new n.LatLng(this._northEast.lat,this._southWest.lng)},getSouthEast:function(){return new n.LatLng(this._southWest.lat,this._northEast.lng)},contains:function(t){t="number"==typeof t[0]||t instanceof n.LatLng?n.latLng(t):n.latLngBounds(t);var e,i,o=this._southWest,s=this._northEast;return t instanceof n.LatLngBounds?(e=t.getSouthWest(),i=t.getNorthEast()):e=i=t,e.lat>=o.lat&&i.lat<=s.lat&&e.lng>=o.lng&&i.lng<=s.lng},intersects:function(t){t=n.latLngBounds(t);var e=this._southWest,i=this._northEast,o=t.getSouthWest(),s=t.getNorthEast(),a=s.lat>=e.lat&&o.lat<=i.lat,r=s.lng>=e.lng&&o.lng<=i.lng;return a&&r},toBBoxString:function(){var t=this._southWest,e=this._northEast;return[t.lng,t.lat,e.lng,e.lat].join(",")},equals:function(t){return t?(t=n.latLngBounds(t),this._southWest.equals(t.getSouthWest())&&this._northEast.equals(t.getNorthEast())):!1},isValid:function(){return!(!this._southWest||!this._northEast)}},n.latLngBounds=function(t,e){return!t||t instanceof n.LatLngBounds?t:new n.LatLngBounds(t,e)},n.Projection={},n.Projection.SphericalMercator={MAX_LATITUDE:85.0511287798,project:function(t){var e=n.LatLng.DEG_TO_RAD,i=this.MAX_LATITUDE,o=Math.max(Math.min(i,t.lat),-i),s=t.lng*e,a=o*e;return a=Math.log(Math.tan(Math.PI/4+a/2)),new n.Point(s,a)},unproject:function(t){var e=n.LatLng.RAD_TO_DEG,i=t.x*e,o=(2*Math.atan(Math.exp(t.y))-Math.PI/2)*e;return new n.LatLng(o,i)}},n.Projection.LonLat={project:function(t){return new n.Point(t.lng,t.lat)},unproject:function(t){return new n.LatLng(t.y,t.x)}},n.CRS={latLngToPoint:function(t,e){var i=this.projection.project(t),n=this.scale(e);return this.transformation._transform(i,n)},pointToLatLng:function(t,e){var i=this.scale(e),n=this.transformation.untransform(t,i);return this.projection.unproject(n)},project:function(t){return this.projection.project(t)},scale:function(t){return 256*Math.pow(2,t)}},n.CRS.Simple=n.extend({},n.CRS,{projection:n.Projection.LonLat,transformation:new n.Transformation(1,0,-1,0),scale:function(t){return Math.pow(2,t)}}),n.CRS.EPSG3857=n.extend({},n.CRS,{code:"EPSG:3857",projection:n.Projection.SphericalMercator,transformation:new n.Transformation(.5/Math.PI,.5,-.5/Math.PI,.5),project:function(t){var e=this.projection.project(t),i=6378137;return e.multiplyBy(i)}}),n.CRS.EPSG900913=n.extend({},n.CRS.EPSG3857,{code:"EPSG:900913"}),n.CRS.EPSG4326=n.extend({},n.CRS,{code:"EPSG:4326",projection:n.Projection.LonLat,transformation:new n.Transformation(1/360,.5,-1/360,.5)}),n.Map=n.Class.extend({includes:n.Mixin.Events,options:{crs:n.CRS.EPSG3857,fadeAnimation:n.DomUtil.TRANSITION&&!n.Browser.android23,trackResize:!0,markerZoomAnimation:n.DomUtil.TRANSITION&&n.Browser.any3d},initialize:function(t,e){e=n.setOptions(this,e),this._initContainer(t),this._initLayout(),this.callInitHooks(),this._initEvents(),e.maxBounds&&this.setMaxBounds(e.maxBounds),e.center&&e.zoom!==i&&this.setView(n.latLng(e.center),e.zoom,!0),this._initLayers(e.layers)},setView:function(t,e){return this._resetView(n.latLng(t),this._limitZoom(e)),this},setZoom:function(t){return this.setView(this.getCenter(),t)},zoomIn:function(t){return this.setZoom(this._zoom+(t||1))},zoomOut:function(t){return this.setZoom(this._zoom-(t||1))},fitBounds:function(t){var e=this.getBoundsZoom(t);return this.setView(n.latLngBounds(t).getCenter(),e)},fitWorld:function(){var t=new n.LatLng(-60,-170),e=new n.LatLng(85,179);return this.fitBounds(new n.LatLngBounds(t,e))},panTo:function(t){return this.setView(t,this._zoom)},panBy:function(t){return this.fire("movestart"),this._rawPanBy(n.point(t)),this.fire("move"),this.fire("moveend")},setMaxBounds:function(t){if(t=n.latLngBounds(t),this.options.maxBounds=t,!t)return this._boundsMinZoom=null,this;var e=this.getBoundsZoom(t,!0);return this._boundsMinZoom=e,this._loaded&&(e>this._zoom?this.setView(t.getCenter(),e):this.panInsideBounds(t)),this},panInsideBounds:function(t){t=n.latLngBounds(t);var e=this.getBounds(),i=this.project(e.getSouthWest()),o=this.project(e.getNorthEast()),s=this.project(t.getSouthWest()),a=this.project(t.getNorthEast()),r=0,h=0;return o.ya.x&&(r=a.x-o.x),i.y>s.y&&(h=s.y-i.y),i.x=r);return c&&e?null:e?r:r-1},getSize:function(){return(!this._size||this._sizeChanged)&&(this._size=new n.Point(this._container.clientWidth,this._container.clientHeight),this._sizeChanged=!1),this._size.clone()},getPixelBounds:function(){var t=this._getTopLeftPoint();return new n.Bounds(t,t.add(this.getSize()))},getPixelOrigin:function(){return this._initialTopLeftPoint},getPanes:function(){return this._panes},getContainer:function(){return this._container},getZoomScale:function(t){var e=this.options.crs;return e.scale(t)/e.scale(this._zoom)},getScaleZoom:function(t){return this._zoom+Math.log(t)/Math.LN2},project:function(t,e){return e=e===i?this._zoom:e,this.options.crs.latLngToPoint(n.latLng(t),e)},unproject:function(t,e){return e=e===i?this._zoom:e,this.options.crs.pointToLatLng(n.point(t),e)},layerPointToLatLng:function(t){var e=n.point(t).add(this._initialTopLeftPoint);return this.unproject(e)},latLngToLayerPoint:function(t){var e=this.project(n.latLng(t))._round();return e._subtract(this._initialTopLeftPoint)},containerPointToLayerPoint:function(t){return n.point(t).subtract(this._getMapPanePos())},layerPointToContainerPoint:function(t){return n.point(t).add(this._getMapPanePos())},containerPointToLatLng:function(t){var e=this.containerPointToLayerPoint(n.point(t));return this.layerPointToLatLng(e)},latLngToContainerPoint:function(t){return this.layerPointToContainerPoint(this.latLngToLayerPoint(n.latLng(t)))},mouseEventToContainerPoint:function(t){return n.DomEvent.getMousePosition(t,this._container)},mouseEventToLayerPoint:function(t){return this.containerPointToLayerPoint(this.mouseEventToContainerPoint(t))},mouseEventToLatLng:function(t){return this.layerPointToLatLng(this.mouseEventToLayerPoint(t))},_initContainer:function(t){var e=this._container=n.DomUtil.get(t);if(e._leaflet)throw Error("Map container is already initialized.");e._leaflet=!0},_initLayout:function(){var t=this._container;n.DomUtil.addClass(t,"leaflet-container"),n.Browser.touch&&n.DomUtil.addClass(t,"leaflet-touch"),this.options.fadeAnimation&&n.DomUtil.addClass(t,"leaflet-fade-anim");var e=n.DomUtil.getStyle(t,"position");"absolute"!==e&&"relative"!==e&&"fixed"!==e&&(t.style.position="relative"),this._initPanes(),this._initControlPos&&this._initControlPos()},_initPanes:function(){var t=this._panes={};this._mapPane=t.mapPane=this._createPane("leaflet-map-pane",this._container),this._tilePane=t.tilePane=this._createPane("leaflet-tile-pane",this._mapPane),t.objectsPane=this._createPane("leaflet-objects-pane",this._mapPane),t.shadowPane=this._createPane("leaflet-shadow-pane"),t.overlayPane=this._createPane("leaflet-overlay-pane"),t.markerPane=this._createPane("leaflet-marker-pane"),t.popupPane=this._createPane("leaflet-popup-pane");var e=" leaflet-zoom-hide";this.options.markerZoomAnimation||(n.DomUtil.addClass(t.markerPane,e),n.DomUtil.addClass(t.shadowPane,e),n.DomUtil.addClass(t.popupPane,e))},_createPane:function(t,e){return n.DomUtil.create("div",t,e||this._panes.objectsPane)},_initLayers:function(t){t=t?n.Util.isArray(t)?t:[t]:[],this._layers={},this._zoomBoundLayers={},this._tileLayersNum=0;var e,i;for(e=0,i=t.length;i>e;e++)this.addLayer(t[e])},_resetView:function(t,e,i,o){var s=this._zoom!==e;o||(this.fire("movestart"),s&&this.fire("zoomstart")),this._zoom=e,this._initialTopLeftPoint=this._getNewTopLeftPoint(t),i?this._initialTopLeftPoint._add(this._getMapPanePos()):n.DomUtil.setPosition(this._mapPane,new n.Point(0,0)),this._tileLayersToLoad=this._tileLayersNum;var a=!this._loaded;this._loaded=!0,this.fire("viewreset",{hard:!i}),this.fire("move"),(s||o)&&this.fire("zoomend"),this.fire("moveend",{hard:!i}),a&&this.fire("load")},_rawPanBy:function(t){n.DomUtil.setPosition(this._mapPane,this._getMapPanePos().subtract(t))},_updateZoomLevels:function(){var t,e=1/0,n=-1/0;for(t in this._zoomBoundLayers)if(this._zoomBoundLayers.hasOwnProperty(t)){var o=this._zoomBoundLayers[t];isNaN(o.options.minZoom)||(e=Math.min(e,o.options.minZoom)),isNaN(o.options.maxZoom)||(n=Math.max(n,o.options.maxZoom))}t===i?this._layersMaxZoom=this._layersMinZoom=i:(this._layersMaxZoom=n,this._layersMinZoom=e)},_initEvents:function(){if(n.DomEvent){n.DomEvent.on(this._container,"click",this._onMouseClick,this);var e,i,o=["dblclick","mousedown","mouseup","mouseenter","mouseleave","mousemove","contextmenu"];for(e=0,i=o.length;i>e;e++)n.DomEvent.on(this._container,o[e],this._fireMouseEvent,this);this.options.trackResize&&n.DomEvent.on(t,"resize",this._onResize,this)}},_onResize:function(){n.Util.cancelAnimFrame(this._resizeRequest),this._resizeRequest=n.Util.requestAnimFrame(this.invalidateSize,this,!1,this._container)},_onMouseClick:function(t){!this._loaded||this.dragging&&this.dragging.moved()||(this.fire("preclick"),this._fireMouseEvent(t))},_fireMouseEvent:function(t){if(this._loaded){var e=t.type;if(e="mouseenter"===e?"mouseover":"mouseleave"===e?"mouseout":e,this.hasEventListeners(e)){"contextmenu"===e&&n.DomEvent.preventDefault(t);var i=this.mouseEventToContainerPoint(t),o=this.containerPointToLayerPoint(i),s=this.layerPointToLatLng(o);this.fire(e,{latlng:s,layerPoint:o,containerPoint:i,originalEvent:t})}}},_onTileLayerLoad:function(){this._tileLayersToLoad--,this._tileLayersNum&&!this._tileLayersToLoad&&this._tileBg&&(clearTimeout(this._clearTileBgTimer),this._clearTileBgTimer=setTimeout(n.bind(this._clearTileBg,this),500))},whenReady:function(t,e){return this._loaded?t.call(e||this,this):this.on("load",t,e),this},_getMapPanePos:function(){return n.DomUtil.getPosition(this._mapPane)},_getTopLeftPoint:function(){if(!this._loaded)throw Error("Set map center and zoom first.");return this._initialTopLeftPoint.subtract(this._getMapPanePos())},_getNewTopLeftPoint:function(t,e){var i=this.getSize()._divideBy(2);return this.project(t,e)._subtract(i)._round()},_latLngToNewLayerPoint:function(t,e,i){var n=this._getNewTopLeftPoint(i,e).add(this._getMapPanePos());return this.project(t,e)._subtract(n)},_getCenterLayerPoint:function(){return this.containerPointToLayerPoint(this.getSize()._divideBy(2))},_getCenterOffset:function(t){return this.latLngToLayerPoint(t).subtract(this._getCenterLayerPoint())},_limitZoom:function(t){var e=this.getMinZoom(),i=this.getMaxZoom();return Math.max(e,Math.min(i,t))}}),n.map=function(t,e){return new n.Map(t,e)},n.Projection.Mercator={MAX_LATITUDE:85.0840591556,R_MINOR:6356752.3142,R_MAJOR:6378137,project:function(t){var e=n.LatLng.DEG_TO_RAD,i=this.MAX_LATITUDE,o=Math.max(Math.min(i,t.lat),-i),s=this.R_MAJOR,a=this.R_MINOR,r=t.lng*e*s,h=o*e,l=a/s,u=Math.sqrt(1-l*l),c=u*Math.sin(h);c=Math.pow((1-c)/(1+c),.5*u);var _=Math.tan(.5*(.5*Math.PI-h))/c;return h=-a*Math.log(_),new n.Point(r,h)},unproject:function(t){for(var e,i=n.LatLng.RAD_TO_DEG,o=this.R_MAJOR,s=this.R_MINOR,a=t.x*i/o,r=s/o,h=Math.sqrt(1-r*r),l=Math.exp(-t.y/s),u=Math.PI/2-2*Math.atan(l),c=15,_=1e-7,d=c,p=.1;Math.abs(p)>_&&--d>0;)e=h*Math.sin(u),p=Math.PI/2-2*Math.atan(l*Math.pow((1-e)/(1+e),.5*h))-u,u+=p;return new n.LatLng(u*i,a)}},n.CRS.EPSG3395=n.extend({},n.CRS,{code:"EPSG:3395",projection:n.Projection.Mercator,transformation:function(){var t=n.Projection.Mercator,e=t.R_MAJOR,i=t.R_MINOR;return new n.Transformation(.5/(Math.PI*e),.5,-.5/(Math.PI*i),.5)}()}),n.TileLayer=n.Class.extend({includes:n.Mixin.Events,options:{minZoom:0,maxZoom:18,tileSize:256,subdomains:"abc",errorTileUrl:"",attribution:"",zoomOffset:0,opacity:1,unloadInvisibleTiles:n.Browser.mobile,updateWhenIdle:n.Browser.mobile},initialize:function(t,e){e=n.setOptions(this,e),e.detectRetina&&n.Browser.retina&&e.maxZoom>0&&(e.tileSize=Math.floor(e.tileSize/2),e.zoomOffset++,e.minZoom>0&&e.minZoom--,this.options.maxZoom--),this._url=t;var i=this.options.subdomains;"string"==typeof i&&(this.options.subdomains=i.split(""))},onAdd:function(t){this._map=t,this._initContainer(),this._createTileProto(),t.on({viewreset:this._resetCallback,moveend:this._update},this),this.options.updateWhenIdle||(this._limitedUpdate=n.Util.limitExecByInterval(this._update,150,this),t.on("move",this._limitedUpdate,this)),this._reset(),this._update()},addTo:function(t){return t.addLayer(this),this},onRemove:function(t){this._container.parentNode.removeChild(this._container),t.off({viewreset:this._resetCallback,moveend:this._update},this),this.options.updateWhenIdle||t.off("move",this._limitedUpdate,this),this._container=null,this._map=null},bringToFront:function(){var t=this._map._panes.tilePane;return this._container&&(t.appendChild(this._container),this._setAutoZIndex(t,Math.max)),this},bringToBack:function(){var t=this._map._panes.tilePane;return this._container&&(t.insertBefore(this._container,t.firstChild),this._setAutoZIndex(t,Math.min)),this},getAttribution:function(){return this.options.attribution},setOpacity:function(t){return this.options.opacity=t,this._map&&this._updateOpacity(),this},setZIndex:function(t){return this.options.zIndex=t,this._updateZIndex(),this},setUrl:function(t,e){return this._url=t,e||this.redraw(),this},redraw:function(){return this._map&&(this._map._panes.tilePane.empty=!1,this._reset(!0),this._update()),this},_updateZIndex:function(){this._container&&this.options.zIndex!==i&&(this._container.style.zIndex=this.options.zIndex)},_setAutoZIndex:function(t,e){var i,n,o,s=t.children,a=-e(1/0,-1/0);for(n=0,o=s.length;o>n;n++)s[n]!==this._container&&(i=parseInt(s[n].style.zIndex,10),isNaN(i)||(a=e(a,i)));this.options.zIndex=this._container.style.zIndex=(isFinite(a)?a:0)+e(1,-1)},_updateOpacity:function(){n.DomUtil.setOpacity(this._container,this.options.opacity);var t,e=this._tiles;if(n.Browser.webkit)for(t in e)e.hasOwnProperty(t)&&(e[t].style.webkitTransform+=" translate(0,0)")},_initContainer:function(){var t=this._map._panes.tilePane;(!this._container||t.empty)&&(this._container=n.DomUtil.create("div","leaflet-layer"),this._updateZIndex(),t.appendChild(this._container),1>this.options.opacity&&this._updateOpacity())},_resetCallback:function(t){this._reset(t.hard)},_reset:function(t){var e=this._tiles;for(var i in e)e.hasOwnProperty(i)&&this.fire("tileunload",{tile:e[i]});this._tiles={},this._tilesToLoad=0,this.options.reuseTiles&&(this._unusedTiles=[]),t&&this._container&&(this._container.innerHTML=""),this._initContainer()},_update:function(){if(this._map){var t=this._map.getPixelBounds(),e=this._map.getZoom(),i=this.options.tileSize;if(!(e>this.options.maxZoom||this.options.minZoom>e)){var o=new n.Point(Math.floor(t.min.x/i),Math.floor(t.min.y/i)),s=new n.Point(Math.floor(t.max.x/i),Math.floor(t.max.y/i)),a=new n.Bounds(o,s);this._addTilesFromCenterOut(a),(this.options.unloadInvisibleTiles||this.options.reuseTiles)&&this._removeOtherTiles(a)}}},_addTilesFromCenterOut:function(t){var i,o,s,a=[],r=t.getCenter();for(i=t.min.y;t.max.y>=i;i++)for(o=t.min.x;t.max.x>=o;o++)s=new n.Point(o,i),this._tileShouldBeLoaded(s)&&a.push(s);var h=a.length;if(0!==h){a.sort(function(t,e){return t.distanceTo(r)-e.distanceTo(r)});var l=e.createDocumentFragment();for(this._tilesToLoad||this.fire("loading"),this._tilesToLoad+=h,o=0;h>o;o++)this._addTile(a[o],l);this._container.appendChild(l)}},_tileShouldBeLoaded:function(t){if(t.x+":"+t.y in this._tiles)return!1;if(!this.options.continuousWorld){var e=this._getWrapTileNum();if(this.options.noWrap&&(0>t.x||t.x>=e)||0>t.y||t.y>=e)return!1}return!0},_removeOtherTiles:function(t){var e,i,n,o;for(o in this._tiles)this._tiles.hasOwnProperty(o)&&(e=o.split(":"),i=parseInt(e[0],10),n=parseInt(e[1],10),(t.min.x>i||i>t.max.x||t.min.y>n||n>t.max.y)&&this._removeTile(o))},_removeTile:function(t){var e=this._tiles[t];this.fire("tileunload",{tile:e,url:e.src}),this.options.reuseTiles?(n.DomUtil.removeClass(e,"leaflet-tile-loaded"),this._unusedTiles.push(e)):e.parentNode===this._container&&this._container.removeChild(e),n.Browser.android||(e.src=n.Util.emptyImageUrl),delete this._tiles[t]},_addTile:function(t,e){var i=this._getTilePos(t),o=this._getTile();n.DomUtil.setPosition(o,i,n.Browser.chrome||n.Browser.android23),this._tiles[t.x+":"+t.y]=o,this._loadTile(o,t),o.parentNode!==this._container&&e.appendChild(o) +},_getZoomForUrl:function(){var t=this.options,e=this._map.getZoom();return t.zoomReverse&&(e=t.maxZoom-e),e+t.zoomOffset},_getTilePos:function(t){var e=this._map.getPixelOrigin(),i=this.options.tileSize;return t.multiplyBy(i).subtract(e)},getTileUrl:function(t){return this._adjustTilePoint(t),n.Util.template(this._url,n.extend({s:this._getSubdomain(t),z:this._getZoomForUrl(),x:t.x,y:t.y},this.options))},_getWrapTileNum:function(){return Math.pow(2,this._getZoomForUrl())},_adjustTilePoint:function(t){var e=this._getWrapTileNum();this.options.continuousWorld||this.options.noWrap||(t.x=(t.x%e+e)%e),this.options.tms&&(t.y=e-t.y-1)},_getSubdomain:function(t){var e=(t.x+t.y)%this.options.subdomains.length;return this.options.subdomains[e]},_createTileProto:function(){var t=this._tileImg=n.DomUtil.create("img","leaflet-tile");t.style.width=t.style.height=this.options.tileSize+"px",t.galleryimg="no"},_getTile:function(){if(this.options.reuseTiles&&this._unusedTiles.length>0){var t=this._unusedTiles.pop();return this._resetTile(t),t}return this._createTile()},_resetTile:function(){},_createTile:function(){var t=this._tileImg.cloneNode(!1);return t.onselectstart=t.onmousemove=n.Util.falseFn,t},_loadTile:function(t,e){t._layer=this,t.onload=this._tileOnLoad,t.onerror=this._tileOnError,t.src=this.getTileUrl(e)},_tileLoaded:function(){this._tilesToLoad--,this._tilesToLoad||this.fire("load")},_tileOnLoad:function(){var t=this._layer;this.src!==n.Util.emptyImageUrl&&(n.DomUtil.addClass(this,"leaflet-tile-loaded"),t.fire("tileload",{tile:this,url:this.src})),t._tileLoaded()},_tileOnError:function(){var t=this._layer;t.fire("tileerror",{tile:this,url:this.src});var e=t.options.errorTileUrl;e&&(this.src=e),t._tileLoaded()}}),n.tileLayer=function(t,e){return new n.TileLayer(t,e)},n.TileLayer.WMS=n.TileLayer.extend({defaultWmsParams:{service:"WMS",request:"GetMap",version:"1.1.1",layers:"",styles:"",format:"image/jpeg",transparent:!1},initialize:function(t,e){this._url=t;var i=n.extend({},this.defaultWmsParams);i.width=i.height=e.detectRetina&&n.Browser.retina?2*this.options.tileSize:this.options.tileSize;for(var o in e)this.options.hasOwnProperty(o)||(i[o]=e[o]);this.wmsParams=i,n.setOptions(this,e)},onAdd:function(t){var e=parseFloat(this.wmsParams.version)>=1.3?"crs":"srs";this.wmsParams[e]=t.options.crs.code,n.TileLayer.prototype.onAdd.call(this,t)},getTileUrl:function(t,e){this._adjustTilePoint(t);var i=this._map,o=i.options.crs,s=this.options.tileSize,a=t.multiplyBy(s),r=a.add(new n.Point(s,s)),h=o.project(i.unproject(a,e)),l=o.project(i.unproject(r,e)),u=[h.x,l.y,l.x,h.y].join(","),c=n.Util.template(this._url,{s:this._getSubdomain(t)});return c+n.Util.getParamString(this.wmsParams,c)+"&bbox="+u},setParams:function(t,e){return n.extend(this.wmsParams,t),e||this.redraw(),this}}),n.tileLayer.wms=function(t,e){return new n.TileLayer.WMS(t,e)},n.TileLayer.Canvas=n.TileLayer.extend({options:{async:!1},initialize:function(t){n.setOptions(this,t)},redraw:function(){var t=this._tiles;for(var e in t)t.hasOwnProperty(e)&&this._redrawTile(t[e])},_redrawTile:function(t){this.drawTile(t,t._tilePoint,this._map._zoom)},_createTileProto:function(){var t=this._canvasProto=n.DomUtil.create("canvas","leaflet-tile");t.width=t.height=this.options.tileSize},_createTile:function(){var t=this._canvasProto.cloneNode(!1);return t.onselectstart=t.onmousemove=n.Util.falseFn,t},_loadTile:function(t,e){t._layer=this,t._tilePoint=e,this._redrawTile(t),this.options.async||this.tileDrawn(t)},drawTile:function(){},tileDrawn:function(t){this._tileOnLoad.call(t)}}),n.tileLayer.canvas=function(t){return new n.TileLayer.Canvas(t)},n.ImageOverlay=n.Class.extend({includes:n.Mixin.Events,options:{opacity:1},initialize:function(t,e,i){this._url=t,this._bounds=n.latLngBounds(e),n.setOptions(this,i)},onAdd:function(t){this._map=t,this._image||this._initImage(),t._panes.overlayPane.appendChild(this._image),t.on("viewreset",this._reset,this),t.options.zoomAnimation&&n.Browser.any3d&&t.on("zoomanim",this._animateZoom,this),this._reset()},onRemove:function(t){t.getPanes().overlayPane.removeChild(this._image),t.off("viewreset",this._reset,this),t.options.zoomAnimation&&t.off("zoomanim",this._animateZoom,this)},addTo:function(t){return t.addLayer(this),this},setOpacity:function(t){return this.options.opacity=t,this._updateOpacity(),this},bringToFront:function(){return this._image&&this._map._panes.overlayPane.appendChild(this._image),this},bringToBack:function(){var t=this._map._panes.overlayPane;return this._image&&t.insertBefore(this._image,t.firstChild),this},_initImage:function(){this._image=n.DomUtil.create("img","leaflet-image-layer"),this._map.options.zoomAnimation&&n.Browser.any3d?n.DomUtil.addClass(this._image,"leaflet-zoom-animated"):n.DomUtil.addClass(this._image,"leaflet-zoom-hide"),this._updateOpacity(),n.extend(this._image,{galleryimg:"no",onselectstart:n.Util.falseFn,onmousemove:n.Util.falseFn,onload:n.bind(this._onImageLoad,this),src:this._url})},_animateZoom:function(t){var e=this._map,i=this._image,o=e.getZoomScale(t.zoom),s=this._bounds.getNorthWest(),a=this._bounds.getSouthEast(),r=e._latLngToNewLayerPoint(s,t.zoom,t.center),h=e._latLngToNewLayerPoint(a,t.zoom,t.center)._subtract(r),l=r._add(h._multiplyBy(.5*(1-1/o)));i.style[n.DomUtil.TRANSFORM]=n.DomUtil.getTranslateString(l)+" scale("+o+") "},_reset:function(){var t=this._image,e=this._map.latLngToLayerPoint(this._bounds.getNorthWest()),i=this._map.latLngToLayerPoint(this._bounds.getSouthEast())._subtract(e);n.DomUtil.setPosition(t,e),t.style.width=i.x+"px",t.style.height=i.y+"px"},_onImageLoad:function(){this.fire("load")},_updateOpacity:function(){n.DomUtil.setOpacity(this._image,this.options.opacity)}}),n.imageOverlay=function(t,e,i){return new n.ImageOverlay(t,e,i)},n.Icon=n.Class.extend({options:{className:""},initialize:function(t){n.setOptions(this,t)},createIcon:function(){return this._createIcon("icon")},createShadow:function(){return this._createIcon("shadow")},_createIcon:function(t){var e=this._getIconUrl(t);if(!e){if("icon"===t)throw Error("iconUrl not set in Icon options (see the docs).");return null}var i=this._createImg(e);return this._setIconStyles(i,t),i},_setIconStyles:function(t,e){var i,o=this.options,s=n.point(o[e+"Size"]);i="shadow"===e?n.point(o.shadowAnchor||o.iconAnchor):n.point(o.iconAnchor),!i&&s&&(i=s.divideBy(2,!0)),t.className="leaflet-marker-"+e+" "+o.className,i&&(t.style.marginLeft=-i.x+"px",t.style.marginTop=-i.y+"px"),s&&(t.style.width=s.x+"px",t.style.height=s.y+"px")},_createImg:function(t){var i;return n.Browser.ie6?(i=e.createElement("div"),i.style.filter='progid:DXImageTransform.Microsoft.AlphaImageLoader(src="'+t+'")'):(i=e.createElement("img"),i.src=t),i},_getIconUrl:function(t){return n.Browser.retina&&this.options[t+"RetinaUrl"]?this.options[t+"RetinaUrl"]:this.options[t+"Url"]}}),n.icon=function(t){return new n.Icon(t)},n.Icon.Default=n.Icon.extend({options:{iconSize:new n.Point(25,41),iconAnchor:new n.Point(12,41),popupAnchor:new n.Point(1,-34),shadowSize:new n.Point(41,41)},_getIconUrl:function(t){var e=t+"Url";if(this.options[e])return this.options[e];n.Browser.retina&&"icon"===t&&(t+="@2x");var i=n.Icon.Default.imagePath;if(!i)throw Error("Couldn't autodetect L.Icon.Default.imagePath, set it manually.");return i+"/marker-"+t+".png"}}),n.Icon.Default.imagePath=function(){var t,i,n,o,s=e.getElementsByTagName("script"),a=/\/?leaflet[\-\._]?([\w\-\._]*)\.js\??/;for(t=0,i=s.length;i>t;t++)if(n=s[t].src,o=n.match(a))return n.split(a)[0]+"/images"}(),n.Marker=n.Class.extend({includes:n.Mixin.Events,options:{icon:new n.Icon.Default,title:"",clickable:!0,draggable:!1,zIndexOffset:0,opacity:1,riseOnHover:!1,riseOffset:250},initialize:function(t,e){n.setOptions(this,e),this._latlng=n.latLng(t)},onAdd:function(t){this._map=t,t.on("viewreset",this.update,this),this._initIcon(),this.update(),t.options.zoomAnimation&&t.options.markerZoomAnimation&&t.on("zoomanim",this._animateZoom,this)},addTo:function(t){return t.addLayer(this),this},onRemove:function(t){this._removeIcon(),this.fire("remove"),t.off({viewreset:this.update,zoomanim:this._animateZoom},this),this._map=null},getLatLng:function(){return this._latlng},setLatLng:function(t){return this._latlng=n.latLng(t),this.update(),this.fire("move",{latlng:this._latlng})},setZIndexOffset:function(t){return this.options.zIndexOffset=t,this.update(),this},setIcon:function(t){return this._map&&this._removeIcon(),this.options.icon=t,this._map&&(this._initIcon(),this.update()),this},update:function(){if(this._icon){var t=this._map.latLngToLayerPoint(this._latlng).round();this._setPos(t)}return this},_initIcon:function(){var t=this.options,e=this._map,i=e.options.zoomAnimation&&e.options.markerZoomAnimation,o=i?"leaflet-zoom-animated":"leaflet-zoom-hide",s=!1;this._icon||(this._icon=t.icon.createIcon(),t.title&&(this._icon.title=t.title),this._initInteraction(),s=1>this.options.opacity,n.DomUtil.addClass(this._icon,o),t.riseOnHover&&n.DomEvent.on(this._icon,"mouseover",this._bringToFront,this).on(this._icon,"mouseout",this._resetZIndex,this)),this._shadow||(this._shadow=t.icon.createShadow(),this._shadow&&(n.DomUtil.addClass(this._shadow,o),s=1>this.options.opacity)),s&&this._updateOpacity();var a=this._map._panes;a.markerPane.appendChild(this._icon),this._shadow&&a.shadowPane.appendChild(this._shadow)},_removeIcon:function(){var t=this._map._panes;this.options.riseOnHover&&n.DomEvent.off(this._icon,"mouseover",this._bringToFront).off(this._icon,"mouseout",this._resetZIndex),t.markerPane.removeChild(this._icon),this._shadow&&t.shadowPane.removeChild(this._shadow),this._icon=this._shadow=null},_setPos:function(t){n.DomUtil.setPosition(this._icon,t),this._shadow&&n.DomUtil.setPosition(this._shadow,t),this._zIndex=t.y+this.options.zIndexOffset,this._resetZIndex()},_updateZIndex:function(t){this._icon.style.zIndex=this._zIndex+t},_animateZoom:function(t){var e=this._map._latLngToNewLayerPoint(this._latlng,t.zoom,t.center);this._setPos(e)},_initInteraction:function(){if(this.options.clickable){var t=this._icon,e=["dblclick","mousedown","mouseover","mouseout","contextmenu"];n.DomUtil.addClass(t,"leaflet-clickable"),n.DomEvent.on(t,"click",this._onMouseClick,this);for(var i=0;e.length>i;i++)n.DomEvent.on(t,e[i],this._fireMouseEvent,this);n.Handler.MarkerDrag&&(this.dragging=new n.Handler.MarkerDrag(this),this.options.draggable&&this.dragging.enable())}},_onMouseClick:function(t){var e=this.dragging&&this.dragging.moved();(this.hasEventListeners(t.type)||e)&&n.DomEvent.stopPropagation(t),e||(this.dragging&&this.dragging._enabled||!this._map.dragging||!this._map.dragging.moved())&&this.fire(t.type,{originalEvent:t})},_fireMouseEvent:function(t){this.fire(t.type,{originalEvent:t}),"contextmenu"===t.type&&this.hasEventListeners(t.type)&&n.DomEvent.preventDefault(t),"mousedown"!==t.type&&n.DomEvent.stopPropagation(t)},setOpacity:function(t){this.options.opacity=t,this._map&&this._updateOpacity()},_updateOpacity:function(){n.DomUtil.setOpacity(this._icon,this.options.opacity),this._shadow&&n.DomUtil.setOpacity(this._shadow,this.options.opacity)},_bringToFront:function(){this._updateZIndex(this.options.riseOffset)},_resetZIndex:function(){this._updateZIndex(0)}}),n.marker=function(t,e){return new n.Marker(t,e)},n.DivIcon=n.Icon.extend({options:{iconSize:new n.Point(12,12),className:"leaflet-div-icon"},createIcon:function(){var t=e.createElement("div"),i=this.options;return i.html&&(t.innerHTML=i.html),i.bgPos&&(t.style.backgroundPosition=-i.bgPos.x+"px "+-i.bgPos.y+"px"),this._setIconStyles(t,"icon"),t},createShadow:function(){return null}}),n.divIcon=function(t){return new n.DivIcon(t)},n.Map.mergeOptions({closePopupOnClick:!0}),n.Popup=n.Class.extend({includes:n.Mixin.Events,options:{minWidth:50,maxWidth:300,maxHeight:null,autoPan:!0,closeButton:!0,offset:new n.Point(0,6),autoPanPadding:new n.Point(5,5),className:"",zoomAnimation:!0},initialize:function(t,e){n.setOptions(this,t),this._source=e,this._animated=n.Browser.any3d&&this.options.zoomAnimation},onAdd:function(t){this._map=t,this._container||this._initLayout(),this._updateContent();var e=t.options.fadeAnimation;e&&n.DomUtil.setOpacity(this._container,0),t._panes.popupPane.appendChild(this._container),t.on("viewreset",this._updatePosition,this),this._animated&&t.on("zoomanim",this._zoomAnimation,this),t.options.closePopupOnClick&&t.on("preclick",this._close,this),this._update(),e&&n.DomUtil.setOpacity(this._container,1)},addTo:function(t){return t.addLayer(this),this},openOn:function(t){return t.openPopup(this),this},onRemove:function(t){t._panes.popupPane.removeChild(this._container),n.Util.falseFn(this._container.offsetWidth),t.off({viewreset:this._updatePosition,preclick:this._close,zoomanim:this._zoomAnimation},this),t.options.fadeAnimation&&n.DomUtil.setOpacity(this._container,0),this._map=null},setLatLng:function(t){return this._latlng=n.latLng(t),this._update(),this},setContent:function(t){return this._content=t,this._update(),this},_close:function(){var t=this._map;t&&(t._popup=null,t.removeLayer(this).fire("popupclose",{popup:this}))},_initLayout:function(){var t,e="leaflet-popup",i=e+" "+this.options.className+" leaflet-zoom-"+(this._animated?"animated":"hide"),o=this._container=n.DomUtil.create("div",i);this.options.closeButton&&(t=this._closeButton=n.DomUtil.create("a",e+"-close-button",o),t.href="#close",t.innerHTML="×",n.DomEvent.on(t,"click",this._onCloseButtonClick,this));var s=this._wrapper=n.DomUtil.create("div",e+"-content-wrapper",o);n.DomEvent.disableClickPropagation(s),this._contentNode=n.DomUtil.create("div",e+"-content",s),n.DomEvent.on(this._contentNode,"mousewheel",n.DomEvent.stopPropagation),this._tipContainer=n.DomUtil.create("div",e+"-tip-container",o),this._tip=n.DomUtil.create("div",e+"-tip",this._tipContainer)},_update:function(){this._map&&(this._container.style.visibility="hidden",this._updateContent(),this._updateLayout(),this._updatePosition(),this._container.style.visibility="",this._adjustPan())},_updateContent:function(){if(this._content){if("string"==typeof this._content)this._contentNode.innerHTML=this._content;else{for(;this._contentNode.hasChildNodes();)this._contentNode.removeChild(this._contentNode.firstChild);this._contentNode.appendChild(this._content)}this.fire("contentupdate")}},_updateLayout:function(){var t=this._contentNode,e=t.style;e.width="",e.whiteSpace="nowrap";var i=t.offsetWidth;i=Math.min(i,this.options.maxWidth),i=Math.max(i,this.options.minWidth),e.width=i+1+"px",e.whiteSpace="",e.height="";var o=t.offsetHeight,s=this.options.maxHeight,a="leaflet-popup-scrolled";s&&o>s?(e.height=s+"px",n.DomUtil.addClass(t,a)):n.DomUtil.removeClass(t,a),this._containerWidth=this._container.offsetWidth},_updatePosition:function(){if(this._map){var t=this._map.latLngToLayerPoint(this._latlng),e=this._animated,i=this.options.offset;e&&n.DomUtil.setPosition(this._container,t),this._containerBottom=-i.y-(e?0:t.y),this._containerLeft=-Math.round(this._containerWidth/2)+i.x+(e?0:t.x),this._container.style.bottom=this._containerBottom+"px",this._container.style.left=this._containerLeft+"px"}},_zoomAnimation:function(t){var e=this._map._latLngToNewLayerPoint(this._latlng,t.zoom,t.center);n.DomUtil.setPosition(this._container,e)},_adjustPan:function(){if(this.options.autoPan){var t=this._map,e=this._container.offsetHeight,i=this._containerWidth,o=new n.Point(this._containerLeft,-e-this._containerBottom);this._animated&&o._add(n.DomUtil.getPosition(this._container));var s=t.layerPointToContainerPoint(o),a=this.options.autoPanPadding,r=t.getSize(),h=0,l=0;0>s.x&&(h=s.x-a.x),s.x+i>r.x&&(h=s.x+i-r.x+a.x),0>s.y&&(l=s.y-a.y),s.y+e>r.y&&(l=s.y+e-r.y+a.y),(h||l)&&t.panBy(new n.Point(h,l))}},_onCloseButtonClick:function(t){this._close(),n.DomEvent.stop(t)}}),n.popup=function(t,e){return new n.Popup(t,e)},n.Marker.include({openPopup:function(){return this._popup&&this._map&&(this._popup.setLatLng(this._latlng),this._map.openPopup(this._popup)),this},closePopup:function(){return this._popup&&this._popup._close(),this},bindPopup:function(t,e){var i=n.point(this.options.icon.options.popupAnchor)||new n.Point(0,0);return i=i.add(n.Popup.prototype.options.offset),e&&e.offset&&(i=i.add(e.offset)),e=n.extend({offset:i},e),this._popup||this.on("click",this.openPopup,this).on("remove",this.closePopup,this).on("move",this._movePopup,this),this._popup=new n.Popup(e,this).setContent(t),this},unbindPopup:function(){return this._popup&&(this._popup=null,this.off("click",this.openPopup).off("remove",this.closePopup).off("move",this._movePopup)),this},_movePopup:function(t){this._popup.setLatLng(t.latlng)}}),n.Map.include({openPopup:function(t){return this.closePopup(),this._popup=t,this.addLayer(t).fire("popupopen",{popup:this._popup})},closePopup:function(){return this._popup&&this._popup._close(),this}}),n.LayerGroup=n.Class.extend({initialize:function(t){this._layers={};var e,i;if(t)for(e=0,i=t.length;i>e;e++)this.addLayer(t[e])},addLayer:function(t){var e=n.stamp(t);return this._layers[e]=t,this._map&&this._map.addLayer(t),this},removeLayer:function(t){var e=n.stamp(t);return delete this._layers[e],this._map&&this._map.removeLayer(t),this},clearLayers:function(){return this.eachLayer(this.removeLayer,this),this},invoke:function(t){var e,i,n=Array.prototype.slice.call(arguments,1);for(e in this._layers)this._layers.hasOwnProperty(e)&&(i=this._layers[e],i[t]&&i[t].apply(i,n));return this},onAdd:function(t){this._map=t,this.eachLayer(t.addLayer,t)},onRemove:function(t){this.eachLayer(t.removeLayer,t),this._map=null},addTo:function(t){return t.addLayer(this),this},eachLayer:function(t,e){for(var i in this._layers)this._layers.hasOwnProperty(i)&&t.call(e,this._layers[i])},setZIndex:function(t){return this.invoke("setZIndex",t)}}),n.layerGroup=function(t){return new n.LayerGroup(t)},n.FeatureGroup=n.LayerGroup.extend({includes:n.Mixin.Events,statics:{EVENTS:"click dblclick mouseover mouseout mousemove contextmenu"},addLayer:function(t){return this._layers[n.stamp(t)]?this:(t.on(n.FeatureGroup.EVENTS,this._propagateEvent,this),n.LayerGroup.prototype.addLayer.call(this,t),this._popupContent&&t.bindPopup&&t.bindPopup(this._popupContent,this._popupOptions),this.fire("layeradd",{layer:t}))},removeLayer:function(t){return t.off(n.FeatureGroup.EVENTS,this._propagateEvent,this),n.LayerGroup.prototype.removeLayer.call(this,t),this._popupContent&&this.invoke("unbindPopup"),this.fire("layerremove",{layer:t})},bindPopup:function(t,e){return this._popupContent=t,this._popupOptions=e,this.invoke("bindPopup",t,e)},setStyle:function(t){return this.invoke("setStyle",t)},bringToFront:function(){return this.invoke("bringToFront")},bringToBack:function(){return this.invoke("bringToBack")},getBounds:function(){var t=new n.LatLngBounds;return this.eachLayer(function(e){t.extend(e instanceof n.Marker?e.getLatLng():e.getBounds())}),t},_propagateEvent:function(t){t.layer=t.target,t.target=this,this.fire(t.type,t)}}),n.featureGroup=function(t){return new n.FeatureGroup(t)},n.Path=n.Class.extend({includes:[n.Mixin.Events],statics:{CLIP_PADDING:n.Browser.mobile?Math.max(0,Math.min(.5,(1280/Math.max(t.innerWidth,t.innerHeight)-1)/2)):.5},options:{stroke:!0,color:"#0033ff",dashArray:null,weight:5,opacity:.5,fill:!1,fillColor:null,fillOpacity:.2,clickable:!0},initialize:function(t){n.setOptions(this,t)},onAdd:function(t){this._map=t,this._container||(this._initElements(),this._initEvents()),this.projectLatlngs(),this._updatePath(),this._container&&this._map._pathRoot.appendChild(this._container),this.fire("add"),t.on({viewreset:this.projectLatlngs,moveend:this._updatePath},this)},addTo:function(t){return t.addLayer(this),this},onRemove:function(t){t._pathRoot.removeChild(this._container),this.fire("remove"),this._map=null,n.Browser.vml&&(this._container=null,this._stroke=null,this._fill=null),t.off({viewreset:this.projectLatlngs,moveend:this._updatePath},this)},projectLatlngs:function(){},setStyle:function(t){return n.setOptions(this,t),this._container&&this._updateStyle(),this},redraw:function(){return this._map&&(this.projectLatlngs(),this._updatePath()),this}}),n.Map.include({_updatePathViewport:function(){var t=n.Path.CLIP_PADDING,e=this.getSize(),i=n.DomUtil.getPosition(this._mapPane),o=i.multiplyBy(-1)._subtract(e.multiplyBy(t)._round()),s=o.add(e.multiplyBy(1+2*t)._round());this._pathViewport=new n.Bounds(o,s)}}),n.Path.SVG_NS="http://www.w3.org/2000/svg",n.Browser.svg=!(!e.createElementNS||!e.createElementNS(n.Path.SVG_NS,"svg").createSVGRect),n.Path=n.Path.extend({statics:{SVG:n.Browser.svg},bringToFront:function(){var t=this._map._pathRoot,e=this._container;return e&&t.lastChild!==e&&t.appendChild(e),this},bringToBack:function(){var t=this._map._pathRoot,e=this._container,i=t.firstChild;return e&&i!==e&&t.insertBefore(e,i),this},getPathString:function(){},_createElement:function(t){return e.createElementNS(n.Path.SVG_NS,t)},_initElements:function(){this._map._initPathRoot(),this._initPath(),this._initStyle()},_initPath:function(){this._container=this._createElement("g"),this._path=this._createElement("path"),this._container.appendChild(this._path)},_initStyle:function(){this.options.stroke&&(this._path.setAttribute("stroke-linejoin","round"),this._path.setAttribute("stroke-linecap","round")),this.options.fill&&this._path.setAttribute("fill-rule","evenodd"),this._updateStyle()},_updateStyle:function(){this.options.stroke?(this._path.setAttribute("stroke",this.options.color),this._path.setAttribute("stroke-opacity",this.options.opacity),this._path.setAttribute("stroke-width",this.options.weight),this.options.dashArray?this._path.setAttribute("stroke-dasharray",this.options.dashArray):this._path.removeAttribute("stroke-dasharray")):this._path.setAttribute("stroke","none"),this.options.fill?(this._path.setAttribute("fill",this.options.fillColor||this.options.color),this._path.setAttribute("fill-opacity",this.options.fillOpacity)):this._path.setAttribute("fill","none")},_updatePath:function(){var t=this.getPathString();t||(t="M0 0"),this._path.setAttribute("d",t)},_initEvents:function(){if(this.options.clickable){(n.Browser.svg||!n.Browser.vml)&&this._path.setAttribute("class","leaflet-clickable"),n.DomEvent.on(this._container,"click",this._onMouseClick,this);for(var t=["dblclick","mousedown","mouseover","mouseout","mousemove","contextmenu"],e=0;t.length>e;e++)n.DomEvent.on(this._container,t[e],this._fireMouseEvent,this)}},_onMouseClick:function(t){this._map.dragging&&this._map.dragging.moved()||this._fireMouseEvent(t)},_fireMouseEvent:function(t){if(this.hasEventListeners(t.type)){var e=this._map,i=e.mouseEventToContainerPoint(t),o=e.containerPointToLayerPoint(i),s=e.layerPointToLatLng(o);this.fire(t.type,{latlng:s,layerPoint:o,containerPoint:i,originalEvent:t}),"contextmenu"===t.type&&n.DomEvent.preventDefault(t),"mousemove"!==t.type&&n.DomEvent.stopPropagation(t)}}}),n.Map.include({_initPathRoot:function(){this._pathRoot||(this._pathRoot=n.Path.prototype._createElement("svg"),this._panes.overlayPane.appendChild(this._pathRoot),this.options.zoomAnimation&&n.Browser.any3d?(this._pathRoot.setAttribute("class"," leaflet-zoom-animated"),this.on({zoomanim:this._animatePathZoom,zoomend:this._endPathZoom})):this._pathRoot.setAttribute("class"," leaflet-zoom-hide"),this.on("moveend",this._updateSvgViewport),this._updateSvgViewport())},_animatePathZoom:function(t){var e=this.getZoomScale(t.zoom),i=this._getCenterOffset(t.center)._multiplyBy(-e)._add(this._pathViewport.min);this._pathRoot.style[n.DomUtil.TRANSFORM]=n.DomUtil.getTranslateString(i)+" scale("+e+") ",this._pathZooming=!0},_endPathZoom:function(){this._pathZooming=!1},_updateSvgViewport:function(){if(!this._pathZooming){this._updatePathViewport();var t=this._pathViewport,e=t.min,i=t.max,o=i.x-e.x,s=i.y-e.y,a=this._pathRoot,r=this._panes.overlayPane;n.Browser.mobileWebkit&&r.removeChild(a),n.DomUtil.setPosition(a,e),a.setAttribute("width",o),a.setAttribute("height",s),a.setAttribute("viewBox",[e.x,e.y,o,s].join(" ")),n.Browser.mobileWebkit&&r.appendChild(a)}}}),n.Path.include({bindPopup:function(t,e){return(!this._popup||e)&&(this._popup=new n.Popup(e,this)),this._popup.setContent(t),this._popupHandlersAdded||(this.on("click",this._openPopup,this).on("remove",this.closePopup,this),this._popupHandlersAdded=!0),this},unbindPopup:function(){return this._popup&&(this._popup=null,this.off("click",this._openPopup).off("remove",this.closePopup),this._popupHandlersAdded=!1),this},openPopup:function(t){return this._popup&&(t=t||this._latlng||this._latlngs[Math.floor(this._latlngs.length/2)],this._openPopup({latlng:t})),this},closePopup:function(){return this._popup&&this._popup._close(),this},_openPopup:function(t){this._popup.setLatLng(t.latlng),this._map.openPopup(this._popup)}}),n.Browser.vml=!n.Browser.svg&&function(){try{var t=e.createElement("div");t.innerHTML='';var i=t.firstChild;return i.style.behavior="url(#default#VML)",i&&"object"==typeof i.adj}catch(n){return!1}}(),n.Path=n.Browser.svg||!n.Browser.vml?n.Path:n.Path.extend({statics:{VML:!0,CLIP_PADDING:.02},_createElement:function(){try{return e.namespaces.add("lvml","urn:schemas-microsoft-com:vml"),function(t){return e.createElement("')}}catch(t){return function(t){return e.createElement("<"+t+' xmlns="urn:schemas-microsoft.com:vml" class="lvml">')}}}(),_initPath:function(){var t=this._container=this._createElement("shape");n.DomUtil.addClass(t,"leaflet-vml-shape"),this.options.clickable&&n.DomUtil.addClass(t,"leaflet-clickable"),t.coordsize="1 1",this._path=this._createElement("path"),t.appendChild(this._path),this._map._pathRoot.appendChild(t)},_initStyle:function(){this._updateStyle()},_updateStyle:function(){var t=this._stroke,e=this._fill,i=this.options,n=this._container;n.stroked=i.stroke,n.filled=i.fill,i.stroke?(t||(t=this._stroke=this._createElement("stroke"),t.endcap="round",n.appendChild(t)),t.weight=i.weight+"px",t.color=i.color,t.opacity=i.opacity,t.dashStyle=i.dashArray?i.dashArray instanceof Array?i.dashArray.join(" "):i.dashArray.replace(/ *, */g," "):""):t&&(n.removeChild(t),this._stroke=null),i.fill?(e||(e=this._fill=this._createElement("fill"),n.appendChild(e)),e.color=i.fillColor||i.color,e.opacity=i.fillOpacity):e&&(n.removeChild(e),this._fill=null)},_updatePath:function(){var t=this._container.style;t.display="none",this._path.v=this.getPathString()+" ",t.display=""}}),n.Map.include(n.Browser.svg||!n.Browser.vml?{}:{_initPathRoot:function(){if(!this._pathRoot){var t=this._pathRoot=e.createElement("div");t.className="leaflet-vml-container",this._panes.overlayPane.appendChild(t),this.on("moveend",this._updatePathViewport),this._updatePathViewport()}}}),n.Browser.canvas=function(){return!!e.createElement("canvas").getContext}(),n.Path=n.Path.SVG&&!t.L_PREFER_CANVAS||!n.Browser.canvas?n.Path:n.Path.extend({statics:{CANVAS:!0,SVG:!1},redraw:function(){return this._map&&(this.projectLatlngs(),this._requestUpdate()),this},setStyle:function(t){return n.setOptions(this,t),this._map&&(this._updateStyle(),this._requestUpdate()),this},onRemove:function(t){t.off("viewreset",this.projectLatlngs,this).off("moveend",this._updatePath,this),this.options.clickable&&this._map.off("click",this._onClick,this),this._requestUpdate(),this._map=null},_requestUpdate:function(){this._map&&!n.Path._updateRequest&&(n.Path._updateRequest=n.Util.requestAnimFrame(this._fireMapMoveEnd,this._map))},_fireMapMoveEnd:function(){n.Path._updateRequest=null,this.fire("moveend")},_initElements:function(){this._map._initPathRoot(),this._ctx=this._map._canvasCtx},_updateStyle:function(){var t=this.options;t.stroke&&(this._ctx.lineWidth=t.weight,this._ctx.strokeStyle=t.color),t.fill&&(this._ctx.fillStyle=t.fillColor||t.color)},_drawPath:function(){var t,e,i,o,s,a;for(this._ctx.beginPath(),t=0,i=this._parts.length;i>t;t++){for(e=0,o=this._parts[t].length;o>e;e++)s=this._parts[t][e],a=(0===e?"move":"line")+"To",this._ctx[a](s.x,s.y);this instanceof n.Polygon&&this._ctx.closePath()}},_checkIfEmpty:function(){return!this._parts.length},_updatePath:function(){if(!this._checkIfEmpty()){var t=this._ctx,e=this.options;this._drawPath(),t.save(),this._updateStyle(),e.fill&&(t.globalAlpha=e.fillOpacity,t.fill()),e.stroke&&(t.globalAlpha=e.opacity,t.stroke()),t.restore()}},_initEvents:function(){this.options.clickable&&this._map.on("click",this._onClick,this)},_onClick:function(t){this._containsPoint(t.layerPoint)&&this.fire("click",{latlng:t.latlng,layerPoint:t.layerPoint,containerPoint:t.containerPoint,originalEvent:t})}}),n.Map.include(n.Path.SVG&&!t.L_PREFER_CANVAS||!n.Browser.canvas?{}:{_initPathRoot:function(){var t,i=this._pathRoot;i||(i=this._pathRoot=e.createElement("canvas"),i.style.position="absolute",t=this._canvasCtx=i.getContext("2d"),t.lineCap="round",t.lineJoin="round",this._panes.overlayPane.appendChild(i),this.options.zoomAnimation&&(this._pathRoot.className="leaflet-zoom-animated",this.on("zoomanim",this._animatePathZoom),this.on("zoomend",this._endPathZoom)),this.on("moveend",this._updateCanvasViewport),this._updateCanvasViewport())},_updateCanvasViewport:function(){if(!this._pathZooming){this._updatePathViewport();var t=this._pathViewport,e=t.min,i=t.max.subtract(e),o=this._pathRoot;n.DomUtil.setPosition(o,e),o.width=i.x,o.height=i.y,o.getContext("2d").translate(-e.x,-e.y)}}}),n.LineUtil={simplify:function(t,e){if(!e||!t.length)return t.slice();var i=e*e;return t=this._reducePoints(t,i),t=this._simplifyDP(t,i)},pointToSegmentDistance:function(t,e,i){return Math.sqrt(this._sqClosestPointOnSegment(t,e,i,!0))},closestPointOnSegment:function(t,e,i){return this._sqClosestPointOnSegment(t,e,i)},_simplifyDP:function(t,e){var n=t.length,o=typeof Uint8Array!=i+""?Uint8Array:Array,s=new o(n);s[0]=s[n-1]=1,this._simplifyDPStep(t,s,e,0,n-1);var a,r=[];for(a=0;n>a;a++)s[a]&&r.push(t[a]);return r},_simplifyDPStep:function(t,e,i,n,o){var s,a,r,h=0;for(a=n+1;o-1>=a;a++)r=this._sqClosestPointOnSegment(t[a],t[n],t[o],!0),r>h&&(s=a,h=r);h>i&&(e[s]=1,this._simplifyDPStep(t,e,i,n,s),this._simplifyDPStep(t,e,i,s,o))},_reducePoints:function(t,e){for(var i=[t[0]],n=1,o=0,s=t.length;s>n;n++)this._sqDist(t[n],t[o])>e&&(i.push(t[n]),o=n);return s-1>o&&i.push(t[s-1]),i},clipSegment:function(t,e,i,n){var o,s,a,r=n?this._lastCode:this._getBitCode(t,i),h=this._getBitCode(e,i);for(this._lastCode=h;;){if(!(r|h))return[t,e];if(r&h)return!1;o=r||h,s=this._getEdgeIntersection(t,e,o,i),a=this._getBitCode(s,i),o===r?(t=s,r=a):(e=s,h=a)}},_getEdgeIntersection:function(t,e,o,s){var a=e.x-t.x,r=e.y-t.y,h=s.min,l=s.max;return 8&o?new n.Point(t.x+a*(l.y-t.y)/r,l.y):4&o?new n.Point(t.x+a*(h.y-t.y)/r,h.y):2&o?new n.Point(l.x,t.y+r*(l.x-t.x)/a):1&o?new n.Point(h.x,t.y+r*(h.x-t.x)/a):i},_getBitCode:function(t,e){var i=0;return t.xe.max.x&&(i|=2),t.ye.max.y&&(i|=8),i},_sqDist:function(t,e){var i=e.x-t.x,n=e.y-t.y;return i*i+n*n},_sqClosestPointOnSegment:function(t,e,i,o){var s,a=e.x,r=e.y,h=i.x-a,l=i.y-r,u=h*h+l*l;return u>0&&(s=((t.x-a)*h+(t.y-r)*l)/u,s>1?(a=i.x,r=i.y):s>0&&(a+=h*s,r+=l*s)),h=t.x-a,l=t.y-r,o?h*h+l*l:new n.Point(a,r)}},n.Polyline=n.Path.extend({initialize:function(t,e){n.Path.prototype.initialize.call(this,e),this._latlngs=this._convertLatLngs(t)},options:{smoothFactor:1,noClip:!1},projectLatlngs:function(){this._originalPoints=[];for(var t=0,e=this._latlngs.length;e>t;t++)this._originalPoints[t]=this._map.latLngToLayerPoint(this._latlngs[t])},getPathString:function(){for(var t=0,e=this._parts.length,i="";e>t;t++)i+=this._getPathPartStr(this._parts[t]);return i},getLatLngs:function(){return this._latlngs},setLatLngs:function(t){return this._latlngs=this._convertLatLngs(t),this.redraw()},addLatLng:function(t){return this._latlngs.push(n.latLng(t)),this.redraw()},spliceLatLngs:function(){var t=[].splice.apply(this._latlngs,arguments);return this._convertLatLngs(this._latlngs),this.redraw(),t},closestLayerPoint:function(t){for(var e,i,o=1/0,s=this._parts,a=null,r=0,h=s.length;h>r;r++)for(var l=s[r],u=1,c=l.length;c>u;u++){e=l[u-1],i=l[u]; +var _=n.LineUtil._sqClosestPointOnSegment(t,e,i,!0);o>_&&(o=_,a=n.LineUtil._sqClosestPointOnSegment(t,e,i))}return a&&(a.distance=Math.sqrt(o)),a},getBounds:function(){var t,e,i=new n.LatLngBounds,o=this.getLatLngs();for(t=0,e=o.length;e>t;t++)i.extend(o[t]);return i},_convertLatLngs:function(t){var e,i;for(e=0,i=t.length;i>e;e++){if(n.Util.isArray(t[e])&&"number"!=typeof t[e][0])return;t[e]=n.latLng(t[e])}return t},_initEvents:function(){n.Path.prototype._initEvents.call(this)},_getPathPartStr:function(t){for(var e,i=n.Path.VML,o=0,s=t.length,a="";s>o;o++)e=t[o],i&&e._round(),a+=(o?"L":"M")+e.x+" "+e.y;return a},_clipPoints:function(){var t,e,o,s=this._originalPoints,a=s.length;if(this.options.noClip)return this._parts=[s],i;this._parts=[];var r=this._parts,h=this._map._pathViewport,l=n.LineUtil;for(t=0,e=0;a-1>t;t++)o=l.clipSegment(s[t],s[t+1],h,t),o&&(r[e]=r[e]||[],r[e].push(o[0]),(o[1]!==s[t+1]||t===a-2)&&(r[e].push(o[1]),e++))},_simplifyPoints:function(){for(var t=this._parts,e=n.LineUtil,i=0,o=t.length;o>i;i++)t[i]=e.simplify(t[i],this.options.smoothFactor)},_updatePath:function(){this._map&&(this._clipPoints(),this._simplifyPoints(),n.Path.prototype._updatePath.call(this))}}),n.polyline=function(t,e){return new n.Polyline(t,e)},n.PolyUtil={},n.PolyUtil.clipPolygon=function(t,e){var i,o,s,a,r,h,l,u,c,_=[1,4,2,8],d=n.LineUtil;for(o=0,l=t.length;l>o;o++)t[o]._code=d._getBitCode(t[o],e);for(a=0;4>a;a++){for(u=_[a],i=[],o=0,l=t.length,s=l-1;l>o;s=o++)r=t[o],h=t[s],r._code&u?h._code&u||(c=d._getEdgeIntersection(h,r,u,e),c._code=d._getBitCode(c,e),i.push(c)):(h._code&u&&(c=d._getEdgeIntersection(h,r,u,e),c._code=d._getBitCode(c,e),i.push(c)),i.push(r));t=i}return t},n.Polygon=n.Polyline.extend({options:{fill:!0},initialize:function(t,e){n.Polyline.prototype.initialize.call(this,t,e),t&&n.Util.isArray(t[0])&&"number"!=typeof t[0][0]&&(this._latlngs=this._convertLatLngs(t[0]),this._holes=t.slice(1))},projectLatlngs:function(){if(n.Polyline.prototype.projectLatlngs.call(this),this._holePoints=[],this._holes){var t,e,i,o;for(t=0,i=this._holes.length;i>t;t++)for(this._holePoints[t]=[],e=0,o=this._holes[t].length;o>e;e++)this._holePoints[t][e]=this._map.latLngToLayerPoint(this._holes[t][e])}},_clipPoints:function(){var t=this._originalPoints,e=[];if(this._parts=[t].concat(this._holePoints),!this.options.noClip){for(var i=0,o=this._parts.length;o>i;i++){var s=n.PolyUtil.clipPolygon(this._parts[i],this._map._pathViewport);s.length&&e.push(s)}this._parts=e}},_getPathPartStr:function(t){var e=n.Polyline.prototype._getPathPartStr.call(this,t);return e+(n.Browser.svg?"z":"x")}}),n.polygon=function(t,e){return new n.Polygon(t,e)},function(){function t(t){return n.FeatureGroup.extend({initialize:function(t,e){this._layers={},this._options=e,this.setLatLngs(t)},setLatLngs:function(e){var i=0,n=e.length;for(this.eachLayer(function(t){n>i?t.setLatLngs(e[i++]):this.removeLayer(t)},this);n>i;)this.addLayer(new t(e[i++],this._options));return this}})}n.MultiPolyline=t(n.Polyline),n.MultiPolygon=t(n.Polygon),n.multiPolyline=function(t,e){return new n.MultiPolyline(t,e)},n.multiPolygon=function(t,e){return new n.MultiPolygon(t,e)}}(),n.Rectangle=n.Polygon.extend({initialize:function(t,e){n.Polygon.prototype.initialize.call(this,this._boundsToLatLngs(t),e)},setBounds:function(t){this.setLatLngs(this._boundsToLatLngs(t))},_boundsToLatLngs:function(t){return t=n.latLngBounds(t),[t.getSouthWest(),t.getNorthWest(),t.getNorthEast(),t.getSouthEast()]}}),n.rectangle=function(t,e){return new n.Rectangle(t,e)},n.Circle=n.Path.extend({initialize:function(t,e,i){n.Path.prototype.initialize.call(this,i),this._latlng=n.latLng(t),this._mRadius=e},options:{fill:!0},setLatLng:function(t){return this._latlng=n.latLng(t),this.redraw()},setRadius:function(t){return this._mRadius=t,this.redraw()},projectLatlngs:function(){var t=this._getLngRadius(),e=new n.LatLng(this._latlng.lat,this._latlng.lng-t),i=this._map.latLngToLayerPoint(e);this._point=this._map.latLngToLayerPoint(this._latlng),this._radius=Math.max(Math.round(this._point.x-i.x),1)},getBounds:function(){var t=this._getLngRadius(),e=360*(this._mRadius/40075017),i=this._latlng,o=new n.LatLng(i.lat-e,i.lng-t),s=new n.LatLng(i.lat+e,i.lng+t);return new n.LatLngBounds(o,s)},getLatLng:function(){return this._latlng},getPathString:function(){var t=this._point,e=this._radius;return this._checkIfEmpty()?"":n.Browser.svg?"M"+t.x+","+(t.y-e)+"A"+e+","+e+",0,1,1,"+(t.x-.1)+","+(t.y-e)+" z":(t._round(),e=Math.round(e),"AL "+t.x+","+t.y+" "+e+","+e+" 0,"+23592600)},getRadius:function(){return this._mRadius},_getLatRadius:function(){return 360*(this._mRadius/40075017)},_getLngRadius:function(){return this._getLatRadius()/Math.cos(n.LatLng.DEG_TO_RAD*this._latlng.lat)},_checkIfEmpty:function(){if(!this._map)return!1;var t=this._map._pathViewport,e=this._radius,i=this._point;return i.x-e>t.max.x||i.y-e>t.max.y||i.x+ei;i++)for(l=this._parts[i],o=0,r=l.length,s=r-1;r>o;s=o++)if((e||0!==o)&&(h=n.LineUtil.pointToSegmentDistance(t,l[s],l[o]),u>=h))return!0;return!1}}:{}),n.Polygon.include(n.Path.CANVAS?{_containsPoint:function(t){var e,i,o,s,a,r,h,l,u=!1;if(n.Polyline.prototype._containsPoint.call(this,t,!0))return!0;for(s=0,h=this._parts.length;h>s;s++)for(e=this._parts[s],a=0,l=e.length,r=l-1;l>a;r=a++)i=e[a],o=e[r],i.y>t.y!=o.y>t.y&&t.x<(o.x-i.x)*(t.y-i.y)/(o.y-i.y)+i.x&&(u=!u);return u}}:{}),n.Circle.include(n.Path.CANVAS?{_drawPath:function(){var t=this._point;this._ctx.beginPath(),this._ctx.arc(t.x,t.y,this._radius,0,2*Math.PI,!1)},_containsPoint:function(t){var e=this._point,i=this.options.stroke?this.options.weight/2:0;return t.distanceTo(e)<=this._radius+i}}:{}),n.GeoJSON=n.FeatureGroup.extend({initialize:function(t,e){n.setOptions(this,e),this._layers={},t&&this.addData(t)},addData:function(t){var e,i,o=n.Util.isArray(t)?t:t.features;if(o){for(e=0,i=o.length;i>e;e++)(o[e].geometries||o[e].geometry)&&this.addData(o[e]);return this}var s=this.options;if(!s.filter||s.filter(t)){var a=n.GeoJSON.geometryToLayer(t,s.pointToLayer);return a.feature=t,a.defaultOptions=a.options,this.resetStyle(a),s.onEachFeature&&s.onEachFeature(t,a),this.addLayer(a)}},resetStyle:function(t){var e=this.options.style;e&&(n.Util.extend(t.options,t.defaultOptions),this._setLayerStyle(t,e))},setStyle:function(t){this.eachLayer(function(e){this._setLayerStyle(e,t)},this)},_setLayerStyle:function(t,e){"function"==typeof e&&(e=e(t.feature)),t.setStyle&&t.setStyle(e)}}),n.extend(n.GeoJSON,{geometryToLayer:function(t,e){var i,o,s,a,r,h="Feature"===t.type?t.geometry:t,l=h.coordinates,u=[];switch(h.type){case"Point":return i=this.coordsToLatLng(l),e?e(t,i):new n.Marker(i);case"MultiPoint":for(s=0,a=l.length;a>s;s++)i=this.coordsToLatLng(l[s]),r=e?e(t,i):new n.Marker(i),u.push(r);return new n.FeatureGroup(u);case"LineString":return o=this.coordsToLatLngs(l),new n.Polyline(o);case"Polygon":return o=this.coordsToLatLngs(l,1),new n.Polygon(o);case"MultiLineString":return o=this.coordsToLatLngs(l,1),new n.MultiPolyline(o);case"MultiPolygon":return o=this.coordsToLatLngs(l,2),new n.MultiPolygon(o);case"GeometryCollection":for(s=0,a=h.geometries.length;a>s;s++)r=this.geometryToLayer({geometry:h.geometries[s],type:"Feature",properties:t.properties},e),u.push(r);return new n.FeatureGroup(u);default:throw Error("Invalid GeoJSON object.")}},coordsToLatLng:function(t,e){var i=parseFloat(t[e?0:1]),o=parseFloat(t[e?1:0]);return new n.LatLng(i,o)},coordsToLatLngs:function(t,e,i){var n,o,s,a=[];for(o=0,s=t.length;s>o;o++)n=e?this.coordsToLatLngs(t[o],e-1,i):this.coordsToLatLng(t[o],i),a.push(n);return a}}),n.geoJson=function(t,e){return new n.GeoJSON(t,e)},n.DomEvent={addListener:function(t,e,o,s){var a,r,h,l=n.stamp(o),u="_leaflet_"+e+l;return t[u]?this:(a=function(e){return o.call(s||t,e||n.DomEvent._getEvent())},n.Browser.msTouch&&0===e.indexOf("touch")?this.addMsTouchListener(t,e,a,l):(n.Browser.touch&&"dblclick"===e&&this.addDoubleTapListener&&this.addDoubleTapListener(t,a,l),"addEventListener"in t?"mousewheel"===e?(t.addEventListener("DOMMouseScroll",a,!1),t.addEventListener(e,a,!1)):"mouseenter"===e||"mouseleave"===e?(r=a,h="mouseenter"===e?"mouseover":"mouseout",a=function(e){return n.DomEvent._checkMouse(t,e)?r(e):i},t.addEventListener(h,a,!1)):t.addEventListener(e,a,!1):"attachEvent"in t&&t.attachEvent("on"+e,a),t[u]=a,this))},removeListener:function(t,e,i){var o=n.stamp(i),s="_leaflet_"+e+o,a=t[s];if(a)return n.Browser.msTouch&&0===e.indexOf("touch")?this.removeMsTouchListener(t,e,o):n.Browser.touch&&"dblclick"===e&&this.removeDoubleTapListener?this.removeDoubleTapListener(t,o):"removeEventListener"in t?"mousewheel"===e?(t.removeEventListener("DOMMouseScroll",a,!1),t.removeEventListener(e,a,!1)):"mouseenter"===e||"mouseleave"===e?t.removeEventListener("mouseenter"===e?"mouseover":"mouseout",a,!1):t.removeEventListener(e,a,!1):"detachEvent"in t&&t.detachEvent("on"+e,a),t[s]=null,this},stopPropagation:function(t){return t.stopPropagation?t.stopPropagation():t.cancelBubble=!0,this},disableClickPropagation:function(t){for(var e=n.DomEvent.stopPropagation,i=n.Draggable.START.length-1;i>=0;i--)n.DomEvent.addListener(t,n.Draggable.START[i],e);return n.DomEvent.addListener(t,"click",e).addListener(t,"dblclick",e)},preventDefault:function(t){return t.preventDefault?t.preventDefault():t.returnValue=!1,this},stop:function(t){return n.DomEvent.preventDefault(t).stopPropagation(t)},getMousePosition:function(t,i){var o=e.body,s=e.documentElement,a=t.pageX?t.pageX:t.clientX+o.scrollLeft+s.scrollLeft,r=t.pageY?t.pageY:t.clientY+o.scrollTop+s.scrollTop,h=new n.Point(a,r);return i?h._subtract(n.DomUtil.getViewportOffset(i)):h},getWheelDelta:function(t){var e=0;return t.wheelDelta&&(e=t.wheelDelta/120),t.detail&&(e=-t.detail/3),e},_checkMouse:function(t,e){var i=e.relatedTarget;if(!i)return!0;try{for(;i&&i!==t;)i=i.parentNode}catch(n){return!1}return i!==t},_getEvent:function(){var e=t.event;if(!e)for(var i=arguments.callee.caller;i&&(e=i.arguments[0],!e||t.Event!==e.constructor);)i=i.caller;return e}},n.DomEvent.on=n.DomEvent.addListener,n.DomEvent.off=n.DomEvent.removeListener,n.Draggable=n.Class.extend({includes:n.Mixin.Events,statics:{START:n.Browser.touch?["touchstart","mousedown"]:["mousedown"],END:{mousedown:"mouseup",touchstart:"touchend",MSPointerDown:"touchend"},MOVE:{mousedown:"mousemove",touchstart:"touchmove",MSPointerDown:"touchmove"},TAP_TOLERANCE:15},initialize:function(t,e,i){this._element=t,this._dragStartTarget=e||t,this._longPress=i&&!n.Browser.msTouch},enable:function(){if(!this._enabled){for(var t=n.Draggable.START.length-1;t>=0;t--)n.DomEvent.on(this._dragStartTarget,n.Draggable.START[t],this._onDown,this);this._enabled=!0}},disable:function(){if(this._enabled){for(var t=n.Draggable.START.length-1;t>=0;t--)n.DomEvent.off(this._dragStartTarget,n.Draggable.START[t],this._onDown,this);this._enabled=!1,this._moved=!1}},_onDown:function(t){if(!(!n.Browser.touch&&t.shiftKey||1!==t.which&&1!==t.button&&!t.touches||(n.DomEvent.preventDefault(t),n.DomEvent.stopPropagation(t),n.Draggable._disabled))){if(this._simulateClick=!0,t.touches&&t.touches.length>1)return this._simulateClick=!1,clearTimeout(this._longPressTimeout),i;var o=t.touches&&1===t.touches.length?t.touches[0]:t,s=o.target;n.Browser.touch&&"a"===s.tagName.toLowerCase()&&n.DomUtil.addClass(s,"leaflet-active"),this._moved=!1,this._moving||(this._startPoint=new n.Point(o.clientX,o.clientY),this._startPos=this._newPos=n.DomUtil.getPosition(this._element),t.touches&&1===t.touches.length&&n.Browser.touch&&this._longPress&&(this._longPressTimeout=setTimeout(n.bind(function(){var t=this._newPos&&this._newPos.distanceTo(this._startPos)||0;n.Draggable.TAP_TOLERANCE>t&&(this._simulateClick=!1,this._onUp(),this._simulateEvent("contextmenu",o))},this),1e3)),n.DomEvent.on(e,n.Draggable.MOVE[t.type],this._onMove,this),n.DomEvent.on(e,n.Draggable.END[t.type],this._onUp,this))}},_onMove:function(t){if(!(t.touches&&t.touches.length>1)){var e=t.touches&&1===t.touches.length?t.touches[0]:t,i=new n.Point(e.clientX,e.clientY),o=i.subtract(this._startPoint);(o.x||o.y)&&(n.DomEvent.preventDefault(t),this._moved||(this.fire("dragstart"),this._moved=!0,this._startPos=n.DomUtil.getPosition(this._element).subtract(o),n.Browser.touch||(n.DomUtil.disableTextSelection(),this._setMovingCursor())),this._newPos=this._startPos.add(o),this._moving=!0,n.Util.cancelAnimFrame(this._animRequest),this._animRequest=n.Util.requestAnimFrame(this._updatePosition,this,!0,this._dragStartTarget))}},_updatePosition:function(){this.fire("predrag"),n.DomUtil.setPosition(this._element,this._newPos),this.fire("drag")},_onUp:function(t){var i;if(clearTimeout(this._longPressTimeout),this._simulateClick&&t.changedTouches){var o=t.changedTouches[0],s=o.target,a=this._newPos&&this._newPos.distanceTo(this._startPos)||0;"a"===s.tagName.toLowerCase()&&n.DomUtil.removeClass(s,"leaflet-active"),n.Draggable.TAP_TOLERANCE>a&&(i=o)}n.Browser.touch||(n.DomUtil.enableTextSelection(),this._restoreCursor());for(var r in n.Draggable.MOVE)n.Draggable.MOVE.hasOwnProperty(r)&&(n.DomEvent.off(e,n.Draggable.MOVE[r],this._onMove),n.DomEvent.off(e,n.Draggable.END[r],this._onUp));this._moved&&(n.Util.cancelAnimFrame(this._animRequest),this.fire("dragend")),this._moving=!1,i&&(this._moved=!1,this._simulateEvent("click",i))},_setMovingCursor:function(){n.DomUtil.addClass(e.body,"leaflet-dragging")},_restoreCursor:function(){n.DomUtil.removeClass(e.body,"leaflet-dragging")},_simulateEvent:function(i,n){var o=e.createEvent("MouseEvents");o.initMouseEvent(i,!0,!0,t,1,n.screenX,n.screenY,n.clientX,n.clientY,!1,!1,!1,!1,0,null),n.target.dispatchEvent(o)}}),n.Handler=n.Class.extend({initialize:function(t){this._map=t},enable:function(){this._enabled||(this._enabled=!0,this.addHooks())},disable:function(){this._enabled&&(this._enabled=!1,this.removeHooks())},enabled:function(){return!!this._enabled}}),n.Map.mergeOptions({dragging:!0,inertia:!n.Browser.android23,inertiaDeceleration:3400,inertiaMaxSpeed:1/0,inertiaThreshold:n.Browser.touch?32:18,easeLinearity:.25,longPress:!0,worldCopyJump:!1}),n.Map.Drag=n.Handler.extend({addHooks:function(){if(!this._draggable){var t=this._map;this._draggable=new n.Draggable(t._mapPane,t._container,t.options.longPress),this._draggable.on({dragstart:this._onDragStart,drag:this._onDrag,dragend:this._onDragEnd},this),t.options.worldCopyJump&&(this._draggable.on("predrag",this._onPreDrag,this),t.on("viewreset",this._onViewReset,this))}this._draggable.enable()},removeHooks:function(){this._draggable.disable()},moved:function(){return this._draggable&&this._draggable._moved},_onDragStart:function(){var t=this._map;t._panAnim&&t._panAnim.stop(),t.fire("movestart").fire("dragstart"),t.options.inertia&&(this._positions=[],this._times=[])},_onDrag:function(){if(this._map.options.inertia){var t=this._lastTime=+new Date,e=this._lastPos=this._draggable._newPos;this._positions.push(e),this._times.push(t),t-this._times[0]>200&&(this._positions.shift(),this._times.shift())}this._map.fire("move").fire("drag")},_onViewReset:function(){var t=this._map.getSize()._divideBy(2),e=this._map.latLngToLayerPoint(new n.LatLng(0,0));this._initialWorldOffset=e.subtract(t).x,this._worldWidth=this._map.project(new n.LatLng(0,180)).x},_onPreDrag:function(){var t=this._worldWidth,e=Math.round(t/2),i=this._initialWorldOffset,n=this._draggable._newPos.x,o=(n-e+i)%t+e-i,s=(n+e+i)%t-e-i,a=Math.abs(o+i)e.inertiaThreshold||!this._positions[0];if(o)t.fire("moveend");else{var s=this._lastPos.subtract(this._positions[0]),a=(this._lastTime+i-this._times[0])/1e3,r=e.easeLinearity,h=s.multiplyBy(r/a),l=h.distanceTo(new n.Point(0,0)),u=Math.min(e.inertiaMaxSpeed,l),c=h.multiplyBy(u/l),_=u/(e.inertiaDeceleration*r),d=c.multiplyBy(-_/2).round();n.Util.requestAnimFrame(function(){t.panBy(d,_,r)})}t.fire("dragend"),e.maxBounds&&n.Util.requestAnimFrame(this._panInsideMaxBounds,t,!0,t._container)},_panInsideMaxBounds:function(){this.panInsideBounds(this.options.maxBounds)}}),n.Map.addInitHook("addHandler","dragging",n.Map.Drag),n.Map.mergeOptions({doubleClickZoom:!0}),n.Map.DoubleClickZoom=n.Handler.extend({addHooks:function(){this._map.on("dblclick",this._onDoubleClick)},removeHooks:function(){this._map.off("dblclick",this._onDoubleClick)},_onDoubleClick:function(t){this.setView(t.latlng,this._zoom+1)}}),n.Map.addInitHook("addHandler","doubleClickZoom",n.Map.DoubleClickZoom),n.Map.mergeOptions({scrollWheelZoom:!0}),n.Map.ScrollWheelZoom=n.Handler.extend({addHooks:function(){n.DomEvent.on(this._map._container,"mousewheel",this._onWheelScroll,this),this._delta=0},removeHooks:function(){n.DomEvent.off(this._map._container,"mousewheel",this._onWheelScroll)},_onWheelScroll:function(t){var e=n.DomEvent.getWheelDelta(t);this._delta+=e,this._lastMousePos=this._map.mouseEventToContainerPoint(t),this._startTime||(this._startTime=+new Date);var i=Math.max(40-(+new Date-this._startTime),0);clearTimeout(this._timer),this._timer=setTimeout(n.bind(this._performZoom,this),i),n.DomEvent.preventDefault(t),n.DomEvent.stopPropagation(t)},_performZoom:function(){var t=this._map,e=this._delta,i=t.getZoom();if(e=e>0?Math.ceil(e):Math.round(e),e=Math.max(Math.min(e,4),-4),e=t._limitZoom(i+e)-i,this._delta=0,this._startTime=null,e){var n=i+e,o=this._getCenterForScrollWheelZoom(n);t.setView(o,n)}},_getCenterForScrollWheelZoom:function(t){var e=this._map,i=e.getZoomScale(t),n=e.getSize()._divideBy(2),o=this._lastMousePos._subtract(n)._multiplyBy(1-1/i),s=e._getTopLeftPoint()._add(n)._add(o);return e.unproject(s)}}),n.Map.addInitHook("addHandler","scrollWheelZoom",n.Map.ScrollWheelZoom),n.extend(n.DomEvent,{_touchstart:n.Browser.msTouch?"MSPointerDown":"touchstart",_touchend:n.Browser.msTouch?"MSPointerUp":"touchend",addDoubleTapListener:function(t,i,o){function s(t){var e;if(n.Browser.msTouch?(p.push(t.pointerId),e=p.length):e=t.touches.length,!(e>1)){var i=Date.now(),o=i-(r||i);h=t.touches?t.touches[0]:t,l=o>0&&u>=o,r=i}}function a(t){if(n.Browser.msTouch){var e=p.indexOf(t.pointerId);if(-1===e)return;p.splice(e,1)}if(l){if(n.Browser.msTouch){var o,s={};for(var a in h)o=h[a],s[a]="function"==typeof o?o.bind(h):o;h=s}h.type="dblclick",i(h),r=null}}var r,h,l=!1,u=250,c="_leaflet_",_=this._touchstart,d=this._touchend,p=[];t[c+_+o]=s,t[c+d+o]=a;var m=n.Browser.msTouch?e.documentElement:t;return t.addEventListener(_,s,!1),m.addEventListener(d,a,!1),n.Browser.msTouch&&m.addEventListener("MSPointerCancel",a,!1),this},removeDoubleTapListener:function(t,i){var o="_leaflet_";return t.removeEventListener(this._touchstart,t[o+this._touchstart+i],!1),(n.Browser.msTouch?e.documentElement:t).removeEventListener(this._touchend,t[o+this._touchend+i],!1),n.Browser.msTouch&&e.documentElement.removeEventListener("MSPointerCancel",t[o+this._touchend+i],!1),this}}),n.extend(n.DomEvent,{_msTouches:[],_msDocumentListener:!1,addMsTouchListener:function(t,e,i,n){switch(e){case"touchstart":return this.addMsTouchListenerStart(t,e,i,n);case"touchend":return this.addMsTouchListenerEnd(t,e,i,n);case"touchmove":return this.addMsTouchListenerMove(t,e,i,n);default:throw"Unknown touch event type"}},addMsTouchListenerStart:function(t,i,n,o){var s="_leaflet_",a=this._msTouches,r=function(t){for(var e=!1,i=0;a.length>i;i++)if(a[i].pointerId===t.pointerId){e=!0;break}e||a.push(t),t.touches=a.slice(),t.changedTouches=[t],n(t)};if(t[s+"touchstart"+o]=r,t.addEventListener("MSPointerDown",r,!1),!this._msDocumentListener){var h=function(t){for(var e=0;a.length>e;e++)if(a[e].pointerId===t.pointerId){a.splice(e,1);break}};e.documentElement.addEventListener("MSPointerUp",h,!1),e.documentElement.addEventListener("MSPointerCancel",h,!1),this._msDocumentListener=!0}return this},addMsTouchListenerMove:function(t,e,i,n){function o(t){if(t.pointerType!==t.MSPOINTER_TYPE_MOUSE||0!==t.buttons){for(var e=0;a.length>e;e++)if(a[e].pointerId===t.pointerId){a[e]=t;break}t.touches=a.slice(),t.changedTouches=[t],i(t)}}var s="_leaflet_",a=this._msTouches;return t[s+"touchmove"+n]=o,t.addEventListener("MSPointerMove",o,!1),this},addMsTouchListenerEnd:function(t,e,i,n){var o="_leaflet_",s=this._msTouches,a=function(t){for(var e=0;s.length>e;e++)if(s[e].pointerId===t.pointerId){s.splice(e,1);break}t.touches=s.slice(),t.changedTouches=[t],i(t)};return t[o+"touchend"+n]=a,t.addEventListener("MSPointerUp",a,!1),t.addEventListener("MSPointerCancel",a,!1),this},removeMsTouchListener:function(t,e,i){var n="_leaflet_",o=t[n+e+i];switch(e){case"touchstart":t.removeEventListener("MSPointerDown",o,!1);break;case"touchmove":t.removeEventListener("MSPointerMove",o,!1);break;case"touchend":t.removeEventListener("MSPointerUp",o,!1),t.removeEventListener("MSPointerCancel",o,!1)}return this}}),n.Map.mergeOptions({touchZoom:n.Browser.touch&&!n.Browser.android23}),n.Map.TouchZoom=n.Handler.extend({addHooks:function(){n.DomEvent.on(this._map._container,"touchstart",this._onTouchStart,this)},removeHooks:function(){n.DomEvent.off(this._map._container,"touchstart",this._onTouchStart,this)},_onTouchStart:function(t){var i=this._map;if(t.touches&&2===t.touches.length&&!i._animatingZoom&&!this._zooming){var o=i.mouseEventToLayerPoint(t.touches[0]),s=i.mouseEventToLayerPoint(t.touches[1]),a=i._getCenterLayerPoint();this._startCenter=o.add(s)._divideBy(2),this._startDist=o.distanceTo(s),this._moved=!1,this._zooming=!0,this._centerOffset=a.subtract(this._startCenter),i._panAnim&&i._panAnim.stop(),n.DomEvent.on(e,"touchmove",this._onTouchMove,this).on(e,"touchend",this._onTouchEnd,this),n.DomEvent.preventDefault(t)}},_onTouchMove:function(t){if(t.touches&&2===t.touches.length){var e=this._map,i=e.mouseEventToLayerPoint(t.touches[0]),o=e.mouseEventToLayerPoint(t.touches[1]);this._scale=i.distanceTo(o)/this._startDist,this._delta=i._add(o)._divideBy(2)._subtract(this._startCenter),1!==this._scale&&(this._moved||(n.DomUtil.addClass(e._mapPane,"leaflet-zoom-anim leaflet-touching"),e.fire("movestart").fire("zoomstart")._prepareTileBg(),this._moved=!0),n.Util.cancelAnimFrame(this._animRequest),this._animRequest=n.Util.requestAnimFrame(this._updateOnMove,this,!0,this._map._container),n.DomEvent.preventDefault(t))}},_updateOnMove:function(){var t=this._map,e=this._getScaleOrigin(),i=t.layerPointToLatLng(e);t.fire("zoomanim",{center:i,zoom:t.getScaleZoom(this._scale)}),t._tileBg.style[n.DomUtil.TRANSFORM]=n.DomUtil.getTranslateString(this._delta)+" "+n.DomUtil.getScaleString(this._scale,this._startCenter)},_onTouchEnd:function(){if(this._moved&&this._zooming){var t=this._map;this._zooming=!1,n.DomUtil.removeClass(t._mapPane,"leaflet-touching"),n.DomEvent.off(e,"touchmove",this._onTouchMove).off(e,"touchend",this._onTouchEnd);var i=this._getScaleOrigin(),o=t.layerPointToLatLng(i),s=t.getZoom(),a=t.getScaleZoom(this._scale)-s,r=a>0?Math.ceil(a):Math.floor(a),h=t._limitZoom(s+r);t.fire("zoomanim",{center:o,zoom:h}),t._runAnimation(o,h,t.getZoomScale(h)/this._scale,i,!0)}},_getScaleOrigin:function(){var t=this._centerOffset.subtract(this._delta).divideBy(this._scale);return this._startCenter.add(t)}}),n.Map.addInitHook("addHandler","touchZoom",n.Map.TouchZoom),n.Map.mergeOptions({boxZoom:!0}),n.Map.BoxZoom=n.Handler.extend({initialize:function(t){this._map=t,this._container=t._container,this._pane=t._panes.overlayPane},addHooks:function(){n.DomEvent.on(this._container,"mousedown",this._onMouseDown,this)},removeHooks:function(){n.DomEvent.off(this._container,"mousedown",this._onMouseDown)},_onMouseDown:function(t){return!t.shiftKey||1!==t.which&&1!==t.button?!1:(n.DomUtil.disableTextSelection(),this._startLayerPoint=this._map.mouseEventToLayerPoint(t),this._box=n.DomUtil.create("div","leaflet-zoom-box",this._pane),n.DomUtil.setPosition(this._box,this._startLayerPoint),this._container.style.cursor="crosshair",n.DomEvent.on(e,"mousemove",this._onMouseMove,this).on(e,"mouseup",this._onMouseUp,this).preventDefault(t),this._map.fire("boxzoomstart"),i)},_onMouseMove:function(t){var e=this._startLayerPoint,i=this._box,o=this._map.mouseEventToLayerPoint(t),s=o.subtract(e),a=new n.Point(Math.min(o.x,e.x),Math.min(o.y,e.y));n.DomUtil.setPosition(i,a),i.style.width=Math.max(0,Math.abs(s.x)-4)+"px",i.style.height=Math.max(0,Math.abs(s.y)-4)+"px"},_onMouseUp:function(t){this._pane.removeChild(this._box),this._container.style.cursor="",n.DomUtil.enableTextSelection(),n.DomEvent.off(e,"mousemove",this._onMouseMove).off(e,"mouseup",this._onMouseUp);var i=this._map,o=i.mouseEventToLayerPoint(t);if(!this._startLayerPoint.equals(o)){var s=new n.LatLngBounds(i.layerPointToLatLng(this._startLayerPoint),i.layerPointToLatLng(o));i.fitBounds(s),i.fire("boxzoomend",{boxZoomBounds:s})}}}),n.Map.addInitHook("addHandler","boxZoom",n.Map.BoxZoom),n.Map.mergeOptions({keyboard:!0,keyboardPanOffset:80,keyboardZoomOffset:1}),n.Map.Keyboard=n.Handler.extend({keyCodes:{left:[37],right:[39],down:[40],up:[38],zoomIn:[187,107,61],zoomOut:[189,109]},initialize:function(t){this._map=t,this._setPanOffset(t.options.keyboardPanOffset),this._setZoomOffset(t.options.keyboardZoomOffset)},addHooks:function(){var t=this._map._container;-1===t.tabIndex&&(t.tabIndex="0"),n.DomEvent.on(t,"focus",this._onFocus,this).on(t,"blur",this._onBlur,this).on(t,"mousedown",this._onMouseDown,this),this._map.on("focus",this._addHooks,this).on("blur",this._removeHooks,this)},removeHooks:function(){this._removeHooks();var t=this._map._container;n.DomEvent.off(t,"focus",this._onFocus,this).off(t,"blur",this._onBlur,this).off(t,"mousedown",this._onMouseDown,this),this._map.off("focus",this._addHooks,this).off("blur",this._removeHooks,this)},_onMouseDown:function(){this._focused||this._map._container.focus()},_onFocus:function(){this._focused=!0,this._map.fire("focus")},_onBlur:function(){this._focused=!1,this._map.fire("blur")},_setPanOffset:function(t){var e,i,n=this._panKeys={},o=this.keyCodes;for(e=0,i=o.left.length;i>e;e++)n[o.left[e]]=[-1*t,0];for(e=0,i=o.right.length;i>e;e++)n[o.right[e]]=[t,0];for(e=0,i=o.down.length;i>e;e++)n[o.down[e]]=[0,t];for(e=0,i=o.up.length;i>e;e++)n[o.up[e]]=[0,-1*t]},_setZoomOffset:function(t){var e,i,n=this._zoomKeys={},o=this.keyCodes;for(e=0,i=o.zoomIn.length;i>e;e++)n[o.zoomIn[e]]=t;for(e=0,i=o.zoomOut.length;i>e;e++)n[o.zoomOut[e]]=-t},_addHooks:function(){n.DomEvent.on(e,"keydown",this._onKeyDown,this)},_removeHooks:function(){n.DomEvent.off(e,"keydown",this._onKeyDown,this)},_onKeyDown:function(t){var e=t.keyCode,i=this._map;if(this._panKeys.hasOwnProperty(e))i.panBy(this._panKeys[e]),i.options.maxBounds&&i.panInsideBounds(i.options.maxBounds);else{if(!this._zoomKeys.hasOwnProperty(e))return;i.setZoom(i.getZoom()+this._zoomKeys[e])}n.DomEvent.stop(t)}}),n.Map.addInitHook("addHandler","keyboard",n.Map.Keyboard),n.Handler.MarkerDrag=n.Handler.extend({initialize:function(t){this._marker=t},addHooks:function(){var t=this._marker._icon;this._draggable||(this._draggable=new n.Draggable(t,t).on("dragstart",this._onDragStart,this).on("drag",this._onDrag,this).on("dragend",this._onDragEnd,this)),this._draggable.enable()},removeHooks:function(){this._draggable.disable()},moved:function(){return this._draggable&&this._draggable._moved},_onDragStart:function(){this._marker.closePopup().fire("movestart").fire("dragstart")},_onDrag:function(){var t=this._marker,e=t._shadow,i=n.DomUtil.getPosition(t._icon),o=t._map.layerPointToLatLng(i);e&&n.DomUtil.setPosition(e,i),t._latlng=o,t.fire("move",{latlng:o}).fire("drag")},_onDragEnd:function(){this._marker.fire("moveend").fire("dragend")}}),n.Handler.PolyEdit=n.Handler.extend({options:{icon:new n.DivIcon({iconSize:new n.Point(8,8),className:"leaflet-div-icon leaflet-editing-icon"})},initialize:function(t,e){this._poly=t,n.setOptions(this,e)},addHooks:function(){this._poly._map&&(this._markerGroup||this._initMarkers(),this._poly._map.addLayer(this._markerGroup))},removeHooks:function(){this._poly._map&&(this._poly._map.removeLayer(this._markerGroup),delete this._markerGroup,delete this._markers)},updateMarkers:function(){this._markerGroup.clearLayers(),this._initMarkers()},_initMarkers:function(){this._markerGroup||(this._markerGroup=new n.LayerGroup),this._markers=[];var t,e,i,o,s=this._poly._latlngs;for(t=0,i=s.length;i>t;t++)o=this._createMarker(s[t],t),o.on("click",this._onMarkerClick,this),this._markers.push(o);var a,r;for(t=0,e=i-1;i>t;e=t++)(0!==t||n.Polygon&&this._poly instanceof n.Polygon)&&(a=this._markers[e],r=this._markers[t],this._createMiddleMarker(a,r),this._updatePrevNext(a,r))},_createMarker:function(t,e){var i=new n.Marker(t,{draggable:!0,icon:this.options.icon});return i._origLatLng=t,i._index=e,i.on("drag",this._onMarkerDrag,this),i.on("dragend",this._fireEdit,this),this._markerGroup.addLayer(i),i},_fireEdit:function(){this._poly.fire("edit")},_onMarkerDrag:function(t){var e=t.target;n.extend(e._origLatLng,e._latlng),e._middleLeft&&e._middleLeft.setLatLng(this._getMiddleLatLng(e._prev,e)),e._middleRight&&e._middleRight.setLatLng(this._getMiddleLatLng(e,e._next)),this._poly.redraw()},_onMarkerClick:function(t){if(!(3>this._poly._latlngs.length)){var e=t.target,i=e._index;this._markerGroup.removeLayer(e),this._markers.splice(i,1),this._poly.spliceLatLngs(i,1),this._updateIndexes(i,-1),this._updatePrevNext(e._prev,e._next),e._middleLeft&&this._markerGroup.removeLayer(e._middleLeft),e._middleRight&&this._markerGroup.removeLayer(e._middleRight),e._prev&&e._next?this._createMiddleMarker(e._prev,e._next):e._prev?e._next||(e._prev._middleRight=null):e._next._middleLeft=null,this._poly.fire("edit")}},_updateIndexes:function(t,e){this._markerGroup.eachLayer(function(i){i._index>t&&(i._index+=e)})},_createMiddleMarker:function(t,e){var i,n,o,s=this._getMiddleLatLng(t,e),a=this._createMarker(s);a.setOpacity(.6),t._middleRight=e._middleLeft=a,n=function(){var n=e._index;a._index=n,a.off("click",i).on("click",this._onMarkerClick,this),s.lat=a.getLatLng().lat,s.lng=a.getLatLng().lng,this._poly.spliceLatLngs(n,0,s),this._markers.splice(n,0,a),a.setOpacity(1),this._updateIndexes(n,1),e._index++,this._updatePrevNext(t,a),this._updatePrevNext(a,e)},o=function(){a.off("dragstart",n,this),a.off("dragend",o,this),this._createMiddleMarker(t,a),this._createMiddleMarker(a,e)},i=function(){n.call(this),o.call(this),this._poly.fire("edit")},a.on("click",i,this).on("dragstart",n,this).on("dragend",o,this),this._markerGroup.addLayer(a)},_updatePrevNext:function(t,e){t&&(t._next=e),e&&(e._prev=t)},_getMiddleLatLng:function(t,e){var i=this._poly._map,n=i.latLngToLayerPoint(t.getLatLng()),o=i.latLngToLayerPoint(e.getLatLng());return i.layerPointToLatLng(n._add(o)._divideBy(2))}}),n.Polyline.addInitHook(function(){n.Handler.PolyEdit&&(this.editing=new n.Handler.PolyEdit(this),this.options.editable&&this.editing.enable()),this.on("add",function(){this.editing&&this.editing.enabled()&&this.editing.addHooks()}),this.on("remove",function(){this.editing&&this.editing.enabled()&&this.editing.removeHooks()})}),n.Control=n.Class.extend({options:{position:"topright"},initialize:function(t){n.setOptions(this,t)},getPosition:function(){return this.options.position},setPosition:function(t){var e=this._map;return e&&e.removeControl(this),this.options.position=t,e&&e.addControl(this),this +},addTo:function(t){this._map=t;var e=this._container=this.onAdd(t),i=this.getPosition(),o=t._controlCorners[i];return n.DomUtil.addClass(e,"leaflet-control"),-1!==i.indexOf("bottom")?o.insertBefore(e,o.firstChild):o.appendChild(e),this},removeFrom:function(t){var e=this.getPosition(),i=t._controlCorners[e];return i.removeChild(this._container),this._map=null,this.onRemove&&this.onRemove(t),this}}),n.control=function(t){return new n.Control(t)},n.Map.include({addControl:function(t){return t.addTo(this),this},removeControl:function(t){return t.removeFrom(this),this},_initControlPos:function(){function t(t,s){var a=i+t+" "+i+s;e[t+s]=n.DomUtil.create("div",a,o)}var e=this._controlCorners={},i="leaflet-",o=this._controlContainer=n.DomUtil.create("div",i+"control-container",this._container);t("top","left"),t("top","right"),t("bottom","left"),t("bottom","right")}}),n.Control.Zoom=n.Control.extend({options:{position:"topleft"},onAdd:function(t){var e="leaflet-control-zoom",i="leaflet-bar",o=i+"-part",s=n.DomUtil.create("div",e+" "+i);return this._map=t,this._zoomInButton=this._createButton("+","Zoom in",e+"-in "+o+" "+o+"-top",s,this._zoomIn,this),this._zoomOutButton=this._createButton("-","Zoom out",e+"-out "+o+" "+o+"-bottom",s,this._zoomOut,this),t.on("zoomend",this._updateDisabled,this),s},onRemove:function(t){t.off("zoomend",this._updateDisabled,this)},_zoomIn:function(t){this._map.zoomIn(t.shiftKey?3:1)},_zoomOut:function(t){this._map.zoomOut(t.shiftKey?3:1)},_createButton:function(t,e,i,o,s,a){var r=n.DomUtil.create("a",i,o);r.innerHTML=t,r.href="#",r.title=e;var h=n.DomEvent.stopPropagation;return n.DomEvent.on(r,"click",h).on(r,"mousedown",h).on(r,"dblclick",h).on(r,"click",n.DomEvent.preventDefault).on(r,"click",s,a),r},_updateDisabled:function(){var t=this._map,e="leaflet-control-zoom-disabled";n.DomUtil.removeClass(this._zoomInButton,e),n.DomUtil.removeClass(this._zoomOutButton,e),t._zoom===t.getMinZoom()&&n.DomUtil.addClass(this._zoomOutButton,e),t._zoom===t.getMaxZoom()&&n.DomUtil.addClass(this._zoomInButton,e)}}),n.Map.mergeOptions({zoomControl:!0}),n.Map.addInitHook(function(){this.options.zoomControl&&(this.zoomControl=new n.Control.Zoom,this.addControl(this.zoomControl))}),n.control.zoom=function(t){return new n.Control.Zoom(t)},n.Control.Attribution=n.Control.extend({options:{position:"bottomright",prefix:'Powered by Leaflet'},initialize:function(t){n.setOptions(this,t),this._attributions={}},onAdd:function(t){return this._container=n.DomUtil.create("div","leaflet-control-attribution"),n.DomEvent.disableClickPropagation(this._container),t.on("layeradd",this._onLayerAdd,this).on("layerremove",this._onLayerRemove,this),this._update(),this._container},onRemove:function(t){t.off("layeradd",this._onLayerAdd).off("layerremove",this._onLayerRemove)},setPrefix:function(t){return this.options.prefix=t,this._update(),this},addAttribution:function(t){return t?(this._attributions[t]||(this._attributions[t]=0),this._attributions[t]++,this._update(),this):i},removeAttribution:function(t){return t?(this._attributions[t]--,this._update(),this):i},_update:function(){if(this._map){var t=[];for(var e in this._attributions)this._attributions.hasOwnProperty(e)&&this._attributions[e]&&t.push(e);var i=[];this.options.prefix&&i.push(this.options.prefix),t.length&&i.push(t.join(", ")),this._container.innerHTML=i.join(" — ")}},_onLayerAdd:function(t){t.layer.getAttribution&&this.addAttribution(t.layer.getAttribution())},_onLayerRemove:function(t){t.layer.getAttribution&&this.removeAttribution(t.layer.getAttribution())}}),n.Map.mergeOptions({attributionControl:!0}),n.Map.addInitHook(function(){this.options.attributionControl&&(this.attributionControl=(new n.Control.Attribution).addTo(this))}),n.control.attribution=function(t){return new n.Control.Attribution(t)},n.Control.Scale=n.Control.extend({options:{position:"bottomleft",maxWidth:100,metric:!0,imperial:!0,updateWhenIdle:!1},onAdd:function(t){this._map=t;var e="leaflet-control-scale",i=n.DomUtil.create("div",e),o=this.options;return this._addScales(o,e,i),t.on(o.updateWhenIdle?"moveend":"move",this._update,this),t.whenReady(this._update,this),i},onRemove:function(t){t.off(this.options.updateWhenIdle?"moveend":"move",this._update,this)},_addScales:function(t,e,i){t.metric&&(this._mScale=n.DomUtil.create("div",e+"-line",i)),t.imperial&&(this._iScale=n.DomUtil.create("div",e+"-line",i))},_update:function(){var t=this._map.getBounds(),e=t.getCenter().lat,i=6378137*Math.PI*Math.cos(e*Math.PI/180),n=i*(t.getNorthEast().lng-t.getSouthWest().lng)/180,o=this._map.getSize(),s=this.options,a=0;o.x>0&&(a=n*(s.maxWidth/o.x)),this._updateScales(s,a)},_updateScales:function(t,e){t.metric&&e&&this._updateMetric(e),t.imperial&&e&&this._updateImperial(e)},_updateMetric:function(t){var e=this._getRoundNum(t);this._mScale.style.width=this._getScaleWidth(e/t)+"px",this._mScale.innerHTML=1e3>e?e+" m":e/1e3+" km"},_updateImperial:function(t){var e,i,n,o=3.2808399*t,s=this._iScale;o>5280?(e=o/5280,i=this._getRoundNum(e),s.style.width=this._getScaleWidth(i/e)+"px",s.innerHTML=i+" mi"):(n=this._getRoundNum(o),s.style.width=this._getScaleWidth(n/o)+"px",s.innerHTML=n+" ft")},_getScaleWidth:function(t){return Math.round(this.options.maxWidth*t)-10},_getRoundNum:function(t){var e=Math.pow(10,(Math.floor(t)+"").length-1),i=t/e;return i=i>=10?10:i>=5?5:i>=3?3:i>=2?2:1,e*i}}),n.control.scale=function(t){return new n.Control.Scale(t)},n.Control.Layers=n.Control.extend({options:{collapsed:!0,position:"topright",autoZIndex:!0},initialize:function(t,e,i){n.setOptions(this,i),this._layers={},this._lastZIndex=0,this._handlingClick=!1;for(var o in t)t.hasOwnProperty(o)&&this._addLayer(t[o],o);for(o in e)e.hasOwnProperty(o)&&this._addLayer(e[o],o,!0)},onAdd:function(t){return this._initLayout(),this._update(),t.on("layeradd",this._onLayerChange,this).on("layerremove",this._onLayerChange,this),this._container},onRemove:function(t){t.off("layeradd",this._onLayerChange).off("layerremove",this._onLayerChange)},addBaseLayer:function(t,e){return this._addLayer(t,e),this._update(),this},addOverlay:function(t,e){return this._addLayer(t,e,!0),this._update(),this},removeLayer:function(t){var e=n.stamp(t);return delete this._layers[e],this._update(),this},_initLayout:function(){var t="leaflet-control-layers",e=this._container=n.DomUtil.create("div",t);n.Browser.touch?n.DomEvent.on(e,"click",n.DomEvent.stopPropagation):(n.DomEvent.disableClickPropagation(e),n.DomEvent.on(e,"mousewheel",n.DomEvent.stopPropagation));var i=this._form=n.DomUtil.create("form",t+"-list");if(this.options.collapsed){n.DomEvent.on(e,"mouseover",this._expand,this).on(e,"mouseout",this._collapse,this);var o=this._layersLink=n.DomUtil.create("a",t+"-toggle",e);o.href="#",o.title="Layers",n.Browser.touch?n.DomEvent.on(o,"click",n.DomEvent.stopPropagation).on(o,"click",n.DomEvent.preventDefault).on(o,"click",this._expand,this):n.DomEvent.on(o,"focus",this._expand,this),this._map.on("movestart",this._collapse,this)}else this._expand();this._baseLayersList=n.DomUtil.create("div",t+"-base",i),this._separator=n.DomUtil.create("div",t+"-separator",i),this._overlaysList=n.DomUtil.create("div",t+"-overlays",i),e.appendChild(i)},_addLayer:function(t,e,i){var o=n.stamp(t);this._layers[o]={layer:t,name:e,overlay:i},this.options.autoZIndex&&t.setZIndex&&(this._lastZIndex++,t.setZIndex(this._lastZIndex))},_update:function(){if(this._container){this._baseLayersList.innerHTML="",this._overlaysList.innerHTML="";var t=!1,e=!1;for(var i in this._layers)if(this._layers.hasOwnProperty(i)){var n=this._layers[i];this._addItem(n),e=e||n.overlay,t=t||!n.overlay}this._separator.style.display=e&&t?"":"none"}},_onLayerChange:function(t){var e=n.stamp(t.layer);this._layers[e]&&!this._handlingClick&&this._update()},_createRadioElement:function(t,i){var n='t;t++)e=o[t],i=this._layers[e.layerId],e.checked&&!this._map.hasLayer(i.layer)?(this._map.addLayer(i.layer),i.overlay||(n=i.layer)):!e.checked&&this._map.hasLayer(i.layer)&&this._map.removeLayer(i.layer);n&&(this._map.setZoom(this._map.getZoom()),this._map.fire("baselayerchange",{layer:n})),this._handlingClick=!1},_expand:function(){n.DomUtil.addClass(this._container,"leaflet-control-layers-expanded")},_collapse:function(){this._container.className=this._container.className.replace(" leaflet-control-layers-expanded","")}}),n.control.layers=function(t,e,i){return new n.Control.Layers(t,e,i)},n.PosAnimation=n.Class.extend({includes:n.Mixin.Events,run:function(t,e,i,o){this.stop(),this._el=t,this._inProgress=!0,this.fire("start"),t.style[n.DomUtil.TRANSITION]="all "+(i||.25)+"s cubic-bezier(0,0,"+(o||.5)+",1)",n.DomEvent.on(t,n.DomUtil.TRANSITION_END,this._onTransitionEnd,this),n.DomUtil.setPosition(t,e),n.Util.falseFn(t.offsetWidth),this._stepTimer=setInterval(n.bind(this.fire,this,"step"),50)},stop:function(){this._inProgress&&(n.DomUtil.setPosition(this._el,this._getPos()),this._onTransitionEnd(),n.Util.falseFn(this._el.offsetWidth))},_transformRe:/(-?[\d\.]+), (-?[\d\.]+)\)/,_getPos:function(){var e,i,o,s=this._el,a=t.getComputedStyle(s);return n.Browser.any3d?(o=a[n.DomUtil.TRANSFORM].match(this._transformRe),e=parseFloat(o[1]),i=parseFloat(o[2])):(e=parseFloat(a.left),i=parseFloat(a.top)),new n.Point(e,i,!0)},_onTransitionEnd:function(){n.DomEvent.off(this._el,n.DomUtil.TRANSITION_END,this._onTransitionEnd,this),this._inProgress&&(this._inProgress=!1,this._el.style[n.DomUtil.TRANSITION]="",clearInterval(this._stepTimer),this.fire("step").fire("end"))}}),n.Map.include({setView:function(t,e,i){e=this._limitZoom(e);var n=this._zoom!==e;if(this._loaded&&!i&&this._layers){this._panAnim&&this._panAnim.stop();var o=n?this._zoomToIfClose&&this._zoomToIfClose(t,e):this._panByIfClose(t);if(o)return clearTimeout(this._sizeTimer),this}return this._resetView(t,e),this},panBy:function(t,e,i){if(t=n.point(t),!t.x&&!t.y)return this;this._panAnim||(this._panAnim=new n.PosAnimation,this._panAnim.on({step:this._onPanTransitionStep,end:this._onPanTransitionEnd},this)),this.fire("movestart"),n.DomUtil.addClass(this._mapPane,"leaflet-pan-anim");var o=n.DomUtil.getPosition(this._mapPane).subtract(t)._round();return this._panAnim.run(this._mapPane,o,e||.25,i),this},_onPanTransitionStep:function(){this.fire("move")},_onPanTransitionEnd:function(){n.DomUtil.removeClass(this._mapPane,"leaflet-pan-anim"),this.fire("moveend")},_panByIfClose:function(t){var e=this._getCenterOffset(t)._floor();return this._offsetIsWithinView(e)?(this.panBy(e),!0):!1},_offsetIsWithinView:function(t,e){var i=e||1,n=this.getSize();return Math.abs(t.x)<=n.x*i&&Math.abs(t.y)<=n.y*i}}),n.PosAnimation=n.DomUtil.TRANSITION?n.PosAnimation:n.PosAnimation.extend({run:function(t,e,i,o){this.stop(),this._el=t,this._inProgress=!0,this._duration=i||.25,this._easeOutPower=1/Math.max(o||.5,.2),this._startPos=n.DomUtil.getPosition(t),this._offset=e.subtract(this._startPos),this._startTime=+new Date,this.fire("start"),this._animate()},stop:function(){this._inProgress&&(this._step(),this._complete())},_animate:function(){this._animId=n.Util.requestAnimFrame(this._animate,this),this._step()},_step:function(){var t=+new Date-this._startTime,e=1e3*this._duration;e>t?this._runFrame(this._easeOut(t/e)):(this._runFrame(1),this._complete())},_runFrame:function(t){var e=this._startPos.add(this._offset.multiplyBy(t));n.DomUtil.setPosition(this._el,e),this.fire("step")},_complete:function(){n.Util.cancelAnimFrame(this._animId),this._inProgress=!1,this.fire("end")},_easeOut:function(t){return 1-Math.pow(1-t,this._easeOutPower)}}),n.Map.mergeOptions({zoomAnimation:n.DomUtil.TRANSITION&&!n.Browser.android23&&!n.Browser.mobileOpera}),n.DomUtil.TRANSITION&&n.Map.addInitHook(function(){n.DomEvent.on(this._mapPane,n.DomUtil.TRANSITION_END,this._catchTransitionEnd,this)}),n.Map.include(n.DomUtil.TRANSITION?{_zoomToIfClose:function(t,e){if(this._animatingZoom)return!0;if(!this.options.zoomAnimation)return!1;var i=this.getZoomScale(e),o=this._getCenterOffset(t)._divideBy(1-1/i);if(!this._offsetIsWithinView(o,1))return!1;n.DomUtil.addClass(this._mapPane,"leaflet-zoom-anim"),this.fire("movestart").fire("zoomstart"),this.fire("zoomanim",{center:t,zoom:e});var s=this._getCenterLayerPoint().add(o);return this._prepareTileBg(),this._runAnimation(t,e,i,s),!0},_catchTransitionEnd:function(){this._animatingZoom&&this._onZoomTransitionEnd()},_runAnimation:function(t,e,i,o,s){this._animateToCenter=t,this._animateToZoom=e,this._animatingZoom=!0,n.Draggable&&(n.Draggable._disabled=!0);var a=n.DomUtil.TRANSFORM,r=this._tileBg;clearTimeout(this._clearTileBgTimer),n.Util.falseFn(r.offsetWidth);var h=n.DomUtil.getScaleString(i,o),l=r.style[a];r.style[a]=s?l+" "+h:h+" "+l},_prepareTileBg:function(){var t=this._tilePane,e=this._tileBg;if(e&&this._getLoadedTilesPercentage(e)>.5&&.5>this._getLoadedTilesPercentage(t))return t.style.visibility="hidden",t.empty=!0,this._stopLoadingImages(t),i;e||(e=this._tileBg=this._createPane("leaflet-tile-pane",this._mapPane),e.style.zIndex=1),e.style[n.DomUtil.TRANSFORM]="",e.style.visibility="hidden",e.empty=!0,t.empty=!1,this._tilePane=this._panes.tilePane=e;var o=this._tileBg=t;n.DomUtil.addClass(o,"leaflet-zoom-animated"),this._stopLoadingImages(o)},_getLoadedTilesPercentage:function(t){var e,i,n=t.getElementsByTagName("img"),o=0;for(e=0,i=n.length;i>e;e++)n[e].complete&&o++;return o/i},_stopLoadingImages:function(t){var e,i,o,s=Array.prototype.slice.call(t.getElementsByTagName("img"));for(e=0,i=s.length;i>e;e++)o=s[e],o.complete||(o.onload=n.Util.falseFn,o.onerror=n.Util.falseFn,o.src=n.Util.emptyImageUrl,o.parentNode.removeChild(o))},_onZoomTransitionEnd:function(){this._restoreTileFront(),n.DomUtil.removeClass(this._mapPane,"leaflet-zoom-anim"),n.Util.falseFn(this._tileBg.offsetWidth),this._animatingZoom=!1,this._resetView(this._animateToCenter,this._animateToZoom,!0,!0),n.Draggable&&(n.Draggable._disabled=!1)},_restoreTileFront:function(){this._tilePane.innerHTML="",this._tilePane.style.visibility="",this._tilePane.style.zIndex=2,this._tileBg.style.zIndex=1},_clearTileBg:function(){this._animatingZoom||this.touchZoom._zooming||(this._tileBg.innerHTML="")}}:{}),n.Map.include({_defaultLocateOptions:{watch:!1,setView:!1,maxZoom:1/0,timeout:1e4,maximumAge:0,enableHighAccuracy:!1},locate:function(t){if(t=this._locationOptions=n.extend(this._defaultLocateOptions,t),!navigator.geolocation)return this._handleGeolocationError({code:0,message:"Geolocation not supported."}),this;var e=n.bind(this._handleGeolocationResponse,this),i=n.bind(this._handleGeolocationError,this);return t.watch?this._locationWatchId=navigator.geolocation.watchPosition(e,i,t):navigator.geolocation.getCurrentPosition(e,i,t),this},stopLocate:function(){return navigator.geolocation&&navigator.geolocation.clearWatch(this._locationWatchId),this},_handleGeolocationError:function(t){var e=t.code,i=t.message||(1===e?"permission denied":2===e?"position unavailable":"timeout");this._locationOptions.setView&&!this._loaded&&this.fitWorld(),this.fire("locationerror",{code:e,message:"Geolocation error: "+i+"."})},_handleGeolocationResponse:function(t){var e=180*t.coords.accuracy/4e7,i=2*e,o=t.coords.latitude,s=t.coords.longitude,a=new n.LatLng(o,s),r=new n.LatLng(o-e,s-i),h=new n.LatLng(o+e,s+i),l=new n.LatLngBounds(r,h),u=this._locationOptions;if(u.setView){var c=Math.min(this.getBoundsZoom(l),u.maxZoom);this.setView(a,c)}this.fire("locationfound",{latlng:a,bounds:l,accuracy:t.coords.accuracy})}})})(this,document); \ No newline at end of file diff --git a/spec/suites/control/Control.LayersSpec.js b/spec/suites/control/Control.LayersSpec.js index d6ab20a5..92a96c43 100644 --- a/spec/suites/control/Control.LayersSpec.js +++ b/spec/suites/control/Control.LayersSpec.js @@ -11,11 +11,13 @@ describe("Control.Layers", function () { layers = L.control.layers(baseLayers).addTo(map), spy = jasmine.createSpy(); - map.on('baselayerchange', spy); - happen.click(layers._baseLayersList.getElementsByTagName("input")[0]); + map.on('baselayerchange', spy) + .whenReady(function(){ + happen.click(layers._baseLayersList.getElementsByTagName("input")[0]); - expect(spy).toHaveBeenCalled(); - expect(spy.mostRecentCall.args[0].layer).toBe(baseLayers["Layer 1"]); + expect(spy).toHaveBeenCalled(); + expect(spy.mostRecentCall.args[0].layer).toBe(baseLayers["Layer 1"]); + }); }); it("is not fired on input that doesn't change the base layer", function () { @@ -29,4 +31,37 @@ describe("Control.Layers", function () { expect(spy).not.toHaveBeenCalled(); }); }); + + describe("updates", function () { + beforeEach(function () { + map.setView([0, 0], 14); + }); + + it("when an included layer is addded or removed", function () { + var baseLayer = L.tileLayer(), + overlay = L.marker([0, 0]), + layers = L.control.layers({"Base": baseLayer}, {"Overlay": overlay}).addTo(map); + + spyOn(layers, '_update').andCallThrough(); + + map.addLayer(overlay); + map.removeLayer(overlay); + + expect(layers._update).toHaveBeenCalled(); + expect(layers._update.callCount).toEqual(2); + }); + + it("not when a non-included layer is added or removed", function () { + var baseLayer = L.tileLayer(), + overlay = L.marker([0, 0]), + layers = L.control.layers({"Base": baseLayer}).addTo(map); + + spyOn(layers, '_update').andCallThrough(); + + map.addLayer(overlay); + map.removeLayer(overlay); + + expect(layers._update).not.toHaveBeenCalled(); + }); + }); }); diff --git a/spec/suites/core/ClassSpec.js b/spec/suites/core/ClassSpec.js index 414cd0dc..e60e629c 100644 --- a/spec/suites/core/ClassSpec.js +++ b/spec/suites/core/ClassSpec.js @@ -1,10 +1,10 @@ describe("Class", function() { - + describe("#extend", function() { var Klass, constructor, method; - + beforeEach(function() { constructor = jasmine.createSpy("Klass constructor"); method = jasmine.createSpy("Klass#bar method"); @@ -12,78 +12,78 @@ describe("Class", function() { Klass = L.Class.extend({ statics: {bla: 1}, includes: {mixin: true}, - + initialize: constructor, foo: 5, bar: method }); }); - + it("should create a class with the given constructor & properties", function() { var a = new Klass(); - + expect(constructor).toHaveBeenCalled(); expect(a.foo).toEqual(5); - + a.bar(); - + expect(method).toHaveBeenCalled(); }); - + it("should inherit parent classes' constructor & properties", function() { var Klass2 = Klass.extend({baz: 2}); - + var b = new Klass2(); - + expect(b instanceof Klass).toBeTruthy(); expect(b instanceof Klass2).toBeTruthy(); - + expect(constructor).toHaveBeenCalled(); expect(b.baz).toEqual(2); - + b.bar(); - + expect(method).toHaveBeenCalled(); }); - + it("should support static properties", function() { expect(Klass.bla).toEqual(1); }); - + it("should inherit parent static properties", function() { var Klass2 = Klass.extend({}); - + expect(Klass2.bla).toEqual(1); }); - + it("should override parent static properties", function() { var Klass2 = Klass.extend({statics: {bla: 2}}); - + expect(Klass2.bla).toEqual(2); }); - + it("should include the given mixin", function() { var a = new Klass(); expect(a.mixin).toBeTruthy(); }); - + it("should be able to include multiple mixins", function() { var Klass2 = L.Class.extend({ includes: [{mixin: true}, {mixin2: true}] }); var a = new Klass2(); - + expect(a.mixin).toBeTruthy(); expect(a.mixin2).toBeTruthy(); }); - + it("should grant 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() { var KlassWithOptions1 = L.Class.extend({ options: { @@ -97,18 +97,60 @@ describe("Class", function() { foo3: 4 } }); - + var a = new KlassWithOptions2(); - + expect(a.options).toEqual({ foo1: 1, foo2: 3, foo3: 4 }); }); + + it("should add constructor hooks correctly", function () { + var spy1 = jasmine.createSpy("init hook 1"); + + Klass.addInitHook(spy1); + Klass.addInitHook('bar', 1, 2, 3); + + var a = new Klass(); + + expect(spy1).toHaveBeenCalled(); + expect(method).toHaveBeenCalledWith(1, 2, 3); + }); + + it("should inherit constructor hooks", function () { + var spy1 = jasmine.createSpy("init hook 1"), + spy2 = jasmine.createSpy("init hook 2"); + + var Klass2 = Klass.extend({}); + + Klass.addInitHook(spy1); + Klass2.addInitHook(spy2); + + var a = new Klass2(); + + expect(spy1).toHaveBeenCalled(); + expect(spy2).toHaveBeenCalled(); + }); + + it("should not call child constructor hooks", function () { + var spy1 = jasmine.createSpy("init hook 1"), + spy2 = jasmine.createSpy("init hook 2"); + + var Klass2 = Klass.extend({}); + + Klass.addInitHook(spy1); + Klass2.addInitHook(spy2); + + var a = new Klass(); + + expect(spy1).toHaveBeenCalled(); + expect(spy2).not.toHaveBeenCalled(); + }); }); // TODO Class.include // TODO Class.mergeOptions -}); \ No newline at end of file +}); diff --git a/spec/suites/core/UtilSpec.js b/spec/suites/core/UtilSpec.js index 09f536df..5784cf7c 100644 --- a/spec/suites/core/UtilSpec.js +++ b/spec/suites/core/UtilSpec.js @@ -61,6 +61,35 @@ describe('Util', function() { }); }); + + describe('#getParamString', function() { + it('should create a valid query string for appending depending on url input', function() { + var a = { + url: "http://example.com/get", + obj: {bar: 7, baz: 3}, + result: "?bar=7&baz=3" + }; + + expect(L.Util.getParamString(a.obj,a.url)).toEqual(a.result); + + var b = { + url: "http://example.com/get?justone=qs", + obj: {bar: 7, baz: 3}, + result: "&bar=7&baz=3" + }; + + expect(L.Util.getParamString(b.obj,b.url)).toEqual(b.result); + + var c = { + url: undefined, + obj: {bar: 7, baz: 3}, + result: "?bar=7&baz=3" + }; + + expect(L.Util.getParamString(c.obj,c.url)).toEqual(c.result); + }); + }); + // TODO cancel/requestAnimFrame? // TODO limitExecByInterval @@ -71,7 +100,5 @@ describe('Util', function() { // TODO setOptions - // TODO getParamString - // TODO template -}); \ No newline at end of file +}); diff --git a/spec/suites/geo/LatLngSpec.js b/spec/suites/geo/LatLngSpec.js index 692f88a1..28462004 100644 --- a/spec/suites/geo/LatLngSpec.js +++ b/spec/suites/geo/LatLngSpec.js @@ -4,73 +4,59 @@ describe('LatLng', 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); }); - - it("should clamp latitude to lie between -90 and 90", function() { - var a = new L.LatLng(150, 0).lat; - expect(a).toEqual(90); - - var b = new L.LatLng(-230, 0).lat; - expect(b).toEqual(-90); - }); - - it("should clamp longitude to lie between -180 and 180", function() { - var a = new L.LatLng(0, 190).lng; - expect(a).toEqual(-170); - - var b = new L.LatLng(0, 360).lng; - expect(b).toEqual(0); - - var c = new L.LatLng(0, 380).lng; - expect(c).toEqual(20); - - var d = new L.LatLng(0, -190).lng; - expect(d).toEqual(170); - - var e = new L.LatLng(0, -360).lng; - expect(e).toEqual(0); - - var f = new L.LatLng(0, -380).lng; - 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 longitude if unbounded flag set to true", function() { - var a = new L.LatLng(150, 0, true).lat; - expect(a).toEqual(150); - - var b = new L.LatLng(-230, 0, true).lat; - expect(b).toEqual(-230); - - var c = new L.LatLng(0, 250, true).lng; - expect(c).toEqual(250); - - var d = new L.LatLng(0, -190, true).lng; - expect(d).toEqual(-190); - }); }); - + describe('#equals', function() { it("should return 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() { var a = new L.LatLng(10, 20); var b = new L.LatLng(10, 23.3); expect(a.equals(b)).toBe(false); }); }); + + describe('#wrap', function () { + it("#wrap should wrap longitude to lie between -180 and 180 by default", function() { + var a = new L.LatLng(0, 190).wrap().lng; + expect(a).toEqual(-170); + + var b = new L.LatLng(0, 360).wrap().lng; + expect(b).toEqual(0); + + var c = new L.LatLng(0, 380).wrap().lng; + expect(c).toEqual(20); + + var d = new L.LatLng(0, -190).wrap().lng; + expect(d).toEqual(170); + + var e = new L.LatLng(0, -360).wrap().lng; + expect(e).toEqual(0); + + var f = new L.LatLng(0, -380).wrap().lng; + expect(f).toEqual(-20); + + var g = new L.LatLng(0, 90).wrap().lng; + expect(g).toEqual(90); + + var h = new L.LatLng(0, 180).wrap().lng; + expect(h).toEqual(180); + }); + + it("#wrap should wrap longitude within the given range", function() { + var a = new L.LatLng(0, 190).wrap(-100, 100).lng; + expect(a).toEqual(-10); + }); + + }) }); diff --git a/spec/suites/layer/TileLayerSpec.js b/spec/suites/layer/TileLayerSpec.js index 28517ad9..fddfffc0 100644 --- a/spec/suites/layer/TileLayerSpec.js +++ b/spec/suites/layer/TileLayerSpec.js @@ -1 +1,75 @@ -describe('TileLayer', noSpecs); \ No newline at end of file + +describe('TileLayer', function () { + 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 () { + var maxZoom = 10, + minZoom = 5; + map.setView([0, 0], 1); + + L.tileLayer("{z}{x}{y}", { + maxZoom: maxZoom, + minZoom: minZoom + }).addTo(map); + expect(map.getMaxZoom() === maxZoom).toBeTruthy(); + expect(map.getMinZoom() === minZoom).toBeTruthy(); + }); + }); + 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 () { + map.setView([0, 0], 1); + + L.tileLayer("{z}{x}{y}", { 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); + expect(map.getMinZoom()).toBe(5); // changed + expect(map.getMaxZoom()).toBe(15); // unchanged + + + L.tileLayer("{z}{x}{y}",{ 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); + 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) + ]; + map.whenReady(function() { + expect(map.getMinZoom()).toBe(0); + expect(map.getMaxZoom()).toBe(25); + + map.removeLayer(tiles[0]); + expect(map.getMinZoom()).toBe(0); + expect(map.getMaxZoom()).toBe(25); + + map.removeLayer(tiles[3]); + expect(map.getMinZoom()).toBe(5); + expect(map.getMaxZoom()).toBe(20); + + map.removeLayer(tiles[2]); + expect(map.getMinZoom()).toBe(5); + expect(map.getMaxZoom()).toBe(10); + + map.removeLayer(tiles[1]); + expect(map.getMinZoom()).toBe(0); + expect(map.getMaxZoom()).toBe(Infinity); + }); + }); + }); + }); +}); \ No newline at end of file diff --git a/spec/suites/map/MapSpec.js b/spec/suites/map/MapSpec.js index f962dd1a..5f10ba25 100644 --- a/spec/suites/map/MapSpec.js +++ b/spec/suites/map/MapSpec.js @@ -10,7 +10,7 @@ describe("Map", function () { map.setView([0, 0], 1); expect(spy).toHaveBeenCalled(); - }) + }); }); describe("when the map has already been loaded", function () { @@ -27,7 +27,8 @@ describe("Map", function () { }); describe("#getBounds", function () { - it("is safe to call from within a moveend callback during initial load (#1027)", 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); @@ -38,4 +39,16 @@ describe("Map", function () { map.setView([51.505, -0.09], 13); }); }); + + describe("#getMinZoom and #getMaxZoom", function () { + it("The 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); + L.tileLayer("{z}{x}{y}", { minZoom:5, maxZoom: 15 }).addTo(map); + expect(map.getMinZoom()).toBe(5); + expect(map.getMaxZoom()).toBe(10); + }); + }); }); diff --git a/src/Leaflet.js b/src/Leaflet.js index 27a7c082..02889ee2 100644 --- a/src/Leaflet.js +++ b/src/Leaflet.js @@ -1,3 +1,8 @@ +/* + * The L namespace contains all Leaflet classes and functions. + * This code allows you to handle any possible namespace conflicts. + */ + var L, originalL; if (typeof exports !== undefined + '') { diff --git a/src/control/Control.Attribution.js b/src/control/Control.Attribution.js index 3a58bb63..9bfd1f87 100644 --- a/src/control/Control.Attribution.js +++ b/src/control/Control.Attribution.js @@ -1,3 +1,7 @@ +/* + * L.Control.Attribution is used for displaying attribution on the map (added by default). + */ + L.Control.Attribution = L.Control.extend({ options: { position: 'bottomright', diff --git a/src/control/Control.Layers.js b/src/control/Control.Layers.js index c1c936a3..282dd296 100644 --- a/src/control/Control.Layers.js +++ b/src/control/Control.Layers.js @@ -1,3 +1,7 @@ +/* + * L.Control.Layers is a control to allow users to switch between different layers on the map. + */ + L.Control.Layers = L.Control.extend({ options: { collapsed: true, @@ -10,6 +14,7 @@ L.Control.Layers = L.Control.extend({ this._layers = {}; this._lastZIndex = 0; + this._handlingClick = false; for (var i in baseLayers) { if (baseLayers.hasOwnProperty(i)) { @@ -28,9 +33,19 @@ L.Control.Layers = L.Control.extend({ this._initLayout(); this._update(); + map + .on('layeradd', this._onLayerChange, this) + .on('layerremove', this._onLayerChange, this); + return this._container; }, + onRemove: function (map) { + map + .off('layeradd', this._onLayerChange) + .off('layerremove', this._onLayerChange); + }, + addBaseLayer: function (layer, name) { this._addLayer(layer, name); this._update(); @@ -56,6 +71,7 @@ L.Control.Layers = L.Control.extend({ if (!L.Browser.touch) { L.DomEvent.disableClickPropagation(container); + L.DomEvent.on(container, 'mousewheel', L.DomEvent.stopPropagation); } else { L.DomEvent.on(container, 'click', L.DomEvent.stopPropagation); } @@ -132,6 +148,14 @@ L.Control.Layers = L.Control.extend({ this._separator.style.display = (overlaysPresent && baseLayersPresent ? '' : 'none'); }, + _onLayerChange: function (e) { + var id = L.stamp(e.layer); + + if (this._layers[id] && !this._handlingClick) { + this._update(); + } + }, + // IE7 bugs out if you create a radio dynamically, so you have to do it this hacky way (see http://bit.ly/PqYLBe) _createRadioElement: function (name, checked) { @@ -173,6 +197,8 @@ L.Control.Layers = L.Control.extend({ var container = obj.overlay ? this._overlaysList : this._baseLayersList; container.appendChild(label); + + return label; }, _onInputClick: function () { @@ -181,6 +207,8 @@ L.Control.Layers = L.Control.extend({ inputsLen = inputs.length, baseLayer; + this._handlingClick = true; + for (i = 0; i < inputsLen; i++) { input = inputs[i]; obj = this._layers[input.layerId]; @@ -196,8 +224,11 @@ L.Control.Layers = L.Control.extend({ } if (baseLayer) { + this._map.setZoom(this._map.getZoom()); this._map.fire('baselayerchange', {layer: baseLayer}); } + + this._handlingClick = false; }, _expand: function () { diff --git a/src/control/Control.Scale.js b/src/control/Control.Scale.js index 6f911c34..6043b023 100644 --- a/src/control/Control.Scale.js +++ b/src/control/Control.Scale.js @@ -1,3 +1,7 @@ +/* + * L.Control.Scale is used for displaying metric/imperial scale on the map. + */ + L.Control.Scale = L.Control.extend({ options: { position: 'bottomleft', diff --git a/src/control/Control.Zoom.js b/src/control/Control.Zoom.js index 7e993481..98e5388c 100644 --- a/src/control/Control.Zoom.js +++ b/src/control/Control.Zoom.js @@ -1,20 +1,41 @@ +/* + * L.Control.Zoom is used for the default zoom buttons on the map. + */ + L.Control.Zoom = L.Control.extend({ options: { position: 'topleft' }, onAdd: function (map) { - var className = 'leaflet-control-zoom', - container = L.DomUtil.create('div', className); + var zoomName = 'leaflet-control-zoom', + barName = 'leaflet-bar', + partName = barName + '-part', + container = L.DomUtil.create('div', zoomName + ' ' + barName); this._map = map; - this._createButton('+', 'Zoom in', className + '-in', container, this._zoomIn, this); - this._createButton('-', 'Zoom out', className + '-out', container, this._zoomOut, this); + this._zoomInButton = this._createButton('+', 'Zoom in', + zoomName + '-in ' + + partName + ' ' + + partName + '-top', + container, this._zoomIn, this); + + this._zoomOutButton = this._createButton('-', 'Zoom out', + zoomName + '-out ' + + partName + ' ' + + partName + '-bottom', + container, this._zoomOut, this); + + map.on('zoomend', this._updateDisabled, this); return container; }, + onRemove: function (map) { + map.off('zoomend', this._updateDisabled, this); + }, + _zoomIn: function (e) { this._map.zoomIn(e.shiftKey ? 3 : 1); }, @@ -29,14 +50,31 @@ L.Control.Zoom = L.Control.extend({ link.href = '#'; link.title = title; + var stop = L.DomEvent.stopPropagation; + L.DomEvent - .on(link, 'click', L.DomEvent.stopPropagation) - .on(link, 'mousedown', L.DomEvent.stopPropagation) - .on(link, 'dblclick', L.DomEvent.stopPropagation) + .on(link, 'click', stop) + .on(link, 'mousedown', stop) + .on(link, 'dblclick', stop) .on(link, 'click', L.DomEvent.preventDefault) .on(link, 'click', fn, context); return link; + }, + + _updateDisabled: function () { + var map = this._map, + className = 'leaflet-control-zoom-disabled'; + + L.DomUtil.removeClass(this._zoomInButton, className); + L.DomUtil.removeClass(this._zoomOutButton, className); + + if (map._zoom === map.getMinZoom()) { + L.DomUtil.addClass(this._zoomOutButton, className); + } + if (map._zoom === map.getMaxZoom()) { + L.DomUtil.addClass(this._zoomInButton, className); + } } }); diff --git a/src/control/Control.js b/src/control/Control.js index 73060cf6..6105cc45 100644 --- a/src/control/Control.js +++ b/src/control/Control.js @@ -1,3 +1,7 @@ +/* + * L.Control is a base class for implementing map controls. Handles positioning. + * All other controls extend from this class. + */ L.Control = L.Class.extend({ options: { diff --git a/src/copyright.js b/src/copyright.js new file mode 100644 index 00000000..1466ca63 --- /dev/null +++ b/src/copyright.js @@ -0,0 +1,4 @@ +/* + Leaflet, a JavaScript library for mobile-friendly interactive maps. http://leafletjs.com + (c) 2010-2013, Vladimir Agafonkin, CloudMade +*/ diff --git a/src/core/Browser.js b/src/core/Browser.js index 1292016f..06496d1e 100644 --- a/src/core/Browser.js +++ b/src/core/Browser.js @@ -1,23 +1,26 @@ +/* + * L.Browser handles different browser and feature detections for internal Leaflet use. + */ + (function () { var ie = !!window.ActiveXObject, - // http://tanalin.com/en/articles/ie-version-js/ ie6 = ie && !window.XMLHttpRequest, ie7 = ie && !document.querySelector, // terrible browser detection to work around Safari / iOS / Android browser bugs - // see TileLayer._addTile and debug/hacks/jitter.html - ua = navigator.userAgent.toLowerCase(), - webkit = ua.indexOf("webkit") !== -1, - chrome = ua.indexOf("chrome") !== -1, - android = ua.indexOf("android") !== -1, - android23 = ua.search("android [23]") !== -1, + webkit = ua.indexOf('webkit') !== -1, + chrome = ua.indexOf('chrome') !== -1, + android = ua.indexOf('android') !== -1, + android23 = ua.search('android [23]') !== -1, mobile = typeof orientation !== undefined + '', - msTouch = (window.navigator && window.navigator.msPointerEnabled && window.navigator.msMaxTouchPoints), - retina = (('devicePixelRatio' in window && window.devicePixelRatio > 1) || - ('matchMedia' in window && window.matchMedia("(min-resolution:144dpi)").matches)), + msTouch = window.navigator && window.navigator.msPointerEnabled && + window.navigator.msMaxTouchPoints, + retina = ('devicePixelRatio' in window && window.devicePixelRatio > 1) || + ('matchMedia' in window && window.matchMedia('(min-resolution:144dpi)') && + window.matchMedia('(min-resolution:144dpi)').matches), doc = document.documentElement, ie3d = ie && ('transition' in doc.style), @@ -57,6 +60,7 @@ L.Browser = { + ie: ie, ie6: ie6, ie7: ie7, webkit: webkit, diff --git a/src/core/Class.js b/src/core/Class.js index 94c4eb8a..7ba19e89 100644 --- a/src/core/Class.js +++ b/src/core/Class.js @@ -1,16 +1,24 @@ /* - * Class powers the OOP facilities of the library. Thanks to John Resig and Dean Edwards for inspiration! + * L.Class powers the OOP facilities of the library. + * Thanks to John Resig and Dean Edwards for inspiration! */ L.Class = function () {}; -L.Class.extend = function (/*Object*/ props) /*-> Class*/ { +L.Class.extend = function (props) { // extended class with the new prototype var NewClass = function () { + + // call the constructor if (this.initialize) { this.initialize.apply(this, arguments); } + + // call all constructor hooks + if (this._initHooks) { + this.callInitHooks(); + } }; // instantiate class without calling constructor @@ -49,6 +57,25 @@ L.Class.extend = function (/*Object*/ props) /*-> Class*/ { // mix given properties into the prototype L.extend(proto, props); + proto._initHooks = []; + + var parent = this; + // add method for calling all hooks + proto.callInitHooks = function () { + + if (this._initHooksCalled) { return; } + + if (parent.prototype.callInitHooks) { + parent.prototype.callInitHooks.call(this); + } + + this._initHooksCalled = true; + + for (var i = 0, len = proto._initHooks.length; i < len; i++) { + proto._initHooks[i].call(this); + } + }; + return NewClass; }; @@ -58,6 +85,19 @@ L.Class.include = function (props) { L.extend(this.prototype, props); }; +// merge new default options to the Class L.Class.mergeOptions = function (options) { L.extend(this.prototype.options, options); }; + +// add a constructor hook +L.Class.addInitHook = function (fn) { // (Function) || (String, args...) + var args = Array.prototype.slice.call(arguments, 1); + + var init = typeof fn === 'function' ? fn : function () { + this[fn].apply(this, args); + }; + + this.prototype._initHooks = this.prototype._initHooks || []; + this.prototype._initHooks.push(init); +}; diff --git a/src/core/Events.js b/src/core/Events.js index 70cb4789..9f66e160 100644 --- a/src/core/Events.js +++ b/src/core/Events.js @@ -1,5 +1,5 @@ /* - * L.Mixin.Events adds custom events functionality to Leaflet classes + * L.Mixin.Events is used to add custom events functionality to Leaflet classes. */ var key = '_leaflet_events'; diff --git a/src/core/Handler.js b/src/core/Handler.js index 96ebf4a9..9b121123 100644 --- a/src/core/Handler.js +++ b/src/core/Handler.js @@ -1,6 +1,7 @@ /* - * L.Handler classes are used internally to inject interaction features to classes like Map and Marker. - */ + L.Handler is a base class for handler classes that are used internally to inject + interaction features like dragging to classes like Map and Marker. +*/ L.Handler = L.Class.extend({ initialize: function (map) { diff --git a/src/core/Util.js b/src/core/Util.js index 48af1588..efb797a4 100644 --- a/src/core/Util.js +++ b/src/core/Util.js @@ -1,5 +1,5 @@ /* - * L.Util is a namespace for various utility functions. + * L.Util contains various utility functions used throughout Leaflet code. */ L.Util = { @@ -77,14 +77,14 @@ L.Util = { return obj.options; }, - getParamString: function (obj) { + getParamString: function (obj, existingUrl) { var params = []; for (var i in obj) { if (obj.hasOwnProperty(i)) { params.push(i + '=' + obj[i]); } } - return '?' + params.join('&'); + return ((!existingUrl || existingUrl.indexOf('?') === -1) ? '?' : '&') + params.join('&'); }, template: function (str, data) { @@ -97,6 +97,10 @@ L.Util = { }); }, + isArray: function (obj) { + return (Object.prototype.toString.call(obj) === '[object Array]'); + }, + emptyImageUrl: 'data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=' }; diff --git a/src/dom/DomEvent.DoubleTap.js b/src/dom/DomEvent.DoubleTap.js index 6178b934..32d94864 100644 --- a/src/dom/DomEvent.DoubleTap.js +++ b/src/dom/DomEvent.DoubleTap.js @@ -1,3 +1,7 @@ +/* + * Extends the event handling code with double tap support for mobile browsers. + */ + L.extend(L.DomEvent, { _touchstart: L.Browser.msTouch ? 'MSPointerDown' : 'touchstart', @@ -33,7 +37,9 @@ L.extend(L.DomEvent, { doubleTap = (delta > 0 && delta <= delay); last = now; } + function onTouchEnd(e) { + /*jshint forin:false */ if (L.Browser.msTouch) { var idx = trackedTouches.indexOf(e.pointerId); if (idx === -1) { @@ -47,14 +53,13 @@ L.extend(L.DomEvent, { //Work around .type being readonly with MSPointer* events var newTouch = { }, prop; + for (var i in touch) { - if (true) { //Make JSHint happy, we want to copy all properties - prop = touch[i]; - if (typeof prop === 'function') { - newTouch[i] = prop.bind(touch); - } else { - newTouch[i] = prop; - } + prop = touch[i]; + if (typeof prop === 'function') { + newTouch[i] = prop.bind(touch); + } else { + newTouch[i] = prop; } } touch = newTouch; diff --git a/src/dom/DomEvent.MsTouch.js b/src/dom/DomEvent.MsTouch.js index c5ba6a3c..685baaa9 100644 --- a/src/dom/DomEvent.MsTouch.js +++ b/src/dom/DomEvent.MsTouch.js @@ -1,3 +1,7 @@ +/* + * Extends L.DomEvent to provide touch support for Internet Explorer and Windows-based devices. + */ + L.extend(L.DomEvent, { _msTouches: [], diff --git a/src/dom/DomEvent.js b/src/dom/DomEvent.js index c495514f..6246a7be 100644 --- a/src/dom/DomEvent.js +++ b/src/dom/DomEvent.js @@ -18,10 +18,12 @@ L.DomEvent = { if (L.Browser.msTouch && type.indexOf('touch') === 0) { return this.addMsTouchListener(obj, type, handler, id); - } else if (L.Browser.touch && (type === 'dblclick') && this.addDoubleTapListener) { - return this.addDoubleTapListener(obj, handler, id); + } + if (L.Browser.touch && (type === 'dblclick') && this.addDoubleTapListener) { + this.addDoubleTapListener(obj, handler, id); + } - } else if ('addEventListener' in obj) { + if ('addEventListener' in obj) { if (type === 'mousewheel') { obj.addEventListener('DOMMouseScroll', handler, false); @@ -99,8 +101,11 @@ L.DomEvent = { var stop = L.DomEvent.stopPropagation; + for (var i = L.Draggable.START.length - 1; i >= 0; i--) { + L.DomEvent.addListener(el, L.Draggable.START[i], stop); + } + return L.DomEvent - .addListener(el, L.Draggable.START, stop) .addListener(el, 'click', stop) .addListener(el, 'dblclick', stop); }, @@ -160,9 +165,8 @@ L.DomEvent = { return (related !== el); }, - /*jshint noarg:false */ _getEvent: function () { // evil magic for IE - + /*jshint noarg:false */ var e = window.event; if (!e) { var caller = arguments.callee.caller; @@ -176,7 +180,6 @@ L.DomEvent = { } return e; } - /*jshint noarg:false */ }; L.DomEvent.on = L.DomEvent.addListener; diff --git a/src/dom/DomUtil.js b/src/dom/DomUtil.js index 34780d84..b7bf6a03 100644 --- a/src/dom/DomUtil.js +++ b/src/dom/DomUtil.js @@ -35,6 +35,11 @@ L.DomUtil = { do { top += el.offsetTop || 0; left += el.offsetLeft || 0; + + //add borders + top += parseInt(L.DomUtil.getStyle(el, "borderTopWidth"), 10) || 0; + left += parseInt(L.DomUtil.getStyle(el, "borderLeftWidth"), 10) || 0; + pos = L.DomUtil.getStyle(el, 'position'); if (el.offsetParent === docBody && pos === 'absolute') { break; } @@ -100,7 +105,7 @@ L.DomUtil = { document.selection.empty(); } if (!this._onselectstart) { - this._onselectstart = document.onselectstart; + this._onselectstart = document.onselectstart || null; document.onselectstart = L.Util.falseFn; } }, @@ -221,8 +226,11 @@ L.DomUtil = { L.DomUtil.TRANSFORM = L.DomUtil.testProp( ['transform', 'WebkitTransform', 'OTransform', 'MozTransform', 'msTransform']); +// webkitTransition comes first because some browser versions that drop vendor prefix don't do +// the same for the transitionend event, in particular the Android 4.1 stock browser + L.DomUtil.TRANSITION = L.DomUtil.testProp( - ['transition', 'webkitTransition', 'OTransition', 'MozTransition', 'msTransition']); + ['webkitTransition', 'transition', 'OTransition', 'MozTransition', 'msTransition']); L.DomUtil.TRANSITION_END = L.DomUtil.TRANSITION === 'webkitTransition' || L.DomUtil.TRANSITION === 'OTransition' ? diff --git a/src/dom/Draggable.js b/src/dom/Draggable.js index a04372f9..08d51c8b 100644 --- a/src/dom/Draggable.js +++ b/src/dom/Draggable.js @@ -6,9 +6,17 @@ L.Draggable = L.Class.extend({ includes: L.Mixin.Events, statics: { - START: L.Browser.touch ? 'touchstart' : 'mousedown', - END: L.Browser.touch ? 'touchend' : 'mouseup', - MOVE: L.Browser.touch ? 'touchmove' : 'mousemove', + START: L.Browser.touch ? ['touchstart', 'mousedown'] : ['mousedown'], + END: { + mousedown: 'mouseup', + touchstart: 'touchend', + MSPointerDown: 'touchend' + }, + MOVE: { + mousedown: 'mousemove', + touchstart: 'touchmove', + MSPointerDown: 'touchmove' + }, TAP_TOLERANCE: 15 }, @@ -21,14 +29,18 @@ L.Draggable = L.Class.extend({ enable: function () { if (this._enabled) { return; } - L.DomEvent.on(this._dragStartTarget, L.Draggable.START, this._onDown, this); + for (var i = L.Draggable.START.length - 1; i >= 0; i--) { + L.DomEvent.on(this._dragStartTarget, L.Draggable.START[i], this._onDown, this); + } this._enabled = true; }, disable: function () { if (!this._enabled) { return; } - L.DomEvent.off(this._dragStartTarget, L.Draggable.START, this._onDown); + for (var i = L.Draggable.START.length - 1; i >= 0; i--) { + L.DomEvent.off(this._dragStartTarget, L.Draggable.START[i], this._onDown, this); + } this._enabled = false; this._moved = false; }, @@ -76,8 +88,8 @@ L.Draggable = L.Class.extend({ }, this), 1000); } - L.DomEvent.on(document, L.Draggable.MOVE, this._onMove, this); - L.DomEvent.on(document, L.Draggable.END, this._onUp, this); + L.DomEvent.on(document, L.Draggable.MOVE[e.type], this._onMove, this); + L.DomEvent.on(document, L.Draggable.END[e.type], this._onUp, this); }, _onMove: function (e) { @@ -138,8 +150,12 @@ L.Draggable = L.Class.extend({ this._restoreCursor(); } - L.DomEvent.off(document, L.Draggable.MOVE, this._onMove); - L.DomEvent.off(document, L.Draggable.END, this._onUp); + for (var i in L.Draggable.MOVE) { + if (L.Draggable.MOVE.hasOwnProperty(i)) { + L.DomEvent.off(document, L.Draggable.MOVE[i], this._onMove); + L.DomEvent.off(document, L.Draggable.END[i], this._onUp); + } + } if (this._moved) { // ensure drag is not fired after dragend diff --git a/src/geo/LatLng.js b/src/geo/LatLng.js index 9873a76a..94ab9bf6 100644 --- a/src/geo/LatLng.js +++ b/src/geo/LatLng.js @@ -1,8 +1,8 @@ /* - L.LatLng represents a geographical point with latitude and longitude coordinates. -*/ + * L.LatLng represents a geographical point with latitude and longitude coordinates. + */ -L.LatLng = function (rawLat, rawLng, noWrap) { // (Number, Number[, Boolean]) +L.LatLng = function (rawLat, rawLng) { // (Number, Number) var lat = parseFloat(rawLat), lng = parseFloat(rawLng); @@ -10,11 +10,6 @@ L.LatLng = function (rawLat, rawLng, noWrap) { // (Number, Number[, Boolean]) throw new Error('Invalid LatLng object: (' + rawLat + ', ' + rawLng + ')'); } - if (noWrap !== true) { - lat = Math.max(Math.min(lat, 90), -90); // clamp latitude into -90..90 - lng = (lng + 180) % 360 + ((lng < -180 || lng === 180) ? 180 : -180); // wrap longitude into -180..180 - } - this.lat = lat; this.lng = lng; }; @@ -31,17 +26,21 @@ L.LatLng.prototype = { obj = L.latLng(obj); - var margin = Math.max(Math.abs(this.lat - obj.lat), Math.abs(this.lng - obj.lng)); + var margin = Math.max( + Math.abs(this.lat - obj.lat), + Math.abs(this.lng - obj.lng)); + return margin <= L.LatLng.MAX_MARGIN; }, - toString: function (precision) { // -> String + toString: function (precision) { // (Number) -> String return 'LatLng(' + L.Util.formatNum(this.lat, precision) + ', ' + L.Util.formatNum(this.lng, precision) + ')'; }, // Haversine distance formula, see http://en.wikipedia.org/wiki/Haversine_formula + // TODO move to projection code, LatLng shouldn't know about Earth distanceTo: function (other) { // (LatLng) -> Number other = L.latLng(other); @@ -57,19 +56,30 @@ L.LatLng.prototype = { var a = sin1 * sin1 + sin2 * sin2 * Math.cos(lat1) * Math.cos(lat2); return R * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); + }, + + wrap: function (a, b) { // (Number, Number) -> LatLng + var lng = this.lng; + + a = a || -180; + b = b || 180; + + lng = (lng + b) % (b - a) + (lng < a || lng === b ? b : a); + + return new L.LatLng(this.lat, lng); } }; -L.latLng = function (a, b, c) { // (LatLng) or ([Number, Number]) or (Number, Number, Boolean) +L.latLng = function (a, b) { // (LatLng) or ([Number, Number]) or (Number, Number) if (a instanceof L.LatLng) { return a; } - if (a instanceof Array) { + if (L.Util.isArray(a)) { return new L.LatLng(a[0], a[1]); } if (isNaN(a)) { return a; } - return new L.LatLng(a, b, c); + return new L.LatLng(a, b); }; diff --git a/src/geo/LatLngBounds.js b/src/geo/LatLngBounds.js index fc91fdba..bb407069 100644 --- a/src/geo/LatLngBounds.js +++ b/src/geo/LatLngBounds.js @@ -2,20 +2,20 @@ * L.LatLngBounds represents a rectangular area on the map in geographical coordinates. */ -L.LatLngBounds = L.Class.extend({ - initialize: function (southWest, northEast) { // (LatLng, LatLng) or (LatLng[]) - if (!southWest) { return; } +L.LatLngBounds = function (southWest, northEast) { // (LatLng, LatLng) or (LatLng[]) + if (!southWest) { return; } - var latlngs = northEast ? [southWest, northEast] : southWest; + var latlngs = northEast ? [southWest, northEast] : southWest; - for (var i = 0, len = latlngs.length; i < len; i++) { - this.extend(latlngs[i]); - } - }, + for (var i = 0, len = latlngs.length; i < len; i++) { + this.extend(latlngs[i]); + } +}; +L.LatLngBounds.prototype = { // extend the bounds to contain the given point or bounds extend: function (obj) { // (LatLng) or (LatLngBounds) - if (typeof obj[0] === 'number' || obj instanceof L.LatLng) { + if (typeof obj[0] === 'number' || typeof obj[0] === 'string' || obj instanceof L.LatLng) { obj = L.latLng(obj); } else { obj = L.latLngBounds(obj); @@ -23,8 +23,8 @@ L.LatLngBounds = L.Class.extend({ if (obj instanceof L.LatLng) { if (!this._southWest && !this._northEast) { - this._southWest = new L.LatLng(obj.lat, obj.lng, true); - this._northEast = new L.LatLng(obj.lat, obj.lng, true); + this._southWest = new L.LatLng(obj.lat, obj.lng); + this._northEast = new L.LatLng(obj.lat, obj.lng); } else { this._southWest.lat = Math.min(obj.lat, this._southWest.lat); this._southWest.lng = Math.min(obj.lng, this._southWest.lng); @@ -66,11 +66,11 @@ L.LatLngBounds = L.Class.extend({ }, getNorthWest: function () { - return new L.LatLng(this._northEast.lat, this._southWest.lng, true); + return new L.LatLng(this._northEast.lat, this._southWest.lng); }, getSouthEast: function () { - return new L.LatLng(this._southWest.lat, this._northEast.lng, true); + return new L.LatLng(this._southWest.lat, this._northEast.lng); }, contains: function (obj) { // (LatLngBounds) or (LatLng) -> Boolean @@ -128,7 +128,7 @@ L.LatLngBounds = L.Class.extend({ isValid: function () { return !!(this._southWest && this._northEast); } -}); +}; //TODO International date line? diff --git a/src/geo/crs/CRS.EPSG3857.js b/src/geo/crs/CRS.EPSG3857.js index 4ee972c7..23551a14 100644 --- a/src/geo/crs/CRS.EPSG3857.js +++ b/src/geo/crs/CRS.EPSG3857.js @@ -1,3 +1,7 @@ +/* + * L.CRS.EPSG3857 (Spherical Mercator) is the most common CRS for web mapping + * and is used by Leaflet by default. + */ L.CRS.EPSG3857 = L.extend({}, L.CRS, { code: 'EPSG:3857', diff --git a/src/geo/crs/CRS.EPSG4326.js b/src/geo/crs/CRS.EPSG4326.js index c2a04c6e..bcd33bda 100644 --- a/src/geo/crs/CRS.EPSG4326.js +++ b/src/geo/crs/CRS.EPSG4326.js @@ -1,3 +1,6 @@ +/* + * L.CRS.EPSG4326 is a CRS popular among advanced GIS specialists. + */ L.CRS.EPSG4326 = L.extend({}, L.CRS, { code: 'EPSG:4326', diff --git a/src/geo/crs/CRS.Simple.js b/src/geo/crs/CRS.Simple.js index 5db96f43..a4924925 100644 --- a/src/geo/crs/CRS.Simple.js +++ b/src/geo/crs/CRS.Simple.js @@ -1,5 +1,12 @@ +/* + * A simple CRS that can be used for flat non-Earth maps like panoramas or game maps. + */ L.CRS.Simple = L.extend({}, L.CRS, { projection: L.Projection.LonLat, - transformation: new L.Transformation(1, 0, 1, 0) + transformation: new L.Transformation(1, 0, -1, 0), + + scale: function (zoom) { + return Math.pow(2, zoom); + } }); diff --git a/src/geo/crs/CRS.js b/src/geo/crs/CRS.js index 5da70a3f..cb70d9b8 100644 --- a/src/geo/crs/CRS.js +++ b/src/geo/crs/CRS.js @@ -1,3 +1,6 @@ +/* + * L.CRS is a base object for all defined CRS (Coordinate Reference Systems) in Leaflet. + */ L.CRS = { latLngToPoint: function (latlng, zoom) { // (LatLng, Number) -> Point diff --git a/src/geo/projection/Projection.LonLat.js b/src/geo/projection/Projection.LonLat.js index 70f9f40a..449dabae 100644 --- a/src/geo/projection/Projection.LonLat.js +++ b/src/geo/projection/Projection.LonLat.js @@ -1,3 +1,6 @@ +/* + * Simple equirectangular (Plate Carree) projection, used by CRS like EPSG:4326 and Simple. + */ L.Projection.LonLat = { project: function (latlng) { @@ -5,6 +8,6 @@ L.Projection.LonLat = { }, unproject: function (point) { - return new L.LatLng(point.y, point.x, true); + return new L.LatLng(point.y, point.x); } }; diff --git a/src/geo/projection/Projection.Mercator.js b/src/geo/projection/Projection.Mercator.js index 8298edb0..2ecf6d67 100644 --- a/src/geo/projection/Projection.Mercator.js +++ b/src/geo/projection/Projection.Mercator.js @@ -1,3 +1,7 @@ +/* + * Mercator projection that takes into account that the Earth is not a perfect sphere. + * Less popular than spherical mercator; used by projections like EPSG:3395. + */ L.Projection.Mercator = { MAX_LATITUDE: 85.0840591556, @@ -47,6 +51,6 @@ L.Projection.Mercator = { phi += dphi; } - return new L.LatLng(phi * d, lng, true); + return new L.LatLng(phi * d, lng); } }; diff --git a/src/geo/projection/Projection.SphericalMercator.js b/src/geo/projection/Projection.SphericalMercator.js index 5425f450..0cf969f8 100644 --- a/src/geo/projection/Projection.SphericalMercator.js +++ b/src/geo/projection/Projection.SphericalMercator.js @@ -1,3 +1,6 @@ +/* + * Spherical Mercator is the most popular map projection, used by EPSG:3857 CRS used by default. + */ L.Projection.SphericalMercator = { MAX_LATITUDE: 85.0511287798, @@ -19,7 +22,6 @@ L.Projection.SphericalMercator = { lng = point.x * d, lat = (2 * Math.atan(Math.exp(point.y)) - (Math.PI / 2)) * d; - // TODO refactor LatLng wrapping - return new L.LatLng(lat, lng, true); + return new L.LatLng(lat, lng); } }; diff --git a/src/geometry/Bounds.js b/src/geometry/Bounds.js index 24b213aa..78ddc25c 100644 --- a/src/geometry/Bounds.js +++ b/src/geometry/Bounds.js @@ -2,18 +2,17 @@ * L.Bounds represents a rectangular area on the screen in pixel coordinates. */ -L.Bounds = L.Class.extend({ +L.Bounds = function (a, b) { //(Point, Point) or Point[] + if (!a) { return; } - initialize: function (a, b) { //(Point, Point) or Point[] - if (!a) { return; } + var points = b ? [a, b] : a; - var points = b ? [a, b] : a; - - for (var i = 0, len = points.length; i < len; i++) { - this.extend(points[i]); - } - }, + for (var i = 0, len = points.length; i < len; i++) { + this.extend(points[i]); + } +}; +L.Bounds.prototype = { // extend the bounds to contain the given point extend: function (point) { // (Point) point = L.point(point); @@ -44,6 +43,10 @@ L.Bounds = L.Class.extend({ return new L.Point(this.max.x, this.min.y); }, + getSize: function () { + return this.max.subtract(this.min); + }, + contains: function (obj) { // (Bounds) or (Point) -> Boolean var min, max; @@ -82,7 +85,7 @@ L.Bounds = L.Class.extend({ isValid: function () { return !!(this.min && this.max); } -}); +}; L.bounds = function (a, b) { // (Bounds) or (Point, Point) or (Point[]) if (!a || a instanceof L.Bounds) { diff --git a/src/geometry/LineUtil.js b/src/geometry/LineUtil.js index 913eb328..cf2f4044 100644 --- a/src/geometry/LineUtil.js +++ b/src/geometry/LineUtil.js @@ -3,6 +3,8 @@ * and polylines (clipping, simplification, distances, etc.) */ +/*jshint bitwise:false */ // allow bitwise oprations for this file + L.LineUtil = { // Simplify polyline with vertex reduction and Douglas-Peucker simplification. @@ -94,16 +96,11 @@ L.LineUtil = { return reducedPoints; }, - /*jshint bitwise:false */ // temporarily allow bitwise oprations - // Cohen-Sutherland line clipping algorithm. // Used to avoid rendering parts of a polyline that are not currently visible. clipSegment: function (a, b, bounds, useLastCode) { - var min = bounds.min, - max = bounds.max, - - codeA = useLastCode ? this._lastCode : this._getBitCode(a, bounds), + var codeA = useLastCode ? this._lastCode : this._getBitCode(a, bounds), codeB = this._getBitCode(b, bounds), codeOut, p, newCode; @@ -169,8 +166,6 @@ L.LineUtil = { return code; }, - /*jshint bitwise:true */ - // square distance (to avoid unnecessary Math.sqrt calls) _sqDist: function (p1, p2) { var dx = p2.x - p1.x, diff --git a/src/geometry/Point.js b/src/geometry/Point.js index e749c18a..a34b8a0f 100644 --- a/src/geometry/Point.js +++ b/src/geometry/Point.js @@ -84,6 +84,11 @@ L.Point.prototype = { return Math.sqrt(x * x + y * y); }, + equals: function (point) { + return point.x === this.x && + point.y === this.y; + }, + toString: function () { return 'Point(' + L.Util.formatNum(this.x) + ', ' + @@ -95,7 +100,7 @@ L.point = function (x, y, round) { if (x instanceof L.Point) { return x; } - if (x instanceof Array) { + if (L.Util.isArray(x)) { return new L.Point(x[0], x[1]); } if (isNaN(x)) { diff --git a/src/geometry/PolyUtil.js b/src/geometry/PolyUtil.js index dbce39df..68aa36d8 100644 --- a/src/geometry/PolyUtil.js +++ b/src/geometry/PolyUtil.js @@ -11,9 +11,7 @@ L.PolyUtil = {}; * Used to avoid rendering parts of a polygon that are not currently visible. */ L.PolyUtil.clipPolygon = function (points, bounds) { - var min = bounds.min, - max = bounds.max, - clippedPoints, + var clippedPoints, edges = [1, 4, 2, 8], i, j, k, a, b, @@ -55,5 +53,3 @@ L.PolyUtil.clipPolygon = function (points, bounds) { return points; }; - -/*jshint bitwise:true */ diff --git a/src/geometry/Transformation.js b/src/geometry/Transformation.js index 76e5b2b4..df5c6ef5 100644 --- a/src/geometry/Transformation.js +++ b/src/geometry/Transformation.js @@ -2,30 +2,30 @@ * L.Transformation is an utility class to perform simple point transformations through a 2d-matrix. */ -L.Transformation = L.Class.extend({ - initialize: function (/*Number*/ a, /*Number*/ b, /*Number*/ c, /*Number*/ d) { - this._a = a; - this._b = b; - this._c = c; - this._d = d; - }, +L.Transformation = function (a, b, c, d) { + this._a = a; + this._b = b; + this._c = c; + this._d = d; +}; - transform: function (point, scale) { +L.Transformation.prototype = { + transform: function (point, scale) { // (Point, Number) -> Point return this._transform(point.clone(), scale); }, // destructive transform (faster) - _transform: function (/*Point*/ point, /*Number*/ scale) /*-> Point*/ { + _transform: function (point, scale) { scale = scale || 1; point.x = scale * (this._a * point.x + this._b); point.y = scale * (this._c * point.y + this._d); return point; }, - untransform: function (/*Point*/ point, /*Number*/ scale) /*-> Point*/ { + untransform: function (point, scale) { scale = scale || 1; return new L.Point( (point.x / scale - this._b) / this._a, (point.y / scale - this._d) / this._c); } -}); +}; diff --git a/src/images/marker.svg b/src/images/marker.svg new file mode 100644 index 00000000..5e1ee07c --- /dev/null +++ b/src/images/marker.svg @@ -0,0 +1,344 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + Use this one.Select it and the area around with box select.should be exactly 25x41 at 90dpi + How to do the inset border:Delete the existing inset border. Select the background, duplicate.Path, Dynamic offset.Fill: None, Stroke Paint: RGBA #ffffff1f Zoom down to the top and grab the diamond, drag it down and fiddle it untill it looks in linewith the main color layers border. + + + diff --git a/src/layer/FeatureGroup.js b/src/layer/FeatureGroup.js index 036586db..51b8042c 100644 --- a/src/layer/FeatureGroup.js +++ b/src/layer/FeatureGroup.js @@ -1,5 +1,6 @@ /* - * L.FeatureGroup extends L.LayerGroup by introducing mouse events and bindPopup method shared between a group of layers. + * L.FeatureGroup extends L.LayerGroup by introducing mouse events and additional methods + * shared between a group of interactive layers (like vectors or markers). */ L.FeatureGroup = L.LayerGroup.extend({ @@ -19,7 +20,7 @@ L.FeatureGroup = L.LayerGroup.extend({ L.LayerGroup.prototype.addLayer.call(this, layer); if (this._popupContent && layer.bindPopup) { - layer.bindPopup(this._popupContent); + layer.bindPopup(this._popupContent, this._popupOptions); } return this.fire('layeradd', {layer: layer}); @@ -38,9 +39,10 @@ L.FeatureGroup = L.LayerGroup.extend({ return this.fire('layerremove', {layer: layer}); }, - bindPopup: function (content) { + bindPopup: function (content, options) { this._popupContent = content; - return this.invoke('bindPopup', content); + this._popupOptions = options; + return this.invoke('bindPopup', content, options); }, setStyle: function (style) { diff --git a/src/layer/GeoJSON.js b/src/layer/GeoJSON.js index d07e5d1b..0d618a75 100644 --- a/src/layer/GeoJSON.js +++ b/src/layer/GeoJSON.js @@ -1,4 +1,9 @@ +/* + * L.GeoJSON turns any GeoJSON data into a Leaflet layer. + */ + L.GeoJSON = L.FeatureGroup.extend({ + initialize: function (geojson, options) { L.setOptions(this, options); @@ -10,12 +15,15 @@ L.GeoJSON = L.FeatureGroup.extend({ }, addData: function (geojson) { - var features = geojson instanceof Array ? geojson : geojson.features, + var features = L.Util.isArray(geojson) ? geojson : geojson.features, i, len; if (features) { for (i = 0, len = features.length; i < len; i++) { - this.addData(features[i]); + // Only add this if geometry or geometries are set and not null + if (features[i].geometries || features[i].geometry) { + this.addData(features[i]); + } } return this; } @@ -27,6 +35,7 @@ L.GeoJSON = L.FeatureGroup.extend({ var layer = L.GeoJSON.geometryToLayer(geojson, options.pointToLayer); layer.feature = geojson; + layer.defaultOptions = layer.options; this.resetStyle(layer); if (options.onEachFeature) { @@ -39,6 +48,9 @@ L.GeoJSON = L.FeatureGroup.extend({ resetStyle: function (layer) { var style = this.options.style; if (style) { + // reset any custom styles + L.Util.extend(layer.options, layer.defaultOptions); + this._setLayerStyle(layer, style); } }, @@ -91,13 +103,17 @@ L.extend(L.GeoJSON, { latlngs = this.coordsToLatLngs(coords, 1); return new L.MultiPolyline(latlngs); - case "MultiPolygon": + case 'MultiPolygon': latlngs = this.coordsToLatLngs(coords, 2); return new L.MultiPolygon(latlngs); - case "GeometryCollection": + case 'GeometryCollection': for (i = 0, len = geometry.geometries.length; i < len; i++) { - layer = this.geometryToLayer(geometry.geometries[i], pointToLayer); + layer = this.geometryToLayer({ + geometry: geometry.geometries[i], + type: 'Feature', + properties: geojson.properties + }, pointToLayer); layers.push(layer); } return new L.FeatureGroup(layers); @@ -111,7 +127,7 @@ L.extend(L.GeoJSON, { var lat = parseFloat(coords[reverse ? 0 : 1]), lng = parseFloat(coords[reverse ? 1 : 0]); - return new L.LatLng(lat, lng, true); + return new L.LatLng(lat, lng); }, coordsToLatLngs: function (coords, levelsDeep, reverse) { // (Array, Number, Boolean) -> Array diff --git a/src/layer/ImageOverlay.js b/src/layer/ImageOverlay.js index e9fd5867..73fc6528 100644 --- a/src/layer/ImageOverlay.js +++ b/src/layer/ImageOverlay.js @@ -1,3 +1,7 @@ +/* + * L.ImageOverlay is used to overlay images over the map (to specific geographical bounds). + */ + L.ImageOverlay = L.Class.extend({ includes: L.Mixin.Events, @@ -97,8 +101,7 @@ L.ImageOverlay = L.Class.extend({ topLeft = map._latLngToNewLayerPoint(nw, e.zoom, e.center), size = map._latLngToNewLayerPoint(se, e.zoom, e.center)._subtract(topLeft), - currentSize = map.latLngToLayerPoint(se)._subtract(map.latLngToLayerPoint(nw)), - origin = topLeft._add(size._subtract(currentSize)._divideBy(2)); + origin = topLeft._add(size._multiplyBy((1 / 2) * (1 - 1 / scale))); image.style[L.DomUtil.TRANSFORM] = L.DomUtil.getTranslateString(origin) + ' scale(' + scale + ') '; @@ -107,7 +110,7 @@ L.ImageOverlay = L.Class.extend({ _reset: function () { var image = this._image, topLeft = this._map.latLngToLayerPoint(this._bounds.getNorthWest()), - size = this._map.latLngToLayerPoint(this._bounds.getSouthEast())._subtract(topLeft); + size = this._map.latLngToLayerPoint(this._bounds.getSouthEast())._subtract(topLeft); L.DomUtil.setPosition(image, topLeft); diff --git a/src/layer/LayerGroup.js b/src/layer/LayerGroup.js index 7095d919..b593b59e 100644 --- a/src/layer/LayerGroup.js +++ b/src/layer/LayerGroup.js @@ -1,5 +1,6 @@ /* - * L.LayerGroup is a class to combine several layers so you can manipulate the group (e.g. add/remove it) as one layer. + * L.LayerGroup is a class to combine several layers into one so that + * you can manipulate the group (e.g. add/remove it) as one layer. */ L.LayerGroup = L.Class.extend({ @@ -82,6 +83,10 @@ L.LayerGroup = L.Class.extend({ method.call(context, this._layers[i]); } } + }, + + setZIndex: function (zIndex) { + return this.invoke('setZIndex', zIndex); } }); diff --git a/src/layer/Popup.js b/src/layer/Popup.js index 4b609c57..d94e9002 100644 --- a/src/layer/Popup.js +++ b/src/layer/Popup.js @@ -1,3 +1,6 @@ +/* + * L.Popup is used for displaying popups on the map. + */ L.Map.mergeOptions({ closePopupOnClick: true @@ -14,13 +17,15 @@ L.Popup = L.Class.extend({ closeButton: true, offset: new L.Point(0, 6), autoPanPadding: new L.Point(5, 5), - className: '' + className: '', + zoomAnimation: true }, initialize: function (options, source) { L.setOptions(this, options); this._source = source; + this._animated = L.Browser.any3d && this.options.zoomAnimation; }, onAdd: function (map) { @@ -40,7 +45,7 @@ L.Popup = L.Class.extend({ map.on('viewreset', this._updatePosition, this); - if (L.Browser.any3d) { + if (this._animated) { map.on('zoomanim', this._zoomAnimation, this); } @@ -109,7 +114,8 @@ L.Popup = L.Class.extend({ _initLayout: function () { var prefix = 'leaflet-popup', - containerClass = prefix + ' ' + this.options.className + ' leaflet-zoom-animated', + containerClass = prefix + ' ' + this.options.className + ' leaflet-zoom-' + + (this._animated ? 'animated' : 'hide'), container = this._container = L.DomUtil.create('div', containerClass), closeButton; @@ -195,15 +201,15 @@ L.Popup = L.Class.extend({ if (!this._map) { return; } var pos = this._map.latLngToLayerPoint(this._latlng), - is3d = L.Browser.any3d, + animated = this._animated, offset = this.options.offset; - if (is3d) { + if (animated) { L.DomUtil.setPosition(this._container, pos); } - this._containerBottom = -offset.y - (is3d ? 0 : pos.y); - this._containerLeft = -Math.round(this._containerWidth / 2) + offset.x + (is3d ? 0 : pos.x); + this._containerBottom = -offset.y - (animated ? 0 : pos.y); + this._containerLeft = -Math.round(this._containerWidth / 2) + offset.x + (animated ? 0 : pos.x); //Bottom position the popup in case the height of the popup changes (images loading etc) this._container.style.bottom = this._containerBottom + 'px'; @@ -225,7 +231,7 @@ L.Popup = L.Class.extend({ layerPos = new L.Point(this._containerLeft, -containerHeight - this._containerBottom); - if (L.Browser.any3d) { + if (this._animated) { layerPos._add(L.DomUtil.getPosition(this._container)); } diff --git a/src/layer/marker/DivIcon.js b/src/layer/marker/DivIcon.js index 99a2f273..6868b155 100644 --- a/src/layer/marker/DivIcon.js +++ b/src/layer/marker/DivIcon.js @@ -1,3 +1,8 @@ +/* + * L.DivIcon is a lightweight HTML-based icon class (as opposed to the image-based L.Icon) + * to use with L.Marker. + */ + L.DivIcon = L.Icon.extend({ options: { iconSize: new L.Point(12, 12), // also can be set through CSS diff --git a/src/layer/marker/Icon.Default.js b/src/layer/marker/Icon.Default.js index 23909a24..25094bee 100644 --- a/src/layer/marker/Icon.Default.js +++ b/src/layer/marker/Icon.Default.js @@ -1,3 +1,6 @@ +/* + * L.Icon.Default is the blue marker icon used by default in Leaflet. + */ L.Icon.Default = L.Icon.extend({ @@ -16,6 +19,10 @@ L.Icon.Default = L.Icon.extend({ return this.options[key]; } + if (L.Browser.retina && name === 'icon') { + name += '@2x'; + } + var path = L.Icon.Default.imagePath; if (!path) { diff --git a/src/layer/marker/Icon.js b/src/layer/marker/Icon.js index 1a673259..76b680a9 100644 --- a/src/layer/marker/Icon.js +++ b/src/layer/marker/Icon.js @@ -1,11 +1,17 @@ +/* + * L.Icon is an image-based icon class that you can use with L.Marker for custom markers. + */ + L.Icon = L.Class.extend({ options: { /* iconUrl: (String) (required) + iconRetinaUrl: (String) (optional, used for retina devices if detected) iconSize: (Point) (can be set through CSS) iconAnchor: (Point) (centered by default, can be set in CSS with negative margins) popupAnchor: (Point) (if not specified, popup opens in the anchor point) shadowUrl: (Point) (no shadow by default) + shadowRetinaUrl: (String) (optional, used for retina devices if detected) shadowSize: (Point) shadowAnchor: (Point) */ @@ -83,6 +89,9 @@ L.Icon = L.Class.extend({ }, _getIconUrl: function (name) { + if (L.Browser.retina && this.options[name + 'RetinaUrl']) { + return this.options[name + 'RetinaUrl']; + } return this.options[name + 'Url']; } }); diff --git a/src/layer/marker/Marker.Drag.js b/src/layer/marker/Marker.Drag.js index 38834fa2..93786d41 100644 --- a/src/layer/marker/Marker.Drag.js +++ b/src/layer/marker/Marker.Drag.js @@ -26,14 +26,14 @@ L.Handler.MarkerDrag = L.Handler.extend({ return this._draggable && this._draggable._moved; }, - _onDragStart: function (e) { + _onDragStart: function () { this._marker .closePopup() .fire('movestart') .fire('dragstart'); }, - _onDrag: function (e) { + _onDrag: function () { var marker = this._marker, shadow = marker._shadow, iconPos = L.DomUtil.getPosition(marker._icon), diff --git a/src/layer/marker/Marker.Popup.js b/src/layer/marker/Marker.Popup.js index d05a3d7e..2171bbff 100644 --- a/src/layer/marker/Marker.Popup.js +++ b/src/layer/marker/Marker.Popup.js @@ -1,5 +1,5 @@ /* - * Popup extension to L.Marker, adding openPopup & bindPopup methods. + * Popup extension to L.Marker, adding popup-related methods. */ L.Marker.include({ diff --git a/src/layer/marker/Marker.js b/src/layer/marker/Marker.js index 7e8a4bdf..2c3a76a8 100644 --- a/src/layer/marker/Marker.js +++ b/src/layer/marker/Marker.js @@ -62,12 +62,14 @@ L.Marker = L.Class.extend({ this.update(); - this.fire('move', { latlng: this._latlng }); + return this.fire('move', { latlng: this._latlng }); }, setZIndexOffset: function (offset) { this.options.zIndexOffset = offset; this.update(); + + return this; }, setIcon: function (icon) { @@ -81,13 +83,17 @@ L.Marker = L.Class.extend({ this._initIcon(); this.update(); } + + return this; }, update: function () { - if (!this._icon) { return; } + if (this._icon) { + var pos = this._map.latLngToLayerPoint(this._latlng).round(); + this._setPos(pos); + } - var pos = this._map.latLngToLayerPoint(this._latlng).round(); - this._setPos(pos); + return this; }, _initIcon: function () { @@ -179,12 +185,13 @@ L.Marker = L.Class.extend({ }, _initInteraction: function () { - if (!this.options.clickable) { - return; - } + + if (!this.options.clickable) { return; } + + // TODO refactor into something shared with Map/Path/etc. to DRY it up var icon = this._icon, - events = ['dblclick', 'mousedown', 'mouseover', 'mouseout']; + events = ['dblclick', 'mousedown', 'mouseover', 'mouseout', 'contextmenu']; L.DomUtil.addClass(icon, 'leaflet-clickable'); L.DomEvent.on(icon, 'click', this._onMouseClick, this); @@ -204,20 +211,31 @@ L.Marker = L.Class.extend({ _onMouseClick: function (e) { var wasDragged = this.dragging && this.dragging.moved(); + if (this.hasEventListeners(e.type) || wasDragged) { L.DomEvent.stopPropagation(e); } + if (wasDragged) { return; } - if (this._map.dragging && this._map.dragging.moved()) { return; } + + if ((!this.dragging || !this.dragging._enabled) && this._map.dragging && this._map.dragging.moved()) { return; } + this.fire(e.type, { originalEvent: e }); }, _fireMouseEvent: function (e) { + this.fire(e.type, { originalEvent: e }); + + // TODO proper custom event propagation + // this line will always be called if marker is in a FeatureGroup + if (e.type === 'contextmenu' && this.hasEventListeners(e.type)) { + L.DomEvent.preventDefault(e); + } if (e.type !== 'mousedown') { L.DomEvent.stopPropagation(e); } diff --git a/src/layer/tile/TileLayer.Canvas.js b/src/layer/tile/TileLayer.Canvas.js index 1012118b..8c6ac3d3 100644 --- a/src/layer/tile/TileLayer.Canvas.js +++ b/src/layer/tile/TileLayer.Canvas.js @@ -1,3 +1,8 @@ +/* + * L.TileLayer.Canvas is a class that you can use as a base for creating + * dynamically drawn Canvas-based tile layers. + */ + L.TileLayer.Canvas = L.TileLayer.extend({ options: { async: false @@ -43,7 +48,7 @@ L.TileLayer.Canvas = L.TileLayer.extend({ } }, - drawTile: function (tile, tilePoint) { + drawTile: function (/*tile, tilePoint*/) { // override with rendering code }, diff --git a/src/layer/tile/TileLayer.WMS.js b/src/layer/tile/TileLayer.WMS.js index 9ecffd45..1daba748 100644 --- a/src/layer/tile/TileLayer.WMS.js +++ b/src/layer/tile/TileLayer.WMS.js @@ -1,3 +1,7 @@ +/* + * L.TileLayer.WMS is used for putting WMS tile layers on the map. + */ + L.TileLayer.WMS = L.TileLayer.extend({ defaultWmsParams: { @@ -44,6 +48,8 @@ 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, @@ -58,7 +64,7 @@ L.TileLayer.WMS = L.TileLayer.extend({ url = L.Util.template(this._url, {s: this._getSubdomain(tilePoint)}); - return url + L.Util.getParamString(this.wmsParams) + "&bbox=" + bbox; + return url + L.Util.getParamString(this.wmsParams, url) + "&bbox=" + bbox; }, setParams: function (params, noRedraw) { diff --git a/src/layer/tile/TileLayer.js b/src/layer/tile/TileLayer.js index ab0ba41c..d17b6184 100644 --- a/src/layer/tile/TileLayer.js +++ b/src/layer/tile/TileLayer.js @@ -81,7 +81,7 @@ L.TileLayer = L.Class.extend({ }, onRemove: function (map) { - map._panes.tilePane.removeChild(this._container); + this._container.parentNode.removeChild(this._container); map.off({ 'viewreset': this._resetCallback, @@ -166,7 +166,7 @@ L.TileLayer = L.Class.extend({ _setAutoZIndex: function (pane, compare) { - var layers = pane.getElementsByClassName('leaflet-layer'), + var layers = pane.children, edgeZIndex = -compare(Infinity, -Infinity), // -Infinity for max, Infinity for min zIndex, i, len; @@ -244,7 +244,7 @@ L.TileLayer = L.Class.extend({ this._initContainer(); }, - _update: function (e) { + _update: function () { if (!this._map) { return; } @@ -465,9 +465,8 @@ L.TileLayer = L.Class.extend({ return this._createTile(); }, - _resetTile: function (tile) { - // Override if data stored on a tile needs to be cleaned up before reuse - }, + // Override if data stored on a tile needs to be cleaned up before reuse + _resetTile: function (/*tile*/) {}, _createTile: function () { var tile = this._tileImg.cloneNode(false); @@ -490,7 +489,7 @@ L.TileLayer = L.Class.extend({ } }, - _tileOnLoad: function (e) { + _tileOnLoad: function () { var layer = this._layer; //Only if we are loading an actual image @@ -506,7 +505,7 @@ L.TileLayer = L.Class.extend({ layer._tileLoaded(); }, - _tileOnError: function (e) { + _tileOnError: function () { var layer = this._layer; layer.fire('tileerror', { diff --git a/src/layer/vector/Circle.js b/src/layer/vector/Circle.js index 89fd5be8..e9cbc489 100644 --- a/src/layer/vector/Circle.js +++ b/src/layer/vector/Circle.js @@ -26,7 +26,7 @@ L.Circle = L.Path.extend({ projectLatlngs: function () { var lngRadius = this._getLngRadius(), - latlng2 = new L.LatLng(this._latlng.lat, this._latlng.lng - lngRadius, true), + latlng2 = new L.LatLng(this._latlng.lat, this._latlng.lng - lngRadius), point2 = this._map.latLngToLayerPoint(latlng2); this._point = this._map.latLngToLayerPoint(this._latlng); diff --git a/src/layer/vector/CircleMarker.js b/src/layer/vector/CircleMarker.js index 39d323bf..f2a9e23e 100644 --- a/src/layer/vector/CircleMarker.js +++ b/src/layer/vector/CircleMarker.js @@ -16,6 +16,11 @@ L.CircleMarker = L.Circle.extend({ projectLatlngs: function () { this._point = this._map.latLngToLayerPoint(this._latlng); }, + + _updateStyle : function () { + L.Circle.prototype._updateStyle.call(this); + this.setRadius(this.options.radius); + }, setRadius: function (radius) { this._radius = radius; diff --git a/src/layer/vector/Path.Popup.js b/src/layer/vector/Path.Popup.js index affb9d8d..ff2a9b9f 100644 --- a/src/layer/vector/Path.Popup.js +++ b/src/layer/vector/Path.Popup.js @@ -1,5 +1,5 @@ /* - * Popup extension to L.Path (polylines, polygons, circles), adding bindPopup method. + * Popup extension to L.Path (polylines, polygons, circles), adding popup-related methods. */ L.Path.include({ @@ -27,7 +27,7 @@ L.Path.include({ if (this._popup) { this._popup = null; this - .off('click', this.openPopup) + .off('click', this._openPopup) .off('remove', this.closePopup); this._popupHandlersAdded = false; diff --git a/src/layer/vector/Path.SVG.js b/src/layer/vector/Path.SVG.js index 879d20dc..eec3b093 100644 --- a/src/layer/vector/Path.SVG.js +++ b/src/layer/vector/Path.SVG.js @@ -1,3 +1,7 @@ +/* + * Extends L.Path with SVG-specific rendering code. + */ + L.Path.SVG_NS = 'http://www.w3.org/2000/svg'; L.Browser.svg = !!(document.createElementNS && document.createElementNS(L.Path.SVG_NS, 'svg').createSVGRect); @@ -159,13 +163,12 @@ L.Map.include({ } }, - _animatePathZoom: function (opt) { - var scale = this.getZoomScale(opt.zoom), - offset = this._getCenterOffset(opt.center), - translate = offset.multiplyBy(-scale)._add(this._pathViewport.min); + _animatePathZoom: function (e) { + var scale = this.getZoomScale(e.zoom), + offset = this._getCenterOffset(e.center)._multiplyBy(-scale)._add(this._pathViewport.min); this._pathRoot.style[L.DomUtil.TRANSFORM] = - L.DomUtil.getTranslateString(translate) + ' scale(' + scale + ') '; + L.DomUtil.getTranslateString(offset) + ' scale(' + scale + ') '; this._pathZooming = true; }, diff --git a/src/layer/vector/Path.VML.js b/src/layer/vector/Path.VML.js index 2dee9855..12e6b201 100644 --- a/src/layer/vector/Path.VML.js +++ b/src/layer/vector/Path.VML.js @@ -74,11 +74,15 @@ L.Path = L.Browser.svg || !L.Browser.vml ? L.Path : L.Path.extend({ stroke.weight = options.weight + 'px'; stroke.color = options.color; stroke.opacity = options.opacity; + if (options.dashArray) { - stroke.dashStyle = options.dashArray.replace(/ *, */g, ' '); + stroke.dashStyle = options.dashArray instanceof Array ? + options.dashArray.join(' ') : + options.dashArray.replace(/ *, */g, ' '); } else { stroke.dashStyle = ''; } + } else if (stroke) { container.removeChild(stroke); this._stroke = null; @@ -91,6 +95,7 @@ L.Path = L.Browser.svg || !L.Browser.vml ? L.Path : L.Path.extend({ } fill.color = options.fillColor || options.color; fill.opacity = options.fillOpacity; + } else if (fill) { container.removeChild(fill); this._fill = null; diff --git a/src/layer/vector/Path.js b/src/layer/vector/Path.js index ad2f1143..ca242fe9 100644 --- a/src/layer/vector/Path.js +++ b/src/layer/vector/Path.js @@ -47,6 +47,8 @@ L.Path = L.Class.extend({ this._map._pathRoot.appendChild(this._container); } + this.fire('add'); + map.on({ 'viewreset': this.projectLatlngs, 'moveend': this._updatePath @@ -61,6 +63,8 @@ L.Path = L.Class.extend({ onRemove: function (map) { map._pathRoot.removeChild(this._container); + // Need to fire remove event before we set _map to null as the event hooks might need the object + this.fire('remove'); this._map = null; if (L.Browser.vml) { @@ -69,8 +73,6 @@ L.Path = L.Class.extend({ this._fill = null; } - this.fire('remove'); - map.off({ 'viewreset': this.projectLatlngs, 'moveend': this._updatePath diff --git a/src/layer/vector/Polygon.js b/src/layer/vector/Polygon.js index 6a38687b..44665593 100644 --- a/src/layer/vector/Polygon.js +++ b/src/layer/vector/Polygon.js @@ -10,7 +10,7 @@ L.Polygon = L.Polyline.extend({ initialize: function (latlngs, options) { L.Polyline.prototype.initialize.call(this, latlngs, options); - if (latlngs && (latlngs[0] instanceof Array) && (typeof latlngs[0][0] !== 'number')) { + if (latlngs && L.Util.isArray(latlngs[0]) && (typeof latlngs[0][0] !== 'number')) { this._latlngs = this._convertLatLngs(latlngs[0]); this._holes = latlngs.slice(1); } @@ -25,7 +25,7 @@ L.Polygon = L.Polyline.extend({ if (!this._holes) { return; } - var i, j, len, len2, hole; + var i, j, len, len2; for (i = 0, len = this._holes.length; i < len; i++) { this._holePoints[i] = []; diff --git a/src/layer/vector/Polyline.Edit.js b/src/layer/vector/Polyline.Edit.js index 9256dbe6..84640340 100644 --- a/src/layer/vector/Polyline.Edit.js +++ b/src/layer/vector/Polyline.Edit.js @@ -1,3 +1,7 @@ +/* + * L.Handler.PolyEdit is an editing handler for polylines and polygons. + */ + L.Handler.PolyEdit = L.Handler.extend({ options: { icon: new L.DivIcon({ @@ -220,3 +224,26 @@ L.Handler.PolyEdit = L.Handler.extend({ 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(); + } + }); +}); diff --git a/src/layer/vector/Polyline.js b/src/layer/vector/Polyline.js index df6e3ed4..767f26f2 100644 --- a/src/layer/vector/Polyline.js +++ b/src/layer/vector/Polyline.js @@ -1,17 +1,12 @@ +/* + * L.Polygon is used to display polylines on a map. + */ + L.Polyline = L.Path.extend({ initialize: function (latlngs, options) { L.Path.prototype.initialize.call(this, options); this._latlngs = this._convertLatLngs(latlngs); - - // TODO refactor: move to Polyline.Edit.js - if (L.Handler.PolyEdit) { - this.editing = new L.Handler.PolyEdit(this); - - if (this.options.editable) { - this.editing.enable(); - } - } }, options: { @@ -50,7 +45,7 @@ L.Polyline = L.Path.extend({ return this.redraw(); }, - spliceLatLngs: function (index, howMany) { + spliceLatLngs: function () { // (Number index, Number howMany) var removed = [].splice.apply(this._latlngs, arguments); this._convertLatLngs(this._latlngs); this.redraw(); @@ -90,27 +85,10 @@ L.Polyline = L.Path.extend({ return bounds; }, - // TODO refactor: move to Polyline.Edit.js - onAdd: function (map) { - L.Path.prototype.onAdd.call(this, map); - - if (this.editing && this.editing.enabled()) { - this.editing.addHooks(); - } - }, - - onRemove: function (map) { - if (this.editing && this.editing.enabled()) { - this.editing.removeHooks(); - } - - L.Path.prototype.onRemove.call(this, map); - }, - _convertLatLngs: function (latlngs) { var i, len; for (i = 0, len = latlngs.length; i < len; i++) { - if (latlngs[i] instanceof Array && typeof latlngs[i][0] !== 'number') { + if (L.Util.isArray(latlngs[i]) && typeof latlngs[i][0] !== 'number') { return; } latlngs[i] = L.latLng(latlngs[i]); diff --git a/src/layer/vector/Rectangle.js b/src/layer/vector/Rectangle.js index d832cc14..108f9680 100644 --- a/src/layer/vector/Rectangle.js +++ b/src/layer/vector/Rectangle.js @@ -1,5 +1,5 @@ /* - * L.Rectangle extends Polygon and creates a rectangle when passed a LatLngBounds + * L.Rectangle extends Polygon and creates a rectangle when passed a LatLngBounds object. */ L.Rectangle = L.Polygon.extend({ @@ -17,8 +17,7 @@ L.Rectangle = L.Polygon.extend({ latLngBounds.getSouthWest(), latLngBounds.getNorthWest(), latLngBounds.getNorthEast(), - latLngBounds.getSouthEast(), - latLngBounds.getSouthWest() + latLngBounds.getSouthEast() ]; } }); diff --git a/src/layer/vector/canvas/Circle.Canvas.js b/src/layer/vector/canvas/Circle.Canvas.js index bfecc2f0..1128b7b0 100644 --- a/src/layer/vector/canvas/Circle.Canvas.js +++ b/src/layer/vector/canvas/Circle.Canvas.js @@ -1,5 +1,5 @@ /* - * Circle canvas specific drawing parts. + * Extends L.Circle with Canvas-specific code. */ L.Circle.include(!L.Path.CANVAS ? {} : { diff --git a/src/layer/vector/canvas/Path.Canvas.js b/src/layer/vector/canvas/Path.Canvas.js index 4a0c1606..f184cf0d 100644 --- a/src/layer/vector/canvas/Path.Canvas.js +++ b/src/layer/vector/canvas/Path.Canvas.js @@ -36,6 +36,10 @@ L.Path = (L.Path.SVG && !window.L_PREFER_CANVAS) || !L.Browser.canvas ? L.Path : .off('viewreset', this.projectLatlngs, this) .off('moveend', this._updatePath, this); + if (this.options.clickable) { + this._map.off('click', this._onClick, this); + } + this._requestUpdate(); this._map = null; @@ -103,16 +107,12 @@ L.Path = (L.Path.SVG && !window.L_PREFER_CANVAS) || !L.Browser.canvas ? L.Path : this._updateStyle(); if (options.fill) { - if (options.fillOpacity < 1) { - ctx.globalAlpha = options.fillOpacity; - } + ctx.globalAlpha = options.fillOpacity; ctx.fill(); } if (options.stroke) { - if (options.opacity < 1) { - ctx.globalAlpha = options.opacity; - } + ctx.globalAlpha = options.opacity; ctx.stroke(); } @@ -131,7 +131,12 @@ L.Path = (L.Path.SVG && !window.L_PREFER_CANVAS) || !L.Browser.canvas ? L.Path : _onClick: function (e) { if (this._containsPoint(e.layerPoint)) { - this.fire('click', e); + this.fire('click', { + latlng: e.latlng, + layerPoint: e.layerPoint, + containerPoint: e.containerPoint, + originalEvent: e + }); } } }); diff --git a/src/layer/vector/canvas/Polygon.Canvas.js b/src/layer/vector/canvas/Polygon.Canvas.js index b18328bc..34513511 100644 --- a/src/layer/vector/canvas/Polygon.Canvas.js +++ b/src/layer/vector/canvas/Polygon.Canvas.js @@ -1,3 +1,6 @@ +/* + * Extends L.Polygon to be able to manually detect clicks on Canvas-rendered polygons. + */ L.Polygon.include(!L.Path.CANVAS ? {} : { _containsPoint: function (p) { diff --git a/src/layer/vector/canvas/Polyline.Canvas.js b/src/layer/vector/canvas/Polyline.Canvas.js index 8c65fbaa..7ecce3b6 100644 --- a/src/layer/vector/canvas/Polyline.Canvas.js +++ b/src/layer/vector/canvas/Polyline.Canvas.js @@ -1,3 +1,6 @@ +/* + * Extends L.Polyline to be able to manually detect clicks on Canvas-rendered polylines. + */ L.Polyline.include(!L.Path.CANVAS ? {} : { _containsPoint: function (p, closed) { diff --git a/src/map/Map.js b/src/map/Map.js index 25817726..b6787738 100644 --- a/src/map/Map.js +++ b/src/map/Map.js @@ -25,7 +25,7 @@ L.Map = L.Class.extend({ this._initContainer(id); this._initLayout(); - this._initHooks(); + this.callInitHooks(); this._initEvents(); if (options.maxBounds) { @@ -148,11 +148,9 @@ L.Map = L.Class.extend({ this._layers[id] = layer; // TODO getMaxZoom, getMinZoom in ILayer (instead of options) - if (layer.options && !isNaN(layer.options.maxZoom)) { - this._layersMaxZoom = Math.max(this._layersMaxZoom || 0, layer.options.maxZoom); - } - if (layer.options && !isNaN(layer.options.minZoom)) { - this._layersMinZoom = Math.min(this._layersMinZoom || Infinity, layer.options.minZoom); + if (layer.options && (!isNaN(layer.options.maxZoom) || !isNaN(layer.options.minZoom))) { + this._zoomBoundLayers[id] = layer; + this._updateZoomLevels(); } // TODO looks ugly, refactor!!! @@ -178,6 +176,10 @@ L.Map = L.Class.extend({ layer.onRemove(this); delete this._layers[id]; + if (this._zoomBoundLayers[id]) { + delete this._zoomBoundLayers[id]; + this._updateZoomLevels(); + } // TODO looks ugly, refactor if (this.options.zoomAnimation && L.TileLayer && (layer instanceof L.TileLayer)) { @@ -415,7 +417,6 @@ L.Map = L.Class.extend({ _initLayout: function () { var container = this._container; - container.innerHTML = ''; L.DomUtil.addClass(container, 'leaflet-container'); if (L.Browser.touch) { @@ -464,19 +465,11 @@ L.Map = L.Class.extend({ return L.DomUtil.create('div', className, container || this._panes.objectsPane); }, - _initializers: [], - - _initHooks: function () { - var i, len; - for (i = 0, len = this._initializers.length; i < len; i++) { - this._initializers[i].call(this); - } - }, - _initLayers: function (layers) { - layers = layers ? (layers instanceof Array ? layers : [layers]) : []; + layers = layers ? (L.Util.isArray(layers) ? layers : [layers]) : []; this._layers = {}; + this._zoomBoundLayers = {}; this._tileLayersNum = 0; var i, len; @@ -535,6 +528,30 @@ L.Map = L.Class.extend({ L.DomUtil.setPosition(this._mapPane, this._getMapPanePos().subtract(offset)); }, + _updateZoomLevels: function () { + var i, + minZoom = Infinity, + maxZoom = -Infinity; + + for (i in this._zoomBoundLayers) { + if (this._zoomBoundLayers.hasOwnProperty(i)) { + var layer = this._zoomBoundLayers[i]; + if (!isNaN(layer.options.minZoom)) { + minZoom = Math.min(minZoom, layer.options.minZoom); + } + if (!isNaN(layer.options.maxZoom)) { + maxZoom = Math.max(maxZoom, layer.options.maxZoom); + } + } + } + + if (i === undefined) { // we have no tilelayers + this._layersMaxZoom = this._layersMinZoom = undefined; + } else { + this._layersMaxZoom = maxZoom; + this._layersMinZoom = minZoom; + } + }, // map events @@ -655,16 +672,6 @@ L.Map = L.Class.extend({ } }); -L.Map.addInitHook = function (fn) { - var args = Array.prototype.slice.call(arguments, 1); - - var init = typeof fn === 'function' ? fn : function () { - this[fn].apply(this, args); - }; - - this.prototype._initializers.push(init); -}; - L.map = function (id, options) { return new L.Map(id, options); }; diff --git a/src/map/anim/Map.PanAnimation.js b/src/map/anim/Map.PanAnimation.js index a0e519a3..c1bd3077 100644 --- a/src/map/anim/Map.PanAnimation.js +++ b/src/map/anim/Map.PanAnimation.js @@ -1,3 +1,6 @@ +/* + * Extends L.Map to handle panning animations. + */ L.Map.include({ @@ -49,7 +52,7 @@ L.Map.include({ L.DomUtil.addClass(this._mapPane, 'leaflet-pan-anim'); - var newPos = L.DomUtil.getPosition(this._mapPane).subtract(offset); + var newPos = L.DomUtil.getPosition(this._mapPane).subtract(offset)._round(); this._panAnim.run(this._mapPane, newPos, duration || 0.25, easeLinearity); return this; diff --git a/src/map/anim/Map.ZoomAnimation.js b/src/map/anim/Map.ZoomAnimation.js index e3a7672e..87387701 100644 --- a/src/map/anim/Map.ZoomAnimation.js +++ b/src/map/anim/Map.ZoomAnimation.js @@ -1,3 +1,7 @@ +/* + * Extends L.Map to handle zoom animations. + */ + L.Map.mergeOptions({ zoomAnimation: L.DomUtil.TRANSITION && !L.Browser.android23 && !L.Browser.mobileOpera }); @@ -41,7 +45,7 @@ L.Map.include(!L.DomUtil.TRANSITION ? {} : { return true; }, - _catchTransitionEnd: function (e) { + _catchTransitionEnd: function () { if (this._animatingZoom) { this._onZoomTransitionEnd(); } @@ -139,11 +143,11 @@ L.Map.include(!L.DomUtil.TRANSITION ? {} : { _onZoomTransitionEnd: function () { this._restoreTileFront(); - L.Util.falseFn(this._tileBg.offsetWidth); // force reflow - this._resetView(this._animateToCenter, this._animateToZoom, true, true); 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; diff --git a/src/map/ext/Map.Control.js b/src/map/ext/Map.Control.js index b1bf085a..b787d102 100644 --- a/src/map/ext/Map.Control.js +++ b/src/map/ext/Map.Control.js @@ -1,3 +1,7 @@ +/* + * Adds control-related methods to L.Map. + */ + L.Map.include({ addControl: function (control) { control.addTo(this); diff --git a/src/map/ext/Map.Geolocation.js b/src/map/ext/Map.Geolocation.js index d1f3dd90..7165377d 100644 --- a/src/map/ext/Map.Geolocation.js +++ b/src/map/ext/Map.Geolocation.js @@ -1,5 +1,5 @@ /* - * Provides L.Map with convenient shortcuts for W3C geolocation. + * Provides L.Map with convenient shortcuts for using browser geolocation features. */ L.Map.include({ diff --git a/src/map/ext/Map.Popup.js b/src/map/ext/Map.Popup.js index e077c529..e59d55e9 100644 --- a/src/map/ext/Map.Popup.js +++ b/src/map/ext/Map.Popup.js @@ -1,3 +1,6 @@ +/* + * Adds popup-related methods to L.Map. + */ L.Map.include({ openPopup: function (popup) { diff --git a/src/map/handler/Map.BoxZoom.js b/src/map/handler/Map.BoxZoom.js index 204c8299..0d8bc184 100644 --- a/src/map/handler/Map.BoxZoom.js +++ b/src/map/handler/Map.BoxZoom.js @@ -1,5 +1,6 @@ /* - * L.Handler.ShiftDragZoom is used internally by L.Map to add shift-drag zoom (zoom to a selected bounding box). + * L.Handler.ShiftDragZoom is used to add shift-drag zoom interaction to the map + * (zoom to a selected bounding box), enabled by default. */ L.Map.mergeOptions({ @@ -71,9 +72,11 @@ L.Map.BoxZoom = L.Handler.extend({ .off(document, 'mouseup', this._onMouseUp); var map = this._map, - layerPoint = map.mouseEventToLayerPoint(e), + layerPoint = map.mouseEventToLayerPoint(e); - bounds = new L.LatLngBounds( + if (this._startLayerPoint.equals(layerPoint)) { return; } + + var bounds = new L.LatLngBounds( map.layerPointToLatLng(this._startLayerPoint), map.layerPointToLatLng(layerPoint)); diff --git a/src/map/handler/Map.DoubleClickZoom.js b/src/map/handler/Map.DoubleClickZoom.js index 7a7850dc..1cc393cd 100644 --- a/src/map/handler/Map.DoubleClickZoom.js +++ b/src/map/handler/Map.DoubleClickZoom.js @@ -1,5 +1,5 @@ /* - * L.Handler.DoubleClickZoom is used internally by L.Map to add double-click zooming. + * L.Handler.DoubleClickZoom is used to handle double-click zoom on the map, enabled by default. */ L.Map.mergeOptions({ @@ -20,4 +20,4 @@ L.Map.DoubleClickZoom = L.Handler.extend({ } }); -L.Map.addInitHook('addHandler', 'doubleClickZoom', L.Map.DoubleClickZoom); \ No newline at end of file +L.Map.addInitHook('addHandler', 'doubleClickZoom', L.Map.DoubleClickZoom); diff --git a/src/map/handler/Map.Drag.js b/src/map/handler/Map.Drag.js index cfec2127..529ebf5d 100644 --- a/src/map/handler/Map.Drag.js +++ b/src/map/handler/Map.Drag.js @@ -1,5 +1,5 @@ /* - * L.Handler.MapDrag is used internally by L.Map to make the map draggable. + * L.Handler.MapDrag is used to make the map draggable (with panning inertia), enabled by default. */ L.Map.mergeOptions({ @@ -14,7 +14,7 @@ L.Map.mergeOptions({ longPress: true, // TODO refactor, move to CRS - worldCopyJump: true + worldCopyJump: false }); L.Map.Drag = L.Handler.extend({ @@ -83,6 +83,7 @@ L.Map.Drag = L.Handler.extend({ }, _onViewReset: function () { + // TODO fix hardcoded Earth values var pxCenter = this._map.getSize()._divideBy(2), pxWorldCenter = this._map.latLngToLayerPoint(new L.LatLng(0, 0)); @@ -92,8 +93,7 @@ L.Map.Drag = L.Handler.extend({ _onPreDrag: function () { // TODO refactor to be able to adjust map pane position after zoom - var map = this._map, - worldWidth = this._worldWidth, + var worldWidth = this._worldWidth, halfWidth = Math.round(worldWidth / 2), dx = this._initialWorldOffset, x = this._draggable._newPos.x, @@ -109,9 +109,7 @@ L.Map.Drag = L.Handler.extend({ options = map.options, delay = +new Date() - this._lastTime, - noInertia = !options.inertia || - delay > options.inertiaThreshold || - !this._positions[0]; + noInertia = !options.inertia || delay > options.inertiaThreshold || !this._positions[0]; if (noInertia) { map.fire('moveend'); @@ -120,18 +118,19 @@ L.Map.Drag = L.Handler.extend({ var direction = this._lastPos.subtract(this._positions[0]), duration = (this._lastTime + delay - this._times[0]) / 1000, + ease = options.easeLinearity, - speedVector = direction.multiplyBy(options.easeLinearity / duration), + speedVector = direction.multiplyBy(ease / duration), speed = speedVector.distanceTo(new L.Point(0, 0)), limitedSpeed = Math.min(options.inertiaMaxSpeed, speed), limitedSpeedVector = speedVector.multiplyBy(limitedSpeed / speed), - decelerationDuration = limitedSpeed / (options.inertiaDeceleration * options.easeLinearity), + decelerationDuration = limitedSpeed / (options.inertiaDeceleration * ease), offset = limitedSpeedVector.multiplyBy(-decelerationDuration / 2).round(); L.Util.requestAnimFrame(function () { - map.panBy(offset, decelerationDuration, options.easeLinearity); + map.panBy(offset, decelerationDuration, ease); }); } diff --git a/src/map/handler/Map.Keyboard.js b/src/map/handler/Map.Keyboard.js index be7ad73e..1f7b07d0 100644 --- a/src/map/handler/Map.Keyboard.js +++ b/src/map/handler/Map.Keyboard.js @@ -1,3 +1,7 @@ +/* + * L.Map.Keyboard is handling keyboard interaction with the map, enabled by default. + */ + L.Map.mergeOptions({ keyboard: true, keyboardPanOffset: 80, @@ -6,7 +10,6 @@ L.Map.mergeOptions({ L.Map.Keyboard = L.Handler.extend({ - // list of e.keyCode values for particular actions keyCodes: { left: [37], right: [39], @@ -32,9 +35,9 @@ L.Map.Keyboard = L.Handler.extend({ } L.DomEvent - .addListener(container, 'focus', this._onFocus, this) - .addListener(container, 'blur', this._onBlur, this) - .addListener(container, 'mousedown', this._onMouseDown, this); + .on(container, 'focus', this._onFocus, this) + .on(container, 'blur', this._onBlur, this) + .on(container, 'mousedown', this._onMouseDown, this); this._map .on('focus', this._addHooks, this) @@ -47,9 +50,9 @@ L.Map.Keyboard = L.Handler.extend({ var container = this._map._container; L.DomEvent - .removeListener(container, 'focus', this._onFocus, this) - .removeListener(container, 'blur', this._onBlur, this) - .removeListener(container, 'mousedown', this._onMouseDown, this); + .off(container, 'focus', this._onFocus, this) + .off(container, 'blur', this._onBlur, this) + .off(container, 'mousedown', this._onMouseDown, this); this._map .off('focus', this._addHooks, this) @@ -105,21 +108,26 @@ L.Map.Keyboard = L.Handler.extend({ }, _addHooks: function () { - L.DomEvent.addListener(document, 'keydown', this._onKeyDown, this); + L.DomEvent.on(document, 'keydown', this._onKeyDown, this); }, _removeHooks: function () { - L.DomEvent.removeListener(document, 'keydown', this._onKeyDown, this); + L.DomEvent.off(document, 'keydown', this._onKeyDown, this); }, _onKeyDown: function (e) { - var key = e.keyCode; + var key = e.keyCode, + map = this._map; if (this._panKeys.hasOwnProperty(key)) { - this._map.panBy(this._panKeys[key]); + map.panBy(this._panKeys[key]); + + if (map.options.maxBounds) { + map.panInsideBounds(map.options.maxBounds); + } } else if (this._zoomKeys.hasOwnProperty(key)) { - this._map.setZoom(this._map.getZoom() + this._zoomKeys[key]); + map.setZoom(map.getZoom() + this._zoomKeys[key]); } else { return; diff --git a/src/map/handler/Map.ScrollWheelZoom.js b/src/map/handler/Map.ScrollWheelZoom.js index af0dfc0b..7d9723fa 100644 --- a/src/map/handler/Map.ScrollWheelZoom.js +++ b/src/map/handler/Map.ScrollWheelZoom.js @@ -3,7 +3,7 @@ */ L.Map.mergeOptions({ - scrollWheelZoom: !L.Browser.touch || L.Browser.msTouch + scrollWheelZoom: true }); L.Map.ScrollWheelZoom = L.Handler.extend({ @@ -37,9 +37,10 @@ L.Map.ScrollWheelZoom = L.Handler.extend({ _performZoom: function () { var map = this._map, - delta = Math.round(this._delta), + delta = this._delta, zoom = map.getZoom(); + delta = delta > 0 ? Math.ceil(delta) : Math.round(delta); delta = Math.max(Math.min(delta, 4), -4); delta = map._limitZoom(zoom + delta) - zoom; diff --git a/src/map/handler/Map.TouchZoom.js b/src/map/handler/Map.TouchZoom.js index 02c6b390..adb5301c 100644 --- a/src/map/handler/Map.TouchZoom.js +++ b/src/map/handler/Map.TouchZoom.js @@ -92,7 +92,7 @@ L.Map.TouchZoom = L.Handler.extend({ L.DomUtil.getScaleString(this._scale, this._startCenter); }, - _onTouchEnd: function (e) { + _onTouchEnd: function () { if (!this._moved || !this._zooming) { return; } var map = this._map;