leaflet-js 0.6.beta4 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (226) hide show
  1. checksums.yaml +6 -14
  2. data/CHANGELOG.rdoc +3 -0
  3. data/leaflet-js.gemspec +2 -2
  4. data/lib/leaflet.draw/CHANGELOG.md +45 -0
  5. data/lib/leaflet.draw/README.md +70 -23
  6. data/lib/leaflet.draw/build/deps.js +1 -0
  7. data/lib/leaflet.draw/dist/images/spritesheet-2x.png +0 -0
  8. data/lib/leaflet.draw/dist/images/spritesheet.png +0 -0
  9. data/lib/leaflet.draw/dist/leaflet.draw-src.js +489 -136
  10. data/lib/leaflet.draw/dist/leaflet.draw.css +34 -2
  11. data/lib/leaflet.draw/dist/leaflet.draw.ie.css +5 -0
  12. data/lib/leaflet.draw/dist/leaflet.draw.js +2 -2
  13. data/lib/leaflet.draw/examples/basic.html +14 -7
  14. data/lib/leaflet.draw/examples/libs/images/layers-2x.png +0 -0
  15. data/lib/leaflet.draw/examples/libs/images/layers.png +0 -0
  16. data/lib/leaflet.draw/examples/libs/images/marker-icon-2x.png +0 -0
  17. data/lib/leaflet.draw/examples/libs/leaflet-src.js +1129 -608
  18. data/lib/leaflet.draw/examples/libs/leaflet.css +85 -66
  19. data/lib/leaflet.draw/package.json +2 -2
  20. data/lib/leaflet.draw/spec/suites/DrawControlSpec.js +1 -1
  21. data/lib/leaflet.draw/spec/suites/GeometryUtilSpec.js +25 -0
  22. data/lib/leaflet.draw/spec/suites/LatLngUtilSpec.js +9 -0
  23. data/lib/leaflet.draw/src/Control.Draw.js +1 -0
  24. data/lib/leaflet.draw/src/Leaflet.draw.js +89 -1
  25. data/lib/leaflet.draw/src/Toolbar.js +2 -6
  26. data/lib/leaflet.draw/src/Tooltip.js +20 -7
  27. data/lib/leaflet.draw/src/draw/DrawToolbar.js +26 -22
  28. data/lib/leaflet.draw/src/draw/handler/Draw.Circle.js +11 -6
  29. data/lib/leaflet.draw/src/draw/handler/Draw.Feature.js +6 -2
  30. data/lib/leaflet.draw/src/draw/handler/Draw.Marker.js +7 -2
  31. data/lib/leaflet.draw/src/draw/handler/Draw.Polygon.js +29 -7
  32. data/lib/leaflet.draw/src/draw/handler/Draw.Polyline.js +44 -21
  33. data/lib/leaflet.draw/src/draw/handler/Draw.Rectangle.js +3 -3
  34. data/lib/leaflet.draw/src/draw/handler/Draw.SimpleShape.js +14 -1
  35. data/lib/leaflet.draw/src/edit/EditToolbar.js +86 -16
  36. data/lib/leaflet.draw/src/edit/handler/Edit.Poly.js +10 -7
  37. data/lib/leaflet.draw/src/edit/handler/Edit.SimpleShape.js +1 -2
  38. data/lib/leaflet.draw/src/edit/handler/EditToolbar.Delete.js +15 -3
  39. data/lib/leaflet.draw/src/edit/handler/EditToolbar.Edit.js +56 -38
  40. data/lib/leaflet.draw/src/ext/GeometryUtil.js +68 -0
  41. data/lib/leaflet.draw/src/images/spritesheet.svg +41 -0
  42. data/lib/leaflet.label/CHANGELOG.md +32 -0
  43. data/lib/leaflet.label/README.md +21 -4
  44. data/lib/leaflet.label/build/build.js +2 -2
  45. data/lib/leaflet.label/build/deps.js +2 -0
  46. data/lib/leaflet.label/build/hintrc.js +4 -0
  47. data/lib/leaflet.label/dist/leaflet.label-src.js +266 -83
  48. data/lib/leaflet.label/dist/leaflet.label.css +23 -4
  49. data/lib/leaflet.label/dist/leaflet.label.js +1 -1
  50. data/lib/leaflet.label/example/label.html +6 -3
  51. data/lib/leaflet.label/libs/leaflet/images/layers-2x.png +0 -0
  52. data/lib/leaflet.label/libs/leaflet/images/layers.png +0 -0
  53. data/lib/leaflet.label/libs/leaflet/images/marker-icon-2x.png +0 -0
  54. data/lib/leaflet.label/libs/leaflet/leaflet-src.js +1129 -608
  55. data/lib/leaflet.label/libs/leaflet/leaflet.css +85 -66
  56. data/lib/leaflet.label/libs/leaflet/leaflet.js +6 -5
  57. data/lib/leaflet.label/package.json +19 -0
  58. data/lib/leaflet.label/src/BaseMarkerMethods.js +129 -0
  59. data/lib/leaflet.label/src/CircleMarker.Label.js +7 -0
  60. data/lib/leaflet.label/src/Label.js +161 -37
  61. data/lib/leaflet.label/src/Leaflet.label.js +1 -1
  62. data/lib/leaflet.label/src/Map.Label.js +0 -2
  63. data/lib/leaflet.label/src/Marker.Label.js +15 -120
  64. data/lib/leaflet.label/src/Path.Label.js +11 -11
  65. data/lib/leaflet/CHANGELOG.md +299 -31
  66. data/lib/leaflet/CONTRIBUTING.md +3 -3
  67. data/lib/leaflet/FAQ.md +138 -0
  68. data/lib/leaflet/Jakefile.js +24 -4
  69. data/lib/leaflet/PLUGIN-GUIDE.md +127 -0
  70. data/lib/leaflet/README.md +10 -6
  71. data/lib/leaflet/build/build.html +3 -19
  72. data/lib/leaflet/build/build.js +21 -51
  73. data/lib/leaflet/build/deps.js +10 -7
  74. data/lib/leaflet/build/hintrc.js +6 -4
  75. data/lib/leaflet/debug/hacks/jitter.html +0 -1
  76. data/lib/leaflet/debug/map/canvas.html +11 -12
  77. data/lib/leaflet/debug/map/controls.html +3 -4
  78. data/lib/leaflet/debug/map/geolocation.html +0 -1
  79. data/lib/leaflet/debug/map/iframe.html +11 -0
  80. data/lib/leaflet/debug/map/image-overlay.html +0 -1
  81. data/lib/leaflet/debug/map/map-mobile.html +0 -1
  82. data/lib/leaflet/debug/map/map.html +1 -2
  83. data/lib/leaflet/debug/map/max-bounds.html +2 -1
  84. data/lib/leaflet/debug/map/opacity.html +223 -0
  85. data/lib/leaflet/debug/map/scroll.html +6 -1
  86. data/lib/leaflet/debug/map/simple-proj.html +0 -1
  87. data/lib/leaflet/debug/map/wms-marble.html +4 -5
  88. data/lib/leaflet/debug/map/wms.html +0 -1
  89. data/lib/leaflet/debug/map/zoomlevels.html +0 -1
  90. data/lib/leaflet/debug/tests/add_remove_layers.html +5 -6
  91. data/lib/leaflet/debug/tests/bringtoback.html +0 -1
  92. data/lib/leaflet/debug/tests/canvasloop.html +47 -0
  93. data/lib/leaflet/debug/tests/click_on_canvas.html +0 -1
  94. data/lib/leaflet/debug/tests/dragging_and_copyworldjump.html +61 -0
  95. data/lib/leaflet/debug/tests/opacity.html +0 -1
  96. data/lib/leaflet/debug/tests/popupcontextmenuclicks.html +59 -0
  97. data/lib/leaflet/debug/tests/remove_while_dragging.html +4 -5
  98. data/lib/leaflet/debug/tests/removetilewhilepan.html +0 -1
  99. data/lib/leaflet/debug/tests/reuse_popups.html +0 -1
  100. data/lib/leaflet/debug/tests/rtl.html +42 -0
  101. data/lib/leaflet/debug/tests/rtl2.html +27 -0
  102. data/lib/leaflet/debug/tests/set_icon_reuse_dom.html +43 -0
  103. data/lib/leaflet/debug/tests/svg_clicks.html +3 -4
  104. data/lib/leaflet/debug/vector/bounds-extend.html +0 -1
  105. data/lib/leaflet/debug/vector/feature-group-bounds.html +0 -1
  106. data/lib/leaflet/debug/vector/geojson.html +0 -1
  107. data/lib/leaflet/debug/vector/rectangle.html +0 -1
  108. data/lib/leaflet/debug/vector/touchzoomemu.html +2 -3
  109. data/lib/leaflet/debug/vector/vector-bounds.html +0 -1
  110. data/lib/leaflet/debug/vector/vector-canvas.html +0 -1
  111. data/lib/leaflet/debug/vector/vector-mobile.html +0 -1
  112. data/lib/leaflet/debug/vector/vector-simple.html +0 -1
  113. data/lib/leaflet/debug/vector/vector.html +0 -1
  114. data/lib/leaflet/dist/images/layers-2x.png +0 -0
  115. data/lib/leaflet/dist/images/layers.png +0 -0
  116. data/lib/leaflet/dist/leaflet.css +85 -66
  117. data/lib/leaflet/package.json +25 -20
  118. data/lib/leaflet/spec/after.js +1 -1
  119. data/lib/leaflet/spec/index.html +21 -13
  120. data/lib/leaflet/spec/karma.conf.js +51 -50
  121. data/lib/leaflet/spec/spec.hintrc.js +25 -0
  122. data/lib/leaflet/spec/suites/LeafletSpec.js +2 -2
  123. data/lib/leaflet/spec/suites/SpecHelper.js +37 -21
  124. data/lib/leaflet/spec/suites/control/Control.LayersSpec.js +1 -1
  125. data/lib/leaflet/spec/suites/core/ClassSpec.js +12 -12
  126. data/lib/leaflet/spec/suites/core/EventsSpec.js +74 -18
  127. data/lib/leaflet/spec/suites/core/UtilSpec.js +69 -25
  128. data/lib/leaflet/spec/suites/dom/DomEventSpec.js +16 -16
  129. data/lib/leaflet/spec/suites/dom/DomUtilSpec.js +9 -16
  130. data/lib/leaflet/spec/suites/dom/PosAnimationSpec.js +27 -0
  131. data/lib/leaflet/spec/suites/geo/CRSSpec.js +47 -0
  132. data/lib/leaflet/spec/suites/geo/LatLngBoundsSpec.js +22 -14
  133. data/lib/leaflet/spec/suites/geo/LatLngSpec.js +22 -8
  134. data/lib/leaflet/spec/suites/geo/ProjectionSpec.js +21 -20
  135. data/lib/leaflet/spec/suites/geometry/BoundsSpec.js +15 -15
  136. data/lib/leaflet/spec/suites/geometry/PointSpec.js +12 -12
  137. data/lib/leaflet/spec/suites/geometry/TransformationSpec.js +4 -4
  138. data/lib/leaflet/spec/suites/layer/FeatureGroupSpec.js +59 -9
  139. data/lib/leaflet/spec/suites/layer/GeoJSONSpec.js +213 -17
  140. data/lib/leaflet/spec/suites/layer/LayerGroupSpec.js +6 -6
  141. data/lib/leaflet/spec/suites/layer/PopupSpec.js +65 -5
  142. data/lib/leaflet/spec/suites/layer/TileLayerSpec.js +16 -15
  143. data/lib/leaflet/spec/suites/layer/marker/MarkerSpec.js +94 -0
  144. data/lib/leaflet/spec/suites/layer/vector/CircleMarkerSpec.js +7 -7
  145. data/lib/leaflet/spec/suites/layer/vector/PolygonSpec.js +38 -2
  146. data/lib/leaflet/spec/suites/layer/vector/PolylineGeometrySpec.js +4 -4
  147. data/lib/leaflet/spec/suites/layer/vector/PolylineSpec.js +2 -2
  148. data/lib/leaflet/spec/suites/map/MapSpec.js +318 -26
  149. data/lib/leaflet/spec/suites/map/handler/Map.DragSpec.js +38 -0
  150. data/lib/leaflet/src/Leaflet.js +2 -2
  151. data/lib/leaflet/src/control/Control.Attribution.js +6 -0
  152. data/lib/leaflet/src/control/Control.Layers.js +33 -24
  153. data/lib/leaflet/src/control/Control.Zoom.js +12 -4
  154. data/lib/leaflet/src/control/Control.js +10 -0
  155. data/lib/leaflet/src/copyright.js +2 -1
  156. data/lib/leaflet/src/core/Browser.js +11 -10
  157. data/lib/leaflet/src/core/Events.js +15 -11
  158. data/lib/leaflet/src/core/Util.js +19 -14
  159. data/lib/leaflet/src/dom/DomEvent.DoubleTap.js +13 -12
  160. data/lib/leaflet/src/dom/DomEvent.Pointer.js +155 -0
  161. data/lib/leaflet/src/dom/DomEvent.js +57 -19
  162. data/lib/leaflet/src/dom/DomUtil.js +89 -34
  163. data/lib/leaflet/src/dom/Draggable.js +26 -89
  164. data/lib/leaflet/src/dom/PosAnimation.js +13 -2
  165. data/lib/leaflet/src/geo/LatLng.js +16 -5
  166. data/lib/leaflet/src/geo/LatLngBounds.js +5 -2
  167. data/lib/leaflet/src/geo/crs/CRS.EPSG3395.js +2 -2
  168. data/lib/leaflet/src/geo/crs/CRS.js +5 -0
  169. data/lib/leaflet/src/geo/projection/Projection.Mercator.js +3 -3
  170. data/lib/leaflet/src/geometry/LineUtil.js +2 -2
  171. data/lib/leaflet/src/images/layers.svg +8 -0
  172. data/lib/leaflet/src/images/marker.svg +61 -1
  173. data/lib/leaflet/src/layer/FeatureGroup.js +24 -7
  174. data/lib/leaflet/src/layer/GeoJSON.js +97 -56
  175. data/lib/leaflet/src/layer/ImageOverlay.js +9 -0
  176. data/lib/leaflet/src/layer/LayerGroup.js +8 -3
  177. data/lib/leaflet/src/layer/Popup.js +56 -34
  178. data/lib/leaflet/src/layer/marker/DivIcon.js +4 -2
  179. data/lib/leaflet/src/layer/marker/Icon.Default.js +1 -1
  180. data/lib/leaflet/src/layer/marker/Icon.js +15 -18
  181. data/lib/leaflet/src/layer/marker/Marker.Drag.js +7 -5
  182. data/lib/leaflet/src/layer/marker/Marker.Popup.js +22 -5
  183. data/lib/leaflet/src/layer/marker/Marker.js +75 -32
  184. data/lib/leaflet/src/layer/tile/TileLayer.Anim.js +14 -26
  185. data/lib/leaflet/src/layer/tile/TileLayer.Canvas.js +7 -6
  186. data/lib/leaflet/src/layer/tile/TileLayer.WMS.js +14 -10
  187. data/lib/leaflet/src/layer/tile/TileLayer.js +53 -32
  188. data/lib/leaflet/src/layer/vector/CircleMarker.js +11 -0
  189. data/lib/leaflet/src/layer/vector/MultiPoly.js +10 -0
  190. data/lib/leaflet/src/layer/vector/Path.SVG.js +14 -3
  191. data/lib/leaflet/src/layer/vector/Path.VML.js +12 -2
  192. data/lib/leaflet/src/layer/vector/Path.js +7 -3
  193. data/lib/leaflet/src/layer/vector/Polygon.js +14 -3
  194. data/lib/leaflet/src/layer/vector/canvas/CircleMarker.Canvas.js +9 -0
  195. data/lib/leaflet/src/layer/vector/canvas/Path.Canvas.js +1 -0
  196. data/lib/leaflet/src/map/Map.js +192 -125
  197. data/lib/leaflet/src/map/anim/Map.PanAnimation.js +29 -19
  198. data/lib/leaflet/src/map/anim/Map.ZoomAnimation.js +21 -9
  199. data/lib/leaflet/src/map/ext/Map.Geolocation.js +11 -4
  200. data/lib/leaflet/src/map/handler/Map.BoxZoom.js +26 -12
  201. data/lib/leaflet/src/map/handler/Map.DoubleClickZoom.js +10 -3
  202. data/lib/leaflet/src/map/handler/Map.Drag.js +12 -6
  203. data/lib/leaflet/src/map/handler/Map.Keyboard.js +5 -2
  204. data/lib/leaflet/src/map/handler/Map.ScrollWheelZoom.js +7 -1
  205. data/lib/leaflet/src/map/handler/Map.Tap.js +107 -0
  206. data/lib/leaflet/src/map/handler/Map.TouchZoom.js +9 -3
  207. data/vendor/assets/images/layers-2x.png +0 -0
  208. data/vendor/assets/images/layers.png +0 -0
  209. data/vendor/assets/images/spritesheet-2x.png +0 -0
  210. data/vendor/assets/images/spritesheet.png +0 -0
  211. data/vendor/assets/javascripts/leaflet.draw.js +2 -4
  212. data/vendor/assets/javascripts/leaflet.js +3 -1
  213. data/vendor/assets/javascripts/leaflet.label.js +2 -0
  214. data/vendor/assets/stylesheets/leaflet.css.erb +337 -318
  215. data/vendor/assets/stylesheets/leaflet.draw.css.erb +35 -3
  216. data/vendor/assets/stylesheets/leaflet.draw.ie.css +5 -0
  217. data/vendor/assets/stylesheets/leaflet.label.css +23 -4
  218. metadata +40 -14
  219. data/lib/leaflet.draw/examples/libs/leaflet.ie.css +0 -51
  220. data/lib/leaflet.label/libs/leaflet/leaflet.ie.css +0 -51
  221. data/lib/leaflet/dist/leaflet-src.js +0 -8579
  222. data/lib/leaflet/dist/leaflet.ie.css +0 -51
  223. data/lib/leaflet/dist/leaflet.js +0 -8
  224. data/lib/leaflet/spec/happen.js +0 -93
  225. data/lib/leaflet/src/dom/DomEvent.MsTouch.js +0 -146
  226. data/vendor/assets/stylesheets/leaflet.ie.css +0 -51
@@ -21,13 +21,32 @@
21
21
  z-index: 6;
22
22
  }
23
23
 
