├── .eslintrc ├── .gitignore ├── .npmignore ├── .travis.yml ├── CHANGELOG.md ├── CONTRIBUTING.md ├── FAQ.md ├── Jakefile.js ├── LICENSE ├── PLUGIN-GUIDE.md ├── README.md ├── bower.json ├── build ├── build.html ├── build.js ├── deps.js ├── docs-index.leafdoc ├── docs-misc.leafdoc ├── docs.js ├── leafdoc-templates │ ├── comments.hbs │ ├── crs.hbs │ ├── event.hbs │ ├── example.hbs │ ├── factory.hbs │ ├── function.hbs │ ├── html.hbs │ ├── inherited.hbs │ ├── method.hbs │ ├── namespace.hbs │ ├── option.hbs │ ├── pane.hbs │ ├── projection.hbs │ ├── property.hbs │ ├── section.hbs │ └── supersection.hbs └── publish.sh ├── debug ├── css │ ├── mobile.css │ └── screen.css ├── hacks │ └── jitter.html ├── leaflet-include.js ├── map │ ├── canvas.html │ ├── control-layers.html │ ├── controls.html │ ├── geolocation.html │ ├── grid.html │ ├── iframe.html │ ├── image-overlay.html │ ├── layer_remove_add.html │ ├── map-mobile.html │ ├── map.html │ ├── markers.html │ ├── max-bounds-bouncy.html │ ├── max-bounds-infinite.html │ ├── max-bounds.html │ ├── opacity.html │ ├── popup.html │ ├── scroll.html │ ├── simple-proj.html │ ├── tile-debug.html │ ├── tile-opacity.html │ ├── wms-marble.html │ ├── wms.html │ ├── zoom-delta.html │ ├── zoom-remain-centered.html │ ├── zoomlevels.html │ └── zoompan.html ├── tests │ ├── add_remove_layers.html │ ├── bringtoback.html │ ├── canvasloop.html │ ├── click_on_canvas.html │ ├── detached-dom-memory-leak.html │ ├── doubleclick-events-slowdown.html │ ├── dragging_and_copyworldjump.html │ ├── dragging_cursors.html │ ├── mousemove_on_polygons.html │ ├── opacity.html │ ├── popup_offset.html │ ├── popupcontextmenuclicks.html │ ├── remove_while_dragging.html │ ├── removetilewhilepan.html │ ├── reuse_popups.html │ ├── rtl.html │ ├── rtl2.html │ ├── set_icon_reuse_dom.html │ ├── svg_clicks.html │ ├── tile-bounds.html │ ├── tile-events.html │ ├── tile-opacity.html │ ├── touch-shake.html │ └── touch-zoom-bounce.html └── vector │ ├── bounds-extend.html │ ├── feature-group-bounds.html │ ├── geojson-sample.js │ ├── geojson.html │ ├── rectangle.html │ ├── route.js │ ├── touchzoomemu.html │ ├── us-states.js │ ├── vector-bounds.html │ ├── vector-canvas.html │ ├── vector-mobile.html │ ├── vector-simple.html │ ├── vector.html │ └── vector2.html ├── dist ├── images │ ├── layers-2x.png │ ├── layers.png │ ├── marker-icon-2x.png │ ├── marker-icon.png │ └── marker-shadow.png └── leaflet.css ├── package.json ├── spec ├── .eslintrc ├── after.js ├── expect.js ├── index.html ├── karma.conf.js ├── sinon.js ├── spec.hintrc.js └── suites │ ├── SpecHelper.js │ ├── control │ ├── Control.AttributionSpec.js │ ├── Control.LayersSpec.js │ ├── Control.ScaleSpec.js │ └── ControlSpec.js │ ├── core │ ├── ClassSpec.js │ ├── EventsSpec.js │ └── UtilSpec.js │ ├── dom │ ├── DomEventSpec.js │ └── DomUtilSpec.js │ ├── geo │ ├── CRSSpec.js │ ├── LatLngBoundsSpec.js │ ├── LatLngSpec.js │ └── ProjectionSpec.js │ ├── geometry │ ├── BoundsSpec.js │ ├── LineUtilSpec.js │ ├── PointSpec.js │ ├── PolyUtilSpec.js │ └── TransformationSpec.js │ ├── layer │ ├── FeatureGroupSpec.js │ ├── GeoJSONSpec.js │ ├── ImageOverlaySpec.js │ ├── LayerGroupSpec.js │ ├── PopupSpec.js │ ├── marker │ │ └── MarkerSpec.js │ ├── tile │ │ ├── GridLayerSpec.js │ │ └── TileLayerSpec.js │ └── vector │ │ ├── CanvasSpec.js │ │ ├── CircleMarkerSpec.js │ │ ├── CircleSpec.js │ │ ├── PathSpec.js │ │ ├── PolygonSpec.js │ │ ├── PolylineGeometrySpec.js │ │ └── PolylineSpec.js │ └── map │ ├── MapSpec.js │ └── handler │ ├── Map.DragSpec.js │ └── Map.TouchZoomSpec.js └── src ├── Leaflet.js ├── control ├── Control.Attribution.js ├── Control.Layers.js ├── Control.Scale.js ├── Control.Zoom.js └── Control.js ├── copyright.js ├── core ├── Browser.js ├── Class.js ├── Class.leafdoc ├── Events.js ├── Events.leafdoc ├── Handler.js └── Util.js ├── dom ├── DomEvent.DoubleTap.js ├── DomEvent.Pointer.js ├── DomEvent.js ├── DomUtil.js ├── Draggable.js └── PosAnimation.js ├── geo ├── LatLng.js ├── LatLngBounds.js ├── crs │ ├── CRS.EPSG3395.js │ ├── CRS.EPSG3857.js │ ├── CRS.EPSG4326.js │ ├── CRS.Earth.js │ ├── CRS.Simple.js │ └── CRS.js └── projection │ ├── Projection.LonLat.js │ ├── Projection.Mercator.js │ ├── Projection.SphericalMercator.js │ └── Projection.leafdoc ├── geometry ├── Bounds.js ├── LineUtil.js ├── Point.js ├── PolyUtil.js └── Transformation.js ├── images ├── layers.svg ├── logo.svg └── marker.svg ├── layer ├── FeatureGroup.js ├── GeoJSON.js ├── ImageOverlay.js ├── Layer.Popup.js ├── Layer.js ├── LayerGroup.js ├── Popup.js ├── marker │ ├── DivIcon.js │ ├── Icon.Default.js │ ├── Icon.js │ ├── Marker.Drag.js │ ├── Marker.Popup.js │ └── Marker.js ├── tile │ ├── GridLayer.js │ ├── TileLayer.WMS.js │ └── TileLayer.js └── vector │ ├── Canvas.js │ ├── Circle.js │ ├── CircleMarker.js │ ├── Path.js │ ├── Polygon.js │ ├── Polyline.js │ ├── Rectangle.js │ ├── Renderer.js │ ├── SVG.VML.js │ └── SVG.js └── map ├── Map.js ├── Map.methodOptions.leafdoc ├── anim ├── Map.FlyTo.js ├── Map.PanAnimation.js └── Map.ZoomAnimation.js ├── ext └── Map.Geolocation.js └── handler ├── Map.BoxZoom.js ├── Map.DoubleClickZoom.js ├── Map.Drag.js ├── Map.Keyboard.js ├── Map.ScrollWheelZoom.js ├── Map.Tap.js └── Map.TouchZoom.js /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "globals": { 3 | "L": true, 4 | }, 5 | "env": { 6 | "commonjs": true, 7 | "amd": true, 8 | "node": false 9 | }, 10 | "extends": "mourner", 11 | "rules": { 12 | "no-mixed-spaces-and-tabs": [2, "smart-tabs"], 13 | "indent": [2, "tab", {"VariableDeclarator": 0}], 14 | "curly": 2, 15 | "spaced-comment": 2, 16 | "strict": 0, 17 | "wrap-iife": 0, 18 | "key-spacing": 0, 19 | "consistent-return": 0 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | tmp/**/* 4 | .idea 5 | .idea/**/* 6 | *.iml 7 | *.sublime-* 8 | _site 9 | dist/*.js 10 | dist/*.map 11 | dist/reference.html 12 | coverage/ 13 | *.js.html 14 | index.html 15 | .mailmap 16 | bower.json 17 | component.json 18 | debug/local/ 19 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | tmp/**/* 4 | .idea 5 | .idea/**/* 6 | *.iml 7 | *.sublime-* 8 | _site 9 | coverage/ 10 | dist/leaflet.zip 11 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: node_js 3 | node_js: stable 4 | addons: 5 | artifacts: 6 | paths: 7 | - dist 8 | target_paths: 9 | - content/leaflet/${NAME} 10 | env: 11 | global: 12 | - ARTIFACTS_BUCKET=leafletjs-cdn 13 | - ARTIFACTS_PERMISSIONS=public-read 14 | - secure: |- 15 | JlC1rD7WryxlUbWmD3NWVX9E60XB/+ss7+j0OaY3WqWziGUWDCuoVkOMGXnp 16 | Ev27O8qhlmRkeyiimUN64UzK0yeZ139DcZMY6r4A5E2kwHYRAO/H/zl5RAGo 17 | Yd9GUPwZfr3xV8WhH2GFy/L/mRjkGwue2o6ZxdsqBOKfYaF9Ryg= 18 | - secure: |- 19 | XW1hzORAtSpTgTKkQwel5gRMDy6SotzeSRsVV2jQCn46VIMx8G/J5nOI+ImL 20 | yeoH12PhCR0h39dM7mq8TYJo5DHwvbotI5nQhpMruSt8eMFbym8nGiqQh806 21 | fSJXkxmQ4MAjUdNFDIirBHhdZme8q3PueFzJ+5odFMvPGn/aITQ= 22 | after_success: 23 | - npm run build 24 | - cd dist && zip -x .DS_Store -r leaflet.zip . && cd .. 25 | - NAME=$TRAVIS_BRANCH 26 | - '[[ $TRAVIS_PULL_REQUEST != ''false'' ]] && NAME=$TRAVIS_PULL_REQUEST' 27 | - '[[ -n $TRAVIS_TAG ]] && NAME=$TRAVIS_TAG' 28 | -------------------------------------------------------------------------------- /Jakefile.js: -------------------------------------------------------------------------------- 1 | /* 2 | Leaflet building, testing and linting scripts. 3 | 4 | To use, install Node, then run the following commands in the project root: 5 | 6 | npm install -g jake 7 | npm install 8 | 9 | To check the code for errors and build Leaflet from source, run "jake". 10 | To run the tests, run "jake test". To build the documentation, run "jake docs". 11 | 12 | For a custom build, open build/build.html in the browser and follow the instructions. 13 | */ 14 | 15 | var build = require('./build/build.js'), 16 | buildDocs = require('./build/docs'), 17 | git = require('git-rev'); 18 | 19 | function hint(msg, args) { 20 | return function () { 21 | console.log(msg); 22 | jake.exec('node node_modules/eslint/bin/eslint.js ' + args, 23 | {printStdout: true}, function () { 24 | console.log('\tCheck passed.\n'); 25 | complete(); 26 | }); 27 | }; 28 | } 29 | 30 | // Returns the version string in package.json, plus a semver build metadata if 31 | // this is not an official release 32 | function calculateVersion(officialRelease, callback) { 33 | 34 | var version = require('./package.json').version; 35 | 36 | if (officialRelease) { 37 | callback(version); 38 | } else { 39 | git.short(function(str) { 40 | callback (version + '+' + str); 41 | }); 42 | } 43 | } 44 | 45 | desc('Check Leaflet source for errors with ESLint'); 46 | task('lint', {async: true}, hint('Checking for JS errors...', 'src --config .eslintrc')); 47 | 48 | desc('Check Leaflet specs source for errors with ESLint'); 49 | task('lintspec', {async: true}, hint('Checking for specs JS errors...', 'spec/suites --config spec/.eslintrc')); 50 | 51 | desc('Combine and compress Leaflet source files'); 52 | task('build', {async: true}, function (compsBase32, buildName, officialRelease) { 53 | calculateVersion(officialRelease, function(v){ 54 | build.build(complete, v, compsBase32, buildName); 55 | }); 56 | }); 57 | 58 | desc('Run PhantomJS tests'); 59 | task('test', ['lint', 'lintspec'], {async: true}, function () { 60 | build.test(complete); 61 | }); 62 | 63 | desc('Build documentation'); 64 | task('docs', {}, function() { 65 | buildDocs(); 66 | }); 67 | 68 | task('default', ['test', 'build']); 69 | 70 | jake.addListener('complete', function () { 71 | process.exit(); 72 | }); 73 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2010-2016, Vladimir Agafonkin 2 | Copyright (c) 2010-2011, CloudMade 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without modification, are 6 | permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this list of 9 | conditions and the following disclaimer. 10 | 11 | 2. Redistributions in binary form must reproduce the above copyright notice, this list 12 | of conditions and the following disclaimer in the documentation and/or other materials 13 | provided with the distribution. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 16 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 17 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 18 | COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 19 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 20 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR 22 | TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Leaflet 2 | 3 | Leaflet is the leading open-source JavaScript library for **mobile-friendly interactive maps**. 4 | Weighing just about 33 KB of gzipped JS code, it has all the mapping [features][] most developers ever need. 5 | 6 | Leaflet is designed with *simplicity*, *performance* and *usability* in mind. 7 | It works efficiently across all major desktop and mobile platforms out of the box, 8 | taking advantage of HTML5 and CSS3 on modern browsers while being accessible on older ones too. 9 | It can be extended with a huge amount of [plugins][], 10 | has a beautiful, easy to use and [well-documented][] API 11 | and a simple, readable [source code][] that is a joy to [contribute][] to. 12 | 13 | For more info, docs and tutorials, check out the [official website][].
14 | For **Leaflet downloads** (including the built master version), check out the [download page][]. 15 | 16 | We're happy to meet new contributors. 17 | If you want to **get involved** with Leaflet development, check out the [contribution guide][contribute]. 18 | Let's make the best mapping library that will ever exist, 19 | and push the limits of what's possible with online maps! 20 | 21 | [![Build Status](https://travis-ci.org/Leaflet/Leaflet.svg?branch=master)](https://travis-ci.org/Leaflet/Leaflet) 22 | 23 | [contributors]: https://github.com/Leaflet/Leaflet/graphs/contributors 24 | [features]: http://leafletjs.com/#features 25 | [plugins]: http://leafletjs.com/plugins.html 26 | [well-documented]: http://leafletjs.com/reference.html "Leaflet API reference" 27 | [source code]: https://github.com/Leaflet/Leaflet "Leaflet GitHub repository" 28 | [hosted on GitHub]: http://github.com/Leaflet/Leaflet 29 | [contribute]: https://github.com/Leaflet/Leaflet/blob/master/CONTRIBUTING.md "A guide to contributing to Leaflet" 30 | [official website]: http://leafletjs.com 31 | [download page]: http://leafletjs.com/download.html 32 | 33 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "leaflet", 3 | "description": "JavaScript library for mobile-friendly interactive maps", 4 | "main": [ 5 | "dist/leaflet.css", 6 | "dist/leaflet-src.js" 7 | ], 8 | "ignore": [ 9 | ".*", 10 | "CHANGELOG.json", 11 | "FAQ.md", 12 | "debug", 13 | "spec", 14 | "src", 15 | "build" 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /build/docs-index.leafdoc: -------------------------------------------------------------------------------- 1 | This file just defines the order of the classes in the docs. 2 | 3 | 4 | 5 | 6 | @class Map 7 | 8 | @class Marker 9 | @class Popup 10 | 11 | @class TileLayer 12 | @class TileLayer.WMS 13 | @class ImageOverlay 14 | 15 | @class Path 16 | @class Polyline 17 | @class Polygon 18 | @class Rectangle 19 | @class Circle 20 | @class CircleMarker 21 | @class SVG 22 | @class Canvas 23 | 24 | @class LayerGroup 25 | @class FeatureGroup 26 | @class GeoJSON 27 | @class GridLayer 28 | 29 | @class LatLng 30 | @class LatLngBounds 31 | @class Point 32 | @class Bounds 33 | @class Icon 34 | @class DivIcon 35 | 36 | @class Control.Zoom 37 | @class Control.Attribution 38 | @class Control.Layers 39 | @class Control.Scale 40 | 41 | @class Browser 42 | @class Util 43 | @class Transformation 44 | @class LineUtil 45 | @class PolyUtil 46 | 47 | @class DomEvent 48 | @class DomUtil 49 | @class PosAnimation 50 | @class Draggable 51 | 52 | @class Class 53 | @class Evented 54 | @class Layer 55 | @class Control 56 | @class Handler 57 | @class Projection 58 | @class CRS 59 | @class Renderer 60 | @class Event objects 61 | -------------------------------------------------------------------------------- /build/docs-misc.leafdoc: -------------------------------------------------------------------------------- 1 | Miscellaneous bits of documentation that don't really fit anywhere else 2 | 3 | 4 | 5 | @namespace Global Switches 6 | 7 | Global switches are created for rare cases and generally make 8 | Leaflet to not detect a particular browser feature even if it's 9 | there. You need to set the switch as a global variable to true 10 | before including Leaflet on the page, like this: 11 | 12 | ```html 13 | 14 | 15 | ``` 16 | 17 | | Switch | Description | 18 | | -------------- | ---------------- | 19 | | `L_NO_TOUCH` | Forces Leaflet to not use touch events even if it detects them. | 20 | | `L_DISABLE_3D` | Forces Leaflet to not use hardware-accelerated CSS 3D transforms for positioning (which may cause glitches in some rare environments) even if they're supported. | 21 | 22 | 23 | @namespace noConflict 24 | 25 | This method restores the `L` global variable to the original value 26 | it had before Leaflet inclusion, and returns the real Leaflet 27 | namespace so you can put it elsewhere, like this: 28 | 29 | ```html 30 | 40 | ``` 41 | 42 | 43 | @namespace version 44 | 45 | A constant that represents the Leaflet version in use. 46 | 47 | ```js 48 | L.version; // contains "1.0.0" (or whatever version is currently in use) 49 | ``` 50 | 51 | 52 | -------------------------------------------------------------------------------- /build/docs.js: -------------------------------------------------------------------------------- 1 | 2 | var packageDef = require('../package.json'); 3 | 4 | function buildDocs() { 5 | 6 | console.log('Building Leaflet documentation with Leafdoc'); 7 | 8 | var LeafDoc = require('leafdoc'); 9 | var doc = new LeafDoc({ 10 | templateDir: 'build/leafdoc-templates', 11 | showInheritancesWhenEmpty: true, 12 | leadingCharacter: '@' 13 | }); 14 | 15 | // Note to Vladimir: Iván's never gonna uncomment the following line. He's 16 | // too proud of the little leaves around the code. 17 | //doc.setLeadingChar('@'); 18 | 19 | // Leaflet uses a couple of non-standard documentable things. They are not 20 | // important enough to be classes/namespaces of their own, and should 21 | // just be listed in a table like the rest of documentables: 22 | doc.registerDocumentable('pane', 'Map panes'); 23 | doc.registerDocumentable('projection', 'Defined projections'); 24 | doc.registerDocumentable('crs', 'Defined CRSs'); 25 | 26 | doc.addFile('build/docs-index.leafdoc', false); 27 | doc.addDir('src'); 28 | doc.addFile('build/docs-misc.leafdoc', false); 29 | 30 | var out = doc.outputStr(); 31 | 32 | var fs = require('fs'); 33 | 34 | fs.writeFileSync('dist/reference-' + packageDef.version + '.html', out); 35 | } 36 | 37 | module.exports = buildDocs; 38 | -------------------------------------------------------------------------------- /build/leafdoc-templates/comments.hbs: -------------------------------------------------------------------------------- 1 | {{{rawmarkdown comments}}} -------------------------------------------------------------------------------- /build/leafdoc-templates/crs.hbs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | {{#each documentables}} 8 | 9 | 10 | 11 | 12 | {{/each}} 13 |
CRSDescription
{{name}}{{{markdown comments}}}
-------------------------------------------------------------------------------- /build/leafdoc-templates/event.hbs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | {{#each documentables}} 9 | 10 | 12 | 13 | 14 | {{/each}} 15 |
EventDataDescription
{{name}} 11 | {{{type type}}}{{{markdown comments}}}
-------------------------------------------------------------------------------- /build/leafdoc-templates/example.hbs: -------------------------------------------------------------------------------- 1 | 2 | {{#each documentables}} 3 | {{{rawmarkdown comments}}} 4 | {{/each}} -------------------------------------------------------------------------------- /build/leafdoc-templates/factory.hbs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | {{#each documentables}} 8 | 9 | 14 | 15 | 16 | {{/each}} 17 |
FactoryDescription
{{name}}( 10 | {{~#each params~}} 11 | {{#if type}}<{{{type type}}}> {{/if}}{{name}} 12 | {{~#unless @last}}, {{/unless}}{{/each~}} 13 | ){{{markdown comments}}}
-------------------------------------------------------------------------------- /build/leafdoc-templates/function.hbs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | {{#each documentables}} 9 | 10 | 15 | 16 | 17 | 18 | {{/each}} 19 |
FunctionReturnsDescription
{{name}}( 11 | {{~#each params~}} 12 | {{#if type}}<{{{type type}}}> {{/if}}{{name}} 13 | {{~#unless @last}}, {{/unless}}{{/each~}} 14 | ){{{type type}}}{{{markdown comments}}}
-------------------------------------------------------------------------------- /build/leafdoc-templates/inherited.hbs: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 |
5 |
{{{inherited}}}
6 |
7 |
8 | -------------------------------------------------------------------------------- /build/leafdoc-templates/method.hbs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | {{#each documentables}} 9 | 10 | 15 | 16 | 17 | 18 | {{/each}} 19 |
MethodReturnsDescription
{{name}}( 11 | {{~#each params~}} 12 | {{#if type}}<{{{type type}}}> {{/if}}{{name}} 13 | {{~#unless @last}}, {{/unless}}{{/each~}} 14 | ){{{type type}}}{{{rawmarkdown comments}}}
-------------------------------------------------------------------------------- /build/leafdoc-templates/namespace.hbs: -------------------------------------------------------------------------------- 1 | {{#if name ~}} 2 |

{{name}}

3 | {{~ else ~}} 4 | 5 | {{/if}} 6 | {{{rawmarkdown comments}}} 7 | {{{supersections}}} -------------------------------------------------------------------------------- /build/leafdoc-templates/option.hbs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | {{#each documentables}} 10 | 11 | 12 | 14 | 15 | 16 | {{/each}} 17 |
OptionTypeDefaultDescription
{{name}}{{{type type}}} 13 | {{defaultValue}}{{{markdown comments}}}
-------------------------------------------------------------------------------- /build/leafdoc-templates/pane.hbs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | {{#each documentables}} 10 | 11 | 12 | 14 | 15 | 16 | {{/each}} 17 |
PaneTypeZ-indexDescription
{{name}}{{{type type}}} 13 | {{defaultValue}}{{{markdown comments}}}
-------------------------------------------------------------------------------- /build/leafdoc-templates/projection.hbs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | {{#each documentables}} 8 | 9 | 10 | 11 | 12 | {{/each}} 13 |
ProjectionDescription
{{name}}{{{markdown comments}}}
-------------------------------------------------------------------------------- /build/leafdoc-templates/property.hbs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | {{#each documentables}} 9 | 10 | 12 | 13 | 14 | {{/each}} 15 |
PropertyTypeDescription
{{name}} 11 | {{{type type}}}{{{markdown comments}}}
-------------------------------------------------------------------------------- /build/leafdoc-templates/section.hbs: -------------------------------------------------------------------------------- 1 |
2 | 3 | {{#if name}}

{{name}}

{{/if}} 4 | 5 | {{#if comments~}} 6 |
{{{markdown comments}}}
7 | {{/if}} 8 | 9 | {{{documentables}}} 10 | 11 |
-------------------------------------------------------------------------------- /build/leafdoc-templates/supersection.hbs: -------------------------------------------------------------------------------- 1 |
2 |

{{name}}

3 | {{markdown comments}} 4 | {{{sections}}} 5 | 6 | {{{inheritances}}} 7 |
-------------------------------------------------------------------------------- /build/publish.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | npm update 4 | 5 | VERSION=$(node --eval "console.log(require('./package.json').version);") 6 | 7 | npm test || exit 1 8 | 9 | git checkout -b build 10 | 11 | jake build[,,true] 12 | git add dist/leaflet-src.js dist/leaflet.js dist/leaflet-src.map -f 13 | 14 | git commit -m "v$VERSION" 15 | 16 | git tag v$VERSION -f 17 | git push --tags -f 18 | 19 | npm publish --tag rc 20 | 21 | git checkout master 22 | git branch -D build 23 | -------------------------------------------------------------------------------- /debug/css/mobile.css: -------------------------------------------------------------------------------- 1 | html, body, #map { 2 | margin: 0; 3 | padding: 0; 4 | width: 100%; 5 | height: 100%; 6 | } -------------------------------------------------------------------------------- /debug/css/screen.css: -------------------------------------------------------------------------------- 1 | #map { 2 | width: 800px; 3 | height: 600px; 4 | border: 1px solid #ccc; 5 | } -------------------------------------------------------------------------------- /debug/hacks/jitter.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Leaflet debug page 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 |
18 |
Click in field then scroll map (in up/left direction) to see shift of map tiles. 19 |
: 20 | 21 |
22 |
23 | Bug tested to occur on: Safari on Mac (Tested in 5.1.7), iPad/iPhone 5.1.1., Android 4 Browser. Hack is in L.Browser.chrome and TileLayer._addTile 24 | 25 |
26 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /debug/leaflet-include.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | function getFiles() { 3 | var memo = {}, 4 | files = [], 5 | i, src; 6 | 7 | function addFiles(srcs) { 8 | for (var j = 0, len = srcs.length; j < len; j++) { 9 | memo[srcs[j]] = true; 10 | } 11 | } 12 | 13 | for (i in deps) { 14 | addFiles(deps[i].src); 15 | } 16 | 17 | for (src in memo) { 18 | files.push(src); 19 | } 20 | 21 | return files; 22 | } 23 | var scripts = getFiles(); 24 | 25 | function getSrcUrl() { 26 | var scripts = document.getElementsByTagName('script'); 27 | for (var i = 0; i < scripts.length; i++) { 28 | var src = scripts[i].src; 29 | if (src) { 30 | var res = src.match(/^(.*)leaflet-include\.js$/); 31 | if (res) { 32 | return res[1] + '../src/'; 33 | } 34 | } 35 | } 36 | } 37 | 38 | var path = getSrcUrl(); 39 | for (var i = 0; i < scripts.length; i++) { 40 | document.writeln(""); 41 | } 42 | document.writeln(''); 43 | })(); 44 | 45 | function getRandomLatLng(map) { 46 | var bounds = map.getBounds(), 47 | southWest = bounds.getSouthWest(), 48 | northEast = bounds.getNorthEast(), 49 | lngSpan = northEast.lng - southWest.lng, 50 | latSpan = northEast.lat - southWest.lat; 51 | 52 | return new L.LatLng( 53 | southWest.lat + latSpan * Math.random(), 54 | southWest.lng + lngSpan * Math.random()); 55 | } 56 | 57 | function logEvent(e) { 58 | console.log(e.type); 59 | } 60 | -------------------------------------------------------------------------------- /debug/map/canvas.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Leaflet debug page 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /debug/map/control-layers.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Leaflet debug page 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /debug/map/controls.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Leaflet debug page 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /debug/map/geolocation.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Leaflet geolocation debug page 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /debug/map/grid.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Leaflet debug page 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /debug/map/iframe.html: -------------------------------------------------------------------------------- 1 | 10 | -------------------------------------------------------------------------------- /debug/map/image-overlay.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Leaflet debug page 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /debug/map/layer_remove_add.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Leaflet debug page 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /debug/map/map-mobile.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Leaflet debug page 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /debug/map/map.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Leaflet debug page 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /debug/map/markers.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Leaflet debug page 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 68 | 69 | 70 | -------------------------------------------------------------------------------- /debug/map/max-bounds-bouncy.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Leaflet debug page 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |

Left: Bouncy maxBounds. Right: Not bouncy.

18 | 19 |
20 |
21 | 22 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /debug/map/max-bounds-infinite.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Leaflet debug page 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /debug/map/max-bounds.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Leaflet debug page 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /debug/map/popup.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Leaflet debug page 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /debug/map/scroll.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Leaflet debug page 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 | 23 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /debug/map/simple-proj.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Leaflet debug page 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /debug/map/tile-opacity.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Leaflet debug page 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /debug/map/wms-marble.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Leaflet debug page 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /debug/map/wms.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Leaflet debug page 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /debug/map/zoom-delta.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Leaflet debug page 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 34 | 35 | 36 | 37 |

Zoom delta test.

38 | 39 |

Zooming with touch zoom, box zoom or flyTo then map.stop() must make the zoom level snap to the value of the zoomSnap option. Zoom interactions (keyboard, mouse wheel, zoom control buttons must change the zoom by the amount in the zoomDelta option.

40 | 41 |
42 | 43 | 44 | 45 |
46 | 47 |
48 | Snap: 0.25. Delta: 0.5. 49 |
50 | 51 |
52 |
53 | Snap: 0 (off). Delta: 0.25. 54 |
55 | 56 |
57 | 58 | 108 | 109 | 110 | -------------------------------------------------------------------------------- /debug/map/zoom-remain-centered.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Leaflet debug page 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /debug/map/zoomlevels.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Leaflet debug page 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /debug/tests/add_remove_layers.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Leaflet debug page 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 |
18 | 19 | 20 |
21 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /debug/tests/bringtoback.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Leaflet debug page 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /debug/tests/canvasloop.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /debug/tests/click_on_canvas.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Leaflet debug page 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 50 | 51 | 52 | 53 | 54 | 55 | 56 |
57 | 58 | 59 | -------------------------------------------------------------------------------- /debug/tests/detached-dom-memory-leak.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Leaflet debug page 5 | 6 | 7 | 8 | 9 | 10 | 44 | 45 | 46 | 47 | This page will destroy and recreate a map div lots of times. Developer tools shall not display a memory leak. 48 | 49 |
50 | 51 | 52 | 53 |
54 | 55 | 56 | -------------------------------------------------------------------------------- /debug/tests/doubleclick-events-slowdown.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Leaflet debug page 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |

This test is meant for testing the performance of doubleclick event handler in IE. See #4127 and #2820

18 | 19 |
20 |
21 | 22 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /debug/tests/dragging_and_copyworldjump.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Leaflet debug page 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |

18 | On the left Map dragging and worldCopyJump are enabled during initialisation.
19 | On the right Map worldCopyJump is enabled. Dragging is enabled by clicking the button. 20 |

21 |
24 |
25 |
26 |
27 | 28 | 29 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /debug/tests/dragging_cursors.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Leaflet debug page 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | This page tests if the cursors for dragging the map and the markers behave as expected. The left marker is draggable, the right one is not. 17 | 18 |
19 | 20 |
Map dragging enabled:
21 |
22 | 23 |
24 | 25 |
Map dragging disabled:
26 |
27 | 28 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /debug/tests/mousemove_on_polygons.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Leaflet debug page 5 | 6 | 7 | 8 | 9 | 10 | 11 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 |
Enter Move Exit Click
Triangle 1:
Triangle 2:
Map:
44 | 45 | 46 | 47 |
48 | 49 | 106 | 107 | 108 | -------------------------------------------------------------------------------- /debug/tests/opacity.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Leaflet debug page 5 | 6 | 7 | 8 | 9 | 10 | 11 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /debug/tests/popup_offset.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Leaflet debug page 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /debug/tests/popupcontextmenuclicks.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Leaflet debug page 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /debug/tests/remove_while_dragging.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Leaflet debug page 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /debug/tests/removetilewhilepan.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Leaflet debug page 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /debug/tests/reuse_popups.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Leaflet debug page 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 | 15 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /debug/tests/rtl.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Leaflet debug page 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 19 | 20 | 21 | 22 |

Click the map to place a popup at the mouse location

23 |
24 | 25 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /debug/tests/rtl2.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 15 | 16 | 17 |
18 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /debug/tests/set_icon_reuse_dom.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Test for preservation of Icon DOM element 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /debug/tests/svg_clicks.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Leaflet debug page 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /debug/tests/tile-bounds.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Leaflet debug page 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 21 | 22 | 23 | 24 | The CSS in this page makes the boundaries of the GridLayer tiles visible. Tiles which do not overlap the map bounds shall not be shown, even at fractional zoom levels. 25 | 26 | 27 | 28 | 29 |
30 | 31 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /debug/tests/tile-opacity.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Leaflet debug page 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 16 | 17 | 18 | 19 | The opacity of the "toner" layer should pulse nicely, even when dragging/zooming the map around with new tiles. 20 |
21 | 22 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /debug/vector/bounds-extend.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Leaflet debug page 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /debug/vector/feature-group-bounds.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Leaflet debug page 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 89 | 90 | 91 | -------------------------------------------------------------------------------- /debug/vector/geojson-sample.js: -------------------------------------------------------------------------------- 1 | var geojsonSample = { 2 | "type": "FeatureCollection", 3 | "features": [ 4 | { 5 | "type": "Feature", 6 | "geometry": { 7 | "type": "Point", 8 | "coordinates": [102.0, 0.5] 9 | }, 10 | "properties": { 11 | "prop0": "value0", 12 | "color": "blue" 13 | } 14 | }, 15 | 16 | { 17 | "type": "Feature", 18 | "geometry": { 19 | "type": "LineString", 20 | "coordinates": [[102.0, 0.0], [103.0, 1.0], [104.0, 0.0], [105.0, 1.0]] 21 | }, 22 | "properties": { 23 | "color": "red", 24 | "prop1": 0.0 25 | } 26 | }, 27 | 28 | { 29 | "type": "Feature", 30 | "geometry": { 31 | "type": "Polygon", 32 | "coordinates": [[[100.0, 0.0], [101.0, 0.0], [101.0, 1.0], [100.0, 1.0], [100.0, 0.0]]] 33 | }, 34 | "properties": { 35 | "color": "green", 36 | "prop1": { 37 | "this": "that" 38 | } 39 | } 40 | }, 41 | 42 | { 43 | "type": "Feature", 44 | "geometry": { 45 | "type": "MultiPolygon", 46 | "coordinates": [[[[100.0, 1.5], [100.5, 1.5], [100.5, 2.0], [100.0, 2.0], [100.0, 1.5]]], [[[100.5, 2.0], [100.5, 2.5], [101.0, 2.5], [101.0, 2.0], [100.5, 2.0]]]] 47 | }, 48 | "properties": { 49 | "color": "purple" 50 | } 51 | } 52 | ] 53 | }; 54 | -------------------------------------------------------------------------------- /debug/vector/rectangle.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Leaflet debug page 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /debug/vector/vector-bounds.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Leaflet debug page 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /debug/vector/vector-canvas.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Leaflet debug page 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 | 96 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /debug/vector/vector-mobile.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Leaflet debug page 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /debug/vector/vector-simple.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Leaflet debug page 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /debug/vector/vector.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Leaflet debug page 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 | 15 | 16 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /debug/vector/vector2.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Leaflet debug page 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 | 15 | 16 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /dist/images/layers-2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/masajid390/leaflet/4ad74d879d2309410773d61e07f8aef23ea09158/dist/images/layers-2x.png -------------------------------------------------------------------------------- /dist/images/layers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/masajid390/leaflet/4ad74d879d2309410773d61e07f8aef23ea09158/dist/images/layers.png -------------------------------------------------------------------------------- /dist/images/marker-icon-2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/masajid390/leaflet/4ad74d879d2309410773d61e07f8aef23ea09158/dist/images/marker-icon-2x.png -------------------------------------------------------------------------------- /dist/images/marker-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/masajid390/leaflet/4ad74d879d2309410773d61e07f8aef23ea09158/dist/images/marker-icon.png -------------------------------------------------------------------------------- /dist/images/marker-shadow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/masajid390/leaflet/4ad74d879d2309410773d61e07f8aef23ea09158/dist/images/marker-shadow.png -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "leaflet", 3 | "version": "1.0.0-rc.1", 4 | "description": "JavaScript library for mobile-friendly interactive maps", 5 | "devDependencies": { 6 | "eslint": "^2.8.0", 7 | "eslint-config-mourner": "^2.0.1", 8 | "git-rev": "^0.2.1", 9 | "happen": "~0.3.1", 10 | "jake": "~8.0.12", 11 | "karma": "~0.13.22", 12 | "karma-chrome-launcher": "^0.2.3", 13 | "karma-coverage": "~0.5.5", 14 | "karma-firefox-launcher": "~0.1.7", 15 | "karma-mocha": "~0.2.2", 16 | "karma-phantomjs-launcher": "^1.0.0", 17 | "karma-safari-launcher": "~0.1.1", 18 | "leafdoc": "^1.2.2", 19 | "mocha": "~2.4.5", 20 | "phantomjs-prebuilt": "^2.1.7", 21 | "prosthetic-hand": "^1.3.0", 22 | "source-map": "^0.5.3", 23 | "uglify-js": "~2.6.2" 24 | }, 25 | "main": "dist/leaflet-src.js", 26 | "style": "dist/leaflet.css", 27 | "scripts": { 28 | "test": "jake test", 29 | "build": "jake build", 30 | "release": "./build/publish.sh" 31 | }, 32 | "repository": { 33 | "type": "git", 34 | "url": "git://github.com/Leaflet/Leaflet.git" 35 | }, 36 | "keywords": [ 37 | "gis", 38 | "map" 39 | ], 40 | "license": "BSD-2-Clause" 41 | } 42 | -------------------------------------------------------------------------------- /spec/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "no-unused-vars": 0, 4 | "quotes": 0, 5 | "no-shadow": 0, 6 | "no-irregular-whitespace": 0, 7 | "no-console": 0, 8 | "no-extend-native": 0 9 | }, 10 | "env": { 11 | "mocha": true 12 | }, 13 | "globals": { 14 | "expect": false, 15 | "sinon": false, 16 | "happen": false, 17 | "Hand": false 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /spec/after.js: -------------------------------------------------------------------------------- 1 | // put after Leaflet files as imagePath can't be detected in a PhantomJS env 2 | L.Icon.Default.imagePath = "/base/dist/images"; 3 | -------------------------------------------------------------------------------- /spec/karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration 2 | module.exports = function (config) { 3 | 4 | var libSources = require(__dirname+'/../build/build.js').getFiles(); 5 | 6 | var files = [ 7 | "spec/sinon.js", 8 | "spec/expect.js" 9 | ].concat(libSources, [ 10 | "spec/after.js", 11 | "node_modules/happen/happen.js", 12 | "node_modules/prosthetic-hand/dist/prosthetic-hand.js", 13 | "spec/suites/SpecHelper.js", 14 | "spec/suites/**/*.js", 15 | {pattern: "dist/images/*.png", included: false} 16 | ]); 17 | 18 | config.set({ 19 | // base path, that will be used to resolve files and exclude 20 | basePath: '../', 21 | 22 | plugins: [ 23 | 'karma-mocha', 24 | 'karma-coverage', 25 | 'karma-phantomjs-launcher', 26 | 'karma-chrome-launcher', 27 | 'karma-safari-launcher', 28 | 'karma-firefox-launcher'], 29 | 30 | // frameworks to use 31 | frameworks: ['mocha'], 32 | 33 | // list of files / patterns to load in the browser 34 | files: files, 35 | exclude: [], 36 | 37 | // test results reporter to use 38 | // possible values: 'dots', 'progress', 'junit', 'growl', 'coverage' 39 | reporters: ['dots'], 40 | 41 | // web server port 42 | port: 9876, 43 | 44 | // level of logging 45 | // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG 46 | logLevel: config.LOG_WARN, 47 | 48 | // enable / disable colors in the output (reporters and logs) 49 | colors: true, 50 | 51 | // enable / disable watching file and executing tests whenever any file changes 52 | autoWatch: false, 53 | 54 | // Start these browsers, currently available: 55 | // - Chrome 56 | // - ChromeCanary 57 | // - Firefox 58 | // - Opera 59 | // - Safari (only Mac) 60 | // - PhantomJS 61 | // - IE (only Windows) 62 | browsers: ['PhantomJS'], 63 | 64 | // If browser does not capture in given timeout [ms], kill it 65 | captureTimeout: 5000, 66 | 67 | // Workaround for PhantomJS random DISCONNECTED error 68 | browserDisconnectTimeout: 10000, // default 2000 69 | browserDisconnectTolerance: 1, // default 0 70 | 71 | // Continuous Integration mode 72 | // if true, it capture browsers, run tests and exit 73 | singleRun: true 74 | }); 75 | }; 76 | -------------------------------------------------------------------------------- /spec/spec.hintrc.js: -------------------------------------------------------------------------------- 1 | { 2 | "browser": true, 3 | "node": true, 4 | "predef": ["define", "L", "expect", "describe", "it", "sinon", "happen", "beforeEach", "afterEach", "Hand"], 5 | "strict": false, 6 | "bitwise": true, 7 | "camelcase": true, 8 | "curly": true, 9 | "eqeqeq": true, 10 | "forin": false, 11 | "immed": true, 12 | "latedef": true, 13 | "newcap": true, 14 | "noarg": true, 15 | "noempty": true, 16 | "nonew": true, 17 | "undef": true, 18 | // "unused": true, 19 | // "quotmark": "single", 20 | "indent": 4, 21 | "trailing": true, 22 | "white": true, 23 | "smarttabs": true 24 | // "maxlen": 120 25 | } 26 | -------------------------------------------------------------------------------- /spec/suites/SpecHelper.js: -------------------------------------------------------------------------------- 1 | if (!Array.prototype.map) { 2 | Array.prototype.map = function (fun) { 3 | "use strict"; 4 | 5 | if (this === undefined || this === null) { 6 | throw new TypeError(); 7 | } 8 | 9 | var t = Object(this); 10 | var len = t.length >>> 0; 11 | if (typeof fun !== "function") { 12 | throw new TypeError(); 13 | } 14 | 15 | var res = new Array(len); 16 | var thisp = arguments[1]; 17 | for (var i = 0; i < len; i++) { 18 | if (i in t) { 19 | res[i] = fun.call(thisp, t[i], i, t); 20 | } 21 | } 22 | 23 | return res; 24 | }; 25 | } 26 | 27 | expect.Assertion.prototype.near = function (expected, delta) { 28 | delta = delta || 1; 29 | expect(this.obj.x).to 30 | .be.within(expected.x - delta, expected.x + delta); 31 | expect(this.obj.y).to 32 | .be.within(expected.y - delta, expected.y + delta); 33 | }; 34 | 35 | expect.Assertion.prototype.nearLatLng = function (expected, delta) { 36 | delta = delta || 1e-4; 37 | expect(this.obj.lat).to 38 | .be.within(expected.lat - delta, expected.lat + delta); 39 | expect(this.obj.lng).to 40 | .be.within(expected.lng - delta, expected.lng + delta); 41 | }; 42 | 43 | happen.at = function (what, x, y, props) { 44 | this.once(document.elementFromPoint(x, y), L.Util.extend({ 45 | type: what, 46 | clientX: x, 47 | clientY: y, 48 | screenX: x, 49 | screenY: y, 50 | which: 1, 51 | button: 0 52 | }, props || {})); 53 | }; 54 | 55 | // We'll want to skip a couple of things when in PhantomJS, due to lack of CSS animations 56 | it.skipInPhantom = L.Browser.any3d ? it : it.skip; 57 | 58 | // A couple of tests need the browser to be touch-capable 59 | it.skipIfNotTouch = window.TouchEvent ? it : it.skip; 60 | 61 | // A couple of tests need the browser to be pointer-capable 62 | it.skipIfNotEdge = window.PointerEvent ? it : it.skip; 63 | -------------------------------------------------------------------------------- /spec/suites/control/Control.ScaleSpec.js: -------------------------------------------------------------------------------- 1 | describe("Control.Scale", function () { 2 | it("can be added to an unloaded map", function () { 3 | var map = L.map(document.createElement('div')); 4 | new L.Control.Scale().addTo(map); 5 | }); 6 | }); 7 | -------------------------------------------------------------------------------- /spec/suites/control/ControlSpec.js: -------------------------------------------------------------------------------- 1 | describe("Control", function () { 2 | var map; 3 | 4 | beforeEach(function () { 5 | map = L.map(document.createElement('div')); 6 | }); 7 | 8 | function onAdd() { 9 | return L.DomUtil.create('div', 'leaflet-test-control'); 10 | } 11 | 12 | describe("#addTo", function () { 13 | it("adds the container to the map", function () { 14 | var control = new L.Control(); 15 | control.onAdd = onAdd; 16 | control.addTo(map); 17 | expect(map.getContainer().querySelector('.leaflet-test-control')).to.equal(control.getContainer()); 18 | }); 19 | 20 | it("removes the control from any existing map", function () { 21 | var control = new L.Control(); 22 | control.onAdd = onAdd; 23 | control.addTo(map); 24 | control.addTo(map); 25 | expect(map.getContainer().querySelectorAll('.leaflet-test-control').length).to.equal(1); 26 | expect(map.getContainer().querySelector('.leaflet-test-control')).to.equal(control.getContainer()); 27 | }); 28 | }); 29 | 30 | describe("#remove", function () { 31 | it("removes the container from the map", function () { 32 | var control = new L.Control(); 33 | control.onAdd = onAdd; 34 | control.addTo(map).remove(); 35 | expect(map.getContainer().querySelector('.leaflet-test-control')).to.equal(null); 36 | }); 37 | 38 | it("calls onRemove if defined", function () { 39 | var control = new L.Control(); 40 | control.onAdd = onAdd; 41 | control.onRemove = sinon.spy(); 42 | control.addTo(map).remove(); 43 | expect(control.onRemove.called).to.be(true); 44 | }); 45 | 46 | it("is a no-op if the control has not been added", function () { 47 | var control = new L.Control(); 48 | expect(control.remove()).to.equal(control); 49 | }); 50 | }); 51 | }); 52 | -------------------------------------------------------------------------------- /spec/suites/dom/DomEventSpec.js: -------------------------------------------------------------------------------- 1 | describe('DomEvent', function () { 2 | var el; 3 | 4 | function simulateClick(el) { 5 | if (document.createEvent) { 6 | var e = document.createEvent('MouseEvents'); 7 | e.initMouseEvent('click', true, true, window, 8 | 0, 0, 0, 0, 0, false, false, false, false, 0, null); 9 | return el.dispatchEvent(e); 10 | } else if (el.fireEvent) { 11 | return el.fireEvent('onclick'); 12 | } 13 | } 14 | 15 | beforeEach(function () { 16 | el = document.createElement('div'); 17 | el.style.position = 'absolute'; 18 | el.style.top = el.style.left = '-10000px'; 19 | document.body.appendChild(el); 20 | }); 21 | 22 | afterEach(function () { 23 | document.body.removeChild(el); 24 | }); 25 | 26 | describe('#addListener', function () { 27 | it('adds a listener and calls it on event', function () { 28 | var listener1 = sinon.spy(), 29 | listener2 = sinon.spy(); 30 | 31 | L.DomEvent.addListener(el, 'click', listener1); 32 | L.DomEvent.addListener(el, 'click', listener2); 33 | 34 | simulateClick(el); 35 | 36 | expect(listener1.called).to.be.ok(); 37 | expect(listener2.called).to.be.ok(); 38 | }); 39 | 40 | it('binds "this" to the given context', function () { 41 | var obj = {foo: 'bar'}, 42 | result; 43 | 44 | L.DomEvent.addListener(el, 'click', function () { 45 | result = this; 46 | }, obj); 47 | 48 | simulateClick(el); 49 | 50 | expect(result).to.eql(obj); 51 | }); 52 | 53 | it('passes an event object to the listener', function () { 54 | var type; 55 | 56 | L.DomEvent.addListener(el, 'click', function (e) { 57 | type = e && e.type; 58 | }); 59 | simulateClick(el); 60 | 61 | expect(type).to.eql('click'); 62 | }); 63 | }); 64 | 65 | describe('#removeListener', function () { 66 | it('removes a previously added listener', function () { 67 | var listener = sinon.spy(); 68 | 69 | L.DomEvent.addListener(el, 'click', listener); 70 | L.DomEvent.removeListener(el, 'click', listener); 71 | 72 | simulateClick(el); 73 | 74 | expect(listener.called).to.not.be.ok(); 75 | }); 76 | }); 77 | 78 | describe('#stopPropagation', function () { 79 | it('stops propagation of the given event', function () { 80 | var child = document.createElement('div'), 81 | listener = sinon.spy(); 82 | 83 | el.appendChild(child); 84 | 85 | L.DomEvent.addListener(child, 'click', L.DomEvent.stopPropagation); 86 | L.DomEvent.addListener(el, 'click', listener); 87 | 88 | simulateClick(child); 89 | 90 | expect(listener.called).to.not.be.ok(); 91 | 92 | el.removeChild(child); 93 | }); 94 | }); 95 | describe('#preventDefault', function () { 96 | it('prevents the default action of event', function () { 97 | L.DomEvent.addListener(el, 'click', L.DomEvent.preventDefault); 98 | 99 | expect(simulateClick(el)).to.be(false); 100 | }); 101 | }); 102 | }); 103 | -------------------------------------------------------------------------------- /spec/suites/dom/DomUtilSpec.js: -------------------------------------------------------------------------------- 1 | describe('DomUtil', function () { 2 | var el; 3 | 4 | beforeEach(function () { 5 | el = document.createElement('div'); 6 | el.style.position = 'absolute'; 7 | el.style.top = el.style.left = '-10000px'; 8 | document.body.appendChild(el); 9 | }); 10 | 11 | afterEach(function () { 12 | document.body.removeChild(el); 13 | }); 14 | 15 | describe('#get', function () { 16 | it('gets element by id if the given argument is string', function () { 17 | el.id = 'testId'; 18 | expect(L.DomUtil.get(el.id)).to.eql(el); 19 | }); 20 | 21 | it('returns the element if it is given as an argument', function () { 22 | expect(L.DomUtil.get(el)).to.eql(el); 23 | }); 24 | }); 25 | 26 | describe('#addClass, #removeClass, #hasClass', function () { 27 | it('has defined class for test element', function () { 28 | el.className = 'bar foo baz '; 29 | expect(L.DomUtil.hasClass(el, 'foo')).to.be.ok(); 30 | expect(L.DomUtil.hasClass(el, 'bar')).to.be.ok(); 31 | expect(L.DomUtil.hasClass(el, 'baz')).to.be.ok(); 32 | expect(L.DomUtil.hasClass(el, 'boo')).to.not.be.ok(); 33 | }); 34 | 35 | it('adds or removes the class', function () { 36 | el.className = ''; 37 | L.DomUtil.addClass(el, 'foo'); 38 | 39 | expect(el.className).to.eql('foo'); 40 | expect(L.DomUtil.hasClass(el, 'foo')).to.be.ok(); 41 | 42 | L.DomUtil.addClass(el, 'bar'); 43 | expect(el.className).to.eql('foo bar'); 44 | expect(L.DomUtil.hasClass(el, 'foo')).to.be.ok(); 45 | 46 | L.DomUtil.removeClass(el, 'foo'); 47 | expect(el.className).to.eql('bar'); 48 | expect(L.DomUtil.hasClass(el, 'foo')).to.not.be.ok(); 49 | 50 | el.className = 'foo bar barz'; 51 | L.DomUtil.removeClass(el, 'bar'); 52 | expect(el.className).to.eql('foo barz'); 53 | }); 54 | }); 55 | 56 | // describe('#setPosition', noSpecs); 57 | 58 | // describe('#getStyle', noSpecs); 59 | }); 60 | -------------------------------------------------------------------------------- /spec/suites/geo/ProjectionSpec.js: -------------------------------------------------------------------------------- 1 | describe("Projection.Mercator", function () { 2 | var p = L.Projection.Mercator; 3 | 4 | describe("#project", function () { 5 | it("projects a center point", function () { 6 | // edge cases 7 | expect(p.project(new L.LatLng(0, 0))).near(new L.Point(0, 0)); 8 | }); 9 | 10 | it("projects the northeast corner of the world", function () { 11 | expect(p.project(new L.LatLng(85.0840591556, 180))).near(new L.Point(20037508, 20037508)); 12 | }); 13 | 14 | it("projects the southwest corner of the world", function () { 15 | expect(p.project(new L.LatLng(-85.0840591556, -180))).near(new L.Point(-20037508, -20037508)); 16 | }); 17 | 18 | it("projects other points", function () { 19 | expect(p.project(new L.LatLng(50, 30))).near(new L.Point(3339584, 6413524)); 20 | 21 | // from https://github.com/Leaflet/Leaflet/issues/1578 22 | expect(p.project(new L.LatLng(51.9371170300465, 80.11230468750001))) 23 | .near(new L.Point(8918060.964088084, 6755099.410887127)); 24 | }); 25 | }); 26 | 27 | describe("#unproject", function () { 28 | function pr(point) { 29 | return p.project(p.unproject(point)); 30 | } 31 | 32 | it("unprojects a center point", function () { 33 | expect(pr(new L.Point(0, 0))).near(new L.Point(0, 0)); 34 | }); 35 | 36 | it("unprojects pi points", function () { 37 | expect(pr(new L.Point(-Math.PI, Math.PI))).near(new L.Point(-Math.PI, Math.PI)); 38 | expect(pr(new L.Point(-Math.PI, -Math.PI))).near(new L.Point(-Math.PI, -Math.PI)); 39 | 40 | expect(pr(new L.Point(0.523598775598, 1.010683188683))).near(new L.Point(0.523598775598, 1.010683188683)); 41 | }); 42 | 43 | it('unprojects other points', function () { 44 | // from https://github.com/Leaflet/Leaflet/issues/1578 45 | expect(pr(new L.Point(8918060.964088084, 6755099.410887127))); 46 | }); 47 | }); 48 | }); 49 | describe("Projection.SphericalMercator", function () { 50 | var p = L.Projection.SphericalMercator; 51 | 52 | describe("#project", function () { 53 | it("projects a center point", function () { 54 | // edge cases 55 | expect(p.project(new L.LatLng(0, 0))).near(new L.Point(0, 0)); 56 | }); 57 | 58 | it("projects the northeast corner of the world", function () { 59 | expect(p.project(new L.LatLng(85.0511287798, 180))).near(new L.Point(20037508, 20037508)); 60 | }); 61 | 62 | it("projects the southwest corner of the world", function () { 63 | expect(p.project(new L.LatLng(-85.0511287798, -180))).near(new L.Point(-20037508, -20037508)); 64 | }); 65 | 66 | it("projects other points", function () { 67 | expect(p.project(new L.LatLng(50, 30))).near(new L.Point(3339584, 6446275)); 68 | 69 | // from https://github.com/Leaflet/Leaflet/issues/1578 70 | expect(p.project(new L.LatLng(51.9371170300465, 80.11230468750001))) 71 | .near(new L.Point(8918060.96409, 6788763.38325)); 72 | }); 73 | }); 74 | 75 | describe("#unproject", function () { 76 | function pr(point) { 77 | return p.project(p.unproject(point)); 78 | } 79 | 80 | it("unprojects a center point", function () { 81 | expect(pr(new L.Point(0, 0))).near(new L.Point(0, 0)); 82 | }); 83 | 84 | it("unprojects pi points", function () { 85 | expect(pr(new L.Point(-Math.PI, Math.PI))).near(new L.Point(-Math.PI, Math.PI)); 86 | expect(pr(new L.Point(-Math.PI, -Math.PI))).near(new L.Point(-Math.PI, -Math.PI)); 87 | 88 | expect(pr(new L.Point(0.523598775598, 1.010683188683))).near(new L.Point(0.523598775598, 1.010683188683)); 89 | }); 90 | 91 | it('unprojects other points', function () { 92 | // from https://github.com/Leaflet/Leaflet/issues/1578 93 | expect(pr(new L.Point(8918060.964088084, 6755099.410887127))); 94 | }); 95 | }); 96 | }); 97 | -------------------------------------------------------------------------------- /spec/suites/geometry/BoundsSpec.js: -------------------------------------------------------------------------------- 1 | describe('Bounds', function () { 2 | var a, b, c; 3 | 4 | beforeEach(function () { 5 | a = new L.Bounds( 6 | new L.Point(14, 12), 7 | new L.Point(30, 40)); 8 | b = new L.Bounds([ 9 | new L.Point(20, 12), 10 | new L.Point(14, 20), 11 | new L.Point(30, 40) 12 | ]); 13 | c = new L.Bounds(); 14 | }); 15 | 16 | describe('constructor', function () { 17 | it('creates bounds with proper min & max on (Point, Point)', function () { 18 | expect(a.min).to.eql(new L.Point(14, 12)); 19 | expect(a.max).to.eql(new L.Point(30, 40)); 20 | }); 21 | it('creates bounds with proper min & max on (Point[])', function () { 22 | expect(b.min).to.eql(new L.Point(14, 12)); 23 | expect(b.max).to.eql(new L.Point(30, 40)); 24 | }); 25 | }); 26 | 27 | describe('#extend', function () { 28 | it('extends the bounds to contain the given point', function () { 29 | a.extend(new L.Point(50, 20)); 30 | expect(a.min).to.eql(new L.Point(14, 12)); 31 | expect(a.max).to.eql(new L.Point(50, 40)); 32 | 33 | b.extend(new L.Point(25, 50)); 34 | expect(b.min).to.eql(new L.Point(14, 12)); 35 | expect(b.max).to.eql(new L.Point(30, 50)); 36 | }); 37 | }); 38 | 39 | describe('#getCenter', function () { 40 | it('returns the center point', function () { 41 | expect(a.getCenter()).to.eql(new L.Point(22, 26)); 42 | }); 43 | }); 44 | 45 | describe('#contains', function () { 46 | it('contains other bounds or point', function () { 47 | a.extend(new L.Point(50, 10)); 48 | expect(a.contains(b)).to.be.ok(); 49 | expect(b.contains(a)).to.not.be.ok(); 50 | expect(a.contains(new L.Point(24, 25))).to.be.ok(); 51 | expect(a.contains(new L.Point(54, 65))).to.not.be.ok(); 52 | }); 53 | }); 54 | 55 | describe('#isValid', function () { 56 | it('returns true if properly set up', function () { 57 | expect(a.isValid()).to.be.ok(); 58 | }); 59 | it('returns false if is invalid', function () { 60 | expect(c.isValid()).to.not.be.ok(); 61 | }); 62 | it('returns true if extended', function () { 63 | c.extend([0, 0]); 64 | expect(c.isValid()).to.be.ok(); 65 | }); 66 | }); 67 | 68 | describe('#getSize', function () { 69 | it('returns the size of the bounds as point', function () { 70 | expect(a.getSize()).to.eql(new L.Point(16, 28)); 71 | }); 72 | }); 73 | 74 | describe('#intersects', function () { 75 | it('returns true if bounds intersect', function () { 76 | expect(a.intersects(b)).to.be(true); 77 | expect(a.intersects(new L.Bounds(new L.Point(100, 100), new L.Point(120, 120)))).to.eql(false); 78 | }); 79 | }); 80 | 81 | describe('L.bounds factory', function () { 82 | it('creates bounds from array of number arrays', function () { 83 | var bounds = L.bounds([[14, 12], [30, 40]]); 84 | expect(bounds).to.eql(a); 85 | }); 86 | }); 87 | }); 88 | -------------------------------------------------------------------------------- /spec/suites/geometry/LineUtilSpec.js: -------------------------------------------------------------------------------- 1 | describe('LineUtil', function () { 2 | 3 | describe('#clipSegment', function () { 4 | 5 | var bounds; 6 | 7 | beforeEach(function () { 8 | bounds = L.bounds([5, 0], [15, 10]); 9 | }); 10 | 11 | it('clips a segment by bounds', function () { 12 | var a = new L.Point(0, 0); 13 | var b = new L.Point(15, 15); 14 | 15 | var segment = L.LineUtil.clipSegment(a, b, bounds); 16 | 17 | expect(segment[0]).to.eql(new L.Point(5, 5)); 18 | expect(segment[1]).to.eql(new L.Point(10, 10)); 19 | 20 | var c = new L.Point(5, -5); 21 | var d = new L.Point(20, 10); 22 | 23 | var segment2 = L.LineUtil.clipSegment(c, d, bounds); 24 | 25 | expect(segment2[0]).to.eql(new L.Point(10, 0)); 26 | expect(segment2[1]).to.eql(new L.Point(15, 5)); 27 | }); 28 | 29 | it('uses last bit code and reject segments out of bounds', function () { 30 | var a = new L.Point(15, 15); 31 | var b = new L.Point(25, 20); 32 | var segment = L.LineUtil.clipSegment(a, b, bounds, true); 33 | 34 | expect(segment).to.be(false); 35 | }); 36 | 37 | it('can round numbers in clipped bounds', function () { 38 | var a = new L.Point(4, 5); 39 | var b = new L.Point(8, 6); 40 | 41 | var segment1 = L.LineUtil.clipSegment(a, b, bounds); 42 | 43 | expect(segment1[0]).to.eql(new L.Point(5, 5.25)); 44 | expect(segment1[1]).to.eql(b); 45 | 46 | var segment2 = L.LineUtil.clipSegment(a, b, bounds, false, true); 47 | 48 | expect(segment2[0]).to.eql(new L.Point(5, 5)); 49 | expect(segment2[1]).to.eql(b); 50 | }); 51 | }); 52 | 53 | describe('#pointToSegmentDistance & #closestPointOnSegment', function () { 54 | 55 | var p1 = new L.Point(0, 10); 56 | var p2 = new L.Point(10, 0); 57 | var p = new L.Point(0, 0); 58 | 59 | it('calculates distance from point to segment', function () { 60 | expect(L.LineUtil.pointToSegmentDistance(p, p1, p2)).to.eql(Math.sqrt(200) / 2); 61 | }); 62 | 63 | it('calculates point closest to segment', function () { 64 | expect(L.LineUtil.closestPointOnSegment(p, p1, p2)).to.eql(new L.Point(5, 5)); 65 | }); 66 | }); 67 | 68 | describe('#simplify', function () { 69 | it('simplifies polylines according to tolerance', function () { 70 | var points = [ 71 | new L.Point(0, 0), 72 | new L.Point(0.01, 0), 73 | new L.Point(0.5, 0.01), 74 | new L.Point(0.7, 0), 75 | new L.Point(1, 0), 76 | new L.Point(1.999, 0.999), 77 | new L.Point(2, 1) 78 | ]; 79 | 80 | var simplified = L.LineUtil.simplify(points, 0.1); 81 | 82 | expect(simplified).to.eql([ 83 | new L.Point(0, 0), 84 | new L.Point(1, 0), 85 | new L.Point(2, 1) 86 | ]); 87 | }); 88 | }); 89 | 90 | }); 91 | -------------------------------------------------------------------------------- /spec/suites/geometry/PolyUtilSpec.js: -------------------------------------------------------------------------------- 1 | describe('PolyUtil', function () { 2 | 3 | describe('#clipPolygon', function () { 4 | it('clips polygon by bounds', function () { 5 | var bounds = L.bounds([0, 0], [10, 10]); 6 | 7 | var points = [ 8 | new L.Point(5, 5), 9 | new L.Point(15, 10), 10 | new L.Point(10, 15) 11 | ]; 12 | 13 | // check clip without rounding 14 | var clipped = L.PolyUtil.clipPolygon(points, bounds); 15 | 16 | for (var i = 0, len = clipped.length; i < len; i++) { 17 | delete clipped[i]._code; 18 | } 19 | 20 | expect(clipped).to.eql([ 21 | new L.Point(7.5, 10), 22 | new L.Point(5, 5), 23 | new L.Point(10, 7.5), 24 | new L.Point(10, 10) 25 | ]); 26 | 27 | // check clip with rounding 28 | var clippedRounded = L.PolyUtil.clipPolygon(points, bounds, true); 29 | 30 | for (i = 0, len = clippedRounded.length; i < len; i++) { 31 | delete clippedRounded[i]._code; 32 | } 33 | 34 | expect(clippedRounded).to.eql([ 35 | new L.Point(8, 10), 36 | new L.Point(5, 5), 37 | new L.Point(10, 8), 38 | new L.Point(10, 10) 39 | ]); 40 | }); 41 | }); 42 | }); 43 | -------------------------------------------------------------------------------- /spec/suites/geometry/TransformationSpec.js: -------------------------------------------------------------------------------- 1 | describe("Transformation", function () { 2 | var t, p; 3 | 4 | beforeEach(function () { 5 | t = new L.Transformation(1, 2, 3, 4); 6 | p = new L.Point(10, 20); 7 | }); 8 | 9 | describe('#transform', function () { 10 | it("performs a transformation", function () { 11 | var p2 = t.transform(p, 2); 12 | expect(p2).to.eql(new L.Point(24, 128)); 13 | }); 14 | it('assumes a scale of 1 if not specified', function () { 15 | var p2 = t.transform(p); 16 | expect(p2).to.eql(new L.Point(12, 64)); 17 | }); 18 | }); 19 | 20 | describe('#untransform', function () { 21 | it("performs a reverse transformation", function () { 22 | var p2 = t.transform(p, 2); 23 | var p3 = t.untransform(p2, 2); 24 | expect(p3).to.eql(p); 25 | }); 26 | it('assumes a scale of 1 if not specified', function () { 27 | var p2 = t.transform(p); 28 | expect(t.untransform(new L.Point(12, 64))).to.eql(new L.Point(10, 20)); 29 | }); 30 | }); 31 | }); 32 | -------------------------------------------------------------------------------- /spec/suites/layer/FeatureGroupSpec.js: -------------------------------------------------------------------------------- 1 | describe('FeatureGroup', function () { 2 | var map; 3 | beforeEach(function () { 4 | map = L.map(document.createElement('div')); 5 | map.setView([0, 0], 1); 6 | }); 7 | describe("#_propagateEvent", function () { 8 | var marker; 9 | beforeEach(function () { 10 | marker = L.marker([0, 0]); 11 | }); 12 | describe("when a Marker is added to multiple FeatureGroups ", function () { 13 | it("e.layer should be the Marker", function () { 14 | var fg1 = L.featureGroup(), 15 | fg2 = L.featureGroup(); 16 | 17 | fg1.addLayer(marker); 18 | fg2.addLayer(marker); 19 | 20 | var wasClicked1, 21 | wasClicked2; 22 | 23 | fg2.on('click', function (e) { 24 | expect(e.layer).to.be(marker); 25 | expect(e.target).to.be(fg2); 26 | wasClicked2 = true; 27 | }); 28 | 29 | fg1.on('click', function (e) { 30 | expect(e.layer).to.be(marker); 31 | expect(e.target).to.be(fg1); 32 | wasClicked1 = true; 33 | }); 34 | 35 | marker.fire('click', {type: 'click'}, true); 36 | 37 | expect(wasClicked1).to.be(true); 38 | expect(wasClicked2).to.be(true); 39 | }); 40 | }); 41 | }); 42 | describe('addLayer', function () { 43 | it('adds the layer', function () { 44 | var fg = L.featureGroup(), 45 | marker = L.marker([0, 0]); 46 | 47 | expect(fg.hasLayer(marker)).to.be(false); 48 | 49 | fg.addLayer(marker); 50 | 51 | expect(fg.hasLayer(marker)).to.be(true); 52 | }); 53 | it('supports non-evented layers', function () { 54 | var fg = L.featureGroup(), 55 | g = L.layerGroup(); 56 | 57 | expect(fg.hasLayer(g)).to.be(false); 58 | 59 | fg.addLayer(g); 60 | 61 | expect(fg.hasLayer(g)).to.be(true); 62 | }); 63 | }); 64 | describe('removeLayer', function () { 65 | it('removes the layer passed to it', function () { 66 | var fg = L.featureGroup(), 67 | marker = L.marker([0, 0]); 68 | 69 | fg.addLayer(marker); 70 | expect(fg.hasLayer(marker)).to.be(true); 71 | 72 | fg.removeLayer(marker); 73 | expect(fg.hasLayer(marker)).to.be(false); 74 | }); 75 | it('removes the layer passed to it by id', function () { 76 | var fg = L.featureGroup(), 77 | marker = L.marker([0, 0]); 78 | 79 | fg.addLayer(marker); 80 | expect(fg.hasLayer(marker)).to.be(true); 81 | 82 | fg.removeLayer(L.stamp(marker)); 83 | expect(fg.hasLayer(marker)).to.be(false); 84 | }); 85 | }); 86 | }); 87 | -------------------------------------------------------------------------------- /spec/suites/layer/ImageOverlaySpec.js: -------------------------------------------------------------------------------- 1 | describe('ImageOverlay', function () { 2 | describe('#setStyle', function () { 3 | it('sets opacity', function () { 4 | var overlay = L.imageOverlay().setStyle({opacity: 0.5}); 5 | expect(overlay.options.opacity).to.equal(0.5); 6 | }); 7 | }); 8 | describe('#setBounds', function () { 9 | it('sets bounds', function () { 10 | var bounds = new L.LatLngBounds( 11 | new L.LatLng(14, 12), 12 | new L.LatLng(30, 40)); 13 | var overlay = L.imageOverlay().setBounds(bounds); 14 | expect(overlay._bounds).to.equal(bounds); 15 | }); 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /spec/suites/layer/LayerGroupSpec.js: -------------------------------------------------------------------------------- 1 | describe('LayerGroup', function () { 2 | describe("#hasLayer", function () { 3 | it("returns false when passed undefined, null, or false", function () { 4 | var lg = L.layerGroup(); 5 | expect(lg.hasLayer(undefined)).to.equal(false); 6 | expect(lg.hasLayer(null)).to.equal(false); 7 | expect(lg.hasLayer(false)).to.equal(false); 8 | }); 9 | }); 10 | 11 | describe("#addLayer", function () { 12 | it('adds a layer', function () { 13 | var lg = L.layerGroup(), 14 | marker = L.marker([0, 0]); 15 | 16 | expect(lg.addLayer(marker)).to.eql(lg); 17 | 18 | expect(lg.hasLayer(marker)).to.be(true); 19 | }); 20 | }); 21 | 22 | describe("#removeLayer", function () { 23 | it('removes a layer', function () { 24 | var lg = L.layerGroup(), 25 | marker = L.marker([0, 0]); 26 | 27 | lg.addLayer(marker); 28 | expect(lg.removeLayer(marker)).to.eql(lg); 29 | 30 | expect(lg.hasLayer(marker)).to.be(false); 31 | }); 32 | }); 33 | 34 | describe("#clearLayers", function () { 35 | it('removes all layers', function () { 36 | var lg = L.layerGroup(), 37 | marker = L.marker([0, 0]); 38 | 39 | lg.addLayer(marker); 40 | expect(lg.clearLayers()).to.eql(lg); 41 | 42 | expect(lg.hasLayer(marker)).to.be(false); 43 | }); 44 | }); 45 | 46 | describe("#getLayers", function () { 47 | it('gets all layers', function () { 48 | var lg = L.layerGroup(), 49 | marker = L.marker([0, 0]); 50 | 51 | lg.addLayer(marker); 52 | 53 | expect(lg.getLayers()).to.eql([marker]); 54 | }); 55 | }); 56 | 57 | describe("#eachLayer", function () { 58 | it('iterates over all layers', function () { 59 | var lg = L.layerGroup(), 60 | marker = L.marker([0, 0]), 61 | ctx = {foo: 'bar'}; 62 | 63 | lg.addLayer(marker); 64 | 65 | lg.eachLayer(function (layer) { 66 | expect(layer).to.eql(marker); 67 | expect(this).to.eql(ctx); 68 | }, ctx); 69 | }); 70 | }); 71 | }); 72 | -------------------------------------------------------------------------------- /spec/suites/layer/vector/CanvasSpec.js: -------------------------------------------------------------------------------- 1 | describe('Canvas', function () { 2 | 3 | var c, map, p2ll, latLngs; 4 | 5 | before(function () { 6 | c = document.createElement('div'); 7 | c.style.width = '400px'; 8 | c.style.height = '400px'; 9 | c.style.position = 'absolute'; 10 | c.style.top = '0'; 11 | c.style.left = '0'; 12 | document.body.appendChild(c); 13 | map = new L.Map(c, {preferCanvas: true, zoomControl: false}); 14 | map.setView([0, 0], 6); 15 | p2ll = function (x, y) { 16 | return map.layerPointToLatLng([x, y]); 17 | }; 18 | latLngs = [p2ll(0, 0), p2ll(0, 100), p2ll(100, 100), p2ll(100, 0)]; 19 | }); 20 | 21 | after(function () { 22 | document.body.removeChild(c); 23 | }); 24 | 25 | describe("#events", function () { 26 | var layer; 27 | 28 | beforeEach(function () { 29 | layer = L.polygon(latLngs).addTo(map); 30 | }); 31 | 32 | afterEach(function () { 33 | layer.remove(); 34 | }); 35 | 36 | it("should fire event when layer contains mouse", function () { 37 | var spy = sinon.spy(); 38 | layer.on('click', spy); 39 | happen.at('click', 50, 50); // Click on the layer. 40 | expect(spy.callCount).to.eql(1); 41 | happen.at('click', 150, 150); // Click outside layer. 42 | expect(spy.callCount).to.eql(1); 43 | layer.off("click", spy); 44 | }); 45 | 46 | it("DOM events propagate from canvas polygon to map", function () { 47 | var spy = sinon.spy(); 48 | map.on("click", spy); 49 | happen.at('click', 50, 50); 50 | expect(spy.callCount).to.eql(1); 51 | map.off("click", spy); 52 | }); 53 | 54 | it("DOM events fired on canvas polygon can be cancelled before being caught by the map", function () { 55 | var mapSpy = sinon.spy(); 56 | var layerSpy = sinon.spy(); 57 | map.on("click", mapSpy); 58 | layer.on("click", L.DomEvent.stopPropagation).on("click", layerSpy); 59 | happen.at('click', 50, 50); 60 | expect(layerSpy.callCount).to.eql(1); 61 | expect(mapSpy.callCount).to.eql(0); 62 | map.off("click", mapSpy); 63 | layer.off("click", L.DomEvent.stopPropagation).off("click", layerSpy); 64 | }); 65 | 66 | it("DOM events fired on canvas polygon are propagated only once to the map even when two layers contains the event", function () { 67 | var spy = sinon.spy(); 68 | var layer2 = L.polygon(latLngs).addTo(map); 69 | map.on("click", spy); 70 | happen.at('click', 50, 50); 71 | expect(spy.callCount).to.eql(1); 72 | layer2.remove(); 73 | map.off("click", spy); 74 | }); 75 | 76 | }); 77 | 78 | describe("#events(interactive=false)", function () { 79 | var layer; 80 | 81 | beforeEach(function () { 82 | layer = L.polygon(latLngs, {interactive: false}).addTo(map); 83 | }); 84 | 85 | afterEach(function () { 86 | layer.remove(); 87 | }); 88 | 89 | it("should not fire click when not interactive", function () { 90 | var spy = sinon.spy(); 91 | layer.on('click', spy); 92 | happen.at('click', 50, 50); // Click on the layer. 93 | expect(spy.callCount).to.eql(0); 94 | happen.at('click', 150, 150); // Click outside layer. 95 | expect(spy.callCount).to.eql(0); 96 | layer.off("click", spy); 97 | }); 98 | 99 | }); 100 | 101 | describe('#dashArray', function () { 102 | it('can add polyline with dashArray', function () { 103 | var layer = L.polygon(latLngs, { 104 | dashArray: "5,5" 105 | }).addTo(map); 106 | }); 107 | it('can setStyle with dashArray', function () { 108 | var layer = L.polygon(latLngs).addTo(map); 109 | layer.setStyle({ 110 | dashArray: "5,5" 111 | }); 112 | }); 113 | }); 114 | 115 | }); 116 | -------------------------------------------------------------------------------- /spec/suites/layer/vector/CircleMarkerSpec.js: -------------------------------------------------------------------------------- 1 | describe('CircleMarker', function () { 2 | describe("#_radius", function () { 3 | var map; 4 | beforeEach(function () { 5 | map = L.map(document.createElement('div')); 6 | map.setView([0, 0], 1); 7 | }); 8 | describe("when a CircleMarker is added to the map ", function () { 9 | describe("with a radius set as an option", function () { 10 | it("takes that radius", function () { 11 | var marker = L.circleMarker([0, 0], {radius: 20}).addTo(map); 12 | 13 | expect(marker._radius).to.be(20); 14 | }); 15 | }); 16 | 17 | describe("and radius is set before adding it", function () { 18 | it("takes that radius", function () { 19 | var marker = L.circleMarker([0, 0], {radius: 20}); 20 | marker.setRadius(15); 21 | marker.addTo(map); 22 | expect(marker._radius).to.be(15); 23 | }); 24 | }); 25 | 26 | describe("and radius is set after adding it", function () { 27 | it("takes that radius", function () { 28 | var marker = L.circleMarker([0, 0], {radius: 20}); 29 | marker.addTo(map); 30 | marker.setRadius(15); 31 | expect(marker._radius).to.be(15); 32 | }); 33 | }); 34 | 35 | describe("and setStyle is used to change the radius after adding", function () { 36 | it("takes the given radius", function () { 37 | var marker = L.circleMarker([0, 0], {radius: 20}); 38 | marker.addTo(map); 39 | marker.setStyle({radius: 15}); 40 | expect(marker._radius).to.be(15); 41 | }); 42 | }); 43 | describe("and setStyle is used to change the radius before adding", function () { 44 | it("takes the given radius", function () { 45 | var marker = L.circleMarker([0, 0], {radius: 20}); 46 | marker.setStyle({radius: 15}); 47 | marker.addTo(map); 48 | expect(marker._radius).to.be(15); 49 | }); 50 | }); 51 | }); 52 | }); 53 | }); 54 | -------------------------------------------------------------------------------- /spec/suites/layer/vector/CircleSpec.js: -------------------------------------------------------------------------------- 1 | describe('Circle', function () { 2 | 3 | describe('#init', function () { 4 | 5 | it('uses default radius if not given', function () { 6 | var circle = L.circle([0, 0]); 7 | expect(circle.getRadius()).to.eql(10); 8 | }); 9 | 10 | it('throws error if radius is NaN', function () { 11 | expect(function () { 12 | L.circle([0, 0], NaN); 13 | }).to.throwException('Circle radius cannot be NaN'); 14 | }); 15 | 16 | }); 17 | 18 | describe('#getBounds', function () { 19 | 20 | var map, circle; 21 | 22 | beforeEach(function () { 23 | map = L.map(document.createElement('div')).setView([0, 0], 4); 24 | circle = L.circle([50, 30], {radius: 200}).addTo(map); 25 | }); 26 | 27 | it('returns bounds', function () { 28 | var bounds = circle.getBounds(); 29 | 30 | expect(bounds.getSouthWest()).nearLatLng(new L.LatLng(49.94347, 29.91211)); 31 | expect(bounds.getNorthEast()).nearLatLng(new L.LatLng(50.05646, 30.08789)); 32 | }); 33 | }); 34 | 35 | describe('Legacy factory', function () { 36 | 37 | var map, circle; 38 | 39 | beforeEach(function () { 40 | map = L.map(document.createElement('div')).setView([0, 0], 4); 41 | circle = L.circle([50, 30], 200).addTo(map); 42 | }); 43 | 44 | it('returns same bounds as 1.0 factory', function () { 45 | var bounds = circle.getBounds(); 46 | 47 | expect(bounds.getSouthWest()).nearLatLng(new L.LatLng(49.94347, 29.91211)); 48 | expect(bounds.getNorthEast()).nearLatLng(new L.LatLng(50.05646, 30.08789)); 49 | }); 50 | }); 51 | 52 | }); 53 | -------------------------------------------------------------------------------- /spec/suites/layer/vector/PathSpec.js: -------------------------------------------------------------------------------- 1 | describe('Path', function () { 2 | describe('#bringToBack', function () { 3 | it('is a no-op for layers not on a map', function () { 4 | var path = new L.Path(); 5 | expect(path.bringToBack()).to.equal(path); 6 | }); 7 | }); 8 | 9 | describe('#bringToFront', function () { 10 | it('is a no-op for layers not on a map', function () { 11 | var path = new L.Path(); 12 | expect(path.bringToFront()).to.equal(path); 13 | }); 14 | }); 15 | }); 16 | -------------------------------------------------------------------------------- /spec/suites/layer/vector/PolylineGeometrySpec.js: -------------------------------------------------------------------------------- 1 | describe('PolylineGeometry', function () { 2 | 3 | var c = document.createElement('div'); 4 | c.style.width = '400px'; 5 | c.style.height = '400px'; 6 | var map = new L.Map(c); 7 | map.setView(new L.LatLng(55.8, 37.6), 6); 8 | 9 | describe("#distanceTo", function () { 10 | it("calculates distances to points", function () { 11 | var p1 = map.latLngToLayerPoint(new L.LatLng(55.8, 37.6)); 12 | var p2 = map.latLngToLayerPoint(new L.LatLng(57.123076977278, 44.861962891635)); 13 | var latlngs = [[56.485503424111, 35.545556640339], [55.972522915346, 36.116845702918], [55.502459116923, 34.930322265253], [55.31534617509, 38.973291015816]] 14 | .map(function (ll) { 15 | return new L.LatLng(ll[0], ll[1]); 16 | }); 17 | var polyline = new L.Polyline([], { 18 | 'noClip': true 19 | }); 20 | map.addLayer(polyline); 21 | 22 | expect(polyline.closestLayerPoint(p1)).to.be(null); 23 | 24 | polyline.setLatLngs(latlngs); 25 | var point = polyline.closestLayerPoint(p1); 26 | expect(point).not.to.be(null); 27 | expect(point.distance).to.not.be(Infinity); 28 | expect(point.distance).to.not.be(NaN); 29 | 30 | var point2 = polyline.closestLayerPoint(p2); 31 | 32 | expect(point.distance).to.be.lessThan(point2.distance); 33 | }); 34 | }); 35 | }); 36 | -------------------------------------------------------------------------------- /spec/suites/map/handler/Map.TouchZoomSpec.js: -------------------------------------------------------------------------------- 1 | describe("Map.TouchZoom", function () { 2 | it.skipIfNotTouch("Increases zoom when pinching out", function (done) { 3 | var container = document.createElement('div'); 4 | container.style.width = container.style.height = '600px'; 5 | container.style.top = container.style.left = 0; 6 | container.style.position = 'absolute'; 7 | document.body.appendChild(container); 8 | 9 | var map = new L.Map(container, { 10 | touchZoom: true, 11 | inertia: false, 12 | zoomAnimation: false // If true, the test has to wait extra 250msec 13 | }); 14 | map.setView([0, 0], 1); 15 | 16 | var hand = new Hand({ 17 | timing: 'fastframe', 18 | onStop: function () { 19 | map.once('zoomend', function () { 20 | var center = map.getCenter(); 21 | var zoom = map.getZoom(); 22 | document.body.removeChild(container); 23 | expect(center.lat).to.be(0); 24 | expect(center.lng).to.be(0); 25 | 26 | // Initial zoom 1, initial distance 50px, final distance 450px 27 | expect(zoom).to.be(4); 28 | 29 | done(); 30 | }); 31 | } 32 | }); 33 | var f1 = hand.growFinger('touch'); 34 | var f2 = hand.growFinger('touch'); 35 | 36 | hand.sync(5); 37 | f1.wait(100).moveTo(275, 300, 0) 38 | .down().moveBy(-200, 0, 500).up(100); 39 | f2.wait(100).moveTo(325, 300, 0) 40 | .down().moveBy(200, 0, 500).up(100); 41 | }); 42 | 43 | 44 | it.skipIfNotTouch("Decreases zoom when pinching in", function (done) { 45 | var container = document.createElement('div'); 46 | container.style.width = container.style.height = '600px'; 47 | container.style.top = container.style.left = 0; 48 | container.style.position = 'absolute'; 49 | document.body.appendChild(container); 50 | 51 | var map = new L.Map(container, { 52 | touchZoom: true, 53 | inertia: false, 54 | zoomAnimation: false // If true, the test has to wait extra 250msec 55 | }); 56 | map.setView([0, 0], 4); 57 | 58 | var hand = new Hand({ 59 | timing: 'fastframe', 60 | onStop: function () { 61 | map.once('zoomend', function () { 62 | var center = map.getCenter(); 63 | var zoom = map.getZoom(); 64 | document.body.removeChild(container); 65 | expect(center.lat).to.be(0); 66 | expect(center.lng).to.be(0); 67 | 68 | // Initial zoom 4, initial distance 450px, final distance 50px 69 | expect(zoom).to.be(1); 70 | 71 | done(); 72 | }); 73 | } 74 | }); 75 | var f1 = hand.growFinger('touch'); 76 | var f2 = hand.growFinger('touch'); 77 | 78 | hand.sync(5); 79 | f1.wait(100).moveTo(75, 300, 0) 80 | .down().moveBy(200, 0, 500).up(100); 81 | f2.wait(100).moveTo(525, 300, 0) 82 | .down().moveBy(-200, 0, 500).up(100); 83 | }); 84 | 85 | 86 | 87 | }); 88 | -------------------------------------------------------------------------------- /src/Leaflet.js: -------------------------------------------------------------------------------- 1 | 2 | var L = { 3 | version: 'dev' 4 | }; 5 | 6 | function expose() { 7 | var oldL = window.L; 8 | 9 | L.noConflict = function () { 10 | window.L = oldL; 11 | return this; 12 | }; 13 | 14 | window.L = L; 15 | } 16 | 17 | // define Leaflet for Node module pattern loaders, including Browserify 18 | if (typeof module === 'object' && typeof module.exports === 'object') { 19 | module.exports = L; 20 | 21 | // define Leaflet as an AMD module 22 | } else if (typeof define === 'function' && define.amd) { 23 | define(L); 24 | } 25 | 26 | // define Leaflet as a global L variable, saving the original L to restore later if needed 27 | if (typeof window !== 'undefined') { 28 | expose(); 29 | } 30 | -------------------------------------------------------------------------------- /src/copyright.js: -------------------------------------------------------------------------------- 1 | /* 2 | Leaflet {VERSION}, a JS library for interactive maps. http://leafletjs.com 3 | (c) 2010-2015 Vladimir Agafonkin, (c) 2010-2011 CloudMade 4 | */ 5 | -------------------------------------------------------------------------------- /src/core/Class.js: -------------------------------------------------------------------------------- 1 | 2 | // @class Class 3 | // @aka L.Class 4 | 5 | // @section 6 | // @uninheritable 7 | 8 | // Thanks to John Resig and Dean Edwards for inspiration! 9 | 10 | L.Class = function () {}; 11 | 12 | L.Class.extend = function (props) { 13 | 14 | // @function extend(props: Object): Function 15 | // [Extends the current class](#class-inheritance) given the properties to be included. 16 | // Returns a Javascript function that is a class constructor (to be called with `new`). 17 | var NewClass = function () { 18 | 19 | // call the constructor 20 | if (this.initialize) { 21 | this.initialize.apply(this, arguments); 22 | } 23 | 24 | // call all constructor hooks 25 | this.callInitHooks(); 26 | }; 27 | 28 | var parentProto = NewClass.__super__ = this.prototype; 29 | 30 | var proto = L.Util.create(parentProto); 31 | proto.constructor = NewClass; 32 | 33 | NewClass.prototype = proto; 34 | 35 | // inherit parent's statics 36 | for (var i in this) { 37 | if (this.hasOwnProperty(i) && i !== 'prototype') { 38 | NewClass[i] = this[i]; 39 | } 40 | } 41 | 42 | // mix static properties into the class 43 | if (props.statics) { 44 | L.extend(NewClass, props.statics); 45 | delete props.statics; 46 | } 47 | 48 | // mix includes into the prototype 49 | if (props.includes) { 50 | L.Util.extend.apply(null, [proto].concat(props.includes)); 51 | delete props.includes; 52 | } 53 | 54 | // merge options 55 | if (proto.options) { 56 | props.options = L.Util.extend(L.Util.create(proto.options), props.options); 57 | } 58 | 59 | // mix given properties into the prototype 60 | L.extend(proto, props); 61 | 62 | proto._initHooks = []; 63 | 64 | // add method for calling all hooks 65 | proto.callInitHooks = function () { 66 | 67 | if (this._initHooksCalled) { return; } 68 | 69 | if (parentProto.callInitHooks) { 70 | parentProto.callInitHooks.call(this); 71 | } 72 | 73 | this._initHooksCalled = true; 74 | 75 | for (var i = 0, len = proto._initHooks.length; i < len; i++) { 76 | proto._initHooks[i].call(this); 77 | } 78 | }; 79 | 80 | return NewClass; 81 | }; 82 | 83 | 84 | // @function include(properties: Object): this 85 | // [Includes a mixin](#class-includes) into the current class. 86 | L.Class.include = function (props) { 87 | L.extend(this.prototype, props); 88 | return this; 89 | }; 90 | 91 | // @function mergeOptions(options: Object): this 92 | // [Merges `options`](#class-options) into the defaults of the class. 93 | L.Class.mergeOptions = function (options) { 94 | L.extend(this.prototype.options, options); 95 | return this; 96 | }; 97 | 98 | // @function addInitHook(fn: Function): this 99 | // Adds a [constructor hook](#class-constructor-hooks) to the class. 100 | L.Class.addInitHook = function (fn) { // (Function) || (String, args...) 101 | var args = Array.prototype.slice.call(arguments, 1); 102 | 103 | var init = typeof fn === 'function' ? fn : function () { 104 | this[fn].apply(this, args); 105 | }; 106 | 107 | this.prototype._initHooks = this.prototype._initHooks || []; 108 | this.prototype._initHooks.push(init); 109 | return this; 110 | }; 111 | -------------------------------------------------------------------------------- /src/core/Handler.js: -------------------------------------------------------------------------------- 1 | /* 2 | L.Handler is a base class for handler classes that are used internally to inject 3 | interaction features like dragging to classes like Map and Marker. 4 | */ 5 | 6 | // @class Handler 7 | // @aka L.Handler 8 | // Abstract class for map interaction handlers 9 | 10 | L.Handler = L.Class.extend({ 11 | initialize: function (map) { 12 | this._map = map; 13 | }, 14 | 15 | // @method enable() 16 | // Enables the handler 17 | enable: function () { 18 | if (this._enabled) { return; } 19 | 20 | this._enabled = true; 21 | this.addHooks(); 22 | }, 23 | 24 | // @method disable() 25 | // Disables the handler 26 | disable: function () { 27 | if (!this._enabled) { return; } 28 | 29 | this._enabled = false; 30 | this.removeHooks(); 31 | }, 32 | 33 | // @method enabled(): Boolean 34 | // Returns `true` if the handler is enabled 35 | enabled: function () { 36 | return !!this._enabled; 37 | } 38 | 39 | // @section Extension methods 40 | // Classes inheriting from `Handler` must implement the two following methods: 41 | // @method addHooks() 42 | // Called when the handler is enabled, should add event hooks. 43 | // @method removeHooks() 44 | // Called when the handler is disabled, should remove the event hooks added previously. 45 | }); 46 | -------------------------------------------------------------------------------- /src/dom/DomEvent.DoubleTap.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Extends the event handling code with double tap support for mobile browsers. 3 | */ 4 | 5 | L.extend(L.DomEvent, { 6 | 7 | _touchstart: L.Browser.msPointer ? 'MSPointerDown' : L.Browser.pointer ? 'pointerdown' : 'touchstart', 8 | _touchend: L.Browser.msPointer ? 'MSPointerUp' : L.Browser.pointer ? 'pointerup' : 'touchend', 9 | 10 | // inspired by Zepto touch code by Thomas Fuchs 11 | addDoubleTapListener: function (obj, handler, id) { 12 | var last, touch, 13 | doubleTap = false, 14 | delay = 250; 15 | 16 | function onTouchStart(e) { 17 | var count; 18 | 19 | if (L.Browser.pointer) { 20 | count = L.DomEvent._pointersCount; 21 | } else { 22 | count = e.touches.length; 23 | } 24 | 25 | if (count > 1) { return; } 26 | 27 | var now = Date.now(), 28 | delta = now - (last || now); 29 | 30 | touch = e.touches ? e.touches[0] : e; 31 | doubleTap = (delta > 0 && delta <= delay); 32 | last = now; 33 | } 34 | 35 | function onTouchEnd() { 36 | if (doubleTap && !touch.cancelBubble) { 37 | if (L.Browser.pointer) { 38 | // work around .type being readonly with MSPointer* events 39 | var newTouch = {}, 40 | prop, i; 41 | 42 | for (i in touch) { 43 | prop = touch[i]; 44 | newTouch[i] = prop && prop.bind ? prop.bind(touch) : prop; 45 | } 46 | touch = newTouch; 47 | } 48 | touch.type = 'dblclick'; 49 | handler(touch); 50 | last = null; 51 | } 52 | } 53 | 54 | var pre = '_leaflet_', 55 | touchstart = this._touchstart, 56 | touchend = this._touchend; 57 | 58 | obj[pre + touchstart + id] = onTouchStart; 59 | obj[pre + touchend + id] = onTouchEnd; 60 | obj[pre + 'dblclick' + id] = handler; 61 | 62 | obj.addEventListener(touchstart, onTouchStart, false); 63 | obj.addEventListener(touchend, onTouchEnd, false); 64 | 65 | // On some platforms (notably, chrome on win10 + touchscreen + mouse), 66 | // the browser doesn't fire touchend/pointerup events but does fire 67 | // native dblclicks. See #4127. 68 | if (!L.Browser.edge) { 69 | obj.addEventListener('dblclick', handler, false); 70 | } 71 | 72 | return this; 73 | }, 74 | 75 | removeDoubleTapListener: function (obj, id) { 76 | var pre = '_leaflet_', 77 | touchstart = obj[pre + this._touchstart + id], 78 | touchend = obj[pre + this._touchend + id], 79 | dblclick = obj[pre + 'dblclick' + id]; 80 | 81 | obj.removeEventListener(this._touchstart, touchstart, false); 82 | obj.removeEventListener(this._touchend, touchend, false); 83 | if (!L.Browser.edge) { 84 | obj.removeEventListener('dblclick', dblclick, false); 85 | } 86 | 87 | return this; 88 | } 89 | }); 90 | -------------------------------------------------------------------------------- /src/dom/PosAnimation.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @class PosAnimation 3 | * @aka L.PosAnimation 4 | * @inherits Evented 5 | * Used internally for panning animations, utilizing CSS3 Transitions for modern browsers and a timer fallback for IE6-9. 6 | * 7 | * @example 8 | * ```js 9 | * var fx = new L.PosAnimation(); 10 | * fx.run(el, [300, 500], 0.5); 11 | * ``` 12 | * 13 | * @constructor L.PosAnimation() 14 | * Creates a `PosAnimation` object. 15 | * 16 | */ 17 | 18 | L.PosAnimation = L.Evented.extend({ 19 | 20 | // @method run(el: HTMLElement, newPos: Point, duration?: Number, easeLinearity?: Number) 21 | // Run an animation of a given element to a new position, optionally setting 22 | // duration in seconds (`0.25` by default) and easing linearity factor (3rd 23 | // argument of the [cubic bezier curve](http://cubic-bezier.com/#0,0,.5,1), 24 | // `0.5` by default). 25 | run: function (el, newPos, duration, easeLinearity) { 26 | this.stop(); 27 | 28 | this._el = el; 29 | this._inProgress = true; 30 | this._duration = duration || 0.25; 31 | this._easeOutPower = 1 / Math.max(easeLinearity || 0.5, 0.2); 32 | 33 | this._startPos = L.DomUtil.getPosition(el); 34 | this._offset = newPos.subtract(this._startPos); 35 | this._startTime = +new Date(); 36 | 37 | // @event start: Event 38 | // Fired when the animation starts 39 | this.fire('start'); 40 | 41 | this._animate(); 42 | }, 43 | 44 | // @method stop() 45 | // Stops the animation (if currently running). 46 | stop: function () { 47 | if (!this._inProgress) { return; } 48 | 49 | this._step(true); 50 | this._complete(); 51 | }, 52 | 53 | _animate: function () { 54 | // animation loop 55 | this._animId = L.Util.requestAnimFrame(this._animate, this); 56 | this._step(); 57 | }, 58 | 59 | _step: function (round) { 60 | var elapsed = (+new Date()) - this._startTime, 61 | duration = this._duration * 1000; 62 | 63 | if (elapsed < duration) { 64 | this._runFrame(this._easeOut(elapsed / duration), round); 65 | } else { 66 | this._runFrame(1); 67 | this._complete(); 68 | } 69 | }, 70 | 71 | _runFrame: function (progress, round) { 72 | var pos = this._startPos.add(this._offset.multiplyBy(progress)); 73 | if (round) { 74 | pos._round(); 75 | } 76 | L.DomUtil.setPosition(this._el, pos); 77 | 78 | // @event step: Event 79 | // Fired continuously during the animation. 80 | this.fire('step'); 81 | }, 82 | 83 | _complete: function () { 84 | L.Util.cancelAnimFrame(this._animId); 85 | 86 | this._inProgress = false; 87 | // @event end: Event 88 | // Fired when the animation ends. 89 | this.fire('end'); 90 | }, 91 | 92 | _easeOut: function (t) { 93 | return 1 - Math.pow(1 - t, this._easeOutPower); 94 | } 95 | }); 96 | -------------------------------------------------------------------------------- /src/geo/crs/CRS.EPSG3395.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @namespace CRS 3 | * @crs L.CRS.EPSG3395 4 | * 5 | * Rarely used by some commercial tile providers. Uses Elliptical Mercator projection. 6 | */ 7 | 8 | L.CRS.EPSG3395 = L.extend({}, L.CRS.Earth, { 9 | code: 'EPSG:3395', 10 | projection: L.Projection.Mercator, 11 | 12 | transformation: (function () { 13 | var scale = 0.5 / (Math.PI * L.Projection.Mercator.R); 14 | return new L.Transformation(scale, 0.5, -scale, 0.5); 15 | }()) 16 | }); 17 | -------------------------------------------------------------------------------- /src/geo/crs/CRS.EPSG3857.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @namespace CRS 3 | * @crs L.CRS.EPSG3857 4 | * 5 | * The most common CRS for online maps, used by almost all free and commercial 6 | * tile providers. Uses Spherical Mercator projection. Set in by default in 7 | * Map's `crs` option. 8 | */ 9 | 10 | L.CRS.EPSG3857 = L.extend({}, L.CRS.Earth, { 11 | code: 'EPSG:3857', 12 | projection: L.Projection.SphericalMercator, 13 | 14 | transformation: (function () { 15 | var scale = 0.5 / (Math.PI * L.Projection.SphericalMercator.R); 16 | return new L.Transformation(scale, 0.5, -scale, 0.5); 17 | }()) 18 | }); 19 | 20 | L.CRS.EPSG900913 = L.extend({}, L.CRS.EPSG3857, { 21 | code: 'EPSG:900913' 22 | }); 23 | -------------------------------------------------------------------------------- /src/geo/crs/CRS.EPSG4326.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @namespace CRS 3 | * @crs L.CRS.EPSG4326 4 | * 5 | * A common CRS among GIS enthusiasts. Uses simple Equirectangular projection. 6 | */ 7 | 8 | L.CRS.EPSG4326 = L.extend({}, L.CRS.Earth, { 9 | code: 'EPSG:4326', 10 | projection: L.Projection.LonLat, 11 | transformation: new L.Transformation(1 / 180, 1, -1 / 180, 0.5) 12 | }); 13 | -------------------------------------------------------------------------------- /src/geo/crs/CRS.Earth.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @namespace CRS 3 | * @crs L.CRS.Earth 4 | * 5 | * Serves as the base for CRS that are global such that they cover the earth. 6 | * Can only be used as the base for other CRS and cannot be used directly, 7 | * since it does not have a `code`, `projection` or `transformation`. `distance()` returns 8 | * meters. 9 | */ 10 | 11 | L.CRS.Earth = L.extend({}, L.CRS, { 12 | wrapLng: [-180, 180], 13 | 14 | // Mean Earth Radius, as recommended for use by 15 | // the International Union of Geodesy and Geophysics, 16 | // see http://rosettacode.org/wiki/Haversine_formula 17 | R: 6371000, 18 | 19 | // distance between two geographical points using spherical law of cosines approximation 20 | distance: function (latlng1, latlng2) { 21 | var rad = Math.PI / 180, 22 | lat1 = latlng1.lat * rad, 23 | lat2 = latlng2.lat * rad, 24 | a = Math.sin(lat1) * Math.sin(lat2) + 25 | Math.cos(lat1) * Math.cos(lat2) * Math.cos((latlng2.lng - latlng1.lng) * rad); 26 | 27 | return this.R * Math.acos(Math.min(a, 1)); 28 | } 29 | }); 30 | -------------------------------------------------------------------------------- /src/geo/crs/CRS.Simple.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @namespace CRS 3 | * @crs L.CRS.Simple 4 | * 5 | * A simple CRS that maps longitude and latitude into `x` and `y` directly. 6 | * May be used for maps of flat surfaces (e.g. game maps). Note that the `y` 7 | * axis should still be inverted (going from bottom to top). `distance()` returns 8 | * simple euclidean distance. 9 | */ 10 | 11 | L.CRS.Simple = L.extend({}, L.CRS, { 12 | projection: L.Projection.LonLat, 13 | transformation: new L.Transformation(1, 0, -1, 0), 14 | 15 | scale: function (zoom) { 16 | return Math.pow(2, zoom); 17 | }, 18 | 19 | zoom: function (scale) { 20 | return Math.log(scale) / Math.LN2; 21 | }, 22 | 23 | distance: function (latlng1, latlng2) { 24 | var dx = latlng2.lng - latlng1.lng, 25 | dy = latlng2.lat - latlng1.lat; 26 | 27 | return Math.sqrt(dx * dx + dy * dy); 28 | }, 29 | 30 | infinite: true 31 | }); 32 | -------------------------------------------------------------------------------- /src/geo/projection/Projection.LonLat.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @namespace Projection 3 | * @section 4 | * Leaflet comes with a set of already defined Projections out of the box: 5 | * 6 | * @projection L.Projection.LonLat 7 | * 8 | * Equirectangular, or Plate Carree projection — the most simple projection, 9 | * mostly used by GIS enthusiasts. Directly maps `x` as longitude, and `y` as 10 | * latitude. Also suitable for flat worlds, e.g. game maps. Used by the 11 | * `EPSG:3395` and `Simple` CRS. 12 | */ 13 | 14 | L.Projection = {}; 15 | 16 | L.Projection.LonLat = { 17 | project: function (latlng) { 18 | return new L.Point(latlng.lng, latlng.lat); 19 | }, 20 | 21 | unproject: function (point) { 22 | return new L.LatLng(point.y, point.x); 23 | }, 24 | 25 | bounds: L.bounds([-180, -90], [180, 90]) 26 | }; 27 | -------------------------------------------------------------------------------- /src/geo/projection/Projection.Mercator.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @namespace Projection 3 | * @projection L.Projection.Mercator 4 | * 5 | * Elliptical Mercator projection — more complex than Spherical Mercator. Takes into account that Earth is a geoid, not a perfect sphere. Used by the EPSG:3395 CRS. 6 | */ 7 | 8 | L.Projection.Mercator = { 9 | R: 6378137, 10 | R_MINOR: 6356752.314245179, 11 | 12 | bounds: L.bounds([-20037508.34279, -15496570.73972], [20037508.34279, 18764656.23138]), 13 | 14 | project: function (latlng) { 15 | var d = Math.PI / 180, 16 | r = this.R, 17 | y = latlng.lat * d, 18 | tmp = this.R_MINOR / r, 19 | e = Math.sqrt(1 - tmp * tmp), 20 | con = e * Math.sin(y); 21 | 22 | var ts = Math.tan(Math.PI / 4 - y / 2) / Math.pow((1 - con) / (1 + con), e / 2); 23 | y = -r * Math.log(Math.max(ts, 1E-10)); 24 | 25 | return new L.Point(latlng.lng * d * r, y); 26 | }, 27 | 28 | unproject: function (point) { 29 | var d = 180 / Math.PI, 30 | r = this.R, 31 | tmp = this.R_MINOR / r, 32 | e = Math.sqrt(1 - tmp * tmp), 33 | ts = Math.exp(-point.y / r), 34 | phi = Math.PI / 2 - 2 * Math.atan(ts); 35 | 36 | for (var i = 0, dphi = 0.1, con; i < 15 && Math.abs(dphi) > 1e-7; i++) { 37 | con = e * Math.sin(phi); 38 | con = Math.pow((1 - con) / (1 + con), e / 2); 39 | dphi = Math.PI / 2 - 2 * Math.atan(ts * con) - phi; 40 | phi += dphi; 41 | } 42 | 43 | return new L.LatLng(phi * d, point.x * d / r); 44 | } 45 | }; 46 | -------------------------------------------------------------------------------- /src/geo/projection/Projection.SphericalMercator.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @namespace Projection 3 | * @projection L.Projection.SphericalMercator 4 | * 5 | * Spherical Mercator projection — the most common projection for online maps, 6 | * used by almost all free and commercial tile providers. Assumes that Earth is 7 | * a sphere. Used by the `EPSG:3857` CRS. 8 | */ 9 | 10 | L.Projection.SphericalMercator = { 11 | 12 | R: 6378137, 13 | MAX_LATITUDE: 85.0511287798, 14 | 15 | project: function (latlng) { 16 | var d = Math.PI / 180, 17 | max = this.MAX_LATITUDE, 18 | lat = Math.max(Math.min(max, latlng.lat), -max), 19 | sin = Math.sin(lat * d); 20 | 21 | return new L.Point( 22 | this.R * latlng.lng * d, 23 | this.R * Math.log((1 + sin) / (1 - sin)) / 2); 24 | }, 25 | 26 | unproject: function (point) { 27 | var d = 180 / Math.PI; 28 | 29 | return new L.LatLng( 30 | (2 * Math.atan(Math.exp(point.y / this.R)) - (Math.PI / 2)) * d, 31 | point.x * d / this.R); 32 | }, 33 | 34 | bounds: (function () { 35 | var d = 6378137 * Math.PI; 36 | return L.bounds([-d, -d], [d, d]); 37 | })() 38 | }; 39 | -------------------------------------------------------------------------------- /src/geo/projection/Projection.leafdoc: -------------------------------------------------------------------------------- 1 | 2 | @class Projection 3 | 4 | An object with methods for projecting geographical coordinates of the world onto 5 | a flat surface (and back). See [Map projection](http://en.wikipedia.org/wiki/Map_projection). 6 | 7 | @property bounds: LatLngBounds 8 | The bounds where the projection is valid 9 | 10 | @method project(latlng: LatLng): Point 11 | Projects geographical coordinates into a 2D point. 12 | 13 | @method unproject(point: Point): LatLng 14 | The inverse of `project`. Projects a 2D point into a geographical location. 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/geometry/PolyUtil.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @namespace PolyUtil 3 | * Various utility functions for polygon geometries. 4 | */ 5 | 6 | L.PolyUtil = {}; 7 | 8 | /* @function clipPolygon(points: Point[], bounds: Bounds, round?: Boolean): Point[] 9 | * Clips the polygon geometry defined by the given `points` by the given bounds (using the [Sutherland-Hodgeman algorithm](https://en.wikipedia.org/wiki/Sutherland%E2%80%93Hodgman_algorithm)). 10 | * Used by Leaflet to only show polygon points that are on the screen or near, increasing 11 | * performance. Note that polygon points needs different algorithm for clipping 12 | * than polyline, so there's a seperate method for it. 13 | */ 14 | L.PolyUtil.clipPolygon = function (points, bounds, round) { 15 | var clippedPoints, 16 | edges = [1, 4, 2, 8], 17 | i, j, k, 18 | a, b, 19 | len, edge, p, 20 | lu = L.LineUtil; 21 | 22 | for (i = 0, len = points.length; i < len; i++) { 23 | points[i]._code = lu._getBitCode(points[i], bounds); 24 | } 25 | 26 | // for each edge (left, bottom, right, top) 27 | for (k = 0; k < 4; k++) { 28 | edge = edges[k]; 29 | clippedPoints = []; 30 | 31 | for (i = 0, len = points.length, j = len - 1; i < len; j = i++) { 32 | a = points[i]; 33 | b = points[j]; 34 | 35 | // if a is inside the clip window 36 | if (!(a._code & edge)) { 37 | // if b is outside the clip window (a->b goes out of screen) 38 | if (b._code & edge) { 39 | p = lu._getEdgeIntersection(b, a, edge, bounds, round); 40 | p._code = lu._getBitCode(p, bounds); 41 | clippedPoints.push(p); 42 | } 43 | clippedPoints.push(a); 44 | 45 | // else if b is inside the clip window (a->b enters the screen) 46 | } else if (!(b._code & edge)) { 47 | p = lu._getEdgeIntersection(b, a, edge, bounds, round); 48 | p._code = lu._getBitCode(p, bounds); 49 | clippedPoints.push(p); 50 | } 51 | } 52 | points = clippedPoints; 53 | } 54 | 55 | return points; 56 | }; 57 | -------------------------------------------------------------------------------- /src/geometry/Transformation.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @class Transformation 3 | * @aka L.Transformation 4 | * 5 | * Represents an affine transformation: a set of coefficients `a`, `b`, `c`, `d` 6 | * for transforming a point of a form `(x, y)` into `(a*x + b, c*y + d)` and doing 7 | * the reverse. Used by Leaflet in its projections code. 8 | * 9 | * @example 10 | * 11 | * ```js 12 | * var transformation = new L.Transformation(2, 5, -1, 10), 13 | * p = L.point(1, 2), 14 | * p2 = transformation.transform(p), // L.point(7, 8) 15 | * p3 = transformation.untransform(p2); // L.point(1, 2) 16 | * ``` 17 | */ 18 | 19 | 20 | // factory new L.Transformation(a: Number, b: Number, c: Number, d: Number) 21 | // Creates a `Transformation` object with the given coefficients. 22 | L.Transformation = function (a, b, c, d) { 23 | this._a = a; 24 | this._b = b; 25 | this._c = c; 26 | this._d = d; 27 | }; 28 | 29 | L.Transformation.prototype = { 30 | // @method transform(point: Point, scale?: Number): Point 31 | // Returns a transformed point, optionally multiplied by the given scale. 32 | // Only accepts real `L.Point` instances, not arrays. 33 | transform: function (point, scale) { // (Point, Number) -> Point 34 | return this._transform(point.clone(), scale); 35 | }, 36 | 37 | // destructive transform (faster) 38 | _transform: function (point, scale) { 39 | scale = scale || 1; 40 | point.x = scale * (this._a * point.x + this._b); 41 | point.y = scale * (this._c * point.y + this._d); 42 | return point; 43 | }, 44 | 45 | // @method untransform(point: Point, scale?: Number): Point 46 | // Returns the reverse transformation of the given point, optionally divided 47 | // by the given scale. Only accepts real `L.Point` instances, not arrays. 48 | untransform: function (point, scale) { 49 | scale = scale || 1; 50 | return new L.Point( 51 | (point.x / scale - this._b) / this._a, 52 | (point.y / scale - this._d) / this._c); 53 | } 54 | }; 55 | -------------------------------------------------------------------------------- /src/images/layers.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/layer/FeatureGroup.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @class FeatureGroup 3 | * @aka L.FeatureGroup 4 | * @inherits LayerGroup 5 | * 6 | * Extended `LayerGroup` that also has mouse events (propagated from members of the group) and a shared bindPopup method. 7 | * 8 | * @example 9 | * 10 | * ```js 11 | * L.featureGroup([marker1, marker2, polyline]) 12 | * .bindPopup('Hello world!') 13 | * .on('click', function() { alert('Clicked on a group!'); }) 14 | * .addTo(map); 15 | * ``` 16 | */ 17 | 18 | L.FeatureGroup = L.LayerGroup.extend({ 19 | 20 | addLayer: function (layer) { 21 | if (this.hasLayer(layer)) { 22 | return this; 23 | } 24 | 25 | layer.addEventParent(this); 26 | 27 | L.LayerGroup.prototype.addLayer.call(this, layer); 28 | 29 | return this.fire('layeradd', {layer: layer}); 30 | }, 31 | 32 | removeLayer: function (layer) { 33 | if (!this.hasLayer(layer)) { 34 | return this; 35 | } 36 | if (layer in this._layers) { 37 | layer = this._layers[layer]; 38 | } 39 | 40 | layer.removeEventParent(this); 41 | 42 | L.LayerGroup.prototype.removeLayer.call(this, layer); 43 | 44 | return this.fire('layerremove', {layer: layer}); 45 | }, 46 | 47 | // @method setStyle(style: Path options): this 48 | // Sets the given path options to each layer of the group that has a `setStyle` method. 49 | setStyle: function (style) { 50 | return this.invoke('setStyle', style); 51 | }, 52 | 53 | // @method bringToFront(): this 54 | // Brings the layer group to the top of all other layers 55 | bringToFront: function () { 56 | return this.invoke('bringToFront'); 57 | }, 58 | 59 | // @method bringToBack(): this 60 | // Brings the layer group to the top of all other layers 61 | bringToBack: function () { 62 | return this.invoke('bringToBack'); 63 | }, 64 | 65 | // @method getBounds(): LatLngBounds 66 | // Returns the LatLngBounds of the Feature Group (created from bounds and coordinates of its children). 67 | getBounds: function () { 68 | var bounds = new L.LatLngBounds(); 69 | 70 | for (var id in this._layers) { 71 | var layer = this._layers[id]; 72 | bounds.extend(layer.getBounds ? layer.getBounds() : layer.getLatLng()); 73 | } 74 | return bounds; 75 | } 76 | }); 77 | 78 | // @factory L.featureGroup(layers: Layer[]) 79 | // Create a feature group, optionally given an initial set of layers. 80 | L.featureGroup = function (layers) { 81 | return new L.FeatureGroup(layers); 82 | }; 83 | -------------------------------------------------------------------------------- /src/layer/marker/DivIcon.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @class DivIcon 3 | * @aka L.DivIcon 4 | * @inherits Icon 5 | * 6 | * Represents a lightweight icon for markers that uses a simple `
` 7 | * element instead of an image. Inherits from `Icon` but ignores the `iconUrl` and shadow options. 8 | * 9 | * @example 10 | * ```js 11 | * var myIcon = L.divIcon({className: 'my-div-icon'}); 12 | * // you can set .my-div-icon styles in CSS 13 | * 14 | * L.marker([50.505, 30.57], {icon: myIcon}).addTo(map); 15 | * ``` 16 | * 17 | * By default, it has a 'leaflet-div-icon' CSS class and is styled as a little white square with a shadow. 18 | */ 19 | 20 | L.DivIcon = L.Icon.extend({ 21 | options: { 22 | // @section 23 | // @aka DivIcon options 24 | iconSize: [12, 12], // also can be set through CSS 25 | 26 | // iconAnchor: (Point), 27 | // popupAnchor: (Point), 28 | 29 | // @option html: String = '' 30 | // Custom HTML code to put inside the div element, empty by default. 31 | html: false, 32 | 33 | // @option bgPos: Point = [0, 0] 34 | // Optional relative position of the background, in pixels 35 | bgPos: null, 36 | 37 | className: 'leaflet-div-icon' 38 | }, 39 | 40 | createIcon: function (oldIcon) { 41 | var div = (oldIcon && oldIcon.tagName === 'DIV') ? oldIcon : document.createElement('div'), 42 | options = this.options; 43 | 44 | div.innerHTML = options.html !== false ? options.html : ''; 45 | 46 | if (options.bgPos) { 47 | var bgPos = L.point(options.bgPos); 48 | div.style.backgroundPosition = (-bgPos.x) + 'px ' + (-bgPos.y) + 'px'; 49 | } 50 | this._setIconStyles(div, 'icon'); 51 | 52 | return div; 53 | }, 54 | 55 | createShadow: function () { 56 | return null; 57 | } 58 | }); 59 | 60 | // @factory L.divIcon(options: DivIcon options) 61 | // Creates a `DivIcon` instance with the given options. 62 | L.divIcon = function (options) { 63 | return new L.DivIcon(options); 64 | }; 65 | -------------------------------------------------------------------------------- /src/layer/marker/Icon.Default.js: -------------------------------------------------------------------------------- 1 | /* 2 | * L.Icon.Default is the blue marker icon used by default in Leaflet. 3 | */ 4 | 5 | L.Icon.Default = L.Icon.extend({ 6 | 7 | options: { 8 | iconSize: [25, 41], 9 | iconAnchor: [12, 41], 10 | popupAnchor: [1, -34], 11 | shadowSize: [41, 41] 12 | }, 13 | 14 | _getIconUrl: function (name) { 15 | var key = name + 'Url'; 16 | 17 | if (this.options[key]) { 18 | return this.options[key]; 19 | } 20 | 21 | var path = L.Icon.Default.imagePath; 22 | 23 | if (!path) { 24 | throw new Error('Couldn\'t autodetect L.Icon.Default.imagePath, set it manually.'); 25 | } 26 | 27 | return path + '/marker-' + name + (L.Browser.retina && name === 'icon' ? '-2x' : '') + '.png'; 28 | } 29 | }); 30 | 31 | L.Icon.Default.imagePath = (function () { 32 | var scripts = document.getElementsByTagName('script'), 33 | leafletRe = /[\/^]leaflet[\-\._]?([\w\-\._]*)\.js\??/; 34 | 35 | var i, len, src, path; 36 | 37 | for (i = 0, len = scripts.length; i < len; i++) { 38 | src = scripts[i].src || ''; 39 | 40 | if (src.match(leafletRe)) { 41 | path = src.split(leafletRe)[0]; 42 | return (path ? path + '/' : '') + 'images'; 43 | } 44 | } 45 | }()); 46 | -------------------------------------------------------------------------------- /src/layer/marker/Marker.Drag.js: -------------------------------------------------------------------------------- 1 | /* 2 | * L.Handler.MarkerDrag is used internally by L.Marker to make the markers draggable. 3 | */ 4 | 5 | 6 | /* @namespace Marker 7 | * @section Interaction handlers 8 | * 9 | * Interaction handlers are properties of a marker instance that allow you to control interaction behavior in runtime, enabling or disabling certain features such as dragging (see `Handler` methods). Example: 10 | * 11 | * ```js 12 | * marker.dragging.disable(); 13 | * ``` 14 | * 15 | * @property dragging: Handler 16 | * Marker dragging handler (by both mouse and touch). 17 | */ 18 | 19 | L.Handler.MarkerDrag = L.Handler.extend({ 20 | initialize: function (marker) { 21 | this._marker = marker; 22 | }, 23 | 24 | addHooks: function () { 25 | var icon = this._marker._icon; 26 | 27 | if (!this._draggable) { 28 | this._draggable = new L.Draggable(icon, icon, true); 29 | } 30 | 31 | this._draggable.on({ 32 | dragstart: this._onDragStart, 33 | drag: this._onDrag, 34 | dragend: this._onDragEnd 35 | }, this).enable(); 36 | 37 | L.DomUtil.addClass(icon, 'leaflet-marker-draggable'); 38 | }, 39 | 40 | removeHooks: function () { 41 | this._draggable.off({ 42 | dragstart: this._onDragStart, 43 | drag: this._onDrag, 44 | dragend: this._onDragEnd 45 | }, this).disable(); 46 | 47 | if (this._marker._icon) { 48 | L.DomUtil.removeClass(this._marker._icon, 'leaflet-marker-draggable'); 49 | } 50 | }, 51 | 52 | moved: function () { 53 | return this._draggable && this._draggable._moved; 54 | }, 55 | 56 | _onDragStart: function () { 57 | // @section Dragging events 58 | // @event dragstart: Event 59 | // Fired when the user starts dragging the marker. 60 | 61 | // @event movestart: Event 62 | // Fired when the marker starts moving (because of dragging). 63 | this._marker 64 | .closePopup() 65 | .fire('movestart') 66 | .fire('dragstart'); 67 | }, 68 | 69 | _onDrag: function (e) { 70 | var marker = this._marker, 71 | shadow = marker._shadow, 72 | iconPos = L.DomUtil.getPosition(marker._icon), 73 | latlng = marker._map.layerPointToLatLng(iconPos); 74 | 75 | // update shadow position 76 | if (shadow) { 77 | L.DomUtil.setPosition(shadow, iconPos); 78 | } 79 | 80 | marker._latlng = latlng; 81 | e.latlng = latlng; 82 | 83 | // @event drag: Event 84 | // Fired repeatedly while the user drags the marker. 85 | marker 86 | .fire('move', e) 87 | .fire('drag', e); 88 | }, 89 | 90 | _onDragEnd: function (e) { 91 | // @event dragend: DragEndEvent 92 | // Fired when the user stops dragging the marker. 93 | 94 | // @event moveend: Event 95 | // Fired when the marker stops moving (because of dragging). 96 | this._marker 97 | .fire('moveend') 98 | .fire('dragend', e); 99 | } 100 | }); 101 | -------------------------------------------------------------------------------- /src/layer/marker/Marker.Popup.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Popup extension to L.Marker, adding popup-related methods. 3 | */ 4 | 5 | L.Marker.include({ 6 | _getPopupAnchor: function () { 7 | return this.options.icon.options.popupAnchor || [0, 0]; 8 | } 9 | }); 10 | -------------------------------------------------------------------------------- /src/layer/vector/Circle.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @class Circle 3 | * @aka L.Circle 4 | * @inherits CircleMarker 5 | * 6 | * A class for drawing circle overlays on a map. Extends `CircleMarker`. 7 | * 8 | * It's an approximation and starts to diverge from a real circle closer to poles (due to projection distortion). 9 | * 10 | * @example 11 | * 12 | * ```js 13 | * L.circle([50.5, 30.5], 200).addTo(map); 14 | * ``` 15 | */ 16 | 17 | L.Circle = L.CircleMarker.extend({ 18 | 19 | initialize: function (latlng, options, legacyOptions) { 20 | if (typeof options === 'number') { 21 | // Backwards compatibility with 0.7.x factory (latlng, radius, options?) 22 | options = L.extend({}, legacyOptions, {radius: options}); 23 | } 24 | L.setOptions(this, options); 25 | this._latlng = L.latLng(latlng); 26 | 27 | if (isNaN(this.options.radius)) { throw new Error('Circle radius cannot be NaN'); } 28 | 29 | // @section 30 | // @aka Circle options 31 | // @option radius: Number; Radius of the circle, in meters. 32 | this._mRadius = this.options.radius; 33 | }, 34 | 35 | // @method setRadius(radius: Number): this 36 | // Sets the radius of a circle. Units are in meters. 37 | setRadius: function (radius) { 38 | this._mRadius = radius; 39 | return this.redraw(); 40 | }, 41 | 42 | // @method getRadius(): Number 43 | // Returns the current radius of a circle. Units are in meters. 44 | getRadius: function () { 45 | return this._mRadius; 46 | }, 47 | 48 | // @method getBounds(): LatLngBounds 49 | // Returns the `LatLngBounds` of the path. 50 | getBounds: function () { 51 | var half = [this._radius, this._radiusY || this._radius]; 52 | 53 | return new L.LatLngBounds( 54 | this._map.layerPointToLatLng(this._point.subtract(half)), 55 | this._map.layerPointToLatLng(this._point.add(half))); 56 | }, 57 | 58 | setStyle: L.Path.prototype.setStyle, 59 | 60 | _project: function () { 61 | 62 | var lng = this._latlng.lng, 63 | lat = this._latlng.lat, 64 | map = this._map, 65 | crs = map.options.crs; 66 | 67 | if (crs.distance === L.CRS.Earth.distance) { 68 | var d = Math.PI / 180, 69 | latR = (this._mRadius / L.CRS.Earth.R) / d, 70 | top = map.project([lat + latR, lng]), 71 | bottom = map.project([lat - latR, lng]), 72 | p = top.add(bottom).divideBy(2), 73 | lat2 = map.unproject(p).lat, 74 | lngR = Math.acos((Math.cos(latR * d) - Math.sin(lat * d) * Math.sin(lat2 * d)) / 75 | (Math.cos(lat * d) * Math.cos(lat2 * d))) / d; 76 | 77 | if (isNaN(lngR) || lngR === 0) { 78 | lngR = latR / Math.cos(Math.PI / 180 * lat); // Fallback for edge case, #2425 79 | } 80 | 81 | this._point = p.subtract(map.getPixelOrigin()); 82 | this._radius = isNaN(lngR) ? 0 : Math.max(Math.round(p.x - map.project([lat2, lng - lngR]).x), 1); 83 | this._radiusY = Math.max(Math.round(p.y - top.y), 1); 84 | 85 | } else { 86 | var latlng2 = crs.unproject(crs.project(this._latlng).subtract([this._mRadius, 0])); 87 | 88 | this._point = map.latLngToLayerPoint(this._latlng); 89 | this._radius = this._point.x - map.latLngToLayerPoint(latlng2).x; 90 | } 91 | 92 | this._updateBounds(); 93 | } 94 | }); 95 | 96 | // @factory L.circle(latlng: LatLng, options?: Circle options) 97 | // Instantiates a circle object given a geographical point, and an options object 98 | // which contains the circle radius. 99 | // @alternative 100 | // @factory L.circle(latlng: LatLng, radius: Number, options?: Circle options) 101 | // Obsolete way of instantiating a circle, for compatibility with 0.7.x code. 102 | // Do not use in new applications or plugins. 103 | L.circle = function (latlng, options, legacyOptions) { 104 | return new L.Circle(latlng, options, legacyOptions); 105 | }; 106 | -------------------------------------------------------------------------------- /src/layer/vector/CircleMarker.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @class CircleMarker 3 | * @aka L.CircleMarker 4 | * @inherits Path 5 | * 6 | * A circle of a fixed size with radius specified in pixels. Extends `Path`. 7 | */ 8 | 9 | L.CircleMarker = L.Path.extend({ 10 | 11 | // @section 12 | // @aka CircleMarker options 13 | options: { 14 | fill: true, 15 | 16 | // @option radius: Number = 10 17 | // Radius of the circle marker, in pixels 18 | radius: 10 19 | }, 20 | 21 | initialize: function (latlng, options) { 22 | L.setOptions(this, options); 23 | this._latlng = L.latLng(latlng); 24 | this._radius = this.options.radius; 25 | }, 26 | 27 | // @method setLatLng(latLng: LatLng): this 28 | // Sets the position of a circle marker to a new location. 29 | setLatLng: function (latlng) { 30 | this._latlng = L.latLng(latlng); 31 | this.redraw(); 32 | return this.fire('move', {latlng: this._latlng}); 33 | }, 34 | 35 | // @method getLatLng(): LatLng 36 | // Returns the current geographical position of the circle marker 37 | getLatLng: function () { 38 | return this._latlng; 39 | }, 40 | 41 | // @method setRadius(radius: Number): this 42 | // Sets the radius of a circle marker. Units are in pixels. 43 | setRadius: function (radius) { 44 | this.options.radius = this._radius = radius; 45 | return this.redraw(); 46 | }, 47 | 48 | // @method getRadius(): Number 49 | // Returns the current radius of the circle 50 | getRadius: function () { 51 | return this._radius; 52 | }, 53 | 54 | setStyle : function (options) { 55 | var radius = options && options.radius || this._radius; 56 | L.Path.prototype.setStyle.call(this, options); 57 | this.setRadius(radius); 58 | return this; 59 | }, 60 | 61 | _project: function () { 62 | this._point = this._map.latLngToLayerPoint(this._latlng); 63 | this._updateBounds(); 64 | }, 65 | 66 | _updateBounds: function () { 67 | var r = this._radius, 68 | r2 = this._radiusY || r, 69 | w = this._clickTolerance(), 70 | p = [r + w, r2 + w]; 71 | this._pxBounds = new L.Bounds(this._point.subtract(p), this._point.add(p)); 72 | }, 73 | 74 | _update: function () { 75 | if (this._map) { 76 | this._updatePath(); 77 | } 78 | }, 79 | 80 | _updatePath: function () { 81 | this._renderer._updateCircle(this); 82 | }, 83 | 84 | _empty: function () { 85 | return this._radius && !this._renderer._bounds.intersects(this._pxBounds); 86 | } 87 | }); 88 | 89 | 90 | // @factory L.circleMarker(latlng: LatLng, options?: CircleMarker options) 91 | // Instantiates a circle marker object given a geographical point, and an optional options object. 92 | L.circleMarker = function (latlng, options) { 93 | return new L.CircleMarker(latlng, options); 94 | }; 95 | -------------------------------------------------------------------------------- /src/layer/vector/Rectangle.js: -------------------------------------------------------------------------------- 1 | /* 2 | * L.Rectangle extends Polygon and creates a rectangle when passed a LatLngBounds object. 3 | */ 4 | 5 | /* 6 | * @class Rectangle 7 | * @aka L.Retangle 8 | * @inherits Polygon 9 | * 10 | * A class for drawing rectangle overlays on a map. Extends `Polygon`. 11 | * 12 | * @example 13 | * 14 | * ```js 15 | * // define rectangle geographical bounds 16 | * var bounds = [[54.559322, -5.767822], [56.1210604, -3.021240]]; 17 | * 18 | * // create an orange rectangle 19 | * L.rectangle(bounds, {color: "#ff7800", weight: 1}).addTo(map); 20 | * 21 | * // zoom the map to the rectangle bounds 22 | * map.fitBounds(bounds); 23 | * ``` 24 | * 25 | */ 26 | 27 | 28 | L.Rectangle = L.Polygon.extend({ 29 | initialize: function (latLngBounds, options) { 30 | L.Polygon.prototype.initialize.call(this, this._boundsToLatLngs(latLngBounds), options); 31 | }, 32 | 33 | // @method setBounds(latLngBounds: LatLngBounds): this 34 | // Redraws the rectangle with the passed bounds. 35 | setBounds: function (latLngBounds) { 36 | return this.setLatLngs(this._boundsToLatLngs(latLngBounds)); 37 | }, 38 | 39 | _boundsToLatLngs: function (latLngBounds) { 40 | latLngBounds = L.latLngBounds(latLngBounds); 41 | return [ 42 | latLngBounds.getSouthWest(), 43 | latLngBounds.getNorthWest(), 44 | latLngBounds.getNorthEast(), 45 | latLngBounds.getSouthEast() 46 | ]; 47 | } 48 | }); 49 | 50 | 51 | // @factory L.rectangle(latLngBounds: LatLngBounds, options?: Polyline options) 52 | L.rectangle = function (latLngBounds, options) { 53 | return new L.Rectangle(latLngBounds, options); 54 | }; 55 | -------------------------------------------------------------------------------- /src/map/anim/Map.FlyTo.js: -------------------------------------------------------------------------------- 1 | // @namespace Map 2 | // @section Methods for modifying map state 3 | L.Map.include({ 4 | 5 | // @method flyTo(latlng: LatLng, zoom?: Number, options?: Zoom/Pan options): this 6 | // Sets the view of the map (geographical center and zoom) performing a smooth 7 | // pan-zoom animation. 8 | flyTo: function (targetCenter, targetZoom, options) { 9 | 10 | options = options || {}; 11 | if (options.animate === false || !L.Browser.any3d) { 12 | return this.setView(targetCenter, targetZoom, options); 13 | } 14 | 15 | this._stop(); 16 | 17 | var from = this.project(this.getCenter()), 18 | to = this.project(targetCenter), 19 | size = this.getSize(), 20 | startZoom = this._zoom; 21 | 22 | targetCenter = L.latLng(targetCenter); 23 | targetZoom = targetZoom === undefined ? startZoom : targetZoom; 24 | 25 | var w0 = Math.max(size.x, size.y), 26 | w1 = w0 * this.getZoomScale(startZoom, targetZoom), 27 | u1 = (to.distanceTo(from)) || 1, 28 | rho = 1.42, 29 | rho2 = rho * rho; 30 | 31 | function r(i) { 32 | var s1 = i ? -1 : 1, 33 | s2 = i ? w1 : w0, 34 | t1 = w1 * w1 - w0 * w0 + s1 * rho2 * rho2 * u1 * u1, 35 | b1 = 2 * s2 * rho2 * u1, 36 | b = t1 / b1, 37 | sq = Math.sqrt(b * b + 1) - b; 38 | 39 | // workaround for floating point precision bug when sq = 0, log = -Infinite, 40 | // thus triggering an infinite loop in flyTo 41 | var log = sq < 0.000000001 ? -18 : Math.log(sq); 42 | 43 | return log; 44 | } 45 | 46 | function sinh(n) { return (Math.exp(n) - Math.exp(-n)) / 2; } 47 | function cosh(n) { return (Math.exp(n) + Math.exp(-n)) / 2; } 48 | function tanh(n) { return sinh(n) / cosh(n); } 49 | 50 | var r0 = r(0); 51 | 52 | function w(s) { return w0 * (cosh(r0) / cosh(r0 + rho * s)); } 53 | function u(s) { return w0 * (cosh(r0) * tanh(r0 + rho * s) - sinh(r0)) / rho2; } 54 | 55 | function easeOut(t) { return 1 - Math.pow(1 - t, 1.5); } 56 | 57 | var start = Date.now(), 58 | S = (r(1) - r0) / rho, 59 | duration = options.duration ? 1000 * options.duration : 1000 * S * 0.8; 60 | 61 | function frame() { 62 | var t = (Date.now() - start) / duration, 63 | s = easeOut(t) * S; 64 | 65 | if (t <= 1) { 66 | this._flyToFrame = L.Util.requestAnimFrame(frame, this); 67 | 68 | this._move( 69 | this.unproject(from.add(to.subtract(from).multiplyBy(u(s) / u1)), startZoom), 70 | this.getScaleZoom(w0 / w(s), startZoom), 71 | {flyTo: true}); 72 | 73 | } else { 74 | this 75 | ._move(targetCenter, targetZoom) 76 | ._moveEnd(true); 77 | } 78 | } 79 | 80 | this._moveStart(true); 81 | 82 | frame.call(this); 83 | return this; 84 | }, 85 | 86 | // @method flyToBounds(bounds: LatLngBounds, options?: fitBounds options): this 87 | // Sets the view of the map with a smooth animation like [`flyTo`](#map-flyto), 88 | // but takes a bounds parameter like [`fitBounds`](#map-fitbounds). 89 | flyToBounds: function (bounds, options) { 90 | var target = this._getBoundsCenterZoom(bounds, options); 91 | return this.flyTo(target.center, target.zoom, options); 92 | } 93 | }); 94 | -------------------------------------------------------------------------------- /src/map/anim/Map.PanAnimation.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Extends L.Map to handle panning animations. 3 | */ 4 | 5 | L.Map.include({ 6 | 7 | setView: function (center, zoom, options) { 8 | 9 | zoom = zoom === undefined ? this._zoom : this._limitZoom(zoom); 10 | center = this._limitCenter(L.latLng(center), zoom, this.options.maxBounds); 11 | options = options || {}; 12 | 13 | this._stop(); 14 | 15 | if (this._loaded && !options.reset && options !== true) { 16 | 17 | if (options.animate !== undefined) { 18 | options.zoom = L.extend({animate: options.animate}, options.zoom); 19 | options.pan = L.extend({animate: options.animate, duration: options.duration}, options.pan); 20 | } 21 | 22 | // try animating pan or zoom 23 | var moved = (this._zoom !== zoom) ? 24 | this._tryAnimatedZoom && this._tryAnimatedZoom(center, zoom, options.zoom) : 25 | this._tryAnimatedPan(center, options.pan); 26 | 27 | if (moved) { 28 | // prevent resize handler call, the view will refresh after animation anyway 29 | clearTimeout(this._sizeTimer); 30 | return this; 31 | } 32 | } 33 | 34 | // animation didn't start, just reset the map view 35 | this._resetView(center, zoom); 36 | 37 | return this; 38 | }, 39 | 40 | panBy: function (offset, options) { 41 | offset = L.point(offset).round(); 42 | options = options || {}; 43 | 44 | if (!offset.x && !offset.y) { 45 | return this.fire('moveend'); 46 | } 47 | // If we pan too far, Chrome gets issues with tiles 48 | // and makes them disappear or appear in the wrong place (slightly offset) #2602 49 | if (options.animate !== true && !this.getSize().contains(offset)) { 50 | this._resetView(this.unproject(this.project(this.getCenter()).add(offset)), this.getZoom()); 51 | return this; 52 | } 53 | 54 | if (!this._panAnim) { 55 | this._panAnim = new L.PosAnimation(); 56 | 57 | this._panAnim.on({ 58 | 'step': this._onPanTransitionStep, 59 | 'end': this._onPanTransitionEnd 60 | }, this); 61 | } 62 | 63 | // don't fire movestart if animating inertia 64 | if (!options.noMoveStart) { 65 | this.fire('movestart'); 66 | } 67 | 68 | // animate pan unless animate: false specified 69 | if (options.animate !== false) { 70 | L.DomUtil.addClass(this._mapPane, 'leaflet-pan-anim'); 71 | 72 | var newPos = this._getMapPanePos().subtract(offset).round(); 73 | this._panAnim.run(this._mapPane, newPos, options.duration || 0.25, options.easeLinearity); 74 | } else { 75 | this._rawPanBy(offset); 76 | this.fire('move').fire('moveend'); 77 | } 78 | 79 | return this; 80 | }, 81 | 82 | _onPanTransitionStep: function () { 83 | this.fire('move'); 84 | }, 85 | 86 | _onPanTransitionEnd: function () { 87 | L.DomUtil.removeClass(this._mapPane, 'leaflet-pan-anim'); 88 | this.fire('moveend'); 89 | }, 90 | 91 | _tryAnimatedPan: function (center, options) { 92 | // difference between the new and current centers in pixels 93 | var offset = this._getCenterOffset(center)._floor(); 94 | 95 | // don't animate too far unless animate: true specified in options 96 | if ((options && options.animate) !== true && !this.getSize().contains(offset)) { return false; } 97 | 98 | this.panBy(offset, options); 99 | 100 | return true; 101 | } 102 | }); 103 | -------------------------------------------------------------------------------- /src/map/ext/Map.Geolocation.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Provides L.Map with convenient shortcuts for using browser geolocation features. 3 | */ 4 | 5 | // @namespace Map 6 | 7 | L.Map.include({ 8 | // @section Geolocation methods 9 | _defaultLocateOptions: { 10 | timeout: 10000, 11 | watch: false 12 | // setView: false 13 | // maxZoom: 14 | // maximumAge: 0 15 | // enableHighAccuracy: false 16 | }, 17 | 18 | // @method locate(options?: Locate options): this 19 | // Tries to locate the user using the Geolocation API, firing a `locationfound` 20 | // event with location data on success or a `locationerror` event on failure, 21 | // and optionally sets the map view to the user's location with respect to 22 | // detection accuracy (or to the world view if geolocation failed). 23 | // See `Locate options` for more details. 24 | locate: function (options) { 25 | 26 | options = this._locateOptions = L.extend({}, this._defaultLocateOptions, options); 27 | 28 | if (!('geolocation' in navigator)) { 29 | this._handleGeolocationError({ 30 | code: 0, 31 | message: 'Geolocation not supported.' 32 | }); 33 | return this; 34 | } 35 | 36 | var onResponse = L.bind(this._handleGeolocationResponse, this), 37 | onError = L.bind(this._handleGeolocationError, this); 38 | 39 | if (options.watch) { 40 | this._locationWatchId = 41 | navigator.geolocation.watchPosition(onResponse, onError, options); 42 | } else { 43 | navigator.geolocation.getCurrentPosition(onResponse, onError, options); 44 | } 45 | return this; 46 | }, 47 | 48 | // @method stopLocate(): this 49 | // Stops watching location previously initiated by `map.locate({watch: true})` 50 | // and aborts resetting the map view if map.locate was called with 51 | // `{setView: true}`. 52 | stopLocate: function () { 53 | if (navigator.geolocation && navigator.geolocation.clearWatch) { 54 | navigator.geolocation.clearWatch(this._locationWatchId); 55 | } 56 | if (this._locateOptions) { 57 | this._locateOptions.setView = false; 58 | } 59 | return this; 60 | }, 61 | 62 | _handleGeolocationError: function (error) { 63 | var c = error.code, 64 | message = error.message || 65 | (c === 1 ? 'permission denied' : 66 | (c === 2 ? 'position unavailable' : 'timeout')); 67 | 68 | if (this._locateOptions.setView && !this._loaded) { 69 | this.fitWorld(); 70 | } 71 | 72 | // @section Location events 73 | // @event locationerror: ErrorEvent 74 | // Fired when geolocation (using the [`locate`](#map-locate) method) failed. 75 | this.fire('locationerror', { 76 | code: c, 77 | message: 'Geolocation error: ' + message + '.' 78 | }); 79 | }, 80 | 81 | _handleGeolocationResponse: function (pos) { 82 | var lat = pos.coords.latitude, 83 | lng = pos.coords.longitude, 84 | latlng = new L.LatLng(lat, lng), 85 | bounds = latlng.toBounds(pos.coords.accuracy), 86 | options = this._locateOptions; 87 | 88 | if (options.setView) { 89 | var zoom = this.getBoundsZoom(bounds); 90 | this.setView(latlng, options.maxZoom ? Math.min(zoom, options.maxZoom) : zoom); 91 | } 92 | 93 | var data = { 94 | latlng: latlng, 95 | bounds: bounds, 96 | timestamp: pos.timestamp 97 | }; 98 | 99 | for (var i in pos.coords) { 100 | if (typeof pos.coords[i] === 'number') { 101 | data[i] = pos.coords[i]; 102 | } 103 | } 104 | 105 | // @event locationfound: LocationEvent 106 | // Fired when geolocation (using the [`locate`](#map-locate) method) 107 | // went successfully. 108 | this.fire('locationfound', data); 109 | } 110 | }); 111 | -------------------------------------------------------------------------------- /src/map/handler/Map.BoxZoom.js: -------------------------------------------------------------------------------- 1 | /* 2 | * L.Handler.BoxZoom is used to add shift-drag zoom interaction to the map 3 | * (zoom to a selected bounding box), enabled by default. 4 | */ 5 | 6 | // @namespace Map 7 | // @section Interaction Options 8 | L.Map.mergeOptions({ 9 | // @option boxZoom: Boolean = true 10 | // Whether the map can be zoomed to a rectangular area specified by 11 | // dragging the mouse while pressing the shift key. 12 | boxZoom: true 13 | }); 14 | 15 | L.Map.BoxZoom = L.Handler.extend({ 16 | initialize: function (map) { 17 | this._map = map; 18 | this._container = map._container; 19 | this._pane = map._panes.overlayPane; 20 | }, 21 | 22 | addHooks: function () { 23 | L.DomEvent.on(this._container, 'mousedown', this._onMouseDown, this); 24 | }, 25 | 26 | removeHooks: function () { 27 | L.DomEvent.off(this._container, 'mousedown', this._onMouseDown, this); 28 | }, 29 | 30 | moved: function () { 31 | return this._moved; 32 | }, 33 | 34 | _resetState: function () { 35 | this._moved = false; 36 | }, 37 | 38 | _onMouseDown: function (e) { 39 | if (!e.shiftKey || ((e.which !== 1) && (e.button !== 1))) { return false; } 40 | 41 | this._resetState(); 42 | 43 | L.DomUtil.disableTextSelection(); 44 | L.DomUtil.disableImageDrag(); 45 | 46 | this._startPoint = this._map.mouseEventToContainerPoint(e); 47 | 48 | L.DomEvent.on(document, { 49 | contextmenu: L.DomEvent.stop, 50 | mousemove: this._onMouseMove, 51 | mouseup: this._onMouseUp, 52 | keydown: this._onKeyDown 53 | }, this); 54 | }, 55 | 56 | _onMouseMove: function (e) { 57 | if (!this._moved) { 58 | this._moved = true; 59 | 60 | this._box = L.DomUtil.create('div', 'leaflet-zoom-box', this._container); 61 | L.DomUtil.addClass(this._container, 'leaflet-crosshair'); 62 | 63 | this._map.fire('boxzoomstart'); 64 | } 65 | 66 | this._point = this._map.mouseEventToContainerPoint(e); 67 | 68 | var bounds = new L.Bounds(this._point, this._startPoint), 69 | size = bounds.getSize(); 70 | 71 | L.DomUtil.setPosition(this._box, bounds.min); 72 | 73 | this._box.style.width = size.x + 'px'; 74 | this._box.style.height = size.y + 'px'; 75 | }, 76 | 77 | _finish: function () { 78 | if (this._moved) { 79 | L.DomUtil.remove(this._box); 80 | L.DomUtil.removeClass(this._container, 'leaflet-crosshair'); 81 | } 82 | 83 | L.DomUtil.enableTextSelection(); 84 | L.DomUtil.enableImageDrag(); 85 | 86 | L.DomEvent.off(document, { 87 | contextmenu: L.DomEvent.stop, 88 | mousemove: this._onMouseMove, 89 | mouseup: this._onMouseUp, 90 | keydown: this._onKeyDown 91 | }, this); 92 | }, 93 | 94 | _onMouseUp: function (e) { 95 | if ((e.which !== 1) && (e.button !== 1)) { return; } 96 | 97 | this._finish(); 98 | 99 | if (!this._moved) { return; } 100 | // Postpone to next JS tick so internal click event handling 101 | // still see it as "moved". 102 | setTimeout(L.bind(this._resetState, this), 0); 103 | 104 | var bounds = new L.LatLngBounds( 105 | this._map.containerPointToLatLng(this._startPoint), 106 | this._map.containerPointToLatLng(this._point)); 107 | 108 | this._map 109 | .fitBounds(bounds) 110 | .fire('boxzoomend', {boxZoomBounds: bounds}); 111 | }, 112 | 113 | _onKeyDown: function (e) { 114 | if (e.keyCode === 27) { 115 | this._finish(); 116 | } 117 | } 118 | }); 119 | 120 | // @section Handlers 121 | // @property boxZoom: Handler 122 | // Box (shift-drag with mouse) zoom handler. 123 | L.Map.addInitHook('addHandler', 'boxZoom', L.Map.BoxZoom); 124 | -------------------------------------------------------------------------------- /src/map/handler/Map.DoubleClickZoom.js: -------------------------------------------------------------------------------- 1 | /* 2 | * L.Handler.DoubleClickZoom is used to handle double-click zoom on the map, enabled by default. 3 | */ 4 | 5 | // @namespace Map 6 | // @section Interaction Options 7 | 8 | L.Map.mergeOptions({ 9 | // @option doubleClickZoom: Boolean|String = true 10 | // Whether the map can be zoomed in by double clicking on it and 11 | // zoomed out by double clicking while holding shift. If passed 12 | // `'center'`, double-click zoom will zoom to the center of the 13 | // view regardless of where the mouse was. 14 | doubleClickZoom: true 15 | }); 16 | 17 | L.Map.DoubleClickZoom = L.Handler.extend({ 18 | addHooks: function () { 19 | this._map.on('dblclick', this._onDoubleClick, this); 20 | }, 21 | 22 | removeHooks: function () { 23 | this._map.off('dblclick', this._onDoubleClick, this); 24 | }, 25 | 26 | _onDoubleClick: function (e) { 27 | var map = this._map, 28 | oldZoom = map.getZoom(), 29 | delta = map.options.zoomDelta, 30 | zoom = e.originalEvent.shiftKey ? oldZoom - delta : oldZoom + delta; 31 | 32 | if (map.options.doubleClickZoom === 'center') { 33 | map.setZoom(zoom); 34 | } else { 35 | map.setZoomAround(e.containerPoint, zoom); 36 | } 37 | } 38 | }); 39 | 40 | // @section Handlers 41 | // 42 | // Map properties include interaction handlers that allow you to control 43 | // interaction behavior in runtime, enabling or disabling certain features such 44 | // as dragging or touch zoom (see `Handler` methods). For example: 45 | // 46 | // ```js 47 | // map.doubleClickZoom.disable(); 48 | // ``` 49 | // 50 | // @property doubleClickZoom: Handler 51 | // Double click zoom handler. 52 | L.Map.addInitHook('addHandler', 'doubleClickZoom', L.Map.DoubleClickZoom); 53 | -------------------------------------------------------------------------------- /src/map/handler/Map.ScrollWheelZoom.js: -------------------------------------------------------------------------------- 1 | /* 2 | * L.Handler.ScrollWheelZoom is used by L.Map to enable mouse scroll wheel zoom on the map. 3 | */ 4 | 5 | // @namespace Map 6 | // @section Interaction Options 7 | L.Map.mergeOptions({ 8 | // @section Mousewheel options 9 | // @option scrollWheelZoom: Boolean|String = true 10 | // Whether the map can be zoomed by using the mouse wheel. If passed `'center'`, 11 | // it will zoom to the center of the view regardless of where the mouse was. 12 | scrollWheelZoom: true, 13 | 14 | // @option wheelDebounceTime: Number = 40 15 | // Limits the rate at which a wheel can fire (in milliseconds). By default 16 | // user can't zoom via wheel more often than once per 40 ms. 17 | wheelDebounceTime: 40, 18 | 19 | // @option wheelPxPerZoomLevel: Number = 50 20 | // How many scroll pixels (as reported by [L.DomEvent.getWheelDelta](#domevent-getwheeldelta)) 21 | // mean a change of one full zoom level. Smaller values will make wheel-zooming 22 | // faster (and vice versa). 23 | wheelPxPerZoomLevel: 50 24 | }); 25 | 26 | L.Map.ScrollWheelZoom = L.Handler.extend({ 27 | addHooks: function () { 28 | L.DomEvent.on(this._map._container, 'mousewheel', this._onWheelScroll, this); 29 | 30 | this._delta = 0; 31 | }, 32 | 33 | removeHooks: function () { 34 | L.DomEvent.off(this._map._container, 'mousewheel', this._onWheelScroll, this); 35 | }, 36 | 37 | _onWheelScroll: function (e) { 38 | var delta = L.DomEvent.getWheelDelta(e); 39 | 40 | var debounce = this._map.options.wheelDebounceTime; 41 | 42 | this._delta += delta; 43 | this._lastMousePos = this._map.mouseEventToContainerPoint(e); 44 | 45 | if (!this._startTime) { 46 | this._startTime = +new Date(); 47 | } 48 | 49 | var left = Math.max(debounce - (+new Date() - this._startTime), 0); 50 | 51 | clearTimeout(this._timer); 52 | this._timer = setTimeout(L.bind(this._performZoom, this), left); 53 | 54 | L.DomEvent.stop(e); 55 | }, 56 | 57 | _performZoom: function () { 58 | var map = this._map, 59 | zoom = map.getZoom(), 60 | snap = this._map.options.zoomSnap || 0; 61 | 62 | map._stop(); // stop panning and fly animations if any 63 | 64 | // map the delta with a sigmoid function to -4..4 range leaning on -1..1 65 | var d2 = this._delta / (this._map.options.wheelPxPerZoomLevel * 4), 66 | d3 = 4 * Math.log(2 / (1 + Math.exp(-Math.abs(d2)))) / Math.LN2, 67 | d4 = snap ? Math.ceil(d3 / snap) * snap : d3, 68 | delta = map._limitZoom(zoom + (this._delta > 0 ? d4 : -d4)) - zoom; 69 | 70 | this._delta = 0; 71 | this._startTime = null; 72 | 73 | if (!delta) { return; } 74 | 75 | if (map.options.scrollWheelZoom === 'center') { 76 | map.setZoom(zoom + delta); 77 | } else { 78 | map.setZoomAround(this._lastMousePos, zoom + delta); 79 | } 80 | } 81 | }); 82 | 83 | // @section Handlers 84 | // @property scrollWheelZoom: Handler 85 | // Scroll wheel zoom handler. 86 | L.Map.addInitHook('addHandler', 'scrollWheelZoom', L.Map.ScrollWheelZoom); 87 | --------------------------------------------------------------------------------