24
- .leaflet-label:before {
25
- border-right: 6px solid black;
26
- border-right-color: inherit;
24
+ .leaflet-label.leaflet-clickable {
25
+ cursor: pointer;
26
+ }
27
+
28
+ .leaflet-label:before,
29
+ .leaflet-label:after {
27
30
  border-top: 6px solid transparent;
28
31
  border-bottom: 6px solid transparent;
29
- content: "";
32
+ content: none;
30
33
  position: absolute;
31
34
  top: 5px;
35
+ }
36
+
37
+ .leaflet-label:before {
38
+ border-right: 6px solid black;
39
+ border-right-color: inherit;
32
40
  left: -10px;
41
+ }
42
+
43
+ .leaflet-label:after {
44
+ border-left: 6px solid black;
45
+ border-left-color: inherit;
46
+ right: -10px;
47
+ }
48
+
49
+ .leaflet-label-right:before,
50
+ .leaflet-label-left:after {
51
+ content: "";
33
52
  }
@@ -6,4 +6,4 @@
6
6
  http://leafletjs.com
7
7
  https://github.com/jacobtoye
8
8
  */
9
- (function(){L.labelVersion="0.1.2-dev",L.Label=L.Popup.extend({options:{autoPan:!1,className:"",closePopupOnClick:!1,noHide:!1,offset:new L.Point(12,-15)},onAdd:function(t){this._map=t,this._pane=this._source instanceof L.Marker?t._panes.markerPane:t._panes.popupPane,this._container||this._initLayout(),this._updateContent();var e=t.options.fadeAnimation;e&&L.DomUtil.setOpacity(this._container,0),this._pane.appendChild(this._container),t.on("viewreset",this._updatePosition,this),this._animated&&t.on("zoomanim",this._zoomAnimation,this),L.Browser.touch&&!this.options.noHide&&L.DomEvent.on(this._container,"click",this.close,this),this._update(),e&&L.DomUtil.setOpacity(this._container,1)},onRemove:function(t){this._pane.removeChild(this._container),L.Util.falseFn(this._container.offsetWidth),t.off({viewreset:this._updatePosition,zoomanim:this._zoomAnimation},this),t.options.fadeAnimation&&L.DomUtil.setOpacity(this._container,0),this._map=null},close:function(){var t=this._map;L.Browser.touch&&!this.options.noHide&&L.DomEvent.off(this._container,"click",this.close),t&&(t._label=null,t.removeLayer(this))},updateZIndex:function(t){this._zIndex=t,this._container&&(this._container.style.zIndex=t)},_initLayout:function(){this._container=L.DomUtil.create("div","leaflet-label "+this.options.className+" leaflet-zoom-animated"),this.updateZIndex(this._zIndex)},_updateContent:function(){this._content&&"string"==typeof this._content&&(this._container.innerHTML=this._content)},_updateLayout:function(){},_updatePosition:function(){var t=this._map.latLngToLayerPoint(this._latlng);this._setPosition(t)},_setPosition:function(t){t=t.add(this.options.offset),L.DomUtil.setPosition(this._container,t)},_zoomAnimation:function(t){var e=this._map._latLngToNewLayerPoint(this._latlng,t.zoom,t.center);this._setPosition(e)}}),L.Icon.Default.mergeOptions({labelAnchor:new L.Point(9,-20)}),L.Marker.mergeOptions({icon:new L.Icon.Default}),L.Marker.include({showLabel:function(){return this._label&&this._map&&(this._label.setLatLng(this._latlng),this._map.showLabel(this._label)),this},hideLabel:function(){return this._label&&this._label.close(),this},setLabelNoHide:function(t){this._labelNoHide!==t&&(this._labelNoHide=t,t?(this._removeLabelRevealHandlers(),this.showLabel()):(this._addLabelRevealHandlers(),this.hideLabel()))},bindLabel:function(t,e){var i=L.point(this.options.icon.options.labelAnchor)||new L.Point(0,0);return i=i.add(L.Label.prototype.options.offset),e&&e.offset&&(i=i.add(e.offset)),e=L.Util.extend({offset:i},e),this._labelNoHide=e.noHide,this._label||(this._labelNoHide||this._addLabelRevealHandlers(),this.on("remove",this.hideLabel,this).on("move",this._moveLabel,this),this._hasLabelHandlers=!0),this._label=new L.Label(e,this).setContent(t),this},unbindLabel:function(){return this._label&&(this.hideLabel(),this._label=null,this._hasLabelHandlers&&(this._labelNoHide||this._removeLabelRevealHandlers(),this.off("remove",this.hideLabel,this).off("move",this._moveLabel,this)),this._hasLabelHandlers=!1),this},updateLabelContent:function(t){this._label&&this._label.setContent(t)},_addLabelRevealHandlers:function(){this.on("mouseover",this.showLabel,this).on("mouseout",this.hideLabel,this),L.Browser.touch&&this.on("click",this.showLabel,this)},_removeLabelRevealHandlers:function(){this.off("mouseover",this.showLabel,this).off("mouseout",this.hideLabel,this).off("remove",this.hideLabel,this).off("move",this._moveLabel,this),L.Browser.touch&&this.off("click",this.showLabel,this)},_moveLabel:function(t){this._label.setLatLng(t.latlng)},_originalUpdateZIndex:L.Marker.prototype._updateZIndex,_updateZIndex:function(t){var e=this._zIndex+t;this._originalUpdateZIndex(t),this._label&&this._label.updateZIndex(e)}}),L.Path.include({bindLabel:function(t,e){return this._label&&this._label.options===e||(this._label=new L.Label(e,this)),this._label.setContent(t),this._showLabelAdded||(this.on("mouseover",this._showLabel,this).on("mousemove",this._moveLabel,this).on("mouseout remove",this._hideLabel,this),L.Browser.touch&&this.on("click",this._showLabel,this),this._showLabelAdded=!0),this},unbindLabel:function(){return this._label&&(this._hideLabel(),this._label=null,this._showLabelAdded=!1,this.off("mouseover",this._showLabel,this).off("mousemove",this._moveLabel,this).off("mouseout remove",this._hideLabel,this)),this},updateLabelContent:function(t){this._label&&this._label.setContent(t)},_showLabel:function(t){this._label.setLatLng(t.latlng),this._map.showLabel(this._label)},_moveLabel:function(t){this._label.setLatLng(t.latlng)},_hideLabel:function(){this._label.close()}}),L.Map.include({showLabel:function(t){return this._label=t,this.addLayer(t)}}),L.FeatureGroup.include({clearLayers:function(){return this.unbindLabel(),this.eachLayer(this.removeLayer,this),this},bindLabel:function(t,e){return this.invoke("bindLabel",t,e)},unbindLabel:function(){return this.invoke("unbindLabel")},updateLabelContent:function(t){this.invoke("updateLabelContent",t)}})})(this,document);
9
+ (function(){L.labelVersion="0.2.1-dev",L.Label=L.Class.extend({includes:L.Mixin.Events,options:{className:"",clickable:!1,direction:"right",noHide:!1,offset:[12,-15],opacity:1,zoomAnimation:!0},initialize:function(t,e){L.setOptions(this,t),this._source=e,this._animated=L.Browser.any3d&&this.options.zoomAnimation,this._isOpen=!1},onAdd:function(t){this._map=t,this._pane=this._source instanceof L.Marker?t._panes.markerPane:t._panes.popupPane,this._container||this._initLayout(),this._pane.appendChild(this._container),this._initInteraction(),this._update(),this.setOpacity(this.options.opacity),t.on("moveend",this._onMoveEnd,this).on("viewreset",this._onViewReset,this),this._animated&&t.on("zoomanim",this._zoomAnimation,this),L.Browser.touch&&!this.options.noHide&&L.DomEvent.on(this._container,"click",this.close,this)},onRemove:function(t){this._pane.removeChild(this._container),t.off({zoomanim:this._zoomAnimation,moveend:this._onMoveEnd,viewreset:this._onViewReset},this),this._removeInteraction(),this._map=null},setLatLng:function(t){return this._latlng=L.latLng(t),this._map&&this._updatePosition(),this},setContent:function(t){return this._previousContent=this._content,this._content=t,this._updateContent(),this},close:function(){var t=this._map;t&&(L.Browser.touch&&!this.options.noHide&&L.DomEvent.off(this._container,"click",this.close),t.removeLayer(this))},updateZIndex:function(t){this._zIndex=t,this._container&&this._zIndex&&(this._container.style.zIndex=t)},setOpacity:function(t){this.options.opacity=t,this._container&&L.DomUtil.setOpacity(this._container,t)},_initLayout:function(){this._container=L.DomUtil.create("div","leaflet-label "+this.options.className+" leaflet-zoom-animated"),this.updateZIndex(this._zIndex)},_update:function(){this._map&&(this._container.style.visibility="hidden",this._updateContent(),this._updatePosition(),this._container.style.visibility="")},_updateContent:function(){this._content&&this._map&&this._prevContent!==this._content&&"string"==typeof this._content&&(this._container.innerHTML=this._content,this._prevContent=this._content,this._labelWidth=this._container.offsetWidth)},_updatePosition:function(){var t=this._map.latLngToLayerPoint(this._latlng);this._setPosition(t)},_setPosition:function(t){var e=this._map,i=this._container,n=e.latLngToContainerPoint(e.getCenter()),o=e.layerPointToContainerPoint(t),s=this.options.direction,a=this._labelWidth,l=L.point(this.options.offset);"right"===s||"auto"===s&&o.x<n.x?(L.DomUtil.addClass(i,"leaflet-label-right"),L.DomUtil.removeClass(i,"leaflet-label-left"),t=t.add(l)):(L.DomUtil.addClass(i,"leaflet-label-left"),L.DomUtil.removeClass(i,"leaflet-label-right"),t=t.add(L.point(-l.x-a,l.y))),L.DomUtil.setPosition(i,t)},_zoomAnimation:function(t){var e=this._map._latLngToNewLayerPoint(this._latlng,t.zoom,t.center).round();this._setPosition(e)},_onMoveEnd:function(){this._animated&&"auto"!==this.options.direction||this._updatePosition()},_onViewReset:function(t){t&&t.hard&&this._update()},_initInteraction:function(){if(this.options.clickable){var t=this._container,e=["dblclick","mousedown","mouseover","mouseout","contextmenu"];L.DomUtil.addClass(t,"leaflet-clickable"),L.DomEvent.on(t,"click",this._onMouseClick,this);for(var i=0;e.length>i;i++)L.DomEvent.on(t,e[i],this._fireMouseEvent,this)}},_removeInteraction:function(){if(this.options.clickable){var t=this._container,e=["dblclick","mousedown","mouseover","mouseout","contextmenu"];L.DomUtil.removeClass(t,"leaflet-clickable"),L.DomEvent.off(t,"click",this._onMouseClick,this);for(var i=0;e.length>i;i++)L.DomEvent.off(t,e[i],this._fireMouseEvent,this)}},_onMouseClick:function(t){this.hasEventListeners(t.type)&&L.DomEvent.stopPropagation(t),this.fire(t.type,{originalEvent:t})},_fireMouseEvent:function(t){this.fire(t.type,{originalEvent:t}),"contextmenu"===t.type&&this.hasEventListeners(t.type)&&L.DomEvent.preventDefault(t),"mousedown"!==t.type?L.DomEvent.stopPropagation(t):L.DomEvent.preventDefault(t)}}),L.BaseMarkerMethods={showLabel:function(){return this.label&&this._map&&(this.label.setLatLng(this._latlng),this._map.showLabel(this.label)),this},hideLabel:function(){return this.label&&this.label.close(),this},setLabelNoHide:function(t){this._labelNoHide!==t&&(this._labelNoHide=t,t?(this._removeLabelRevealHandlers(),this.showLabel()):(this._addLabelRevealHandlers(),this.hideLabel()))},bindLabel:function(t,e){var i=this.options.icon?this.options.icon.options.labelAnchor:this.options.labelAnchor,n=L.point(i)||L.point(0,0);return n=n.add(L.Label.prototype.options.offset),e&&e.offset&&(n=n.add(e.offset)),e=L.Util.extend({offset:n},e),this._labelNoHide=e.noHide,this.label||(this._labelNoHide||this._addLabelRevealHandlers(),this.on("remove",this.hideLabel,this).on("move",this._moveLabel,this).on("add",this._onMarkerAdd,this),this._hasLabelHandlers=!0),this.label=new L.Label(e,this).setContent(t),this},unbindLabel:function(){return this.label&&(this.hideLabel(),this.label=null,this._hasLabelHandlers&&(this._labelNoHide||this._removeLabelRevealHandlers(),this.off("remove",this.hideLabel,this).off("move",this._moveLabel,this).off("add",this._onMarkerAdd,this)),this._hasLabelHandlers=!1),this},updateLabelContent:function(t){this.label&&this.label.setContent(t)},getLabel:function(){return this.label},_onMarkerAdd:function(){this._labelNoHide&&this.showLabel()},_addLabelRevealHandlers:function(){this.on("mouseover",this.showLabel,this).on("mouseout",this.hideLabel,this),L.Browser.touch&&this.on("click",this.showLabel,this)},_removeLabelRevealHandlers:function(){this.off("mouseover",this.showLabel,this).off("mouseout",this.hideLabel,this),L.Browser.touch&&this.off("click",this.showLabel,this)},_moveLabel:function(t){this.label.setLatLng(t.latlng)}},L.Icon.Default.mergeOptions({labelAnchor:new L.Point(9,-20)}),L.Marker.mergeOptions({icon:new L.Icon.Default}),L.Marker.include(L.BaseMarkerMethods),L.Marker.include({_originalUpdateZIndex:L.Marker.prototype._updateZIndex,_updateZIndex:function(t){var e=this._zIndex+t;this._originalUpdateZIndex(t),this.label&&this.label.updateZIndex(e)},_originalSetOpacity:L.Marker.prototype.setOpacity,setOpacity:function(t,e){this.options.labelHasSemiTransparency=e,this._originalSetOpacity(t)},_originalUpdateOpacity:L.Marker.prototype._updateOpacity,_updateOpacity:function(){var t=0===this.options.opacity?0:1;this._originalUpdateOpacity(),this.label&&this.label.setOpacity(this.options.labelHasSemiTransparency?this.options.opacity:t)},_originalSetLatLng:L.Marker.prototype.setLatLng,setLatLng:function(t){return this.label&&!this._labelNoHide&&this.hideLabel(),this._originalSetLatLng(t)}}),L.CircleMarker.mergeOptions({labelAnchor:new L.Point(0,0)}),L.CircleMarker.include(L.BaseMarkerMethods),L.Path.include({bindLabel:function(t,e){return this.label&&this.label.options===e||(this.label=new L.Label(e,this)),this.label.setContent(t),this._showLabelAdded||(this.on("mouseover",this._showLabel,this).on("mousemove",this._moveLabel,this).on("mouseout remove",this._hideLabel,this),L.Browser.touch&&this.on("click",this._showLabel,this),this._showLabelAdded=!0),this},unbindLabel:function(){return this.label&&(this._hideLabel(),this.label=null,this._showLabelAdded=!1,this.off("mouseover",this._showLabel,this).off("mousemove",this._moveLabel,this).off("mouseout remove",this._hideLabel,this)),this},updateLabelContent:function(t){this.label&&this.label.setContent(t)},_showLabel:function(t){this.label.setLatLng(t.latlng),this._map.showLabel(this.label)},_moveLabel:function(t){this.label.setLatLng(t.latlng)},_hideLabel:function(){this.label.close()}}),L.Map.include({showLabel:function(t){return this.addLayer(t)}}),L.FeatureGroup.include({clearLayers:function(){return this.unbindLabel(),this.eachLayer(this.removeLayer,this),this},bindLabel:function(t,e){return this.invoke("bindLabel",t,e)},unbindLabel:function(){return this.invoke("unbindLabel")},updateLabelContent:function(t){this.invoke("updateLabelContent",t)}})})(this,document);
@@ -10,7 +10,9 @@
10
10
  <script src="../libs/leaflet/leaflet-src.js"></script>
11
11
 
12
12
  <script src="../src/Label.js"></script>
13
+ <script src="../src/BaseMarkerMethods.js"></script>
13
14
  <script src="../src/Marker.Label.js"></script>
15
+ <script src="../src/CircleMarker.Label.js"></script>
14
16
  <script src="../src/Path.Label.js"></script>
15
17
  <script src="../src/Map.Label.js"></script>
16
18
  <script src="../src/FeatureGroup.Label.js"></script>
@@ -23,11 +25,12 @@
23
25
  cloudmade = new L.TileLayer(cloudmadeUrl, {maxZoom: 18}),
24
26
  map = new L.Map('map', {layers: [cloudmade], center: new L.LatLng(-37.7772, 175.2756), zoom: 15 });
25
27
 
28
+ var circle = L.circleMarker([-37.7612, 175.2856], { fillColor: "#f00", radius: 8 } ).bindLabel("Hello World", { direction: 'left' }).addTo(map);
29
+
26
30
  L.marker([-37.7772, 175.2606]).bindLabel('Look revealing label!').addTo(map);
27
31
 
28
32
  var m = L.marker([-37.785, 175.263], {draggable:true}).bindLabel('A sweet static label!', { noHide: true })
29
- .addTo(map)
30
- .showLabel();
33
+ .addTo(map);
31
34
 
32
35
  // Move marker to confirm that label moves when marker moves (first click)
33
36
  // Remove marker on click so we can check that the label is also removed (second click)
@@ -47,7 +50,7 @@
47
50
  [-37.7612, 175.2756],
48
51
  [-37.7702, 175.2796],
49
52
  [-37.7802, 175.2750]
50
- ],{ weight: 12, color: '#fe57a1' }).bindLabel('Even polylines can have labels.').addTo(map);
53
+ ],{ weight: 12, color: '#fe57a1' }).bindLabel('Even polylines can have labels.', { direction: 'auto' }).addTo(map);
51
54
 
52
55
  // Remove polyline on click so we can check that the label is also removed
53
56
  p.on('click', function () { map.removeLayer(p); });
@@ -1,12 +1,13 @@
1
1
  /*
2
2
  Leaflet, a JavaScript library for mobile-friendly interactive maps. http://leafletjs.com
3
- (c) 2010-2013, Vladimir Agafonkin, CloudMade
3
+ (c) 2010-2013, Vladimir Agafonkin
4
+ (c) 2010-2011, CloudMade
4
5
  */
5
6
  (function (window, document, undefined) {
6
7
  var oldL = window.L,
7
8
  L = {};
8
9
 
9
- L.version = '0.6-dev';
10
+ L.version = '0.7-dev';
10
11
 
11
12
  // define Leaflet for Node module pattern loaders, including Browserify
12
13
  if (typeof module === 'object' && typeof module.exports === 'object') {
@@ -14,7 +15,7 @@ if (typeof module === 'object' && typeof module.exports === 'object') {
14
15
 
15
16
  // define Leaflet as an AMD module
16
17
  } else if (typeof define === 'function' && define.amd) {
17
- define('leaflet', [], function () { return L; });
18
+ define(L);
18
19
  }
19
20
 
20
21
  // define Leaflet as a global L variable, saving the original L to restore later if needed
@@ -55,8 +56,9 @@ L.Util = {
55
56
  },
56
57
 
57
58
  stamp: (function () {
58
- var lastId = 0, key = '_leaflet_id';
59
- return function (/*Object*/ obj) {
59
+ var lastId = 0,
60
+ key = '_leaflet_id';
61
+ return function (obj) {
60
62
  obj[key] = obj[key] || ++lastId;
61
63
  return obj[key];
62
64
  };
@@ -125,27 +127,31 @@ L.Util = {
125
127
  return obj.options;
126
128
  },
127
129
 
128
- getParamString: function (obj, existingUrl) {
130
+ getParamString: function (obj, existingUrl, uppercase) {
129
131
  var params = [];
130
132
  for (var i in obj) {
131
- params.push(encodeURIComponent(i) + '=' + encodeURIComponent(obj[i]));
133
+ params.push(encodeURIComponent(uppercase ? i.toUpperCase() : i) + '=' + encodeURIComponent(obj[i]));
132
134
  }
133
135
  return ((!existingUrl || existingUrl.indexOf('?') === -1) ? '?' : '&') + params.join('&');
134
136
  },
135
137
 
136
- template: function (str, data) {
137
- return str.replace(/\{ *([\w_]+) *\}/g, function (str, key) {
138
- var value = data[key];
139
- if (value === undefined) {
140
- throw new Error('No value provided for variable ' + str);
141
- } else if (typeof value === 'function') {
142
- value = value(data);
143
- }
144
- return value;
138
+ compileTemplate: function (str, data) {
139
+ // based on https://gist.github.com/padolsey/6008842
140
+ str = str.replace(/"/g, '\\\"');
141
+ str = str.replace(/\{ *([\w_]+) *\}/g, function (str, key) {
142
+ return '" + o["' + key + '"]' + (typeof data[key] === 'function' ? '(o)' : '') + ' + "';
145
143
  });
144
+ // jshint evil: true
145
+ return new Function('o', 'return "' + str + '";');
146
+ },
147
+
148
+ template: function (str, data) {
149
+ var cache = L.Util._templateCache = L.Util._templateCache || {};
150
+ cache[str] = cache[str] || L.Util.compileTemplate(str, data);
151
+ return cache[str](data);
146
152
  },
147
153
 
148
- isArray: function (obj) {
154
+ isArray: Array.isArray || function (obj) {
149
155
  return (Object.prototype.toString.call(obj) === '[object Array]');
150
156
  },
151
157
 
@@ -352,8 +358,8 @@ L.Mixin.Events = {
352
358
  // store listeners of a particular context in a separate hash (if it has an id)
353
359
  // gives a major performance boost when removing thousands of map layers
354
360
 
355
- indexKey = type + '_idx',
356
- indexLenKey = indexKey + '_len',
361
+ indexKey = type + '_idx';
362
+ indexLenKey = indexKey + '_len';
357
363
 
358
364
  typeIndex = events[indexKey] = events[indexKey] || {};
359
365
 
@@ -384,6 +390,10 @@ L.Mixin.Events = {
384
390
 
385
391
  removeEventListener: function (types, fn, context) { // ([String, Function, Object]) or (Object[, Object])
386
392
 
393
+ if (!this[eventsKey]) {
394
+ return this;
395
+ }
396
+
387
397
  if (!types) {
388
398
  return this.clearAllEventListeners();
389
399
  }
@@ -392,7 +402,7 @@ L.Mixin.Events = {
392
402
 
393
403
  var events = this[eventsKey],
394
404
  contextId = context && L.stamp(context),
395
- i, len, type, listeners, j, indexKey, indexLenKey, typeIndex;
405
+ i, len, type, listeners, j, indexKey, indexLenKey, typeIndex, removed;
396
406
 
397
407
  types = L.Util.splitWords(types);
398
408
 
@@ -414,7 +424,10 @@ L.Mixin.Events = {
414
424
  if (listeners) {
415
425
  for (j = listeners.length - 1; j >= 0; j--) {
416
426
  if ((listeners[j].action === fn) && (!context || (listeners[j].context === context))) {
417
- listeners.splice(j, 1);
427
+ removed = listeners.splice(j, 1);
428
+ // set the old action to a no-op, because it is possible
429
+ // that the listener is being iterated over as part of a dispatch
430
+ removed[0].action = L.Util.falseFn;
418
431
  }
419
432
  }
420
433
 
@@ -457,7 +470,7 @@ L.Mixin.Events = {
457
470
  typeIndex = events[type + '_idx'];
458
471
 
459
472
  for (contextId in typeIndex) {
460
- listeners = typeIndex[contextId];
473
+ listeners = typeIndex[contextId].slice();
461
474
 
462
475
  if (listeners) {
463
476
  for (i = 0, len = listeners.length; i < len; i++) {
@@ -497,7 +510,7 @@ L.Mixin.Events.fire = L.Mixin.Events.fireEvent;
497
510
 
498
511
  (function () {
499
512
 
500
- var ie = !!window.ActiveXObject,
513
+ var ie = 'ActiveXObject' in window,
501
514
  ie6 = ie && !window.XMLHttpRequest,
502
515
  ie7 = ie && !document.querySelector,
503
516
  ielt9 = ie && !document.addEventListener,
@@ -509,10 +522,13 @@ L.Mixin.Events.fire = L.Mixin.Events.fireEvent;
509
522
  phantomjs = ua.indexOf('phantom') !== -1,
510
523
  android = ua.indexOf('android') !== -1,
511
524
  android23 = ua.search('android [23]') !== -1,
525
+ gecko = ua.indexOf('gecko') !== -1,
512
526
 
513
527
  mobile = typeof orientation !== undefined + '',
514
- msTouch = window.navigator && window.navigator.msPointerEnabled &&
515
- window.navigator.msMaxTouchPoints,
528
+ msPointer = window.navigator && window.navigator.msPointerEnabled &&
529
+ window.navigator.msMaxTouchPoints && !window.PointerEvent,
530
+ pointer = (window.PointerEvent && window.navigator.pointerEnabled && window.navigator.maxTouchPoints) ||
531
+ msPointer,
516
532
  retina = ('devicePixelRatio' in window && window.devicePixelRatio > 1) ||
517
533
  ('matchMedia' in window && window.matchMedia('(min-resolution:144dpi)') &&
518
534
  window.matchMedia('(min-resolution:144dpi)').matches),
@@ -532,8 +548,8 @@ L.Mixin.Events.fire = L.Mixin.Events.fireEvent;
532
548
 
533
549
  var startName = 'ontouchstart';
534
550
 
535
- // IE10+ (We simulate these into touch* events in L.DomEvent and L.DomEvent.MsTouch) or WebKit, etc.
536
- if (msTouch || (startName in doc)) {
551
+ // IE10+ (We simulate these into touch* events in L.DomEvent and L.DomEvent.Pointer) or WebKit, etc.
552
+ if (pointer || (startName in doc)) {
537
553
  return true;
538
554
  }
539
555
 
@@ -563,6 +579,7 @@ L.Mixin.Events.fire = L.Mixin.Events.fireEvent;
563
579
  ie7: ie7,
564
580
  ielt9: ielt9,
565
581
  webkit: webkit,
582
+ gecko: gecko && !webkit && !window.opera && !ie,
566
583
 
567
584
  android: android,
568
585
  android23: android23,
@@ -581,7 +598,8 @@ L.Mixin.Events.fire = L.Mixin.Events.fireEvent;
581
598
  mobileOpera: mobile && window.opera,
582
599
 
583
600
  touch: touch,
584
- msTouch: msTouch,
601
+ msPointer: msPointer,
602
+ pointer: pointer,
585
603
 
586
604
  retina: retina
587
605
  };
@@ -872,8 +890,7 @@ L.DomUtil = {
872
890
  el = element,
873
891
  docBody = document.body,
874
892
  docEl = document.documentElement,
875
- pos,
876
- ie7 = L.Browser.ie7;
893
+ pos;
877
894
 
878
895
  do {
879
896
  top += el.offsetTop || 0;
@@ -892,6 +909,22 @@ L.DomUtil = {
892
909
  left += docBody.scrollLeft || docEl.scrollLeft || 0;
893
910
  break;
894
911
  }
912
+
913
+ if (pos === 'relative' && !el.offsetLeft) {
914
+ var width = L.DomUtil.getStyle(el, 'width'),
915
+ maxWidth = L.DomUtil.getStyle(el, 'max-width'),
916
+ r = el.getBoundingClientRect();
917
+
918
+ if (width !== 'none' || maxWidth !== 'none') {
919
+ left += r.left + el.clientLeft;
920
+ }
921
+
922
+ //calculate full y offset since we're breaking out of the loop
923
+ top += r.top + (docBody.scrollTop || docEl.scrollTop || 0);
924
+
925
+ break;
926
+ }
927
+
895
928
  el = el.offsetParent;
896
929
 
897
930
  } while (el);
@@ -904,19 +937,6 @@ L.DomUtil = {
904
937
  top -= el.scrollTop || 0;
905
938
  left -= el.scrollLeft || 0;
906
939
 
907
- // webkit (and ie <= 7) handles RTL scrollLeft different to everyone else
908
- // https://code.google.com/p/closure-library/source/browse/trunk/closure/goog/style/bidi.js
909
- if (!L.DomUtil.documentIsLtr() && (L.Browser.webkit || ie7)) {
910
- left += el.scrollWidth - el.clientWidth;
911
-
912
- // ie7 shows the scrollbar by default and provides clientWidth counting it, so we
913
- // need to add it back in if it is visible; scrollbar is on the left as we are RTL
914
- if (ie7 && L.DomUtil.getStyle(el, 'overflow-y') !== 'hidden' &&
915
- L.DomUtil.getStyle(el, 'overflow') !== 'hidden') {
916
- left += 17;
917
- }
918
- }
919
-
920
940
  el = el.parentNode;
921
941
  } while (el);
922
942
 
@@ -943,23 +963,6 @@ L.DomUtil = {
943
963
  return el;
944
964
  },
945
965
 
946
- disableTextSelection: function () {
947
- if (document.selection && document.selection.empty) {
948
- document.selection.empty();
949
- }
950
- if (!this._onselectstart) {
951
- this._onselectstart = document.onselectstart || null;
952
- document.onselectstart = L.Util.falseFn;
953
- }
954
- },
955
-
956
- enableTextSelection: function () {
957
- if (document.onselectstart === L.Util.falseFn) {
958
- document.onselectstart = this._onselectstart;
959
- this._onselectstart = null;
960
- }
961
- },
962
-
963
966
  hasClass: function (el, name) {
964
967
  return (el.className.length > 0) &&
965
968
  new RegExp('(^|\\s)' + name + '(\\s|$)').test(el.className);
@@ -1080,6 +1083,38 @@ L.DomUtil.TRANSITION_END =
1080
1083
  L.DomUtil.TRANSITION === 'webkitTransition' || L.DomUtil.TRANSITION === 'OTransition' ?
1081
1084
  L.DomUtil.TRANSITION + 'End' : 'transitionend';
1082
1085
 
1086
+ (function () {
1087
+ var userSelectProperty = L.DomUtil.testProp(
1088
+ ['userSelect', 'WebkitUserSelect', 'OUserSelect', 'MozUserSelect', 'msUserSelect']);
1089
+
1090
+ L.extend(L.DomUtil, {
1091
+ disableTextSelection: function () {
1092
+ L.DomEvent.on(window, 'selectstart', L.DomEvent.preventDefault);
1093
+ if (userSelectProperty) {
1094
+ var style = document.documentElement.style;
1095
+ this._userSelect = style[userSelectProperty];
1096
+ style[userSelectProperty] = 'none';
1097
+ }
1098
+ },
1099
+
1100
+ enableTextSelection: function () {
1101
+ L.DomEvent.off(window, 'selectstart', L.DomEvent.preventDefault);
1102
+ if (userSelectProperty) {
1103
+ document.documentElement.style[userSelectProperty] = this._userSelect;
1104
+ delete this._userSelect;
1105
+ }
1106
+ },
1107
+
1108
+ disableImageDrag: function () {
1109
+ L.DomEvent.on(window, 'dragstart', L.DomEvent.preventDefault);
1110
+ },
1111
+
1112
+ enableImageDrag: function () {
1113
+ L.DomEvent.off(window, 'dragstart', L.DomEvent.preventDefault);
1114
+ }
1115
+ });
1116
+ })();
1117
+
1083
1118
 
1084
1119
  /*
1085
1120
  * L.LatLng represents a geographical point with latitude and longitude coordinates.
@@ -1158,7 +1193,11 @@ L.latLng = function (a, b) { // (LatLng) or ([Number, Number]) or (Number, Numbe
1158
1193
  return a;
1159
1194
  }
1160
1195
  if (L.Util.isArray(a)) {
1161
- return new L.LatLng(a[0], a[1]);
1196
+ if (typeof a[0] === 'number' || typeof a[0] === 'string') {
1197
+ return new L.LatLng(a[0], a[1]);
1198
+ } else {
1199
+ return null;
1200
+ }
1162
1201
  }
1163
1202
  if (a === undefined || a === null) {
1164
1203
  return a;
@@ -1166,6 +1205,9 @@ L.latLng = function (a, b) { // (LatLng) or ([Number, Number]) or (Number, Numbe
1166
1205
  if (typeof a === 'object' && 'lat' in a) {
1167
1206
  return new L.LatLng(a.lat, 'lng' in a ? a.lng : a.lon);
1168
1207
  }
1208
+ if (b === undefined) {
1209
+ return null;
1210
+ }
1169
1211
  return new L.LatLng(a, b);
1170
1212
  };
1171
1213
 
@@ -1188,8 +1230,11 @@ L.LatLngBounds = function (southWest, northEast) { // (LatLng, LatLng) or (LatLn
1188
1230
  L.LatLngBounds.prototype = {
1189
1231
  // extend the bounds to contain the given point or bounds
1190
1232
  extend: function (obj) { // (LatLng) or (LatLngBounds)
1191
- if (typeof obj[0] === 'number' || typeof obj[0] === 'string' || obj instanceof L.LatLng) {
1192
- obj = L.latLng(obj);
1233
+ if (!obj) { return this; }
1234
+
1235
+ var latLng = L.latLng(obj);
1236
+ if (latLng !== null) {
1237
+ obj = latLng;
1193
1238
  } else {
1194
1239
  obj = L.latLngBounds(obj);
1195
1240
  }
@@ -1480,9 +1525,13 @@ L.Map = L.Class.extend({
1480
1525
  initialize: function (id, options) { // (HTMLElement or String, Object)
1481
1526
  options = L.setOptions(this, options);
1482
1527
 
1528
+
1483
1529
  this._initContainer(id);
1484
1530
  this._initLayout();
1485
- this.callInitHooks();
1531
+
1532
+ // hack for https://github.com/Leaflet/Leaflet/issues/1980
1533
+ this._onResize = L.bind(this._onResize, this);
1534
+
1486
1535
  this._initEvents();
1487
1536
 
1488
1537
  if (options.maxBounds) {
@@ -1490,10 +1539,18 @@ L.Map = L.Class.extend({
1490
1539
  }
1491
1540
 
1492
1541
  if (options.center && options.zoom !== undefined) {
1493
- this.setView(L.latLng(options.center), options.zoom, true);
1542
+ this.setView(L.latLng(options.center), options.zoom, {reset: true});
1494
1543
  }
1495
1544
 
1496
- this._initLayers(options.layers);
1545
+ this._handlers = [];
1546
+
1547
+ this._layers = {};
1548
+ this._zoomBoundLayers = {};
1549
+ this._tileLayersNum = 0;
1550
+
1551
+ this.callInitHooks();
1552
+
1553
+ this._addLayers(options.layers);
1497
1554
  },
1498
1555
 
1499
1556
 
@@ -1501,23 +1558,28 @@ L.Map = L.Class.extend({
1501
1558
 
1502
1559
  // replaced by animation-powered implementation in Map.PanAnimation.js
1503
1560
  setView: function (center, zoom) {
1561
+ zoom = zoom === undefined ? this.getZoom() : zoom;
1504
1562
  this._resetView(L.latLng(center), this._limitZoom(zoom));
1505
1563
  return this;
1506
1564
  },
1507
1565
 
1508
- setZoom: function (zoom) { // (Number)
1509
- return this.setView(this.getCenter(), zoom);
1566
+ setZoom: function (zoom, options) {
1567
+ if (!this._loaded) {
1568
+ this._zoom = this._limitZoom(zoom);
1569
+ return this;
1570
+ }
1571
+ return this.setView(this.getCenter(), zoom, {zoom: options});
1510
1572
  },
1511
1573
 
1512
- zoomIn: function (delta) {
1513
- return this.setZoom(this._zoom + (delta || 1));
1574
+ zoomIn: function (delta, options) {
1575
+ return this.setZoom(this._zoom + (delta || 1), options);
1514
1576
  },
1515
1577
 
1516
- zoomOut: function (delta) {
1517
- return this.setZoom(this._zoom - (delta || 1));
1578
+ zoomOut: function (delta, options) {
1579
+ return this.setZoom(this._zoom - (delta || 1), options);
1518
1580
  },
1519
1581
 
1520
- setZoomAround: function (latlng, zoom) {
1582
+ setZoomAround: function (latlng, zoom, options) {
1521
1583
  var scale = this.getZoomScale(zoom),
1522
1584
  viewHalf = this.getSize().divideBy(2),
1523
1585
  containerPoint = latlng instanceof L.Point ? latlng : this.latLngToContainerPoint(latlng),
@@ -1525,31 +1587,35 @@ L.Map = L.Class.extend({
1525
1587
  centerOffset = containerPoint.subtract(viewHalf).multiplyBy(1 - 1 / scale),
1526
1588
  newCenter = this.containerPointToLatLng(viewHalf.add(centerOffset));
1527
1589
 
1528
- return this.setView(newCenter, zoom);
1590
+ return this.setView(newCenter, zoom, {zoom: options});
1529
1591
  },
1530
1592
 
1531
- fitBounds: function (bounds, paddingTopLeft, paddingBottomRight) { // (LatLngBounds || ILayer[, Point, Point])
1593
+ fitBounds: function (bounds, options) {
1532
1594
 
1595
+ options = options || {};
1533
1596
  bounds = bounds.getBounds ? bounds.getBounds() : L.latLngBounds(bounds);
1534
1597
 
1535
- paddingTopLeft = L.point(paddingTopLeft || [0, 0]);
1536
- paddingBottomRight = L.point(paddingBottomRight || paddingTopLeft);
1598
+ var paddingTL = L.point(options.paddingTopLeft || options.padding || [0, 0]),
1599
+ paddingBR = L.point(options.paddingBottomRight || options.padding || [0, 0]),
1600
+
1601
+ zoom = this.getBoundsZoom(bounds, false, paddingTL.add(paddingBR)),
1602
+ paddingOffset = paddingBR.subtract(paddingTL).divideBy(2),
1537
1603
 
1538
- var zoom = this.getBoundsZoom(bounds, false, paddingTopLeft.add(paddingBottomRight)),
1539
- paddingOffset = paddingBottomRight.subtract(paddingTopLeft).divideBy(2),
1540
1604
  swPoint = this.project(bounds.getSouthWest(), zoom),
1541
1605
  nePoint = this.project(bounds.getNorthEast(), zoom),
1542
1606
  center = this.unproject(swPoint.add(nePoint).divideBy(2).add(paddingOffset), zoom);
1543
1607
 
1544
- return this.setView(center, zoom);
1608
+ zoom = options && options.maxZoom ? Math.min(options.maxZoom, zoom) : zoom;
1609
+
1610
+ return this.setView(center, zoom, options);
1545
1611
  },
1546
1612
 
1547
- fitWorld: function () {
1548
- return this.fitBounds([[-90, -180], [90, 180]]);
1613
+ fitWorld: function (options) {
1614
+ return this.fitBounds([[-90, -180], [90, 180]], options);
1549
1615
  },
1550
1616
 
1551
- panTo: function (center) { // (LatLng)
1552
- return this.setView(center, this._zoom);
1617
+ panTo: function (center, options) { // (LatLng)
1618
+ return this.setView(center, this._zoom, {pan: options});
1553
1619
  },
1554
1620
 
1555
1621
  panBy: function (offset) { // (Point)
@@ -1562,13 +1628,14 @@ L.Map = L.Class.extend({
1562
1628
  return this.fire('moveend');
1563
1629
  },
1564
1630
 
1565
- setMaxBounds: function (bounds) {
1631
+ setMaxBounds: function (bounds, options) {
1566
1632
  bounds = L.latLngBounds(bounds);
1567
1633
 
1568
1634
  this.options.maxBounds = bounds;
1569
1635
 
1570
1636
  if (!bounds) {
1571
1637
  this._boundsMinZoom = null;
1638
+ this.off('moveend', this._panInsideMaxBounds, this);
1572
1639
  return this;
1573
1640
  }
1574
1641
 
@@ -1578,7 +1645,7 @@ L.Map = L.Class.extend({
1578
1645
 
1579
1646
  if (this._loaded) {
1580
1647
  if (this._zoom < minZoom) {
1581
- this.setView(bounds.getCenter(), minZoom);
1648
+ this.setView(bounds.getCenter(), minZoom, options);
1582
1649
  } else {
1583
1650
  this.panInsideBounds(bounds);
1584
1651
  }
@@ -1638,14 +1705,13 @@ L.Map = L.Class.extend({
1638
1705
  // TODO looks ugly, refactor!!!
1639
1706
  if (this.options.zoomAnimation && L.TileLayer && (layer instanceof L.TileLayer)) {
1640
1707
  this._tileLayersNum++;
1641
- this._tileLayersToLoad++;
1642
- layer.on('load', this._onTileLayerLoad, this);
1708
+ this._tileLayersToLoad++;
1709
+ layer.on('load', this._onTileLayerLoad, this);
1643
1710
  }
1644
1711
 
1645
- this.whenReady(function () {
1646
- layer.onAdd(this);
1647
- this.fire('layeradd', {layer: layer});
1648
- }, this);
1712
+ if (this._loaded) {
1713
+ this._layerAdd(layer);
1714
+ }
1649
1715
 
1650
1716
  return this;
1651
1717
  },
@@ -1653,11 +1719,18 @@ L.Map = L.Class.extend({
1653
1719
  removeLayer: function (layer) {
1654
1720
  var id = L.stamp(layer);
1655
1721
 
1656
- if (!this._layers[id]) { return; }
1722
+ if (!this._layers[id]) { return this; }
1657
1723
 
1658
- layer.onRemove(this);
1724
+ if (this._loaded) {
1725
+ layer.onRemove(this);
1726
+ }
1659
1727
 
1660
1728
  delete this._layers[id];
1729
+
1730
+ if (this._loaded) {
1731
+ this.fire('layerremove', {layer: layer});
1732
+ }
1733
+
1661
1734
  if (this._zoomBoundLayers[id]) {
1662
1735
  delete this._zoomBoundLayers[id];
1663
1736
  this._updateZoomLevels();
@@ -1666,11 +1739,11 @@ L.Map = L.Class.extend({
1666
1739
  // TODO looks ugly, refactor
1667
1740
  if (this.options.zoomAnimation && L.TileLayer && (layer instanceof L.TileLayer)) {
1668
1741
  this._tileLayersNum--;
1669
- this._tileLayersToLoad--;
1670
- layer.off('load', this._onTileLayerLoad, this);
1742
+ this._tileLayersToLoad--;
1743
+ layer.off('load', this._onTileLayerLoad, this);
1671
1744
  }
1672
1745
 
1673
- return this.fire('layerremove', {layer: layer});
1746
+ return this;
1674
1747
  },
1675
1748
 
1676
1749
  hasLayer: function (layer) {
@@ -1686,10 +1759,15 @@ L.Map = L.Class.extend({
1686
1759
  return this;
1687
1760
  },
1688
1761
 
1689
- invalidateSize: function (animate) {
1690
- var oldSize = this.getSize();
1762
+ invalidateSize: function (options) {
1763
+ options = L.extend({
1764
+ animate: false,
1765
+ pan: true
1766
+ }, options === true ? {animate: true} : options);
1691
1767
 
1768
+ var oldSize = this.getSize();
1692
1769
  this._sizeChanged = true;
1770
+ this._initialCenter = null;
1693
1771
 
1694
1772
  if (this.options.maxBounds) {
1695
1773
  this.setMaxBounds(this.options.maxBounds);
@@ -1698,35 +1776,43 @@ L.Map = L.Class.extend({
1698
1776
  if (!this._loaded) { return this; }
1699
1777
 
1700
1778
  var newSize = this.getSize(),
1701
- offset = oldSize.subtract(newSize).divideBy(2).round();
1779
+ oldCenter = oldSize.divideBy(2).round(),
1780
+ newCenter = newSize.divideBy(2).round(),
1781
+ offset = oldCenter.subtract(newCenter);
1702
1782
 
1703
- if ((offset.x !== 0) || (offset.y !== 0)) {
1704
- if (animate === true) {
1705
- this.panBy(offset);
1706
- } else {
1707
- this._rawPanBy(offset);
1783
+ if (!offset.x && !offset.y) { return this; }
1708
1784
 
1709
- this.fire('move');
1785
+ if (options.animate && options.pan) {
1786
+ this.panBy(offset);
1710
1787
 
1711
- clearTimeout(this._sizeTimer);
1712
- this._sizeTimer = setTimeout(L.bind(this.fire, this, 'moveend'), 200);
1788
+ } else {
1789
+ if (options.pan) {
1790
+ this._rawPanBy(offset);
1713
1791
  }
1714
- this.fire('resize', {
1715
- oldSize: oldSize,
1716
- newSize: newSize
1717
- });
1792
+
1793
+ this.fire('move');
1794
+
1795
+ // make sure moveend is not fired too often on resize
1796
+ clearTimeout(this._sizeTimer);
1797
+ this._sizeTimer = setTimeout(L.bind(this.fire, this, 'moveend'), 200);
1718
1798
  }
1719
- return this;
1799
+
1800
+ return this.fire('resize', {
1801
+ oldSize: oldSize,
1802
+ newSize: newSize
1803
+ });
1720
1804
  },
1721
1805
 
1722
1806
  // TODO handler.addTo
1723
1807
  addHandler: function (name, HandlerClass) {
1724
- if (!HandlerClass) { return; }
1808
+ if (!HandlerClass) { return this; }
1725
1809
 
1726
- this[name] = new HandlerClass(this);
1810
+ var handler = this[name] = new HandlerClass(this);
1811
+
1812
+ this._handlers.push(handler);
1727
1813
 
1728
1814
  if (this.options[name]) {
1729
- this[name].enable();
1815
+ handler.enable();
1730
1816
  }
1731
1817
 
1732
1818
  return this;
@@ -1736,8 +1822,23 @@ L.Map = L.Class.extend({
1736
1822
  if (this._loaded) {
1737
1823
  this.fire('unload');
1738
1824
  }
1825
+
1739
1826
  this._initEvents('off');
1740
- delete this._container._leaflet;
1827
+
1828
+ try {
1829
+ // throws error in IE6-8
1830
+ delete this._container._leaflet;
1831
+ } catch (e) {
1832
+ this._container._leaflet = undefined;
1833
+ }
1834
+
1835
+ this._clearPanes();
1836
+ if (this._clearControlPos) {
1837
+ this._clearControlPos();
1838
+ }
1839
+
1840
+ this._clearHandlers();
1841
+
1741
1842
  return this;
1742
1843
  },
1743
1844
 
@@ -1747,7 +1848,7 @@ L.Map = L.Class.extend({
1747
1848
  getCenter: function () { // (Boolean) -> LatLng
1748
1849
  this._checkIfLoaded();
1749
1850
 
1750
- if (!this._moved()) {
1851
+ if (this._initialCenter && !this._moved()) {
1751
1852
  return this._initialCenter;
1752
1853
  }
1753
1854
  return this.layerPointToLatLng(this._getCenterLayerPoint());
@@ -1766,18 +1867,15 @@ L.Map = L.Class.extend({
1766
1867
  },
1767
1868
 
1768
1869
  getMinZoom: function () {
1769
- var z1 = this.options.minZoom || 0,
1770
- z2 = this._layersMinZoom || 0,
1771
- z3 = this._boundsMinZoom || 0;
1772
-
1773
- return Math.max(z1, z2, z3);
1870
+ var z1 = this._layersMinZoom === undefined ? 0 : this._layersMinZoom,
1871
+ z2 = this._boundsMinZoom === undefined ? 0 : this._boundsMinZoom;
1872
+ return this.options.minZoom === undefined ? Math.max(z1, z2) : this.options.minZoom;
1774
1873
  },
1775
1874
 
1776
1875
  getMaxZoom: function () {
1777
- var z1 = this.options.maxZoom === undefined ? Infinity : this.options.maxZoom,
1778
- z2 = this._layersMaxZoom === undefined ? Infinity : this._layersMaxZoom;
1779
-
1780
- return Math.min(z1, z2);
1876
+ return this.options.maxZoom === undefined ?
1877
+ (this._layersMaxZoom === undefined ? Infinity : this._layersMaxZoom) :
1878
+ this.options.maxZoom;
1781
1879
  },
1782
1880
 
1783
1881
  getBoundsZoom: function (bounds, inside, padding) { // (LatLngBounds[, Boolean, Point]) -> Number
@@ -1920,15 +2018,10 @@ L.Map = L.Class.extend({
1920
2018
  _initLayout: function () {
1921
2019
  var container = this._container;
1922
2020
 
1923
- L.DomUtil.addClass(container, 'leaflet-container');
1924
-
1925
- if (L.Browser.touch) {
1926
- L.DomUtil.addClass(container, 'leaflet-touch');
1927
- }
1928
-
1929
- if (this.options.fadeAnimation) {
1930
- L.DomUtil.addClass(container, 'leaflet-fade-anim');
1931
- }
2021
+ L.DomUtil.addClass(container, 'leaflet-container' +
2022
+ (L.Browser.touch ? ' leaflet-touch' : '') +
2023
+ (L.Browser.retina ? ' leaflet-retina' : '') +
2024
+ (this.options.fadeAnimation ? ' leaflet-fade-anim' : ''));
1932
2025
 
1933
2026
  var position = L.DomUtil.getStyle(container, 'position');
1934
2027
 
@@ -1968,16 +2061,14 @@ L.Map = L.Class.extend({
1968
2061
  return L.DomUtil.create('div', className, container || this._panes.objectsPane);
1969
2062
  },
1970
2063
 
1971
- _initLayers: function (layers) {
1972
- layers = layers ? (L.Util.isArray(layers) ? layers : [layers]) : [];
1973
-
1974
- this._layers = {};
1975
- this._zoomBoundLayers = {};
1976
- this._tileLayersNum = 0;
2064
+ _clearPanes: function () {
2065
+ this._container.removeChild(this._mapPane);
2066
+ },
1977
2067
 
1978
- var i, len;
2068
+ _addLayers: function (layers) {
2069
+ layers = layers ? (L.Util.isArray(layers) ? layers : [layers]) : [];
1979
2070
 
1980
- for (i = 0, len = layers.length; i < len; i++) {
2071
+ for (var i = 0, len = layers.length; i < len; i++) {
1981
2072
  this.addLayer(layers[i]);
1982
2073
  }
1983
2074
  },
@@ -2015,6 +2106,7 @@ L.Map = L.Class.extend({
2015
2106
 
2016
2107
  if (loading) {
2017
2108
  this.fire('load');
2109
+ this.eachLayer(this._layerAdd, this);
2018
2110
  }
2019
2111
 
2020
2112
  this.fire('viewreset', {hard: !preserveMapOffset});
@@ -2103,14 +2195,17 @@ L.Map = L.Class.extend({
2103
2195
  },
2104
2196
 
2105
2197
  _onMouseClick: function (e) {
2106
- if (!this._loaded || (this.dragging && this.dragging.moved())) { return; }
2198
+ if (!this._loaded || (!e._simulated &&
2199
+ ((this.dragging && this.dragging.moved()) ||
2200
+ (this.boxZoom && this.boxZoom.moved()))) ||
2201
+ L.DomEvent._skipped(e)) { return; }
2107
2202
 
2108
2203
  this.fire('preclick');
2109
2204
  this._fireMouseEvent(e);
2110
2205
  },
2111
2206
 
2112
2207
  _fireMouseEvent: function (e) {
2113
- if (!this._loaded) { return; }
2208
+ if (!this._loaded || L.DomEvent._skipped(e)) { return; }
2114
2209
 
2115
2210
  var type = e.type;
2116
2211
 
@@ -2141,6 +2236,12 @@ L.Map = L.Class.extend({
2141
2236
  }
2142
2237
  },
2143
2238
 
2239
+ _clearHandlers: function () {
2240
+ for (var i = 0, len = this._handlers.length; i < len; i++) {
2241
+ this._handlers[i].disable();
2242
+ }
2243
+ },
2244
+
2144
2245
  whenReady: function (callback, context) {
2145
2246
  if (this._loaded) {
2146
2247
  callback.call(context || this, this);
@@ -2150,6 +2251,11 @@ L.Map = L.Class.extend({
2150
2251
  return this;
2151
2252
  },
2152
2253
 
2254
+ _layerAdd: function (layer) {
2255
+ layer.onAdd(this);
2256
+ this.fire('layeradd', {layer: layer});
2257
+ },
2258
+
2153
2259
 
2154
2260
  // private methods for getting map state
2155
2261
 
@@ -2208,7 +2314,7 @@ L.map = function (id, options) {
2208
2314
  L.Projection.Mercator = {
2209
2315
  MAX_LATITUDE: 85.0840591556,
2210
2316
 
2211
- R_MINOR: 6356752.3142,
2317
+ R_MINOR: 6356752.314245179,
2212
2318
  R_MAJOR: 6378137,
2213
2319
 
2214
2320
  project: function (latlng) { // (LatLng) -> Point
@@ -2226,7 +2332,7 @@ L.Projection.Mercator = {
2226
2332
  con = Math.pow((1 - con) / (1 + con), eccent * 0.5);
2227
2333
 
2228
2334
  var ts = Math.tan(0.5 * ((Math.PI * 0.5) - y)) / con;
2229
- y = -r2 * Math.log(ts);
2335
+ y = -r * Math.log(ts);
2230
2336
 
2231
2337
  return new L.Point(x, y);
2232
2338
  },
@@ -2238,7 +2344,7 @@ L.Projection.Mercator = {
2238
2344
  lng = point.x * d / r,
2239
2345
  tmp = r2 / r,
2240
2346
  eccent = Math.sqrt(1 - (tmp * tmp)),
2241
- ts = Math.exp(- point.y / r2),
2347
+ ts = Math.exp(- point.y / r),
2242
2348
  phi = (Math.PI / 2) - 2 * Math.atan(ts),
2243
2349
  numIter = 15,
2244
2350
  tol = 1e-7,
@@ -2267,9 +2373,9 @@ L.CRS.EPSG3395 = L.extend({}, L.CRS, {
2267
2373
  transformation: (function () {
2268
2374
  var m = L.Projection.Mercator,
2269
2375
  r = m.R_MAJOR,
2270
- r2 = m.R_MINOR;
2376
+ scale = 0.5 / (Math.PI * r);
2271
2377
 
2272
- return new L.Transformation(0.5 / (Math.PI * r), 0.5, -0.5 / (Math.PI * r2), 0.5);
2378
+ return new L.Transformation(scale, 0.5, -scale, 0.5);
2273
2379
  }())
2274
2380
  });
2275
2381
 
@@ -2290,7 +2396,8 @@ L.TileLayer = L.Class.extend({
2290
2396
  attribution: '',
2291
2397
  zoomOffset: 0,
2292
2398
  opacity: 1,
2293
- /* (undefined works too)
2399
+ /*
2400
+ maxNativeZoom: null,
2294
2401
  zIndex: null,
2295
2402
  tms: false,
2296
2403
  continuousWorld: false,
@@ -2334,14 +2441,11 @@ L.TileLayer = L.Class.extend({
2334
2441
 
2335
2442
  onAdd: function (map) {
2336
2443
  this._map = map;
2337
- this._animated = map.options.zoomAnimation && L.Browser.any3d;
2444
+ this._animated = map._zoomAnimated;
2338
2445
 
2339
2446
  // create a container div for tiles
2340
2447
  this._initContainer();
2341
2448
 
2342
- // create an image to clone for tiles
2343
- this._createTileProto();
2344
-
2345
2449
  // set up events
2346
2450
  map.on({
2347
2451
  'viewreset': this._reset,
@@ -2495,13 +2599,6 @@ L.TileLayer = L.Class.extend({
2495
2599
  } else {
2496
2600
  L.DomUtil.setOpacity(this._container, this.options.opacity);
2497
2601
  }
2498
-
2499
- // stupid webkit hack to force redrawing of tiles
2500
- if (L.Browser.webkit) {
2501
- for (i in tiles) {
2502
- tiles[i].style.webkitTransform += ' translate(0,0)';
2503
- }
2504
- }
2505
2602
  },
2506
2603
 
2507
2604
  _initContainer: function () {
@@ -2513,10 +2610,11 @@ L.TileLayer = L.Class.extend({
2513
2610
  this._updateZIndex();
2514
2611
 
2515
2612
  if (this._animated) {
2516
- var className = 'leaflet-tile-container leaflet-zoom-animated';
2613
+ var className = 'leaflet-tile-container';
2517
2614
 
2518
2615
  this._bgBuffer = L.DomUtil.create('div', className, this._container);
2519
2616
  this._tileContainer = L.DomUtil.create('div', className, this._container);
2617
+
2520
2618
  } else {
2521
2619
  this._tileContainer = this._container;
2522
2620
  }
@@ -2550,13 +2648,27 @@ L.TileLayer = L.Class.extend({
2550
2648
  this._initContainer();
2551
2649
  },
2552
2650
 
2651
+ _getTileSize: function () {
2652
+ var map = this._map,
2653
+ zoom = map.getZoom(),
2654
+ zoomN = this.options.maxNativeZoom,
2655
+ tileSize = this.options.tileSize;
2656
+
2657
+ if (zoomN && zoom > zoomN) {
2658
+ tileSize = Math.round(map.getZoomScale(zoom) / map.getZoomScale(zoomN) * tileSize);
2659
+ }
2660
+
2661
+ return tileSize;
2662
+ },
2663
+
2553
2664
  _update: function () {
2554
2665
 
2555
2666
  if (!this._map) { return; }
2556
2667
 
2557
- var bounds = this._map.getPixelBounds(),
2558
- zoom = this._map.getZoom(),
2559
- tileSize = this.options.tileSize;
2668
+ var map = this._map,
2669
+ bounds = map.getPixelBounds(),
2670
+ zoom = map.getZoom(),
2671
+ tileSize = this._getTileSize();
2560
2672
 
2561
2673
  if (zoom > this.options.maxZoom || zoom < this.options.minZoom) {
2562
2674
  return;
@@ -2621,11 +2733,11 @@ L.TileLayer = L.Class.extend({
2621
2733
 
2622
2734
  var options = this.options;
2623
2735
 
2624
- if (!options.continuousWorld && options.noWrap) {
2736
+ if (!options.continuousWorld) {
2625
2737
  var limit = this._getWrapTileNum();
2626
2738
 
2627
2739
  // don't load if exceeds world bounds
2628
- if (tilePoint.x < 0 || tilePoint.x >= limit ||
2740
+ if ((options.noWrap && (tilePoint.x < 0 || tilePoint.x >= limit)) ||
2629
2741
  tilePoint.y < 0 || tilePoint.y >= limit) { return false; }
2630
2742
  }
2631
2743
 
@@ -2719,12 +2831,14 @@ L.TileLayer = L.Class.extend({
2719
2831
  zoom = options.maxZoom - zoom;
2720
2832
  }
2721
2833
 
2722
- return zoom + options.zoomOffset;
2834
+ zoom += options.zoomOffset;
2835
+
2836
+ return options.maxNativeZoom ? Math.min(zoom, options.maxNativeZoom) : zoom;
2723
2837
  },
2724
2838
 
2725
2839
  _getTilePos: function (tilePoint) {
2726
2840
  var origin = this._map.getPixelOrigin(),
2727
- tileSize = this.options.tileSize;
2841
+ tileSize = this._getTileSize();
2728
2842
 
2729
2843
  return tilePoint.multiplyBy(tileSize).subtract(origin);
2730
2844
  },
@@ -2766,12 +2880,6 @@ L.TileLayer = L.Class.extend({
2766
2880
  return this.options.subdomains[index];
2767
2881
  },
2768
2882
 
2769
- _createTileProto: function () {
2770
- var img = this._tileImg = L.DomUtil.create('img', 'leaflet-tile');
2771
- img.style.width = img.style.height = this.options.tileSize + 'px';
2772
- img.galleryimg = 'no';
2773
- },
2774
-
2775
2883
  _getTile: function () {
2776
2884
  if (this.options.reuseTiles && this._unusedTiles.length > 0) {
2777
2885
  var tile = this._unusedTiles.pop();
@@ -2785,7 +2893,10 @@ L.TileLayer = L.Class.extend({
2785
2893
  _resetTile: function (/*tile*/) {},
2786
2894
 
2787
2895
  _createTile: function () {
2788
- var tile = this._tileImg.cloneNode(false);
2896
+ var tile = L.DomUtil.create('img', 'leaflet-tile');
2897
+ tile.style.width = tile.style.height = this._getTileSize() + 'px';
2898
+ tile.galleryimg = 'no';
2899
+
2789
2900
  tile.onselectstart = tile.onmousemove = L.Util.falseFn;
2790
2901
 
2791
2902
  if (L.Browser.ielt9 && this.options.opacity !== undefined) {
@@ -2801,10 +2912,20 @@ L.TileLayer = L.Class.extend({
2801
2912
 
2802
2913
  this._adjustTilePoint(tilePoint);
2803
2914
  tile.src = this.getTileUrl(tilePoint);
2915
+
2916
+ this.fire('tileloadstart', {
2917
+ tile: tile,
2918
+ url: tile.src
2919
+ });
2804
2920
  },
2805
2921
 
2806
2922
  _tileLoaded: function () {
2807
2923
  this._tilesToLoad--;
2924
+
2925
+ if (this._animated) {
2926
+ L.DomUtil.addClass(this._tileContainer, 'leaflet-zoom-animated');
2927
+ }
2928
+
2808
2929
  if (!this._tilesToLoad) {
2809
2930
  this.fire('load');
2810
2931
 
@@ -2885,7 +3006,7 @@ L.TileLayer.WMS = L.TileLayer.extend({
2885
3006
 
2886
3007
  for (var i in options) {
2887
3008
  // all keys that are not TileLayer options go to WMS params
2888
- if (!this.options.hasOwnProperty(i)) {
3009
+ if (!this.options.hasOwnProperty(i) && i !== 'crs') {
2889
3010
  wmsParams[i] = options[i];
2890
3011
  }
2891
3012
  }
@@ -2897,29 +3018,33 @@ L.TileLayer.WMS = L.TileLayer.extend({
2897
3018
 
2898
3019
  onAdd: function (map) {
2899
3020
 
2900
- var projectionKey = parseFloat(this.wmsParams.version) >= 1.3 ? 'crs' : 'srs';
2901
- this.wmsParams[projectionKey] = map.options.crs.code;
3021
+ this._crs = this.options.crs || map.options.crs;
3022
+
3023
+ this._wmsVersion = parseFloat(this.wmsParams.version);
3024
+
3025
+ var projectionKey = this._wmsVersion >= 1.3 ? 'crs' : 'srs';
3026
+ this.wmsParams[projectionKey] = this._crs.code;
2902
3027
 
2903
3028
  L.TileLayer.prototype.onAdd.call(this, map);
2904
3029
  },
2905
3030
 
2906
- getTileUrl: function (tilePoint, zoom) { // (Point, Number) -> String
3031
+ getTileUrl: function (tilePoint) { // (Point, Number) -> String
2907
3032
 
2908
3033
  var map = this._map,
2909
- crs = map.options.crs,
2910
3034
  tileSize = this.options.tileSize,
2911
3035
 
2912
3036
  nwPoint = tilePoint.multiplyBy(tileSize),
2913
3037
  sePoint = nwPoint.add([tileSize, tileSize]),
2914
3038
 
2915
- nw = crs.project(map.unproject(nwPoint, zoom)),
2916
- se = crs.project(map.unproject(sePoint, zoom)),
2917
-
2918
- bbox = [nw.x, se.y, se.x, nw.y].join(','),
3039
+ nw = this._crs.project(map.unproject(nwPoint, tilePoint.z)),
3040
+ se = this._crs.project(map.unproject(sePoint, tilePoint.z)),
3041
+ bbox = this._wmsVersion >= 1.3 && this._crs === L.CRS.EPSG4326 ?
3042
+ [se.y, nw.x, nw.y, se.x].join(',') :
3043
+ [nw.x, se.y, se.x, nw.y].join(','),
2919
3044
 
2920
3045
  url = L.Util.template(this._url, {s: this._getSubdomain(tilePoint)});
2921
3046
 
2922
- return url + L.Util.getParamString(this.wmsParams, url) + '&bbox=' + bbox;
3047
+ return url + L.Util.getParamString(this.wmsParams, url, true) + '&BBOX=' + bbox;
2923
3048
  },
2924
3049
 
2925
3050
  setParams: function (params, noRedraw) {
@@ -2954,6 +3079,11 @@ L.TileLayer.Canvas = L.TileLayer.extend({
2954
3079
  },
2955
3080
 
2956
3081
  redraw: function () {
3082
+ if (this._map) {
3083
+ this._reset({hard: true});
3084
+ this._update();
3085
+ }
3086
+
2957
3087
  for (var i in this._tiles) {
2958
3088
  this._redrawTile(this._tiles[i]);
2959
3089
  }
@@ -2964,13 +3094,9 @@ L.TileLayer.Canvas = L.TileLayer.extend({
2964
3094
  this.drawTile(tile, tile._tilePoint, this._map._zoom);
2965
3095
  },
2966
3096
 
2967
- _createTileProto: function () {
2968
- var proto = this._canvasProto = L.DomUtil.create('canvas', 'leaflet-tile');
2969
- proto.width = proto.height = this.options.tileSize;
2970
- },
2971
-
2972
3097
  _createTile: function () {
2973
- var tile = this._canvasProto.cloneNode(false);
3098
+ var tile = L.DomUtil.create('canvas', 'leaflet-tile');
3099
+ tile.width = tile.height = this.options.tileSize;
2974
3100
  tile.onselectstart = tile.onmousemove = L.Util.falseFn;
2975
3101
  return tile;
2976
3102
  },
@@ -3074,6 +3200,15 @@ L.ImageOverlay = L.Class.extend({
3074
3200
  return this;
3075
3201
  },
3076
3202
 
3203
+ setUrl: function (url) {
3204
+ this._url = url;
3205
+ this._image.src = this._url;
3206
+ },
3207
+
3208
+ getAttribution: function () {
3209
+ return this.options.attribution;
3210
+ },
3211
+
3077
3212
  _initImage: function () {
3078
3213
  this._image = L.DomUtil.create('img', 'leaflet-image-layer');
3079
3214
 
@@ -3147,7 +3282,7 @@ L.Icon = L.Class.extend({
3147
3282
  iconSize: (Point) (can be set through CSS)
3148
3283
  iconAnchor: (Point) (centered by default, can be set in CSS with negative margins)
3149
3284
  popupAnchor: (Point) (if not specified, popup opens in the anchor point)
3150
- shadowUrl: (Point) (no shadow by default)
3285
+ shadowUrl: (String) (no shadow by default)
3151
3286
  shadowRetinaUrl: (String) (optional, used for retina devices if detected)
3152
3287
  shadowSize: (Point)
3153
3288
  shadowAnchor: (Point)
@@ -3159,15 +3294,15 @@ L.Icon = L.Class.extend({
3159
3294
  L.setOptions(this, options);
3160
3295
  },
3161
3296
 
3162
- createIcon: function () {
3163
- return this._createIcon('icon');
3297
+ createIcon: function (oldIcon) {
3298
+ return this._createIcon('icon', oldIcon);
3164
3299
  },
3165
3300
 
3166
- createShadow: function () {
3167
- return this._createIcon('shadow');
3301
+ createShadow: function (oldIcon) {
3302
+ return this._createIcon('shadow', oldIcon);
3168
3303
  },
3169
3304
 
3170
- _createIcon: function (name) {
3305
+ _createIcon: function (name, oldIcon) {
3171
3306
  var src = this._getIconUrl(name);
3172
3307
 
3173
3308
  if (!src) {
@@ -3177,7 +3312,12 @@ L.Icon = L.Class.extend({
3177
3312
  return null;
3178
3313
  }
3179
3314
 
3180
- var img = this._createImg(src);
3315
+ var img;
3316
+ if (!oldIcon || oldIcon.tagName !== 'IMG') {
3317
+ img = this._createImg(src);
3318
+ } else {
3319
+ img = this._createImg(src, oldIcon);
3320
+ }
3181
3321
  this._setIconStyles(img, name);
3182
3322
 
3183
3323
  return img;
@@ -3211,14 +3351,17 @@ L.Icon = L.Class.extend({
3211
3351
  }
3212
3352
  },
3213
3353
 
3214
- _createImg: function (src) {
3215
- var el;
3354
+ _createImg: function (src, el) {
3216
3355
 
3217
3356
  if (!L.Browser.ie6) {
3218
- el = document.createElement('img');
3357
+ if (!el) {
3358
+ el = document.createElement('img');
3359
+ }
3219
3360
  el.src = src;
3220
3361
  } else {
3221
- el = document.createElement('div');
3362
+ if (!el) {
3363
+ el = document.createElement('div');
3364
+ }
3222
3365
  el.style.filter =
3223
3366
  'progid:DXImageTransform.Microsoft.AlphaImageLoader(src="' + src + '")';
3224
3367
  }
@@ -3275,7 +3418,7 @@ L.Icon.Default = L.Icon.extend({
3275
3418
 
3276
3419
  L.Icon.Default.imagePath = (function () {
3277
3420
  var scripts = document.getElementsByTagName('script'),
3278
- leafletRe = /\/?leaflet[\-\._]?([\w\-\._]*)\.js\??/;
3421
+ leafletRe = /[\/^]leaflet[\-\._]?([\w\-\._]*)\.js\??/;
3279
3422
 
3280
3423
  var i, len, src, matches, path;
3281
3424
 
@@ -3302,8 +3445,10 @@ L.Marker = L.Class.extend({
3302
3445
  options: {
3303
3446
  icon: new L.Icon.Default(),
3304
3447
  title: '',
3448
+ alt: '',
3305
3449
  clickable: true,
3306
3450
  draggable: false,
3451
+ keyboard: true,
3307
3452
  zIndexOffset: 0,
3308
3453
  opacity: 1,
3309
3454
  riseOnHover: false,
@@ -3322,6 +3467,7 @@ L.Marker = L.Class.extend({
3322
3467
 
3323
3468
  this._initIcon();
3324
3469
  this.update();
3470
+ this.fire('add');
3325
3471
 
3326
3472
  if (map.options.zoomAnimation && map.options.markerZoomAnimation) {
3327
3473
  map.on('zoomanim', this._animateZoom, this);
@@ -3339,6 +3485,7 @@ L.Marker = L.Class.extend({
3339
3485
  }
3340
3486
 
3341
3487
  this._removeIcon();
3488
+ this._removeShadow();
3342
3489
 
3343
3490
  this.fire('remove');
3344
3491
 
@@ -3370,9 +3517,6 @@ L.Marker = L.Class.extend({
3370
3517
  },
3371
3518
 
3372
3519
  setIcon: function (icon) {
3373
- if (this._map) {
3374
- this._removeIcon();
3375
- }
3376
3520
 
3377
3521
  this.options.icon = icon;
3378
3522
 
@@ -3381,6 +3525,10 @@ L.Marker = L.Class.extend({
3381
3525
  this.update();
3382
3526
  }
3383
3527
 
3528
+ if (this._popup) {
3529
+ this.bindPopup(this._popup);
3530
+ }
3531
+
3384
3532
  return this;
3385
3533
  },
3386
3534
 
@@ -3397,66 +3545,90 @@ L.Marker = L.Class.extend({
3397
3545
  var options = this.options,
3398
3546
  map = this._map,
3399
3547
  animation = (map.options.zoomAnimation && map.options.markerZoomAnimation),
3400
- classToAdd = animation ? 'leaflet-zoom-animated' : 'leaflet-zoom-hide',
3401
- needOpacityUpdate = false;
3548
+ classToAdd = animation ? 'leaflet-zoom-animated' : 'leaflet-zoom-hide';
3402
3549
 
3403
- if (!this._icon) {
3404
- this._icon = options.icon.createIcon();
3550
+ var icon = options.icon.createIcon(this._icon),
3551
+ addIcon = false;
3552
+
3553
+ // if we're not reusing the icon, remove the old one and init new one
3554
+ if (icon !== this._icon) {
3555
+ if (this._icon) {
3556
+ this._removeIcon();
3557
+ }
3558
+ addIcon = true;
3405
3559
 
3406
3560
  if (options.title) {
3407
- this._icon.title = options.title;
3561
+ icon.title = options.title;
3408
3562
  }
3563
+
3564
+ if (options.alt) {
3565
+ icon.alt = options.alt;
3566
+ }
3567
+ }
3409
3568
 
3410
- this._initInteraction();
3411
- needOpacityUpdate = (this.options.opacity < 1);
3569
+ L.DomUtil.addClass(icon, classToAdd);
3412
3570
 
3413
- L.DomUtil.addClass(this._icon, classToAdd);
3571
+ if (options.keyboard) {
3572
+ icon.tabIndex = '0';
3573
+ }
3414
3574
 
3415
- if (options.riseOnHover) {
3416
- L.DomEvent
3417
- .on(this._icon, 'mouseover', this._bringToFront, this)
3418
- .on(this._icon, 'mouseout', this._resetZIndex, this);
3419
- }
3575
+ this._icon = icon;
3576
+
3577
+ this._initInteraction();
3578
+
3579
+ if (options.riseOnHover) {
3580
+ L.DomEvent
3581
+ .on(icon, 'mouseover', this._bringToFront, this)
3582
+ .on(icon, 'mouseout', this._resetZIndex, this);
3420
3583
  }
3421
3584
 
3422
- if (!this._shadow) {
3423
- this._shadow = options.icon.createShadow();
3585
+ var newShadow = options.icon.createShadow(this._shadow),
3586
+ addShadow = false;
3424
3587
 
3425
- if (this._shadow) {
3426
- L.DomUtil.addClass(this._shadow, classToAdd);
3427
- needOpacityUpdate = (this.options.opacity < 1);
3428
- }
3588
+ if (newShadow !== this._shadow) {
3589
+ this._removeShadow();
3590
+ addShadow = true;
3591
+ }
3592
+
3593
+ if (newShadow) {
3594
+ L.DomUtil.addClass(newShadow, classToAdd);
3429
3595
  }
3596
+ this._shadow = newShadow;
3597
+
3430
3598
 
3431
- if (needOpacityUpdate) {
3599
+ if (options.opacity < 1) {
3432
3600
  this._updateOpacity();
3433
3601
  }
3434
3602
 
3603
+
3435
3604
  var panes = this._map._panes;
3436
3605
 
3437
- panes.markerPane.appendChild(this._icon);
3606
+ if (addIcon) {
3607
+ panes.markerPane.appendChild(this._icon);
3608
+ }
3438
3609
 
3439
- if (this._shadow) {
3610
+ if (newShadow && addShadow) {
3440
3611
  panes.shadowPane.appendChild(this._shadow);
3441
3612
  }
3442
3613
  },
3443
3614
 
3444
3615
  _removeIcon: function () {
3445
- var panes = this._map._panes;
3446
-
3447
3616
  if (this.options.riseOnHover) {
3448
3617
  L.DomEvent
3449
3618
  .off(this._icon, 'mouseover', this._bringToFront)
3450
3619
  .off(this._icon, 'mouseout', this._resetZIndex);
3451
3620
  }
3452
3621
 
3453
- panes.markerPane.removeChild(this._icon);
3622
+ this._map._panes.markerPane.removeChild(this._icon);
3623
+
3624
+ this._icon = null;
3625
+ },
3454
3626
 
3627
+ _removeShadow: function () {
3455
3628
  if (this._shadow) {
3456
- panes.shadowPane.removeChild(this._shadow);
3629
+ this._map._panes.shadowPane.removeChild(this._shadow);
3457
3630
  }
3458
-
3459
- this._icon = this._shadow = null;
3631
+ this._shadow = null;
3460
3632
  },
3461
3633
 
3462
3634
  _setPos: function (pos) {
@@ -3476,7 +3648,7 @@ L.Marker = L.Class.extend({
3476
3648
  },
3477
3649
 
3478
3650
  _animateZoom: function (opt) {
3479
- var pos = this._map._latLngToNewLayerPoint(this._latlng, opt.zoom, opt.center);
3651
+ var pos = this._map._latLngToNewLayerPoint(this._latlng, opt.zoom, opt.center).round();
3480
3652
 
3481
3653
  this._setPos(pos);
3482
3654
  },
@@ -3492,6 +3664,7 @@ L.Marker = L.Class.extend({
3492
3664
 
3493
3665
  L.DomUtil.addClass(icon, 'leaflet-clickable');
3494
3666
  L.DomEvent.on(icon, 'click', this._onMouseClick, this);
3667
+ L.DomEvent.on(icon, 'keypress', this._onKeyPress, this);
3495
3668
 
3496
3669
  for (var i = 0; i < events.length; i++) {
3497
3670
  L.DomEvent.on(icon, events[i], this._fireMouseEvent, this);
@@ -3523,6 +3696,15 @@ L.Marker = L.Class.extend({
3523
3696
  });
3524
3697
  },
3525
3698
 
3699
+ _onKeyPress: function (e) {
3700
+ if (e.keyCode === 13) {
3701
+ this.fire('click', {
3702
+ originalEvent: e,
3703
+ latlng: this._latlng
3704
+ });
3705
+ }
3706
+ },
3707
+
3526
3708
  _fireMouseEvent: function (e) {
3527
3709
 
3528
3710
  this.fire(e.type, {
@@ -3537,6 +3719,8 @@ L.Marker = L.Class.extend({
3537
3719
  }
3538
3720
  if (e.type !== 'mousedown') {
3539
3721
  L.DomEvent.stopPropagation(e);
3722
+ } else {
3723
+ L.DomEvent.preventDefault(e);
3540
3724
  }
3541
3725
  },
3542
3726
 
@@ -3545,6 +3729,8 @@ L.Marker = L.Class.extend({
3545
3729
  if (this._map) {
3546
3730
  this._updateOpacity();
3547
3731
  }
3732
+
3733
+ return this;
3548
3734
  },
3549
3735
 
3550
3736
  _updateOpacity: function () {
@@ -3582,15 +3768,18 @@ L.DivIcon = L.Icon.extend({
3582
3768
  html: (String)
3583
3769
  bgPos: (Point)
3584
3770
  */
3585
- className: 'leaflet-div-icon'
3771
+ className: 'leaflet-div-icon',
3772
+ html: false
3586
3773
  },
3587
3774
 
3588
- createIcon: function () {
3589
- var div = document.createElement('div'),
3775
+ createIcon: function (oldIcon) {
3776
+ var div = (oldIcon && oldIcon.tagName === 'DIV') ? oldIcon : document.createElement('div'),
3590
3777
  options = this.options;
3591
3778
 
3592
- if (options.html) {
3779
+ if (options.html !== false) {
3593
3780
  div.innerHTML = options.html;
3781
+ } else {
3782
+ div.innerHTML = '';
3594
3783
  }
3595
3784
 
3596
3785
  if (options.bgPos) {
@@ -3626,11 +3815,13 @@ L.Popup = L.Class.extend({
3626
3815
  options: {
3627
3816
  minWidth: 50,
3628
3817
  maxWidth: 300,
3629
- maxHeight: null,
3818
+ // maxHeight: null,
3630
3819
  autoPan: true,
3631
3820
  closeButton: true,
3632
- offset: [0, 6],
3821
+ offset: [0, 7],
3633
3822
  autoPanPadding: [5, 5],
3823
+ // autoPanPaddingTopLeft: null,
3824
+ // autoPanPaddingBottomRight: null,
3634
3825
  keepInView: false,
3635
3826
  className: '',
3636
3827
  zoomAnimation: true
@@ -3641,6 +3832,7 @@ L.Popup = L.Class.extend({
3641
3832
 
3642
3833
  this._source = source;
3643
3834
  this._animated = L.Browser.any3d && this.options.zoomAnimation;
3835
+ this._isOpen = false;
3644
3836
  },
3645
3837
 
3646
3838
  onAdd: function (map) {
@@ -3649,7 +3841,6 @@ L.Popup = L.Class.extend({
3649
3841
  if (!this._container) {
3650
3842
  this._initLayout();
3651
3843
  }
3652
- this._updateContent();
3653
3844
 
3654
3845
  var animFade = map.options.fadeAnimation;
3655
3846
 
@@ -3660,7 +3851,7 @@ L.Popup = L.Class.extend({
3660
3851
 
3661
3852
  map.on(this._getEvents(), this);
3662
3853
 
3663
- this._update();
3854
+ this.update();
3664
3855
 
3665
3856
  if (animFade) {
3666
3857
  L.DomUtil.setOpacity(this._container, 1);
@@ -3707,18 +3898,40 @@ L.Popup = L.Class.extend({
3707
3898
  }
3708
3899
  },
3709
3900
 
3901
+ getLatLng: function () {
3902
+ return this._latlng;
3903
+ },
3904
+
3710
3905
  setLatLng: function (latlng) {
3711
3906
  this._latlng = L.latLng(latlng);
3712
- this._update();
3907
+ this.update();
3713
3908
  return this;
3714
3909
  },
3715
3910
 
3911
+ getContent: function () {
3912
+ return this._content;
3913
+ },
3914
+
3716
3915
  setContent: function (content) {
3717
3916
  this._content = content;
3718
- this._update();
3917
+ this.update();
3719
3918
  return this;
3720
3919
  },
3721
3920
 
3921
+ update: function () {
3922
+ if (!this._map) { return; }
3923
+
3924
+ this._container.style.visibility = 'hidden';
3925
+
3926
+ this._updateContent();
3927
+ this._updateLayout();
3928
+ this._updatePosition();
3929
+
3930
+ this._container.style.visibility = '';
3931
+
3932
+ this._adjustPan();
3933
+ },
3934
+
3722
3935
  _getEvents: function () {
3723
3936
  var events = {
3724
3937
  viewreset: this._updatePosition
@@ -3727,7 +3940,7 @@ L.Popup = L.Class.extend({
3727
3940
  if (this._animated) {
3728
3941
  events.zoomanim = this._zoomAnimation;
3729
3942
  }
3730
- if (this._map.options.closePopupOnClick) {
3943
+ if ('closeOnClick' in this.options ? this.options.closeOnClick : this._map.options.closePopupOnClick) {
3731
3944
  events.preclick = this._close;
3732
3945
  }
3733
3946
  if (this.options.keepInView) {
@@ -3739,7 +3952,7 @@ L.Popup = L.Class.extend({
3739
3952
 
3740
3953
  _close: function () {
3741
3954
  if (this._map) {
3742
- this._map.removeLayer(this);
3955
+ this._map.closePopup(this);
3743
3956
  }
3744
3957
  },
3745
3958
 
@@ -3765,26 +3978,14 @@ L.Popup = L.Class.extend({
3765
3978
  L.DomEvent.disableClickPropagation(wrapper);
3766
3979
 
3767
3980
  this._contentNode = L.DomUtil.create('div', prefix + '-content', wrapper);
3768
- L.DomEvent.on(this._contentNode, 'mousewheel', L.DomEvent.stopPropagation);
3981
+
3982
+ L.DomEvent.disableScrollPropagation(this._contentNode);
3983
+ L.DomEvent.on(wrapper, 'contextmenu', L.DomEvent.stopPropagation);
3769
3984
 
3770
3985
  this._tipContainer = L.DomUtil.create('div', prefix + '-tip-container', container);
3771
3986
  this._tip = L.DomUtil.create('div', prefix + '-tip', this._tipContainer);
3772
3987
  },
3773
3988
 
3774
- _update: function () {
3775
- if (!this._map) { return; }
3776
-
3777
- this._container.style.visibility = 'hidden';
3778
-
3779
- this._updateContent();
3780
- this._updateLayout();
3781
- this._updatePosition();
3782
-
3783
- this._container.style.visibility = '';
3784
-
3785
- this._adjustPan();
3786
- },
3787
-
3788
3989
  _updateContent: function () {
3789
3990
  if (!this._content) { return; }
3790
3991
 
@@ -3869,21 +4070,23 @@ L.Popup = L.Class.extend({
3869
4070
 
3870
4071
  var containerPos = map.layerPointToContainerPoint(layerPos),
3871
4072
  padding = L.point(this.options.autoPanPadding),
4073
+ paddingTL = L.point(this.options.autoPanPaddingTopLeft || padding),
4074
+ paddingBR = L.point(this.options.autoPanPaddingBottomRight || padding),
3872
4075
  size = map.getSize(),
3873
4076
  dx = 0,
3874
4077
  dy = 0;
3875
4078
 
3876
- if (containerPos.x + containerWidth > size.x) { // right
3877
- dx = containerPos.x + containerWidth - size.x + padding.x;
4079
+ if (containerPos.x + containerWidth + paddingBR.x > size.x) { // right
4080
+ dx = containerPos.x + containerWidth - size.x + paddingBR.x;
3878
4081
  }
3879
- if (containerPos.x - dx < 0) { // left
3880
- dx = containerPos.x - padding.x;
4082
+ if (containerPos.x - dx - paddingTL.x < 0) { // left
4083
+ dx = containerPos.x - paddingTL.x;
3881
4084
  }
3882
- if (containerPos.y + containerHeight > size.y) { // bottom
3883
- dy = containerPos.y + containerHeight - size.y + padding.y;
4085
+ if (containerPos.y + containerHeight + paddingBR.y > size.y) { // bottom
4086
+ dy = containerPos.y + containerHeight - size.y + paddingBR.y;
3884
4087
  }
3885
- if (containerPos.y - dy < 0) { // top
3886
- dy = containerPos.y - padding.y;
4088
+ if (containerPos.y - dy - paddingTL.y < 0) { // top
4089
+ dy = containerPos.y - paddingTL.y;
3887
4090
  }
3888
4091
 
3889
4092
  if (dx || dy) {
@@ -3915,16 +4118,21 @@ L.Map.include({
3915
4118
  .setLatLng(latlng)
3916
4119
  .setContent(content);
3917
4120
  }
4121
+ popup._isOpen = true;
3918
4122
 
3919
4123
  this._popup = popup;
3920
4124
  return this.addLayer(popup);
3921
4125
  },
3922
4126
 
3923
- closePopup: function () {
3924
- if (this._popup) {
3925
- this.removeLayer(this._popup);
4127
+ closePopup: function (popup) {
4128
+ if (!popup || popup === this._popup) {
4129
+ popup = this._popup;
3926
4130
  this._popup = null;
3927
4131
  }
4132
+ if (popup) {
4133
+ this.removeLayer(popup);
4134
+ popup._isOpen = false;
4135
+ }
3928
4136
  return this;
3929
4137
  }
3930
4138
  });
@@ -3951,22 +4159,34 @@ L.Marker.include({
3951
4159
  return this;
3952
4160
  },
3953
4161
 
3954
- bindPopup: function (content, options) {
3955
- var anchor = L.point(this.options.icon.options.popupAnchor || [0, 0]);
3956
-
3957
- anchor = anchor.add(L.Popup.prototype.options.offset);
3958
-
3959
- if (options && options.offset) {
4162
+ togglePopup: function () {
4163
+ if (this._popup) {
4164
+ if (this._popup._isOpen) {
4165
+ this.closePopup();
4166
+ } else {
4167
+ this.openPopup();
4168
+ }
4169
+ }
4170
+ return this;
4171
+ },
4172
+
4173
+ bindPopup: function (content, options) {
4174
+ var anchor = L.point(this.options.icon.options.popupAnchor || [0, 0]);
4175
+
4176
+ anchor = anchor.add(L.Popup.prototype.options.offset);
4177
+
4178
+ if (options && options.offset) {
3960
4179
  anchor = anchor.add(options.offset);
3961
4180
  }
3962
4181
 
3963
4182
  options = L.extend({offset: anchor}, options);
3964
4183
 
3965
- if (!this._popup) {
4184
+ if (!this._popupHandlersAdded) {
3966
4185
  this
3967
- .on('click', this.openPopup, this)
4186
+ .on('click', this.togglePopup, this)
3968
4187
  .on('remove', this.closePopup, this)
3969
4188
  .on('move', this._movePopup, this);
4189
+ this._popupHandlersAdded = true;
3970
4190
  }
3971
4191
 
3972
4192
  if (content instanceof L.Popup) {
@@ -3991,13 +4211,18 @@ L.Marker.include({
3991
4211
  if (this._popup) {
3992
4212
  this._popup = null;
3993
4213
  this
3994
- .off('click', this.openPopup)
3995
- .off('remove', this.closePopup)
3996
- .off('move', this._movePopup);
4214
+ .off('click', this.togglePopup, this)
4215
+ .off('remove', this.closePopup, this)
4216
+ .off('move', this._movePopup, this);
4217
+ this._popupHandlersAdded = false;
3997
4218
  }
3998
4219
  return this;
3999
4220
  },
4000
4221
 
4222
+ getPopup: function () {
4223
+ return this._popup;
4224
+ },
4225
+
4001
4226
  _movePopup: function (e) {
4002
4227
  this._popup.setLatLng(e.latlng);
4003
4228
  }
@@ -4035,10 +4260,10 @@ L.LayerGroup = L.Class.extend({
4035
4260
  },
4036
4261
 
4037
4262
  removeLayer: function (layer) {
4038
- var id = this.getLayerId(layer);
4263
+ var id = layer in this._layers ? layer : this.getLayerId(layer);
4039
4264
 
4040
4265
  if (this._map && this._layers[id]) {
4041
- this._map.removeLayer(layer);
4266
+ this._map.removeLayer(this._layers[id]);
4042
4267
  }
4043
4268
 
4044
4269
  delete this._layers[id];
@@ -4049,7 +4274,7 @@ L.LayerGroup = L.Class.extend({
4049
4274
  hasLayer: function (layer) {
4050
4275
  if (!layer) { return false; }
4051
4276
 
4052
- return (this.getLayerId(layer) in this._layers);
4277
+ return (layer in this._layers || this.getLayerId(layer) in this._layers);
4053
4278
  },
4054
4279
 
4055
4280
  clearLayers: function () {
@@ -4094,8 +4319,13 @@ L.LayerGroup = L.Class.extend({
4094
4319
  return this;
4095
4320
  },
4096
4321
 
4322
+ getLayer: function (id) {
4323
+ return this._layers[id];
4324
+ },
4325
+
4097
4326
  getLayers: function () {
4098
4327
  var layers = [];
4328
+
4099
4329
  for (var i in this._layers) {
4100
4330
  layers.push(this._layers[i]);
4101
4331
  }
@@ -4125,7 +4355,7 @@ L.FeatureGroup = L.LayerGroup.extend({
4125
4355
  includes: L.Mixin.Events,
4126
4356
 
4127
4357
  statics: {
4128
- EVENTS: 'click dblclick mouseover mouseout mousemove contextmenu'
4358
+ EVENTS: 'click dblclick mouseover mouseout mousemove contextmenu popupopen popupclose'
4129
4359
  },
4130
4360
 
4131
4361
  addLayer: function (layer) {
@@ -4133,7 +4363,9 @@ L.FeatureGroup = L.LayerGroup.extend({
4133
4363
  return this;
4134
4364
  }
4135
4365
 
4136
- layer.on(L.FeatureGroup.EVENTS, this._propagateEvent, this);
4366
+ if ('on' in layer) {
4367
+ layer.on(L.FeatureGroup.EVENTS, this._propagateEvent, this);
4368
+ }
4137
4369
 
4138
4370
  L.LayerGroup.prototype.addLayer.call(this, layer);
4139
4371
 
@@ -4145,6 +4377,13 @@ L.FeatureGroup = L.LayerGroup.extend({
4145
4377
  },
4146
4378
 
4147
4379
  removeLayer: function (layer) {
4380
+ if (!this.hasLayer(layer)) {
4381
+ return this;
4382
+ }
4383
+ if (layer in this._layers) {
4384
+ layer = this._layers[layer];
4385
+ }
4386
+
4148
4387
  layer.off(L.FeatureGroup.EVENTS, this._propagateEvent, this);
4149
4388
 
4150
4389
  L.LayerGroup.prototype.removeLayer.call(this, layer);
@@ -4210,15 +4449,19 @@ L.Path = L.Class.extend({
4210
4449
  // how much to extend the clip area around the map view
4211
4450
  // (relative to its size, e.g. 0.5 is half the screen in each direction)
4212
4451
  // set it so that SVG element doesn't exceed 1280px (vectors flicker on dragend if it is)
4213
- CLIP_PADDING: L.Browser.mobile ?
4214
- Math.max(0, Math.min(0.5,
4215
- (1280 / Math.max(window.innerWidth, window.innerHeight) - 1) / 2)) : 0.5
4452
+ CLIP_PADDING: (function () {
4453
+ var max = L.Browser.mobile ? 1280 : 2000,
4454
+ target = (max / Math.max(window.outerWidth, window.outerHeight) - 1) / 2;
4455
+ return Math.max(0, Math.min(0.5, target));
4456
+ })()
4216
4457
  },
4217
4458
 
4218
4459
  options: {
4219
4460
  stroke: true,
4220
4461
  color: '#0033ff',
4221
4462
  dashArray: null,
4463
+ lineCap: null,
4464
+ lineJoin: null,
4222
4465
  weight: 5,
4223
4466
  opacity: 0.5,
4224
4467
 
@@ -4398,6 +4641,12 @@ L.Path = L.Path.extend({
4398
4641
  } else {
4399
4642
  this._path.removeAttribute('stroke-dasharray');
4400
4643
  }
4644
+ if (this.options.lineCap) {
4645
+ this._path.setAttribute('stroke-linecap', this.options.lineCap);
4646
+ }
4647
+ if (this.options.lineJoin) {
4648
+ this._path.setAttribute('stroke-linejoin', this.options.lineJoin);
4649
+ }
4401
4650
  } else {
4402
4651
  this._path.setAttribute('stroke', 'none');
4403
4652
  }
@@ -4682,12 +4931,18 @@ L.Path = L.Browser.svg || !L.Browser.vml ? L.Path : L.Path.extend({
4682
4931
  stroke.opacity = options.opacity;
4683
4932
 
4684
4933
  if (options.dashArray) {
4685
- stroke.dashStyle = options.dashArray instanceof Array ?
4934
+ stroke.dashStyle = L.Util.isArray(options.dashArray) ?
4686
4935
  options.dashArray.join(' ') :
4687
4936
  options.dashArray.replace(/( *, *)/g, ' ');
4688
4937
  } else {
4689
4938
  stroke.dashStyle = '';
4690
4939
  }
4940
+ if (options.lineCap) {
4941
+ stroke.endcap = options.lineCap.replace('butt', 'flat');
4942
+ }
4943
+ if (options.lineJoin) {
4944
+ stroke.joinstyle = options.lineJoin;
4945
+ }
4691
4946
 
4692
4947
  } else if (stroke) {
4693
4948
  container.removeChild(stroke);
@@ -4771,6 +5026,7 @@ L.Path = (L.Path.SVG && !window.L_PREFER_CANVAS) || !L.Browser.canvas ? L.Path :
4771
5026
 
4772
5027
  if (this.options.clickable) {
4773
5028
  this._map.off('click', this._onClick, this);
5029
+ this._map.off('mousemove', this._onMouseMove, this);
4774
5030
  }
4775
5031
 
4776
5032
  this._requestUpdate();
@@ -5048,8 +5304,8 @@ L.LineUtil = {
5048
5304
  return false;
5049
5305
  // other cases
5050
5306
  } else {
5051
- codeOut = codeA || codeB,
5052
- p = this._getEdgeIntersection(a, b, codeOut, bounds),
5307
+ codeOut = codeA || codeB;
5308
+ p = this._getEdgeIntersection(a, b, codeOut, bounds);
5053
5309
  newCode = this._getBitCode(p, bounds);
5054
5310
 
5055
5311
  if (codeOut === codeA) {
@@ -5366,10 +5622,12 @@ L.Polygon = L.Polyline.extend({
5366
5622
  },
5367
5623
 
5368
5624
  initialize: function (latlngs, options) {
5369
- var i, len, hole;
5370
-
5371
5625
  L.Polyline.prototype.initialize.call(this, latlngs, options);
5626
+ this._initWithHoles(latlngs);
5627
+ },
5372
5628
 
5629
+ _initWithHoles: function (latlngs) {
5630
+ var i, len, hole;
5373
5631
  if (latlngs && L.Util.isArray(latlngs[0]) && (typeof latlngs[0][0] !== 'number')) {
5374
5632
  this._latlngs = this._convertLatLngs(latlngs[0]);
5375
5633
  this._holes = latlngs.slice(1);
@@ -5385,7 +5643,7 @@ L.Polygon = L.Polyline.extend({
5385
5643
  // filter out last point if its equal to the first one
5386
5644
  latlngs = this._latlngs;
5387
5645
 
5388
- if (latlngs[0].equals(latlngs[latlngs.length - 1])) {
5646
+ if (latlngs.length >= 2 && latlngs[0].equals(latlngs[latlngs.length - 1])) {
5389
5647
  latlngs.pop();
5390
5648
  }
5391
5649
  },
@@ -5410,6 +5668,15 @@ L.Polygon = L.Polyline.extend({
5410
5668
  }
5411
5669
  },
5412
5670
 
5671
+ setLatLngs: function (latlngs) {
5672
+ if (latlngs && L.Util.isArray(latlngs[0]) && (typeof latlngs[0][0] !== 'number')) {
5673
+ this._initWithHoles(latlngs);
5674
+ return this.redraw();
5675
+ } else {
5676
+ return L.Polyline.prototype.setLatLngs.call(this, latlngs);
5677
+ }
5678
+ },
5679
+
5413
5680
  _clipPoints: function () {
5414
5681
  var points = this._originalPoints,
5415
5682
  newParts = [];
@@ -5471,6 +5738,16 @@ L.polygon = function (latlngs, options) {
5471
5738
  }
5472
5739
 
5473
5740
  return this;
5741
+ },
5742
+
5743
+ getLatLngs: function () {
5744
+ var latlngs = [];
5745
+
5746
+ this.eachLayer(function (layer) {
5747
+ latlngs.push(layer.getLatLngs());
5748
+ });
5749
+
5750
+ return latlngs;
5474
5751
  }
5475
5752
  });
5476
5753
  }
@@ -5641,9 +5918,20 @@ L.CircleMarker = L.Circle.extend({
5641
5918
  this.setRadius(this.options.radius);
5642
5919
  },
5643
5920
 
5921
+ setLatLng: function (latlng) {
5922
+ L.Circle.prototype.setLatLng.call(this, latlng);
5923
+ if (this._popup && this._popup._isOpen) {
5924
+ this._popup.setLatLng(latlng);
5925
+ }
5926
+ },
5927
+
5644
5928
  setRadius: function (radius) {
5645
5929
  this.options.radius = this._radius = radius;
5646
5930
  return this.redraw();
5931
+ },
5932
+
5933
+ getRadius: function () {
5934
+ return this._radius;
5647
5935
  }
5648
5936
  });
5649
5937
 
@@ -5743,6 +6031,17 @@ L.Circle.include(!L.Path.CANVAS ? {} : {
5743
6031
  });
5744
6032
 
5745
6033
 
6034
+ /*
6035
+ * CircleMarker canvas specific drawing parts.
6036
+ */
6037
+
6038
+ L.CircleMarker.include(!L.Path.CANVAS ? {} : {
6039
+ _updateStyle: function () {
6040
+ L.Path.prototype._updateStyle.call(this);
6041
+ }
6042
+ });
6043
+
6044
+
5746
6045
  /*
5747
6046
  * L.GeoJSON turns any GeoJSON data into a Leaflet layer.
5748
6047
  */
@@ -5761,12 +6060,13 @@ L.GeoJSON = L.FeatureGroup.extend({
5761
6060
 
5762
6061
  addData: function (geojson) {
5763
6062
  var features = L.Util.isArray(geojson) ? geojson : geojson.features,
5764
- i, len;
6063
+ i, len, feature;
5765
6064
 
5766
6065
  if (features) {
5767
6066
  for (i = 0, len = features.length; i < len; i++) {
5768
6067
  // Only add this if geometry or geometries are set and not null
5769
- if (features[i].geometries || features[i].geometry || features[i].features) {
6068
+ feature = features[i];
6069
+ if (feature.geometries || feature.geometry || feature.features || feature.coordinates) {
5770
6070
  this.addData(features[i]);
5771
6071
  }
5772
6072
  }
@@ -5777,8 +6077,8 @@ L.GeoJSON = L.FeatureGroup.extend({
5777
6077
 
5778
6078
  if (options.filter && !options.filter(geojson)) { return; }
5779
6079
 
5780
- var layer = L.GeoJSON.geometryToLayer(geojson, options.pointToLayer, options.coordsToLatLng);
5781
- layer.feature = geojson;
6080
+ var layer = L.GeoJSON.geometryToLayer(geojson, options.pointToLayer, options.coordsToLatLng, options);
6081
+ layer.feature = L.GeoJSON.asFeature(geojson);
5782
6082
 
5783
6083
  layer.defaultOptions = layer.options;
5784
6084
  this.resetStyle(layer);
@@ -5817,11 +6117,11 @@ L.GeoJSON = L.FeatureGroup.extend({
5817
6117
  });
5818
6118
 
5819
6119
  L.extend(L.GeoJSON, {
5820
- geometryToLayer: function (geojson, pointToLayer, coordsToLatLng) {
6120
+ geometryToLayer: function (geojson, pointToLayer, coordsToLatLng, vectorOptions) {
5821
6121
  var geometry = geojson.type === 'Feature' ? geojson.geometry : geojson,
5822
6122
  coords = geometry.coordinates,
5823
6123
  layers = [],
5824
- latlng, latlngs, i, len, layer;
6124
+ latlng, latlngs, i, len;
5825
6125
 
5826
6126
  coordsToLatLng = coordsToLatLng || this.coordsToLatLng;
5827
6127
 
@@ -5833,37 +6133,37 @@ L.extend(L.GeoJSON, {
5833
6133
  case 'MultiPoint':
5834
6134
  for (i = 0, len = coords.length; i < len; i++) {
5835
6135
  latlng = coordsToLatLng(coords[i]);
5836
- layer = pointToLayer ? pointToLayer(geojson, latlng) : new L.Marker(latlng);
5837
- layers.push(layer);
6136
+ layers.push(pointToLayer ? pointToLayer(geojson, latlng) : new L.Marker(latlng));
5838
6137
  }
5839
6138
  return new L.FeatureGroup(layers);
5840
6139
 
5841
6140
  case 'LineString':
5842
6141
  latlngs = this.coordsToLatLngs(coords, 0, coordsToLatLng);
5843
- return new L.Polyline(latlngs);
6142
+ return new L.Polyline(latlngs, vectorOptions);
5844
6143
 
5845
6144
  case 'Polygon':
6145
+ if (coords.length === 2 && !coords[1].length) {
6146
+ throw new Error('Invalid GeoJSON object.');
6147
+ }
5846
6148
  latlngs = this.coordsToLatLngs(coords, 1, coordsToLatLng);
5847
- return new L.Polygon(latlngs);
6149
+ return new L.Polygon(latlngs, vectorOptions);
5848
6150
 
5849
6151
  case 'MultiLineString':
5850
6152
  latlngs = this.coordsToLatLngs(coords, 1, coordsToLatLng);
5851
- return new L.MultiPolyline(latlngs);
6153
+ return new L.MultiPolyline(latlngs, vectorOptions);
5852
6154
 
5853
6155
  case 'MultiPolygon':
5854
6156
  latlngs = this.coordsToLatLngs(coords, 2, coordsToLatLng);
5855
- return new L.MultiPolygon(latlngs);
6157
+ return new L.MultiPolygon(latlngs, vectorOptions);
5856
6158
 
5857
6159
  case 'GeometryCollection':
5858
6160
  for (i = 0, len = geometry.geometries.length; i < len; i++) {
5859
6161
 
5860
- layer = this.geometryToLayer({
6162
+ layers.push(this.geometryToLayer({
5861
6163
  geometry: geometry.geometries[i],
5862
6164
  type: 'Feature',
5863
6165
  properties: geojson.properties
5864
- }, pointToLayer);
5865
-
5866
- layers.push(layer);
6166
+ }, pointToLayer, coordsToLatLng, vectorOptions));
5867
6167
  }
5868
6168
  return new L.FeatureGroup(layers);
5869
6169
 
@@ -5903,24 +6203,44 @@ L.extend(L.GeoJSON, {
5903
6203
  }
5904
6204
 
5905
6205
  return coords;
6206
+ },
6207
+
6208
+ getFeature: function (layer, newGeometry) {
6209
+ return layer.feature ? L.extend({}, layer.feature, {geometry: newGeometry}) : L.GeoJSON.asFeature(newGeometry);
6210
+ },
6211
+
6212
+ asFeature: function (geoJSON) {
6213
+ if (geoJSON.type === 'Feature') {
6214
+ return geoJSON;
6215
+ }
6216
+
6217
+ return {
6218
+ type: 'Feature',
6219
+ properties: {},
6220
+ geometry: geoJSON
6221
+ };
5906
6222
  }
5907
6223
  });
5908
6224
 
5909
- L.Marker.include({
6225
+ var PointToGeoJSON = {
5910
6226
  toGeoJSON: function () {
5911
- return {
6227
+ return L.GeoJSON.getFeature(this, {
5912
6228
  type: 'Point',
5913
6229
  coordinates: L.GeoJSON.latLngToCoords(this.getLatLng())
5914
- };
6230
+ });
5915
6231
  }
5916
- });
6232
+ };
6233
+
6234
+ L.Marker.include(PointToGeoJSON);
6235
+ L.Circle.include(PointToGeoJSON);
6236
+ L.CircleMarker.include(PointToGeoJSON);
5917
6237
 
5918
6238
  L.Polyline.include({
5919
6239
  toGeoJSON: function () {
5920
- return {
6240
+ return L.GeoJSON.getFeature(this, {
5921
6241
  type: 'LineString',
5922
6242
  coordinates: L.GeoJSON.latLngsToCoords(this.getLatLngs())
5923
- };
6243
+ });
5924
6244
  }
5925
6245
  });
5926
6246
 
@@ -5939,51 +6259,66 @@ L.Polygon.include({
5939
6259
  }
5940
6260
  }
5941
6261
 
5942
- return {
6262
+ return L.GeoJSON.getFeature(this, {
5943
6263
  type: 'Polygon',
5944
6264
  coordinates: coords
5945
- };
6265
+ });
5946
6266
  }
5947
6267
  });
5948
6268
 
5949
6269
  (function () {
5950
- function includeMulti(Klass, type) {
5951
- Klass.include({
5952
- toGeoJSON: function () {
5953
- var coords = [];
6270
+ function multiToGeoJSON(type) {
6271
+ return function () {
6272
+ var coords = [];
5954
6273
 
5955
- this.eachLayer(function (layer) {
5956
- coords.push(layer.toGeoJSON().coordinates);
5957
- });
6274
+ this.eachLayer(function (layer) {
6275
+ coords.push(layer.toGeoJSON().geometry.coordinates);
6276
+ });
5958
6277
 
5959
- return {
5960
- type: type,
5961
- coordinates: coords
5962
- };
5963
- }
5964
- });
6278
+ return L.GeoJSON.getFeature(this, {
6279
+ type: type,
6280
+ coordinates: coords
6281
+ });
6282
+ };
5965
6283
  }
5966
6284
 
5967
- includeMulti(L.MultiPolyline, 'MultiLineString');
5968
- includeMulti(L.MultiPolygon, 'MultiPolygon');
5969
- }());
6285
+ L.MultiPolyline.include({toGeoJSON: multiToGeoJSON('MultiLineString')});
6286
+ L.MultiPolygon.include({toGeoJSON: multiToGeoJSON('MultiPolygon')});
5970
6287
 
5971
- L.LayerGroup.include({
5972
- toGeoJSON: function () {
5973
- var geoms = [];
6288
+ L.LayerGroup.include({
6289
+ toGeoJSON: function () {
5974
6290
 
5975
- this.eachLayer(function (layer) {
5976
- if (layer.toGeoJSON) {
5977
- geoms.push(layer.toGeoJSON());
6291
+ var geometry = this.feature && this.feature.geometry,
6292
+ jsons = [],
6293
+ json;
6294
+
6295
+ if (geometry && geometry.type === 'MultiPoint') {
6296
+ return multiToGeoJSON('MultiPoint').call(this);
5978
6297
  }
5979
- });
5980
6298
 
5981
- return {
5982
- type: 'GeometryCollection',
5983
- geometries: geoms
5984
- };
5985
- }
5986
- });
6299
+ var isGeometryCollection = geometry && geometry.type === 'GeometryCollection';
6300
+
6301
+ this.eachLayer(function (layer) {
6302
+ if (layer.toGeoJSON) {
6303
+ json = layer.toGeoJSON();
6304
+ jsons.push(isGeometryCollection ? json.geometry : L.GeoJSON.asFeature(json));
6305
+ }
6306
+ });
6307
+
6308
+ if (isGeometryCollection) {
6309
+ return L.GeoJSON.getFeature(this, {
6310
+ geometries: jsons,
6311
+ type: 'GeometryCollection'
6312
+ });
6313
+ }
6314
+
6315
+ return {
6316
+ type: 'FeatureCollection',
6317
+ features: jsons
6318
+ };
6319
+ }
6320
+ });
6321
+ }());
5987
6322
 
5988
6323
  L.geoJson = function (geojson, options) {
5989
6324
  return new L.GeoJSON(geojson, options);
@@ -6008,8 +6343,8 @@ L.DomEvent = {
6008
6343
  return fn.call(context || obj, e || L.DomEvent._getEvent());
6009
6344
  };
6010
6345
 
6011
- if (L.Browser.msTouch && type.indexOf('touch') === 0) {
6012
- return this.addMsTouchListener(obj, type, handler, id);
6346
+ if (L.Browser.pointer && type.indexOf('touch') === 0) {
6347
+ return this.addPointerListener(obj, type, handler, id);
6013
6348
  }
6014
6349
  if (L.Browser.touch && (type === 'dblclick') && this.addDoubleTapListener) {
6015
6350
  this.addDoubleTapListener(obj, handler, id);
@@ -6061,8 +6396,8 @@ L.DomEvent = {
6061
6396
 
6062
6397
  if (!handler) { return this; }
6063
6398
 
6064
- if (L.Browser.msTouch && type.indexOf('touch') === 0) {
6065
- this.removeMsTouchListener(obj, type, id);
6399
+ if (L.Browser.pointer && type.indexOf('touch') === 0) {
6400
+ this.removePointerListener(obj, type, id);
6066
6401
  } else if (L.Browser.touch && (type === 'dblclick') && this.removeDoubleTapListener) {
6067
6402
  this.removeDoubleTapListener(obj, id);
6068
6403
 
@@ -6093,20 +6428,29 @@ L.DomEvent = {
6093
6428
  } else {
6094
6429
  e.cancelBubble = true;
6095
6430
  }
6431
+ L.DomEvent._skipped(e);
6432
+
6096
6433
  return this;
6097
6434
  },
6098
6435
 
6099
- disableClickPropagation: function (el) {
6436
+ disableScrollPropagation: function (el) {
6437
+ var stop = L.DomEvent.stopPropagation;
6100
6438
 
6439
+ return L.DomEvent
6440
+ .on(el, 'mousewheel', stop)
6441
+ .on(el, 'MozMousePixelScroll', stop);
6442
+ },
6443
+
6444
+ disableClickPropagation: function (el) {
6101
6445
  var stop = L.DomEvent.stopPropagation;
6102
6446
 
6103
6447
  for (var i = L.Draggable.START.length - 1; i >= 0; i--) {
6104
- L.DomEvent.addListener(el, L.Draggable.START[i], stop);
6448
+ L.DomEvent.on(el, L.Draggable.START[i], stop);
6105
6449
  }
6106
6450
 
6107
6451
  return L.DomEvent
6108
- .addListener(el, 'click', stop)
6109
- .addListener(el, 'dblclick', stop);
6452
+ .on(el, 'click', L.DomEvent._fakeStop)
6453
+ .on(el, 'dblclick', stop);
6110
6454
  },
6111
6455
 
6112
6456
  preventDefault: function (e) {
@@ -6120,18 +6464,32 @@ L.DomEvent = {
6120
6464
  },
6121
6465
 
6122
6466
  stop: function (e) {
6123
- return L.DomEvent.preventDefault(e).stopPropagation(e);
6467
+ return L.DomEvent
6468
+ .preventDefault(e)
6469
+ .stopPropagation(e);
6124
6470
  },
6125
6471
 
6126
6472
  getMousePosition: function (e, container) {
6127
-
6128
6473
  var body = document.body,
6129
6474
  docEl = document.documentElement,
6130
- x = e.pageX ? e.pageX : e.clientX + body.scrollLeft + docEl.scrollLeft,
6131
- y = e.pageY ? e.pageY : e.clientY + body.scrollTop + docEl.scrollTop,
6475
+ //gecko makes scrollLeft more negative as you scroll in rtl, other browsers don't
6476
+ //ref: https://code.google.com/p/closure-library/source/browse/closure/goog/style/bidi.js
6477
+ x = L.DomUtil.documentIsLtr() ?
6478
+ (e.pageX ? e.pageX - body.scrollLeft - docEl.scrollLeft : e.clientX) :
6479
+ (L.Browser.gecko ? e.pageX - body.scrollLeft - docEl.scrollLeft :
6480
+ e.pageX ? e.pageX - body.scrollLeft + docEl.scrollLeft : e.clientX),
6481
+ y = e.pageY ? e.pageY - body.scrollTop - docEl.scrollTop: e.clientY,
6132
6482
  pos = new L.Point(x, y);
6133
6483
 
6134
- return (container ? pos._subtract(L.DomUtil.getViewportOffset(container)) : pos);
6484
+ if (!container) {
6485
+ return pos;
6486
+ }
6487
+
6488
+ var rect = container.getBoundingClientRect(),
6489
+ left = rect.left - container.clientLeft,
6490
+ top = rect.top - container.clientTop;
6491
+
6492
+ return pos._subtract(new L.Point(left, top));
6135
6493
  },
6136
6494
 
6137
6495
  getWheelDelta: function (e) {
@@ -6147,6 +6505,20 @@ L.DomEvent = {
6147
6505
  return delta;
6148
6506
  },
6149
6507
 
6508
+ _skipEvents: {},
6509
+
6510
+ _fakeStop: function (e) {
6511
+ // fakes stopPropagation by setting a special event flag, checked/reset with L.DomEvent._skipped(e)
6512
+ L.DomEvent._skipEvents[e.type] = true;
6513
+ },
6514
+
6515
+ _skipped: function (e) {
6516
+ var skipped = this._skipEvents[e.type];
6517
+ // reset when checking, as it's only used in map container and propagates outside of the map
6518
+ this._skipEvents[e.type] = false;
6519
+ return skipped;
6520
+ },
6521
+
6150
6522
  // check if element really left/entered the event target (for mouseenter/mouseleave)
6151
6523
  _checkMouse: function (el, e) {
6152
6524
 
@@ -6180,16 +6552,17 @@ L.DomEvent = {
6180
6552
  return e;
6181
6553
  },
6182
6554
 
6183
- // this solves a bug in Android WebView where a single touch triggers two click events.
6555
+ // this is a horrible workaround for a bug in Android where a single touch triggers two click events
6184
6556
  _filterClick: function (e, handler) {
6185
- var timeStamp = (e.timeStamp || e.originalEvent.timeStamp);
6186
- var elapsed = L.DomEvent._lastClick && (timeStamp - L.DomEvent._lastClick);
6557
+ var timeStamp = (e.timeStamp || e.originalEvent.timeStamp),
6558
+ elapsed = L.DomEvent._lastClick && (timeStamp - L.DomEvent._lastClick);
6187
6559
 
6188
- // are they closer together than 400ms yet more than 100ms?
6560
+ // are they closer together than 1000ms yet more than 100ms?
6189
6561
  // Android typically triggers them ~300ms apart while multiple listeners
6190
- // on the same event should be triggered far faster.
6562
+ // on the same event should be triggered far faster;
6563
+ // or check if click is simulated on the element, and if it is, reject any non-simulated events
6191
6564
 
6192
- if (elapsed && elapsed > 100 && elapsed < 400) {
6565
+ if ((elapsed && elapsed > 100 && elapsed < 1000) || (e.target._simulatedClick && !e._simulated)) {
6193
6566
  L.DomEvent.stop(e);
6194
6567
  return;
6195
6568
  }
@@ -6215,20 +6588,20 @@ L.Draggable = L.Class.extend({
6215
6588
  END: {
6216
6589
  mousedown: 'mouseup',
6217
6590
  touchstart: 'touchend',
6591
+ pointerdown: 'touchend',
6218
6592
  MSPointerDown: 'touchend'
6219
6593
  },
6220
6594
  MOVE: {
6221
6595
  mousedown: 'mousemove',
6222
6596
  touchstart: 'touchmove',
6597
+ pointerdown: 'touchmove',
6223
6598
  MSPointerDown: 'touchmove'
6224
- },
6225
- TAP_TOLERANCE: 15
6599
+ }
6226
6600
  },
6227
6601
 
6228
- initialize: function (element, dragStartTarget, longPress) {
6602
+ initialize: function (element, dragStartTarget) {
6229
6603
  this._element = element;
6230
6604
  this._dragStartTarget = dragStartTarget || element;
6231
- this._longPress = longPress && !L.Browser.msTouch;
6232
6605
  },
6233
6606
 
6234
6607
  enable: function () {
@@ -6253,61 +6626,34 @@ L.Draggable = L.Class.extend({
6253
6626
  },
6254
6627
 
6255
6628
  _onDown: function (e) {
6629
+ this._moved = false;
6630
+
6256
6631
  if (e.shiftKey || ((e.which !== 1) && (e.button !== 1) && !e.touches)) { return; }
6257
6632
 
6258
- L.DomEvent
6259
- .preventDefault(e)
6260
- .stopPropagation(e);
6633
+ L.DomEvent.stopPropagation(e);
6261
6634
 
6262
6635
  if (L.Draggable._disabled) { return; }
6263
6636
 
6264
- this._simulateClick = true;
6265
-
6266
- var touchesNum = (e.touches && e.touches.length) || 0;
6267
-
6268
- // don't simulate click or track longpress if more than 1 touch
6269
- if (touchesNum > 1) {
6270
- this._simulateClick = false;
6271
- clearTimeout(this._longPressTimeout);
6272
- return;
6273
- }
6274
-
6275
- var first = touchesNum === 1 ? e.touches[0] : e,
6276
- el = first.target;
6277
-
6278
- // if touching a link, highlight it
6279
- if (L.Browser.touch && el.tagName.toLowerCase() === 'a') {
6280
- L.DomUtil.addClass(el, 'leaflet-active');
6281
- }
6282
-
6283
- this._moved = false;
6637
+ L.DomUtil.disableImageDrag();
6638
+ L.DomUtil.disableTextSelection();
6284
6639
 
6285
6640
  if (this._moving) { return; }
6286
6641
 
6642
+ var first = e.touches ? e.touches[0] : e;
6643
+
6287
6644
  this._startPoint = new L.Point(first.clientX, first.clientY);
6288
6645
  this._startPos = this._newPos = L.DomUtil.getPosition(this._element);
6289
6646
 
6290
- // touch contextmenu event emulation
6291
- if (touchesNum === 1 && L.Browser.touch && this._longPress) {
6292
-
6293
- this._longPressTimeout = setTimeout(L.bind(function () {
6294
- var dist = (this._newPos && this._newPos.distanceTo(this._startPos)) || 0;
6295
-
6296
- if (dist < L.Draggable.TAP_TOLERANCE) {
6297
- this._simulateClick = false;
6298
- this._onUp();
6299
- this._simulateEvent('contextmenu', first);
6300
- }
6301
- }, this), 1000);
6302
- }
6303
-
6304
6647
  L.DomEvent
6305
6648
  .on(document, L.Draggable.MOVE[e.type], this._onMove, this)
6306
6649
  .on(document, L.Draggable.END[e.type], this._onUp, this);
6307
6650
  },
6308
6651
 
6309
6652
  _onMove: function (e) {
6310
- if (e.touches && e.touches.length > 1) { return; }
6653
+ if (e.touches && e.touches.length > 1) {
6654
+ this._moved = true;
6655
+ return;
6656
+ }
6311
6657
 
6312
6658
  var first = (e.touches && e.touches.length === 1 ? e.touches[0] : e),
6313
6659
  newPoint = new L.Point(first.clientX, first.clientY),
@@ -6324,7 +6670,6 @@ L.Draggable = L.Class.extend({
6324
6670
  this._startPos = L.DomUtil.getPosition(this._element).subtract(offset);
6325
6671
 
6326
6672
  if (!L.Browser.touch) {
6327
- L.DomUtil.disableTextSelection();
6328
6673
  L.DomUtil.addClass(document.body, 'leaflet-dragging');
6329
6674
  }
6330
6675
  }
@@ -6342,38 +6687,20 @@ L.Draggable = L.Class.extend({
6342
6687
  this.fire('drag');
6343
6688
  },
6344
6689
 
6345
- _onUp: function (e) {
6346
- var first, el, dist, simulateClickTouch, i;
6347
-
6348
- clearTimeout(this._longPressTimeout);
6349
-
6350
- if (this._simulateClick && e.changedTouches) {
6351
-
6352
- dist = (this._newPos && this._newPos.distanceTo(this._startPos)) || 0;
6353
- first = e.changedTouches[0];
6354
- el = first.target;
6355
-
6356
- if (el.tagName.toLowerCase() === 'a') {
6357
- L.DomUtil.removeClass(el, 'leaflet-active');
6358
- }
6359
-
6360
- // simulate click if the touch didn't move too much
6361
- if (dist < L.Draggable.TAP_TOLERANCE) {
6362
- simulateClickTouch = true;
6363
- }
6364
- }
6365
-
6690
+ _onUp: function () {
6366
6691
  if (!L.Browser.touch) {
6367
- L.DomUtil.enableTextSelection();
6368
6692
  L.DomUtil.removeClass(document.body, 'leaflet-dragging');
6369
6693
  }
6370
6694
 
6371
- for (i in L.Draggable.MOVE) {
6695
+ for (var i in L.Draggable.MOVE) {
6372
6696
  L.DomEvent
6373
6697
  .off(document, L.Draggable.MOVE[i], this._onMove)
6374
6698
  .off(document, L.Draggable.END[i], this._onUp);
6375
6699
  }
6376
6700
 
6701
+ L.DomUtil.enableImageDrag();
6702
+ L.DomUtil.enableTextSelection();
6703
+
6377
6704
  if (this._moved) {
6378
6705
  // ensure drag is not fired after dragend
6379
6706
  L.Util.cancelAnimFrame(this._animRequest);
@@ -6382,23 +6709,6 @@ L.Draggable = L.Class.extend({
6382
6709
  }
6383
6710
 
6384
6711
  this._moving = false;
6385
-
6386
- if (simulateClickTouch) {
6387
- this._moved = false;
6388
- this._simulateEvent('click', first);
6389
- }
6390
- },
6391
-
6392
- _simulateEvent: function (type, e) {
6393
- var simulatedEvent = document.createEvent('MouseEvents');
6394
-
6395
- simulatedEvent.initMouseEvent(
6396
- type, true, true, window, 1,
6397
- e.screenX, e.screenY,
6398
- e.clientX, e.clientY,
6399
- false, false, false, false, 0, null);
6400
-
6401
- e.target.dispatchEvent(simulatedEvent);
6402
6712
  }
6403
6713
  });
6404
6714
 
@@ -6446,8 +6756,6 @@ L.Map.mergeOptions({
6446
6756
  inertiaThreshold: L.Browser.touch ? 32 : 18, // ms
6447
6757
  easeLinearity: 0.25,
6448
6758
 
6449
- longPress: true,
6450
-
6451
6759
  // TODO refactor, move to CRS
6452
6760
  worldCopyJump: false
6453
6761
  });
@@ -6457,7 +6765,7 @@ L.Map.Drag = L.Handler.extend({
6457
6765
  if (!this._draggable) {
6458
6766
  var map = this._map;
6459
6767
 
6460
- this._draggable = new L.Draggable(map._mapPane, map._container, map.options.longPress);
6768
+ this._draggable = new L.Draggable(map._mapPane, map._container);
6461
6769
 
6462
6770
  this._draggable.on({
6463
6771
  'dragstart': this._onDragStart,
@@ -6468,6 +6776,8 @@ L.Map.Drag = L.Handler.extend({
6468
6776
  if (map.options.worldCopyJump) {
6469
6777
  this._draggable.on('predrag', this._onPreDrag, this);
6470
6778
  map.on('viewreset', this._onViewReset, this);
6779
+
6780
+ map.whenReady(this._onViewReset, this);
6471
6781
  }
6472
6782
  }
6473
6783
  this._draggable.enable();
@@ -6571,7 +6881,11 @@ L.Map.Drag = L.Handler.extend({
6571
6881
 
6572
6882
  } else {
6573
6883
  L.Util.requestAnimFrame(function () {
6574
- map.panBy(offset, decelerationDuration, ease, true);
6884
+ map.panBy(offset, {
6885
+ duration: decelerationDuration,
6886
+ easeLinearity: ease,
6887
+ noMoveStart: true
6888
+ });
6575
6889
  });
6576
6890
  }
6577
6891
  }
@@ -6591,15 +6905,22 @@ L.Map.mergeOptions({
6591
6905
 
6592
6906
  L.Map.DoubleClickZoom = L.Handler.extend({
6593
6907
  addHooks: function () {
6594
- this._map.on('dblclick', this._onDoubleClick);
6908
+ this._map.on('dblclick', this._onDoubleClick, this);
6595
6909
  },
6596
6910
 
6597
6911
  removeHooks: function () {
6598
- this._map.off('dblclick', this._onDoubleClick);
6912
+ this._map.off('dblclick', this._onDoubleClick, this);
6599
6913
  },
6600
6914
 
6601
6915
  _onDoubleClick: function (e) {
6602
- this.setZoomAround(e.containerPoint, this._zoom + 1);
6916
+ var map = this._map,
6917
+ zoom = map.getZoom() + 1;
6918
+
6919
+ if (map.options.doubleClickZoom === 'center') {
6920
+ map.setZoom(zoom);
6921
+ } else {
6922
+ map.setZoomAround(e.containerPoint, zoom);
6923
+ }
6603
6924
  }
6604
6925
  });
6605
6926
 
@@ -6617,11 +6938,13 @@ L.Map.mergeOptions({
6617
6938
  L.Map.ScrollWheelZoom = L.Handler.extend({
6618
6939
  addHooks: function () {
6619
6940
  L.DomEvent.on(this._map._container, 'mousewheel', this._onWheelScroll, this);
6941
+ L.DomEvent.on(this._map._container, 'MozMousePixelScroll', L.DomEvent.preventDefault);
6620
6942
  this._delta = 0;
6621
6943
  },
6622
6944
 
6623
6945
  removeHooks: function () {
6624
6946
  L.DomEvent.off(this._map._container, 'mousewheel', this._onWheelScroll);
6947
+ L.DomEvent.off(this._map._container, 'MozMousePixelScroll', L.DomEvent.preventDefault);
6625
6948
  },
6626
6949
 
6627
6950
  _onWheelScroll: function (e) {
@@ -6657,7 +6980,11 @@ L.Map.ScrollWheelZoom = L.Handler.extend({
6657
6980
 
6658
6981
  if (!delta) { return; }
6659
6982
 
6660
- map.setZoomAround(this._lastMousePos, zoom + delta);
6983
+ if (map.options.scrollWheelZoom === 'center') {
6984
+ map.setZoom(zoom + delta);
6985
+ } else {
6986
+ map.setZoomAround(this._lastMousePos, zoom + delta);
6987
+ }
6661
6988
  }
6662
6989
  });
6663
6990
 
@@ -6670,8 +6997,8 @@ L.Map.addInitHook('addHandler', 'scrollWheelZoom', L.Map.ScrollWheelZoom);
6670
6997
 
6671
6998
  L.extend(L.DomEvent, {
6672
6999
 
6673
- _touchstart: L.Browser.msTouch ? 'MSPointerDown' : 'touchstart',
6674
- _touchend: L.Browser.msTouch ? 'MSPointerUp' : 'touchend',
7000
+ _touchstart: L.Browser.msPointer ? 'MSPointerDown' : L.Browser.pointer ? 'pointerdown' : 'touchstart',
7001
+ _touchend: L.Browser.msPointer ? 'MSPointerUp' : L.Browser.pointer ? 'pointerup' : 'touchend',
6675
7002
 
6676
7003
  // inspired by Zepto touch code by Thomas Fuchs
6677
7004
  addDoubleTapListener: function (obj, handler, id) {
@@ -6687,7 +7014,7 @@ L.extend(L.DomEvent, {
6687
7014
  function onTouchStart(e) {
6688
7015
  var count;
6689
7016
 
6690
- if (L.Browser.msTouch) {
7017
+ if (L.Browser.pointer) {
6691
7018
  trackedTouches.push(e.pointerId);
6692
7019
  count = trackedTouches.length;
6693
7020
  } else {
@@ -6706,7 +7033,7 @@ L.extend(L.DomEvent, {
6706
7033
  }
6707
7034
 
6708
7035
  function onTouchEnd(e) {
6709
- if (L.Browser.msTouch) {
7036
+ if (L.Browser.pointer) {
6710
7037
  var idx = trackedTouches.indexOf(e.pointerId);
6711
7038
  if (idx === -1) {
6712
7039
  return;
@@ -6715,7 +7042,7 @@ L.extend(L.DomEvent, {
6715
7042
  }
6716
7043
 
6717
7044
  if (doubleTap) {
6718
- if (L.Browser.msTouch) {
7045
+ if (L.Browser.pointer) {
6719
7046
  // work around .type being readonly with MSPointer* events
6720
7047
  var newTouch = { },
6721
7048
  prop;
@@ -6739,15 +7066,15 @@ L.extend(L.DomEvent, {
6739
7066
  obj[pre + touchstart + id] = onTouchStart;
6740
7067
  obj[pre + touchend + id] = onTouchEnd;
6741
7068
 
6742
- // on msTouch we need to listen on the document, otherwise a drag starting on the map and moving off screen
7069
+ // on pointer we need to listen on the document, otherwise a drag starting on the map and moving off screen
6743
7070
  // will not come through to us, so we will lose track of how many touches are ongoing
6744
- var endElement = L.Browser.msTouch ? document.documentElement : obj;
7071
+ var endElement = L.Browser.pointer ? document.documentElement : obj;
6745
7072
 
6746
7073
  obj.addEventListener(touchstart, onTouchStart, false);
6747
7074
  endElement.addEventListener(touchend, onTouchEnd, false);
6748
7075
 
6749
- if (L.Browser.msTouch) {
6750
- endElement.addEventListener('MSPointerCancel', onTouchEnd, false);
7076
+ if (L.Browser.pointer) {
7077
+ endElement.addEventListener(L.DomEvent.POINTER_CANCEL, onTouchEnd, false);
6751
7078
  }
6752
7079
 
6753
7080
  return this;
@@ -6757,11 +7084,12 @@ L.extend(L.DomEvent, {
6757
7084
  var pre = '_leaflet_';
6758
7085
 
6759
7086
  obj.removeEventListener(this._touchstart, obj[pre + this._touchstart + id], false);
6760
- (L.Browser.msTouch ? document.documentElement : obj).removeEventListener(
7087
+ (L.Browser.pointer ? document.documentElement : obj).removeEventListener(
6761
7088
  this._touchend, obj[pre + this._touchend + id], false);
6762
7089
 
6763
- if (L.Browser.msTouch) {
6764
- document.documentElement.removeEventListener('MSPointerCancel', obj[pre + this._touchend + id], false);
7090
+ if (L.Browser.pointer) {
7091
+ document.documentElement.removeEventListener(L.DomEvent.POINTER_CANCEL, obj[pre + this._touchend + id],
7092
+ false);
6765
7093
  }
6766
7094
 
6767
7095
  return this;
@@ -6775,81 +7103,90 @@ L.extend(L.DomEvent, {
6775
7103
 
6776
7104
  L.extend(L.DomEvent, {
6777
7105
 
6778
- _msTouches: [],
6779
- _msDocumentListener: false,
7106
+ //static
7107
+ POINTER_DOWN: L.Browser.msPointer ? 'MSPointerDown' : 'pointerdown',
7108
+ POINTER_MOVE: L.Browser.msPointer ? 'MSPointerMove' : 'pointermove',
7109
+ POINTER_UP: L.Browser.msPointer ? 'MSPointerUp' : 'pointerup',
7110
+ POINTER_CANCEL: L.Browser.msPointer ? 'MSPointerCancel' : 'pointercancel',
7111
+
7112
+ _pointers: [],
7113
+ _pointerDocumentListener: false,
6780
7114
 
6781
- // Provides a touch events wrapper for msPointer events.
7115
+ // Provides a touch events wrapper for (ms)pointer events.
6782
7116
  // Based on changes by veproza https://github.com/CloudMade/Leaflet/pull/1019
7117
+ //ref http://www.w3.org/TR/pointerevents/ https://www.w3.org/Bugs/Public/show_bug.cgi?id=22890
6783
7118
 
6784
- addMsTouchListener: function (obj, type, handler, id) {
7119
+ addPointerListener: function (obj, type, handler, id) {
6785
7120
 
6786
7121
  switch (type) {
6787
7122
  case 'touchstart':
6788
- return this.addMsTouchListenerStart(obj, type, handler, id);
7123
+ return this.addPointerListenerStart(obj, type, handler, id);
6789
7124
  case 'touchend':
6790
- return this.addMsTouchListenerEnd(obj, type, handler, id);
7125
+ return this.addPointerListenerEnd(obj, type, handler, id);
6791
7126
  case 'touchmove':
6792
- return this.addMsTouchListenerMove(obj, type, handler, id);
7127
+ return this.addPointerListenerMove(obj, type, handler, id);
6793
7128
  default:
6794
7129
  throw 'Unknown touch event type';
6795
7130
  }
6796
7131
  },
6797
7132
 
6798
- addMsTouchListenerStart: function (obj, type, handler, id) {
7133
+ addPointerListenerStart: function (obj, type, handler, id) {
6799
7134
  var pre = '_leaflet_',
6800
- touches = this._msTouches;
7135
+ pointers = this._pointers;
6801
7136
 
6802
7137
  var cb = function (e) {
6803
7138
 
7139
+ L.DomEvent.preventDefault(e);
7140
+
6804
7141
  var alreadyInArray = false;
6805
- for (var i = 0; i < touches.length; i++) {
6806
- if (touches[i].pointerId === e.pointerId) {
7142
+ for (var i = 0; i < pointers.length; i++) {
7143
+ if (pointers[i].pointerId === e.pointerId) {
6807
7144
  alreadyInArray = true;
6808
7145
  break;
6809
7146
  }
6810
7147
  }
6811
7148
  if (!alreadyInArray) {
6812
- touches.push(e);
7149
+ pointers.push(e);
6813
7150
  }
6814
7151
 
6815
- e.touches = touches.slice();
7152
+ e.touches = pointers.slice();
6816
7153
  e.changedTouches = [e];
6817
7154
 
6818
7155
  handler(e);
6819
7156
  };
6820
7157
 
6821
7158
  obj[pre + 'touchstart' + id] = cb;
6822
- obj.addEventListener('MSPointerDown', cb, false);
7159
+ obj.addEventListener(this.POINTER_DOWN, cb, false);
6823
7160
 
6824
- // need to also listen for end events to keep the _msTouches list accurate
7161
+ // need to also listen for end events to keep the _pointers list accurate
6825
7162
  // this needs to be on the body and never go away
6826
- if (!this._msDocumentListener) {
7163
+ if (!this._pointerDocumentListener) {
6827
7164
  var internalCb = function (e) {
6828
- for (var i = 0; i < touches.length; i++) {
6829
- if (touches[i].pointerId === e.pointerId) {
6830
- touches.splice(i, 1);
7165
+ for (var i = 0; i < pointers.length; i++) {
7166
+ if (pointers[i].pointerId === e.pointerId) {
7167
+ pointers.splice(i, 1);
6831
7168
  break;
6832
7169
  }
6833
7170
  }
6834
7171
  };
6835
7172
  //We listen on the documentElement as any drags that end by moving the touch off the screen get fired there
6836
- document.documentElement.addEventListener('MSPointerUp', internalCb, false);
6837
- document.documentElement.addEventListener('MSPointerCancel', internalCb, false);
7173
+ document.documentElement.addEventListener(this.POINTER_UP, internalCb, false);
7174
+ document.documentElement.addEventListener(this.POINTER_CANCEL, internalCb, false);
6838
7175
 
6839
- this._msDocumentListener = true;
7176
+ this._pointerDocumentListener = true;
6840
7177
  }
6841
7178
 
6842
7179
  return this;
6843
7180
  },
6844
7181
 
6845
- addMsTouchListenerMove: function (obj, type, handler, id) {
7182
+ addPointerListenerMove: function (obj, type, handler, id) {
6846
7183
  var pre = '_leaflet_',
6847
- touches = this._msTouches;
7184
+ touches = this._pointers;
6848
7185
 
6849
7186
  function cb(e) {
6850
7187
 
6851
7188
  // don't fire touch moves when mouse isn't down
6852
- if (e.pointerType === e.MSPOINTER_TYPE_MOUSE && e.buttons === 0) { return; }
7189
+ if ((e.pointerType === e.MSPOINTER_TYPE_MOUSE || e.pointerType === 'mouse') && e.buttons === 0) { return; }
6853
7190
 
6854
7191
  for (var i = 0; i < touches.length; i++) {
6855
7192
  if (touches[i].pointerId === e.pointerId) {
@@ -6865,14 +7202,14 @@ L.extend(L.DomEvent, {
6865
7202
  }
6866
7203
 
6867
7204
  obj[pre + 'touchmove' + id] = cb;
6868
- obj.addEventListener('MSPointerMove', cb, false);
7205
+ obj.addEventListener(this.POINTER_MOVE, cb, false);
6869
7206
 
6870
7207
  return this;
6871
7208
  },
6872
7209
 
6873
- addMsTouchListenerEnd: function (obj, type, handler, id) {
7210
+ addPointerListenerEnd: function (obj, type, handler, id) {
6874
7211
  var pre = '_leaflet_',
6875
- touches = this._msTouches;
7212
+ touches = this._pointers;
6876
7213
 
6877
7214
  var cb = function (e) {
6878
7215
  for (var i = 0; i < touches.length; i++) {
@@ -6889,26 +7226,26 @@ L.extend(L.DomEvent, {
6889
7226
  };
6890
7227
 
6891
7228
  obj[pre + 'touchend' + id] = cb;
6892
- obj.addEventListener('MSPointerUp', cb, false);
6893
- obj.addEventListener('MSPointerCancel', cb, false);
7229
+ obj.addEventListener(this.POINTER_UP, cb, false);
7230
+ obj.addEventListener(this.POINTER_CANCEL, cb, false);
6894
7231
 
6895
7232
  return this;
6896
7233
  },
6897
7234
 
6898
- removeMsTouchListener: function (obj, type, id) {
7235
+ removePointerListener: function (obj, type, id) {
6899
7236
  var pre = '_leaflet_',
6900
7237
  cb = obj[pre + type + id];
6901
7238
 
6902
7239
  switch (type) {
6903
7240
  case 'touchstart':
6904
- obj.removeEventListener('MSPointerDown', cb, false);
7241
+ obj.removeEventListener(this.POINTER_DOWN, cb, false);
6905
7242
  break;
6906
7243
  case 'touchmove':
6907
- obj.removeEventListener('MSPointerMove', cb, false);
7244
+ obj.removeEventListener(this.POINTER_MOVE, cb, false);
6908
7245
  break;
6909
7246
  case 'touchend':
6910
- obj.removeEventListener('MSPointerUp', cb, false);
6911
- obj.removeEventListener('MSPointerCancel', cb, false);
7247
+ obj.removeEventListener(this.POINTER_UP, cb, false);
7248
+ obj.removeEventListener(this.POINTER_CANCEL, cb, false);
6912
7249
  break;
6913
7250
  }
6914
7251
 
@@ -6922,7 +7259,8 @@ L.extend(L.DomEvent, {
6922
7259
  */
6923
7260
 
6924
7261
  L.Map.mergeOptions({
6925
- touchZoom: L.Browser.touch && !L.Browser.android23
7262
+ touchZoom: L.Browser.touch && !L.Browser.android23,
7263
+ bounceAtZoomLimits: true
6926
7264
  });
6927
7265
 
6928
7266
  L.Map.TouchZoom = L.Handler.extend({
@@ -6975,6 +7313,11 @@ L.Map.TouchZoom = L.Handler.extend({
6975
7313
 
6976
7314
  if (this._scale === 1) { return; }
6977
7315
 
7316
+ if (!map.options.bounceAtZoomLimits) {
7317
+ if ((map.getZoom() === map.getMinZoom() && this._scale < 1) ||
7318
+ (map.getZoom() === map.getMaxZoom() && this._scale > 1)) { return; }
7319
+ }
7320
+
6978
7321
  if (!this._moved) {
6979
7322
  L.DomUtil.addClass(map._mapPane, 'leaflet-touching');
6980
7323
 
@@ -6998,7 +7341,7 @@ L.Map.TouchZoom = L.Handler.extend({
6998
7341
  center = map.layerPointToLatLng(origin),
6999
7342
  zoom = map.getScaleZoom(this._scale);
7000
7343
 
7001
- map._animateZoom(center, zoom, this._startCenter, this._scale, this._delta, true);
7344
+ map._animateZoom(center, zoom, this._startCenter, this._scale, this._delta);
7002
7345
  },
7003
7346
 
7004
7347
  _onTouchEnd: function () {
@@ -7028,7 +7371,7 @@ L.Map.TouchZoom = L.Handler.extend({
7028
7371
  zoom = map._limitZoom(oldZoom + roundZoomDelta),
7029
7372
  scale = map.getZoomScale(zoom) / this._scale;
7030
7373
 
7031
- map._animateZoom(center, zoom, origin, scale, null, true);
7374
+ map._animateZoom(center, zoom, origin, scale);
7032
7375
  },
7033
7376
 
7034
7377
  _getScaleOrigin: function () {
@@ -7040,6 +7383,115 @@ L.Map.TouchZoom = L.Handler.extend({
7040
7383
  L.Map.addInitHook('addHandler', 'touchZoom', L.Map.TouchZoom);
7041
7384
 
7042
7385
 
7386
+ /*
7387
+ * L.Map.Tap is used to enable mobile hacks like quick taps and long hold.
7388
+ */
7389
+
7390
+ L.Map.mergeOptions({
7391
+ tap: true,
7392
+ tapTolerance: 15
7393
+ });
7394
+
7395
+ L.Map.Tap = L.Handler.extend({
7396
+ addHooks: function () {
7397
+ L.DomEvent.on(this._map._container, 'touchstart', this._onDown, this);
7398
+ },
7399
+
7400
+ removeHooks: function () {
7401
+ L.DomEvent.off(this._map._container, 'touchstart', this._onDown, this);
7402
+ },
7403
+
7404
+ _onDown: function (e) {
7405
+ if (!e.touches) { return; }
7406
+
7407
+ L.DomEvent.preventDefault(e);
7408
+
7409
+ this._fireClick = true;
7410
+
7411
+ // don't simulate click or track longpress if more than 1 touch
7412
+ if (e.touches.length > 1) {
7413
+ this._fireClick = false;
7414
+ clearTimeout(this._holdTimeout);
7415
+ return;
7416
+ }
7417
+
7418
+ var first = e.touches[0],
7419
+ el = first.target;
7420
+
7421
+ this._startPos = this._newPos = new L.Point(first.clientX, first.clientY);
7422
+
7423
+ // if touching a link, highlight it
7424
+ if (el.tagName && el.tagName.toLowerCase() === 'a') {
7425
+ L.DomUtil.addClass(el, 'leaflet-active');
7426
+ }
7427
+
7428
+ // simulate long hold but setting a timeout
7429
+ this._holdTimeout = setTimeout(L.bind(function () {
7430
+ if (this._isTapValid()) {
7431
+ this._fireClick = false;
7432
+ this._onUp();
7433
+ this._simulateEvent('contextmenu', first);
7434
+ }
7435
+ }, this), 1000);
7436
+
7437
+ L.DomEvent
7438
+ .on(document, 'touchmove', this._onMove, this)
7439
+ .on(document, 'touchend', this._onUp, this);
7440
+ },
7441
+
7442
+ _onUp: function (e) {
7443
+ clearTimeout(this._holdTimeout);
7444
+
7445
+ L.DomEvent
7446
+ .off(document, 'touchmove', this._onMove, this)
7447
+ .off(document, 'touchend', this._onUp, this);
7448
+
7449
+ if (this._fireClick && e && e.changedTouches) {
7450
+
7451
+ var first = e.changedTouches[0],
7452
+ el = first.target;
7453
+
7454
+ if (el && el.tagName && el.tagName.toLowerCase() === 'a') {
7455
+ L.DomUtil.removeClass(el, 'leaflet-active');
7456
+ }
7457
+
7458
+ // simulate click if the touch didn't move too much
7459
+ if (this._isTapValid()) {
7460
+ this._simulateEvent('click', first);
7461
+ }
7462
+ }
7463
+ },
7464
+
7465
+ _isTapValid: function () {
7466
+ return this._newPos.distanceTo(this._startPos) <= this._map.options.tapTolerance;
7467
+ },
7468
+
7469
+ _onMove: function (e) {
7470
+ var first = e.touches[0];
7471
+ this._newPos = new L.Point(first.clientX, first.clientY);
7472
+ },
7473
+
7474
+ _simulateEvent: function (type, e) {
7475
+ var simulatedEvent = document.createEvent('MouseEvents');
7476
+
7477
+ simulatedEvent._simulated = true;
7478
+ e.target._simulatedClick = true;
7479
+
7480
+ simulatedEvent.initMouseEvent(
7481
+ type, true, true, window, 1,
7482
+ e.screenX, e.screenY,
7483
+ e.clientX, e.clientY,
7484
+ false, false, false, false, 0, null);
7485
+
7486
+ e.target.dispatchEvent(simulatedEvent);
7487
+ }
7488
+ });
7489
+
7490
+ if (L.Browser.touch && !L.Browser.pointer) {
7491
+ L.Map.addInitHook('addHandler', 'tap', L.Map.Tap);
7492
+ }
7493
+
7494
+
7043
7495
  /*
7044
7496
  * L.Handler.ShiftDragZoom is used to add shift-drag zoom interaction to the map
7045
7497
  * (zoom to a selected bounding box), enabled by default.
@@ -7054,6 +7506,7 @@ L.Map.BoxZoom = L.Handler.extend({
7054
7506
  this._map = map;
7055
7507
  this._container = map._container;
7056
7508
  this._pane = map._panes.overlayPane;
7509
+ this._moved = false;
7057
7510
  },
7058
7511
 
7059
7512
  addHooks: function () {
@@ -7062,12 +7515,20 @@ L.Map.BoxZoom = L.Handler.extend({
7062
7515
 
7063
7516
  removeHooks: function () {
7064
7517
  L.DomEvent.off(this._container, 'mousedown', this._onMouseDown);
7518
+ this._moved = false;
7519
+ },
7520
+
7521
+ moved: function () {
7522
+ return this._moved;
7065
7523
  },
7066
7524
 
7067
7525
  _onMouseDown: function (e) {
7526
+ this._moved = false;
7527
+
7068
7528
  if (!e.shiftKey || ((e.which !== 1) && (e.button !== 1))) { return false; }
7069
7529
 
7070
7530
  L.DomUtil.disableTextSelection();
7531
+ L.DomUtil.disableImageDrag();
7071
7532
 
7072
7533
  this._startLayerPoint = this._map.mouseEventToLayerPoint(e);
7073
7534
 
@@ -7080,8 +7541,7 @@ L.Map.BoxZoom = L.Handler.extend({
7080
7541
  L.DomEvent
7081
7542
  .on(document, 'mousemove', this._onMouseMove, this)
7082
7543
  .on(document, 'mouseup', this._onMouseUp, this)
7083
- .on(document, 'keydown', this._onKeyDown, this)
7084
- .preventDefault(e);
7544
+ .on(document, 'keydown', this._onKeyDown, this);
7085
7545
 
7086
7546
  this._map.fire('boxzoomstart');
7087
7547
  },
@@ -7099,6 +7559,8 @@ L.Map.BoxZoom = L.Handler.extend({
7099
7559
 
7100
7560
  L.DomUtil.setPosition(box, newPos);
7101
7561
 
7562
+ this._moved = true;
7563
+
7102
7564
  // TODO refactor: remove hardcoded 4 pixels
7103
7565
  box.style.width = (Math.max(0, Math.abs(offset.x) - 4)) + 'px';
7104
7566
  box.style.height = (Math.max(0, Math.abs(offset.y) - 4)) + 'px';
@@ -7109,6 +7571,7 @@ L.Map.BoxZoom = L.Handler.extend({
7109
7571
  this._container.style.cursor = '';
7110
7572
 
7111
7573
  L.DomUtil.enableTextSelection();
7574
+ L.DomUtil.enableImageDrag();
7112
7575
 
7113
7576
  L.DomEvent
7114
7577
  .off(document, 'mousemove', this._onMouseMove)
@@ -7163,7 +7626,7 @@ L.Map.Keyboard = L.Handler.extend({
7163
7626
  right: [39],
7164
7627
  down: [40],
7165
7628
  up: [38],
7166
- zoomIn: [187, 107, 61],
7629
+ zoomIn: [187, 107, 61, 171],
7167
7630
  zoomOut: [189, 109, 173]
7168
7631
  },
7169
7632
 
@@ -7213,7 +7676,7 @@ L.Map.Keyboard = L.Handler.extend({
7213
7676
  var body = document.body,
7214
7677
  docEl = document.documentElement,
7215
7678
  top = body.scrollTop || docEl.scrollTop,
7216
- left = body.scrollTop || docEl.scrollLeft;
7679
+ left = body.scrollLeft || docEl.scrollLeft;
7217
7680
 
7218
7681
  this._map._container.focus();
7219
7682
 
@@ -7275,6 +7738,9 @@ L.Map.Keyboard = L.Handler.extend({
7275
7738
  map = this._map;
7276
7739
 
7277
7740
  if (key in this._panKeys) {
7741
+
7742
+ if (map._panAnim && map._panAnim._inProgress) { return; }
7743
+
7278
7744
  map.panBy(this._panKeys[key]);
7279
7745
 
7280
7746
  if (map.options.maxBounds) {
@@ -7315,15 +7781,17 @@ L.Handler.MarkerDrag = L.Handler.extend({
7315
7781
  .on('drag', this._onDrag, this)
7316
7782
  .on('dragend', this._onDragEnd, this);
7317
7783
  this._draggable.enable();
7784
+ L.DomUtil.addClass(this._marker._icon, 'leaflet-marker-draggable');
7318
7785
  },
7319
7786
 
7320
7787
  removeHooks: function () {
7321
7788
  this._draggable
7322
- .off('dragstart', this._onDragStart)
7323
- .off('drag', this._onDrag)
7324
- .off('dragend', this._onDragEnd);
7789
+ .off('dragstart', this._onDragStart, this)
7790
+ .off('drag', this._onDrag, this)
7791
+ .off('dragend', this._onDragEnd, this);
7325
7792
 
7326
7793
  this._draggable.disable();
7794
+ L.DomUtil.removeClass(this._marker._icon, 'leaflet-marker-draggable');
7327
7795
  },
7328
7796
 
7329
7797
  moved: function () {
@@ -7335,6 +7803,7 @@ L.Handler.MarkerDrag = L.Handler.extend({
7335
7803
  .closePopup()
7336
7804
  .fire('movestart')
7337
7805
  .fire('dragstart');
7806
+ L.DomUtil.addClass(this._marker._icon, 'leaflet-marker-dragging');
7338
7807
  },
7339
7808
 
7340
7809
  _onDrag: function () {
@@ -7359,6 +7828,7 @@ L.Handler.MarkerDrag = L.Handler.extend({
7359
7828
  this._marker
7360
7829
  .fire('moveend')
7361
7830
  .fire('dragend');
7831
+ L.DomUtil.removeClass(this._marker._icon, 'leaflet-marker-dragging');
7362
7832
  }
7363
7833
  });
7364
7834
 
@@ -7431,6 +7901,12 @@ L.Control = L.Class.extend({
7431
7901
  }
7432
7902
 
7433
7903
  return this;
7904
+ },
7905
+
7906
+ _refocusOnMap: function () {
7907
+ if (this._map) {
7908
+ this._map.getContainer().focus();
7909
+ }
7434
7910
  }
7435
7911
  });
7436
7912
 
@@ -7468,6 +7944,10 @@ L.Map.include({
7468
7944
  createCorner('top', 'right');
7469
7945
  createCorner('bottom', 'left');
7470
7946
  createCorner('bottom', 'right');
7947
+ },
7948
+
7949
+ _clearControlPos: function () {
7950
+ this._container.removeChild(this._controlContainer);
7471
7951
  }
7472
7952
  });
7473
7953
 
@@ -7478,7 +7958,11 @@ L.Map.include({
7478
7958
 
7479
7959
  L.Control.Zoom = L.Control.extend({
7480
7960
  options: {
7481
- position: 'topleft'
7961
+ position: 'topleft',
7962
+ zoomInText: '+',
7963
+ zoomInTitle: 'Zoom in',
7964
+ zoomOutText: '-',
7965
+ zoomOutTitle: 'Zoom out'
7482
7966
  },
7483
7967
 
7484
7968
  onAdd: function (map) {
@@ -7488,10 +7972,13 @@ L.Control.Zoom = L.Control.extend({
7488
7972
  this._map = map;
7489
7973
 
7490
7974
  this._zoomInButton = this._createButton(
7491
- '+', 'Zoom in', zoomName + '-in', container, this._zoomIn, this);
7975
+ this.options.zoomInText, this.options.zoomInTitle,
7976
+ zoomName + '-in', container, this._zoomIn, this);
7492
7977
  this._zoomOutButton = this._createButton(
7493
- '-', 'Zoom out', zoomName + '-out', container, this._zoomOut, this);
7978
+ this.options.zoomOutText, this.options.zoomOutTitle,
7979
+ zoomName + '-out', container, this._zoomOut, this);
7494
7980
 
7981
+ this._updateDisabled();
7495
7982
  map.on('zoomend zoomlevelschange', this._updateDisabled, this);
7496
7983
 
7497
7984
  return container;
@@ -7522,7 +8009,8 @@ L.Control.Zoom = L.Control.extend({
7522
8009
  .on(link, 'mousedown', stop)
7523
8010
  .on(link, 'dblclick', stop)
7524
8011
  .on(link, 'click', L.DomEvent.preventDefault)
7525
- .on(link, 'click', fn, context);
8012
+ .on(link, 'click', fn, context)
8013
+ .on(link, 'click', this._refocusOnMap, context);
7526
8014
 
7527
8015
  return link;
7528
8016
  },
@@ -7858,9 +8346,13 @@ L.Control.Layers = L.Control.extend({
7858
8346
  var className = 'leaflet-control-layers',
7859
8347
  container = this._container = L.DomUtil.create('div', className);
7860
8348
 
8349
+ //Makes this work on IE10 Touch devices by stopping it from firing a mouseout event when the touch is released
8350
+ container.setAttribute('aria-haspopup', true);
8351
+
7861
8352
  if (!L.Browser.touch) {
7862
- L.DomEvent.disableClickPropagation(container);
7863
- L.DomEvent.on(container, 'mousewheel', L.DomEvent.stopPropagation);
8353
+ L.DomEvent
8354
+ .disableClickPropagation(container)
8355
+ .disableScrollPropagation(container);
7864
8356
  } else {
7865
8357
  L.DomEvent.on(container, 'click', L.DomEvent.stopPropagation);
7866
8358
  }
@@ -7868,25 +8360,25 @@ L.Control.Layers = L.Control.extend({
7868
8360
  var form = this._form = L.DomUtil.create('form', className + '-list');
7869
8361
 
7870
8362
  if (this.options.collapsed) {
7871
- L.DomEvent
7872
- .on(container, 'mouseover', this._expand, this)
7873
- .on(container, 'mouseout', this._collapse, this);
7874
-
8363
+ if (!L.Browser.android) {
8364
+ L.DomEvent
8365
+ .on(container, 'mouseover', this._expand, this)
8366
+ .on(container, 'mouseout', this._collapse, this);
8367
+ }
7875
8368
  var link = this._layersLink = L.DomUtil.create('a', className + '-toggle', container);
7876
8369
  link.href = '#';
7877
8370
  link.title = 'Layers';
7878
8371
 
7879
8372
  if (L.Browser.touch) {
7880
8373
  L.DomEvent
7881
- .on(link, 'click', L.DomEvent.stopPropagation)
7882
- .on(link, 'click', L.DomEvent.preventDefault)
8374
+ .on(link, 'click', L.DomEvent.stop)
7883
8375
  .on(link, 'click', this._expand, this);
7884
8376
  }
7885
8377
  else {
7886
8378
  L.DomEvent.on(link, 'focus', this._expand, this);
7887
8379
  }
7888
8380
 
7889
- this._map.on('movestart', this._collapse, this);
8381
+ this._map.on('click', this._collapse, this);
7890
8382
  // TODO keyboard accessibility
7891
8383
  } else {
7892
8384
  this._expand();
@@ -7937,11 +8429,21 @@ L.Control.Layers = L.Control.extend({
7937
8429
  },
7938
8430
 
7939
8431
  _onLayerChange: function (e) {
7940
- var id = L.stamp(e.layer);
8432
+ var obj = this._layers[L.stamp(e.layer)];
8433
+
8434
+ if (!obj) { return; }
7941
8435
 
7942
- if (this._layers[id] && !this._handlingClick) {
8436
+ if (!this._handlingClick) {
7943
8437
  this._update();
7944
8438
  }
8439
+
8440
+ var type = obj.overlay ?
8441
+ (e.type === 'layeradd' ? 'overlayadd' : 'overlayremove') :
8442
+ (e.type === 'layeradd' ? 'baselayerchange' : null);
8443
+
8444
+ if (type) {
8445
+ this._map.fire(type, obj);
8446
+ }
7945
8447
  },
7946
8448
 
7947
8449
  // IE7 bugs out if you create a radio dynamically, so you have to do it this hacky way (see http://bit.ly/PqYLBe)
@@ -7992,8 +8494,7 @@ L.Control.Layers = L.Control.extend({
7992
8494
  _onInputClick: function () {
7993
8495
  var i, input, obj,
7994
8496
  inputs = this._form.getElementsByTagName('input'),
7995
- inputsLen = inputs.length,
7996
- baseLayer;
8497
+ inputsLen = inputs.length;
7997
8498
 
7998
8499
  this._handlingClick = true;
7999
8500
 
@@ -8003,23 +8504,15 @@ L.Control.Layers = L.Control.extend({
8003
8504
 
8004
8505
  if (input.checked && !this._map.hasLayer(obj.layer)) {
8005
8506
  this._map.addLayer(obj.layer);
8006
- if (!obj.overlay) {
8007
- baseLayer = obj.layer;
8008
- } else {
8009
- this._map.fire('overlayadd', {layer: obj});
8010
- }
8507
+
8011
8508
  } else if (!input.checked && this._map.hasLayer(obj.layer)) {
8012
8509
  this._map.removeLayer(obj.layer);
8013
- this._map.fire('overlayremove', {layer: obj});
8014
8510
  }
8015
8511
  }
8016
8512
 
8017
- if (baseLayer) {
8018
- this._map.setZoom(this._map.getZoom());
8019
- this._map.fire('baselayerchange', {layer: baseLayer});
8020
- }
8021
-
8022
8513
  this._handlingClick = false;
8514
+
8515
+ this._refocusOnMap();
8023
8516
  },
8024
8517
 
8025
8518
  _expand: function () {
@@ -8048,6 +8541,7 @@ L.PosAnimation = L.Class.extend({
8048
8541
 
8049
8542
  this._el = el;
8050
8543
  this._inProgress = true;
8544
+ this._newPos = newPos;
8051
8545
 
8052
8546
  this.fire('start');
8053
8547
 
@@ -8076,9 +8570,14 @@ L.PosAnimation = L.Class.extend({
8076
8570
  },
8077
8571
 
8078
8572
  _onStep: function () {
8573
+ var stepPos = this._getPos();
8574
+ if (!stepPos) {
8575
+ this._onTransitionEnd();
8576
+ return;
8577
+ }
8079
8578
  // jshint camelcase: false
8080
8579
  // make L.DomUtil.getPosition return intermediate position value during animation
8081
- this._el._leaflet_pos = this._getPos();
8580
+ this._el._leaflet_pos = stepPos;
8082
8581
 
8083
8582
  this.fire('step');
8084
8583
  },
@@ -8086,7 +8585,7 @@ L.PosAnimation = L.Class.extend({
8086
8585
  // you can't easily get intermediate values of properties animated with CSS3 Transitions,
8087
8586
  // we need to parse computed style (in case of transform it returns matrix string)
8088
8587
 
8089
- _transformRe: /(-?[\d\.]+), (-?[\d\.]+)\)/,
8588
+ _transformRe: /([-+]?(?:\d*\.)?\d+)\D*, ([-+]?(?:\d*\.)?\d+)\D*\)/,
8090
8589
 
8091
8590
  _getPos: function () {
8092
8591
  var left, top, matches,
@@ -8095,6 +8594,7 @@ L.PosAnimation = L.Class.extend({
8095
8594
 
8096
8595
  if (L.Browser.any3d) {
8097
8596
  matches = style[L.DomUtil.TRANSFORM].match(this._transformRe);
8597
+ if (!matches) { return; }
8098
8598
  left = parseFloat(matches[1]);
8099
8599
  top = parseFloat(matches[2]);
8100
8600
  } else {
@@ -8113,6 +8613,10 @@ L.PosAnimation = L.Class.extend({
8113
8613
 
8114
8614
  this._el.style[L.DomUtil.TRANSITION] = '';
8115
8615
 
8616
+ // jshint camelcase: false
8617
+ // make sure L.DomUtil.getPosition returns the final position value after animation
8618
+ this._el._leaflet_pos = this._newPos;
8619
+
8116
8620
  clearInterval(this._stepTimer);
8117
8621
 
8118
8622
  this.fire('step').fire('end');
@@ -8127,24 +8631,27 @@ L.PosAnimation = L.Class.extend({
8127
8631
 
8128
8632
  L.Map.include({
8129
8633
 
8130
- setView: function (center, zoom, forceReset) {
8634
+ setView: function (center, zoom, options) {
8131
8635
 
8132
- zoom = this._limitZoom(zoom);
8636
+ zoom = zoom === undefined ? this._zoom : this._limitZoom(zoom);
8133
8637
  center = L.latLng(center);
8638
+ options = options || {};
8134
8639
 
8135
8640
  if (this._panAnim) {
8136
8641
  this._panAnim.stop();
8137
8642
  }
8138
8643
 
8139
- var zoomChanged = (this._zoom !== zoom),
8140
- canBeAnimated = this._loaded && !forceReset && !!this._layers;
8644
+ if (this._loaded && !options.reset && options !== true) {
8141
8645
 
8142
- if (canBeAnimated) {
8646
+ if (options.animate !== undefined) {
8647
+ options.zoom = L.extend({animate: options.animate}, options.zoom);
8648
+ options.pan = L.extend({animate: options.animate}, options.pan);
8649
+ }
8143
8650
 
8144
8651
  // try animating pan or zoom
8145
- var animated = zoomChanged ?
8146
- this.options.zoomAnimation && this._animateZoomIfClose && this._animateZoomIfClose(center, zoom) :
8147
- this._animatePanIfClose(center);
8652
+ var animated = (this._zoom !== zoom) ?
8653
+ this._tryAnimatedZoom && this._tryAnimatedZoom(center, zoom, options.zoom) :
8654
+ this._tryAnimatedPan(center, options.pan);
8148
8655
 
8149
8656
  if (animated) {
8150
8657
  // prevent resize handler call, the view will refresh after animation anyway
@@ -8159,10 +8666,9 @@ L.Map.include({
8159
8666
  return this;
8160
8667
  },
8161
8668
 
8162
- panBy: function (offset, duration, easeLinearity, noMoveStart) {
8669
+ panBy: function (offset, options) {
8163
8670
  offset = L.point(offset).round();
8164
-
8165
- // TODO add options instead of arguments to setView/panTo/panBy/etc.
8671
+ options = options || {};
8166
8672
 
8167
8673
  if (!offset.x && !offset.y) {
8168
8674
  return this;
@@ -8178,14 +8684,20 @@ L.Map.include({
8178
8684
  }
8179
8685
 
8180
8686
  // don't fire movestart if animating inertia
8181
- if (!noMoveStart) {
8687
+ if (!options.noMoveStart) {
8182
8688
  this.fire('movestart');
8183
8689
  }
8184
8690
 
8185
- L.DomUtil.addClass(this._mapPane, 'leaflet-pan-anim');
8691
+ // animate pan unless animate: false specified
8692
+ if (options.animate !== false) {
8693
+ L.DomUtil.addClass(this._mapPane, 'leaflet-pan-anim');
8186
8694
 
8187
- var newPos = this._getMapPanePos().subtract(offset);
8188
- this._panAnim.run(this._mapPane, newPos, duration || 0.25, easeLinearity);
8695
+ var newPos = this._getMapPanePos().subtract(offset);
8696
+ this._panAnim.run(this._mapPane, newPos, options.duration || 0.25, options.easeLinearity);
8697
+ } else {
8698
+ this._rawPanBy(offset);
8699
+ this.fire('move').fire('moveend');
8700
+ }
8189
8701
 
8190
8702
  return this;
8191
8703
  },
@@ -8199,13 +8711,15 @@ L.Map.include({
8199
8711
  this.fire('moveend');
8200
8712
  },
8201
8713
 
8202
- _animatePanIfClose: function (center) {
8714
+ _tryAnimatedPan: function (center, options) {
8203
8715
  // difference between the new and current centers in pixels
8204
8716
  var offset = this._getCenterOffset(center)._floor();
8205
8717
 
8206
- if (!this.getSize().contains(offset)) { return false; }
8718
+ // don't animate too far unless animate: true specified in options
8719
+ if ((options && options.animate) !== true && !this.getSize().contains(offset)) { return false; }
8720
+
8721
+ this.panBy(offset, options);
8207
8722
 
8208
- this.panBy(offset);
8209
8723
  return true;
8210
8724
  }
8211
8725
  });
@@ -8285,17 +8799,22 @@ L.PosAnimation = L.DomUtil.TRANSITION ? L.PosAnimation : L.PosAnimation.extend({
8285
8799
  */
8286
8800
 
8287
8801
  L.Map.mergeOptions({
8288
- zoomAnimation: L.DomUtil.TRANSITION && !L.Browser.android23 && !L.Browser.mobileOpera,
8802
+ zoomAnimation: true,
8289
8803
  zoomAnimationThreshold: 4
8290
8804
  });
8291
8805
 
8292
8806
  if (L.DomUtil.TRANSITION) {
8293
8807
 
8294
8808
  L.Map.addInitHook(function () {
8809
+ // don't animate on browsers without hardware-accelerated transitions or old Android/Opera
8810
+ this._zoomAnimated = this.options.zoomAnimation && L.DomUtil.TRANSITION &&
8811
+ L.Browser.any3d && !L.Browser.android23 && !L.Browser.mobileOpera;
8812
+
8295
8813
  // zoom transitions run with the same duration for all layers, so if one of transitionend events
8296
8814
  // happens after starting zoom animation (propagating to the map pane), we know that it ended globally
8297
-
8298
- L.DomEvent.on(this._mapPane, L.DomUtil.TRANSITION_END, this._catchTransitionEnd, this);
8815
+ if (this._zoomAnimated) {
8816
+ L.DomEvent.on(this._mapPane, L.DomUtil.TRANSITION_END, this._catchTransitionEnd, this);
8817
+ }
8299
8818
  });
8300
8819
  }
8301
8820
 
@@ -8307,26 +8826,33 @@ L.Map.include(!L.DomUtil.TRANSITION ? {} : {
8307
8826
  }
8308
8827
  },
8309
8828
 
8310
- _animateZoomIfClose: function (center, zoom) {
8829
+ _nothingToAnimate: function () {
8830
+ return !this._container.getElementsByClassName('leaflet-zoom-animated').length;
8831
+ },
8832
+
8833
+ _tryAnimatedZoom: function (center, zoom, options) {
8311
8834
 
8312
8835
  if (this._animatingZoom) { return true; }
8313
8836
 
8314
- // don't animate if zoom difference is too large
8315
- if (Math.abs(zoom - this._zoom) > this.options.zoomAnimationThreshold) { return false; }
8837
+ options = options || {};
8838
+
8839
+ // don't animate if disabled, not supported or zoom difference is too large
8840
+ if (!this._zoomAnimated || options.animate === false || this._nothingToAnimate() ||
8841
+ Math.abs(zoom - this._zoom) > this.options.zoomAnimationThreshold) { return false; }
8316
8842
 
8317
8843
  // offset is the pixel coords of the zoom origin relative to the current center
8318
8844
  var scale = this.getZoomScale(zoom),
8319
8845
  offset = this._getCenterOffset(center)._divideBy(1 - 1 / scale),
8320
8846
  origin = this._getCenterLayerPoint()._add(offset);
8321
8847
 
8322
- // only animate if the zoom origin is within one screen from the current center
8323
- if (!this.getSize().contains(offset)) { return false; }
8848
+ // don't animate if the zoom origin isn't within one screen from the current center, unless forced
8849
+ if (options.animate !== true && !this.getSize().contains(offset)) { return false; }
8324
8850
 
8325
8851
  this
8326
8852
  .fire('movestart')
8327
8853
  .fire('zoomstart');
8328
8854
 
8329
- this._animateZoom(center, zoom, origin, scale);
8855
+ this._animateZoom(center, zoom, origin, scale, null, true);
8330
8856
 
8331
8857
  return true;
8332
8858
  },
@@ -8378,34 +8904,19 @@ L.Map.include(!L.DomUtil.TRANSITION ? {} : {
8378
8904
 
8379
8905
  L.TileLayer.include({
8380
8906
  _animateZoom: function (e) {
8381
- var firstFrame = false;
8382
-
8383
8907
  if (!this._animating) {
8384
8908
  this._animating = true;
8385
- firstFrame = true;
8386
- }
8387
-
8388
- if (firstFrame) {
8389
8909
  this._prepareBgBuffer();
8390
8910
  }
8391
8911
 
8392
- var transform = L.DomUtil.TRANSFORM,
8393
- bg = this._bgBuffer;
8394
-
8395
- if (firstFrame) {
8396
- //prevent bg buffer from clearing right after zoom
8397
- clearTimeout(this._clearBgBufferTimer);
8398
-
8399
- // hack to make sure transform is updated before running animation
8400
- L.Util.falseFn(bg.offsetWidth);
8401
- }
8402
-
8403
- var scaleStr = L.DomUtil.getScaleString(e.scale, e.origin),
8404
- oldTransform = bg.style[transform];
8912
+ var bg = this._bgBuffer,
8913
+ transform = L.DomUtil.TRANSFORM,
8914
+ initialTransform = e.delta ? L.DomUtil.getTranslateString(e.delta) : bg.style[transform],
8915
+ scaleStr = L.DomUtil.getScaleString(e.scale, e.origin);
8405
8916
 
8406
8917
  bg.style[transform] = e.backwards ?
8407
- (e.delta ? L.DomUtil.getTranslateString(e.delta) : oldTransform) + ' ' + scaleStr :
8408
- scaleStr + ' ' + oldTransform;
8918
+ scaleStr + ' ' + initialTransform :
8919
+ initialTransform + ' ' + scaleStr;
8409
8920
  },
8410
8921
 
8411
8922
  _endZoomAnim: function () {
@@ -8413,9 +8924,7 @@ L.TileLayer.include({
8413
8924
  bg = this._bgBuffer;
8414
8925
 
8415
8926
  front.style.visibility = '';
8416
- front.style.zIndex = 2;
8417
-
8418
- bg.style.zIndex = 1;
8927
+ front.parentNode.appendChild(front); // Bring to fore
8419
8928
 
8420
8929
  // force reflow
8421
8930
  L.Util.falseFn(bg.offsetWidth);
@@ -8440,8 +8949,10 @@ L.TileLayer.include({
8440
8949
  // if foreground layer doesn't have many tiles but bg layer does,
8441
8950
  // keep the existing bg layer and just zoom it some more
8442
8951
 
8443
- if (bg && this._getLoadedTilesPercentage(bg) > 0.5 &&
8444
- this._getLoadedTilesPercentage(front) < 0.5) {
8952
+ var bgLoaded = this._getLoadedTilesPercentage(bg),
8953
+ frontLoaded = this._getLoadedTilesPercentage(front);
8954
+
8955
+ if (bg && bgLoaded > 0.5 && frontLoaded < 0.5) {
8445
8956
 
8446
8957
  front.style.visibility = 'hidden';
8447
8958
  this._stopLoadingImages(front);
@@ -8457,6 +8968,9 @@ L.TileLayer.include({
8457
8968
  bg = this._bgBuffer = front;
8458
8969
 
8459
8970
  this._stopLoadingImages(bg);
8971
+
8972
+ //prevent bg buffer from clearing right after zoom
8973
+ clearTimeout(this._clearBgBufferTimer);
8460
8974
  },
8461
8975
 
8462
8976
  _getLoadedTilesPercentage: function (container) {
@@ -8574,12 +9088,19 @@ L.Map.include({
8574
9088
  this.setView(latlng, zoom);
8575
9089
  }
8576
9090
 
8577
- var event = L.extend({
9091
+ var data = {
8578
9092
  latlng: latlng,
8579
- bounds: bounds
8580
- }, pos.coords);
9093
+ bounds: bounds,
9094
+ timestamp: pos.timestamp
9095
+ };
9096
+
9097
+ for (var i in pos.coords) {
9098
+ if (typeof pos.coords[i] === 'number') {
9099
+ data[i] = pos.coords[i];
9100
+ }
9101
+ }
8581
9102
 
8582
- this.fire('locationfound', event);
9103
+ this.fire('locationfound', data);
8583
9104
  }
8584
9105
  });
8585
9106