Merge branch 'master' into gh-pages-master

This commit is contained in:
mourner 2012-06-21 15:04:19 +03:00
commit 8e4ed9b819
88 changed files with 4435 additions and 1985 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
node_modules

View File

@ -5,19 +5,104 @@ Leaflet Changelog
## 0.4 (master)
An in-progress version being developed on the master branch.
### Notable new features
* Added configurable **panning inertia** - after a quick pan, the map slows down in the same direction.
* Added **polyline and polygon editing**. [#174](https://github.com/CloudMade/Leaflet/issues/174)
* Added an unobtrusive **scale control**.
* Added `DivIcon` class that easily allows you to create lightweight div-based markers.
* Added `Rectangle` vector layer (by [@JasonSanford](https://github.com/JasonSanford)). [#504](https://github.com/CloudMade/Leaflet/pull/504)
### Improvements
* Added `setPosition` and `getPosition` to all controls, as well as ability to pass certain position as an option when creating a control.
#### Usability improvements
* Added smooth **zoom animation of markers, vector layers and popups** (by [@danzel](https://github.com/danzel)). [#740](https://github.com/CloudMade/Leaflet/pull/740)
* Improved zooming so that you don't get a blank map when you zoom in or out twice quickly (by [@danzel](https://github.com/danzel)). [#7](https://github.com/CloudMade/Leaflet/issues/7) [#729](https://github.com/CloudMade/Leaflet/pull/729)
* Drag-panning now works even when there are markers in the starting point (helps on maps with lots of markers). [#506](https://github.com/CloudMade/Leaflet/issues/506)
* Improved panning performance even more (there are no wasted frames now).
* Replaced box-shadow with border on controls for mobile devices to improve performance.
* Slightly improved default popup styling.
* Added `TileLayer` `detectRetina` option (`false` by default) that makes tiles show in a higher resolution on iOS retina displays (by [Mithgol](https://github.com/Mithgol)). [#586](https://github.com/CloudMade/Leaflet/pull/586)
#### Breaking API changes
* Converted `Icon` properties (like `iconUrl`) to options, changed constructor signature to `Icon(options)`.
* Moved default marker icon options to `L.Icon.Default` class (which extends from `L.Icon`).
* Improved `TileLayer` constructor to interpolate URL template values from options (removed third `urlParams` argument).
* Replaced ugly control position constants (e.g. L.Control.Position.TOP_LEFT) with light strings ('topleft', 'bottomright', etc.)
* Removed `Map` `locateAndSetView` method (use `locate` with `setView: true` option)
* Changed popup `minWidth` and `maxWidth` options to be applied to content element, not the whole popup.
* Moved `prefix` argument to `options` in `Control.Attribution` constructor.
* Renamed `L.VERSION` to `L.version`.
#### Other API improvements
* Added `Icon` `className` option to assign a custom class to an icon.
* Added `Icon` `shadowOffset` option to set the position of shadow relative to the icon.
* Made all `Icon` options except `iconUrl` optional (if not specified, they'll be chosen automatically or implemented using CSS). Anchor is centered by default (if size is specified), and otherwise can be set through CSS using negative margins.
* Moved all default marker icon options from `L.Icon` to `L.Icon.Default`.
* Added `originalEvent` property to `MouseEvent` (by [@k4](https://github.com/k4)). [#521](https://github.com/CloudMade/Leaflet/pull/521)
* Added `Circle` `getBounds` method. [#440](https://github.com/CloudMade/Leaflet/issues/440)
* Added `Circle` `getLatLng` and `getRadius` methods (by [Guiswa](https://github.com/Guiswa)). [#655](https://github.com/CloudMade/Leaflet/pull/655)
* Added `Map` `getContainer` method (by [Guiswa](https://github.com/Guiswa)). [#654](https://github.com/CloudMade/Leaflet/pull/654)
* Added `Marker` `opacity` option.
* Added public `redraw` method to vector layers (useful if you manipulate their `LatLng` points directly).
* Added `setPosition` and `getPosition` to all controls, as well as ability to pass certain position as an option when creating a control.
* Added `Popup` `maxHeight` option that makes content inside the popup scrolled if it doesn't fit the specified max height.
* Made controls implementation easier (now more magic happens under the hood).
* Added `Map` `containerPointToLatLng` and `latLngToContainerPoint` methods. [#474](https://github.com/CloudMade/Leaflet/issues/474)
* Added `containerPoint` property to `MouseEvent`. [#413](https://github.com/CloudMade/Leaflet/issues/413)
* Added `LatLngBounds` `pad` method that returns bounds extended by a percentage (by [@jacobtoye](https://github.com/jacobtoye)). [#492](https://github.com/CloudMade/Leaflet/pull/492)
* Added `contextmenu` event to vector layers (by [@ErrorProne](https://github.com/ErrorProne)). [#500](https://github.com/CloudMade/Leaflet/pull/500)
* Added chaining to `DomEvent` methods.
* Added `Map` `addHandler` method.
* Moved dragging cursor styles from JS code to CSS.
* Improved `Marker` `openPopup` not to raise an error if it doesn't have a popup. [#507](https://github.com/CloudMade/Leaflet/issues/507)
* Added `geometry` property to `GeoJSON` `featureparse` event (by [@twinbit](https://github.com/twinbit)). [#716](https://github.com/CloudMade/Leaflet/pull/716)
### Bug fixes
* Fixed a bug where `TileLayer.WMS` wouldn't take `insertAtTheBottom` option into account (by [@bmcbride](https://github.com/bmcbride)). [#478](https://github.com/CloudMade/Leaflet/pull/478)
#### General bugfixes
## 0.3.2 RC
* Fixed a bug where the map was zooming incorrectly inside a `position: fixed` container (by [chx007](https://github.com/chx007)). [#602](https://github.com/CloudMade/Leaflet/pull/602)
* Fixed a bug where scaled tiles weren't cleared up after zoom in some cases (by [cfis](https://github.com/cfis)) [#683](https://github.com/CloudMade/Leaflet/pull/683)
#### API bugfixes
* Fixed a regression where removeLayer would not remove corresponding attribution. [#488](https://github.com/CloudMade/Leaflet/issues/488)
* Fixed a bug where popup close button wouldn't work on manually added popups. [#423](https://github.com/CloudMade/Leaflet/issues/423)
* Fixed a bug where `TileLayer.WMS` wouldn't take `insertAtTheBottom` option into account (by [@bmcbride](https://github.com/bmcbride)). [#478](https://github.com/CloudMade/Leaflet/pull/478)
* Fixed a bug where marker click event would stop working if you dragged it and then disabled dragging. [#434](https://github.com/CloudMade/Leaflet/issues/434)
* Fixed a bug where `TileLayer` `setOpacity` wouldn't work when setting it back to 1.
* Fixed a bug where vector layer `setStyle({stroke: false})` wouldn't remove stroke and the same for fill. [#441](https://github.com/CloudMade/Leaflet/issues/441)
* Fixed a bug where `Marker` `bindPopup` method wouldn't take `offset` option into account.
* Fixed a bug where `TileLayer` `load` event wasn't fired if some tile didn't load (by [cfis](https://github.com/cfis)) [#682](https://github.com/CloudMade/Leaflet/pull/682)
* Fixed error when removing `GeoJSON` layer. [#685](https://github.com/CloudMade/Leaflet/issues/685)
* Fixed error when calling `GeoJSON` `clearLayer` (by [runderwood](https://github.com/runderwood)). [#617](https://github.com/CloudMade/Leaflet/pull/617)
* Fixed a bug where polygons/polylines sometimes throwed an error when making them editable manually (by [cfis](https://github.com/cfis)). [#669](https://github.com/CloudMade/Leaflet/pull/669)
* Fixed a bug where `Control` `setPosition` wasn't always working correctly (by [ericmmartinez](https://github.com/ericmmartinez)). [#657](https://github.com/CloudMade/Leaflet/pull/657)
* Fixed a bug with `Util.bind` sometimes losing arguments (by [johtso](https://github.com/johtso)). [#588](https://github.com/CloudMade/Leaflet/pull/588)
#### Browser bugfixes
* Fixed inability to use scrolled content inside popup due to mouse wheel propagation.
* Fixed a bug that caused jumping/stuttering of panning animation in some cases.
* Fixed a bug where popup size was calculated incorrectly in IE.
* Fixed a bug where cursor would flicker when dragging a marker.
* Fixed a bug where clickable paths on IE9 didn't have a hand cursor (by [naehrstoff](https://github.com/naehrstoff)). [#671](https://github.com/CloudMade/Leaflet/pull/671)
* Fixed a bug in IE with disappearing icons when changing opacity (by [tagliala](https://github.com/tagliala) and [DamonOehlman](https://github.com/DamonOehlman)). [#667](https://github.com/CloudMade/Leaflet/pull/667) [#600](https://github.com/CloudMade/Leaflet/pull/600)
* Fixed a bug where `Control.Layers` didn't work on IE7. [#652](https://github.com/CloudMade/Leaflet/issues/652)
* Fixed a bug that caused popups to be empty in IE when passing a DOM node as the content (by [@nrenner](https://github.com/nrenner)). [#472](https://github.com/CloudMade/Leaflet/pull/472)
#### Mobile browser bugfixes
* Fixed a bug with false map click events on pinch-zoom and zoom/layers controls click. [#485](https://github.com/CloudMade/Leaflet/issues/485)
* Fixed a bug where touching the map with two or more fingers simultaneously would raise an error.
* Fixed a bug where zoom control wasn't always visible on Android 3. [#335](https://github.com/CloudMade/Leaflet/issues/335)
* Fixed a bug where opening the layers control would propagate a click to the map (by [jacobtoye](https://github.com/jacobtoye)). [#638](https://github.com/CloudMade/Leaflet/pull/638)
## 0.3.1 (February 14, 2012)
@ -60,6 +145,7 @@ Leaflet Changelog
* Added `Polyline` `closestLayerPoint` method that's can be useful for interaction features (by [@anru](https://github.com/anru)). [#186](https://github.com/CloudMade/Leaflet/pull/186)
* Added `setLatLngs` method to `MultiPolyline` and `MultiPolygon` (by [@anru](https://github.com/anru)). [#194](https://github.com/CloudMade/Leaflet/pull/194)
* Added `getBounds` method to `Polyline` and `Polygon` (by [@JasonSanford](https://github.com/JasonSanford)). [#253](https://github.com/CloudMade/Leaflet/pull/253)
* Added `getBounds` method to `FeatureGroup` (by [@JasonSanford](https://github.com/JasonSanford)). [#557](https://github.com/CloudMade/Leaflet/pull/557)
* Added `FeatureGroup` `setStyle` method (also inherited by `MultiPolyline` and `MultiPolygon`). [#353](https://github.com/CloudMade/Leaflet/issues/353)
* Added `FeatureGroup` `invoke` method to call a particular method on all layers of the group with the given arguments.
* Added `ImageOverlay` `load` event. [#213](https://github.com/CloudMade/Leaflet/issues/213)
@ -68,16 +154,20 @@ Leaflet Changelog
* Added `LatLng` `distanceTo` method (great circle distance) (by [@mortenbekditlevsen](https://github.com/mortenbekditlevsen)). [#462](https://github.com/CloudMade/Leaflet/pull/462)
* Added `LatLngBounds` `toBBoxString` method for convenience (by [@JasonSanford](https://github.com/JasonSanford)). [#263](https://github.com/CloudMade/Leaflet/pull/263)
* Added `LatLngBounds` `intersects(otherBounds)` method (thanks to [@pagameba](https://github.com/pagameba)). [#350](https://github.com/CloudMade/Leaflet/pull/350)
* Made `LatLngBounds` `extend` method to accept other `LatLngBounds` in addition to `LatLng` (by [@JasonSanford](https://github.com/JasonSanford)). [#553](https://github.com/CloudMade/Leaflet/pull/553)
* Added `Bounds` `intersects(otherBounds)` method. [#461](https://github.com/CloudMade/Leaflet/issues/461)
* Added `L.Util.template` method for simple string template evaluation.
* Added `DomUtil.removeClass` method (by [@anru](https://github.com/anru)).
* Added ability to pass empty imageUrl to icons for creating transparent clickable regions (by [@mortenbekditlevsen](https://github.com/mortenbekditlevsen)). [#460](https://github.com/CloudMade/Leaflet/pull/460)
* Improved browser-specific code to rely more on feature detection rather than user agent string.
* Improved superclass access mechanism to work with inheritance chains of 3 or more classes; now you should use `Klass.superclass` instead of `this.superclass` (by [@anru](https://github.com/anru)). [#179](https://github.com/CloudMade/Leaflet/pull/179)
* Added `Map` `boxzoomstart` and `boxzoomend` events (by [zedd45](https://github.com/zedd45)). [#554](https://github.com/CloudMade/Leaflet/pull/554)
* Added `Popup` `contentupdate` event (by [mehmeta](https://github.com/mehmeta)). [#548](https://github.com/CloudMade/Leaflet/pull/548)
#### Breaking API changes
* `shiftDragZoom` map option/property renamed to `boxZoom`.
* Removed `mouseEventToLatLng` method (bringed back in 0.4).
#### Development workflow improvements

View File

@ -1,10 +1,9 @@
var build = require('./build/build.js'),
lint = require('./build/hint.js');
var crlf = '\r\n',
COPYRIGHT = '/*' + crlf + ' Copyright (c) 2010-2012, CloudMade, Vladimir Agafonkin' + crlf +
' Leaflet is a modern open-source JavaScript library for interactive maps.' + crlf +
' http://leaflet.cloudmade.com' + crlf + '*/' + crlf;
var COPYRIGHT = '/*\n Copyright (c) 2010-2012, CloudMade, Vladimir Agafonkin\n' +
' Leaflet is a modern open-source JavaScript library for interactive maps.\n' +
' http://leaflet.cloudmade.com\n*/\n';
desc('Check Leaflet source for errors with JSHint');
task('lint', function () {
@ -31,6 +30,7 @@ task('build', ['lint'], function (compsBase32, buildName) {
var files = build.getFiles(compsBase32);
console.log('Concatenating ' + files.length + ' files...');
var content = build.combineFiles(files);
var oldSrc = build.load(srcPath),

View File

@ -1,13 +1,13 @@
<img src="http://leaflet.cloudmade.com/docs/images/logo.png" alt="Leaflet" />
Leaflet is a modern, lightweight BSD-licensed JavaScript library for making tile-based interactive maps for both desktop and mobile web browsers, developed by [CloudMade](http://cloudmade.com) to form the core of its next generation JavaScript API.
Leaflet is a modern, lightweight open-source JavaScript library for mobile-friendly interactive maps, developed by [CloudMade](http://cloudmade.com) to form the core of its next generation JavaScript API. Weighting just about 21kb of gzipped JS code, it still has all the [features](http://leaflet.cloudmade.com/features.html) you will ever need for your web mapping needs while providing a fast, smooth, pleasant user experience.
It is built from the ground up to work efficiently and smoothly on both platforms, utilizing cutting-edge technologies included in HTML5. Its top priorities are usability, performance and small size, [A-grade](http://developer.yahoo.com/yui/articles/gbs/) browser support, flexibility and easy to use API. The OOP-based code of the library is designed to be modular, extensible and very easy to understand.
It is built from the ground up to work efficiently and smoothly on both desktop and mobile platforms like iOS and Android, utilizing cutting-edge technologies included in HTML5 and CSS3, focusing on usability, performance, small size, [A-grade](http://developer.yahoo.com/yui/articles/gbs/) browser support, flexibility and [easy to use API](http://leaflet.cloudmade.com/reference.html). The OOP-based code of the library is designed to be modular, extensible and very easy to understand.
Check out the website for more information: [leaflet.cloudmade.com](http://leaflet.cloudmade.com)
## Contributing to Leaflet
Let's make the best open-source library for maps that can possibly exist!
Let's make the best open-source library for maps that can possibly exist!
Contributing is simple: make the changes in your fork, make sure that Leaflet builds successfully (see below) and then create a pull request to [Vladimir Agafonkin](http://github.com/mourner) (Leaflet maintainer). Updates to Leaflet [documentation](http://leaflet.cloudmade.com/reference.html) and [examples](http://leaflet.cloudmade.com/examples.html) (located in the `gh-pages` branch) are really appreciated too.
@ -18,7 +18,7 @@ Leaflet build system is powered by the Node.js platform and Jake, JSHint and Ugl
1. [Download and install Node](http://nodejs.org)
2. Run the following commands in the command line:
```
npm install -g jake
npm install jshint
@ -29,4 +29,4 @@ Now that you have everything installed, run `jake` inside the Leaflet directory.
To make a custom build of the library with only the things you need, use the build helper (`build/build.html`) to choose the components (it figures out dependencies for you) and then run the command generated with it.
If you add any new files to the Leaflet source, make sure to also add them to `build/deps.js` so that the build system knows about them. Happy coding!
If you add any new files to the Leaflet source, make sure to also add them to `build/deps.js` so that the build system knows about them. Happy coding!

View File

@ -86,13 +86,13 @@
<li><a href="http://nodejs.org/#download">Download and install Node</a></li>
<li>Run this in the command line:<br />
<pre><code>npm install -g jake
npm install -g jshint
npm install -g uglify-js</code></pre></li>
npm install jshint
npm install uglify-js</code></pre></li>
<li>Run this command inside the Leaflet directory: <br /><input type="text" id="command2" />
</ol>
<h2>Building using Closure Compiler</h2>
<ol>
<li><a href="http://closure-compiler.googlecode.com/files/compiler-latest.zip">Download Closure Compiler</a> and extract it into <code>lib/closure-compiler</code> directory</li>
<li><a href="http://closure-compiler.googlecode.com/files/compiler-latest.zip">Download Closure Compiler</a>, extract it into <code>closure-compiler</code> directory</li>
<li>Run this command in the root Leaflet directory: <br /><input type="text" id="command" /></li>
</ol>
</div>
@ -139,7 +139,7 @@ npm install -g uglify-js</code></pre></li>
}
}
var command = 'java -jar lib/closure-compiler/compiler.jar ';
var command = 'java -jar closure-compiler/compiler.jar ';
for (var src in files) {
command += '--js src/' + src + ' ';
}
@ -154,7 +154,7 @@ npm install -g uglify-js</code></pre></li>
this.focus();
this.select();
};
commandInput.onclick = inputSelect;
commandInput2.onclick = inputSelect;

View File

@ -43,19 +43,19 @@ exports.uglify = function (code) {
var pro = uglifyjs.uglify;
var ast = uglifyjs.parser.parse(code);
ast = pro.ast_mangle(ast);
ast = pro.ast_squeeze(ast, {keep_comps: false});
ast = pro.ast_mangle(ast, {mangle: true});
ast = pro.ast_squeeze(ast);
ast = pro.ast_squeeze_more(ast);
return pro.gen_code(ast) + ';';
};
exports.combineFiles = function (files) {
var content = '';
var content = '(function () {\n\n';
for (var i = 0, len = files.length; i < len; i++) {
content += fs.readFileSync(files[i], 'utf8') + '\r\n\r\n';
content += fs.readFileSync(files[i], 'utf8') + '\n\n';
}
return content;
return content + '\n\n}());';
};
exports.save = function (savePath, compressed) {

View File

@ -57,6 +57,12 @@ var deps = {
desc: 'Markers to put on the map.'
},
DivIcon: {
src: ['layer/marker/DivIcon.js'],
deps: ['Marker'],
desc: 'Lightweight div-based icon for markers.'
},
Popup: {
src: ['layer/Popup.js', 'layer/marker/Marker.Popup.js', 'map/ext/Map.Popup.js'],
deps: ['Marker'],
@ -110,6 +116,12 @@ var deps = {
desc: 'MultiPolygon and MultyPolyline layers.'
},
Rectangle: {
src: ['layer/vector/Rectangle.js'],
deps: ['Polygon'],
desc: ['Rectangle overlays.']
},
Circle: {
src: ['layer/vector/Circle.js'],
deps: ['Path'],
@ -170,9 +182,16 @@ var deps = {
MarkerDrag: {
src: ['layer/marker/Marker.Drag.js'],
deps: ['Marker'],
desc: 'Makes markers draggable (by mouse or touch).'
},
PolyEdit: {
src: ['layer/vector/Polyline.Edit.js'],
deps: ['Polyline', 'DivIcon'],
desc: 'Polyline and polygon editing.'
},
ControlZoom: {
src: ['control/Control.js',
@ -189,6 +208,13 @@ var deps = {
desc: 'Attribution control.'
},
ControlScale: {
src: ['control/Control.js',
'map/ext/Map.Control.js',
'control/Control.Scale.js'],
desc: 'Scale control.'
},
ControlLayers: {
src: ['control/Control.js',
'map/ext/Map.Control.js',

View File

@ -1,5 +1,6 @@
exports.config = {
"browser": true,
"node": true,
"predef": ["L"],
"debug": false,
@ -17,7 +18,7 @@ exports.config = {
"eqnull": false,
"evil": false,
"expr": false,
"forin": false,
"forin": true,
"immed": true,
"latedef": true,
"loopfunc": false,
@ -28,6 +29,7 @@ exports.config = {
"shadow": false,
"supernew": false,
"undef": true,
"funcscope": false,
"newcap": true,
"noempty": true,

View File

@ -1,29 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title>Leaflet debug page</title>
<link rel="stylesheet" href="../../dist/leaflet.css" />
<!--[if lte IE 8]><link rel="stylesheet" href="../../dist/leaflet.ie.css" /><![endif]-->
<link rel="stylesheet" href="../css/screen.css" />
<script src="../leaflet-include.js"></script>
</head>
<body>
<div id="map"></div>
<script type="text/javascript">
var cloudmadeUrl = 'http://{s}.tile.cloudmade.com/BC9A493B41014CAABB98F0471D759707/997/256/{z}/{x}/{y}.png',
cloudmade = new L.TileLayer(cloudmadeUrl, {maxZoom: 18}),
latlng = new L.LatLng(50.5, 30.51);
var map = new L.Map('map').addLayer(cloudmade).setView(latlng, 15);
var zoomControl = new L.Control.Zoom();
map.addControl(zoomControl);
</script>
</body>
</html>

View File

@ -62,6 +62,7 @@
'layer/Popup.js',
'layer/marker/Icon.js',
'layer/marker/DivIcon.js',
'layer/marker/Marker.js',
'layer/marker/Marker.Popup.js',
'layer/marker/Marker.Drag.js',
@ -72,8 +73,10 @@
'layer/vector/Path.VML.js',
'layer/vector/canvas/Path.Canvas.js',
'layer/vector/Polyline.js',
'layer/vector/Polyline.Edit.js',
'layer/vector/canvas/Polyline.Canvas.js',
'layer/vector/Polygon.js',
'layer/vector/Rectangle.js',
'layer/vector/canvas/Polygon.Canvas.js',
'layer/vector/MultiPoly.js',
'layer/vector/Circle.js',
@ -86,6 +89,7 @@
'control/Control.Zoom.js',
'control/Control.Attribution.js',
'control/Control.Layers.js',
'control/Control.Scale.js'
];
function getSrcUrl() {
@ -102,9 +106,10 @@
}
var path = getSrcUrl();
for (var i = 0; i < scripts.length; i++) {
document.writeln("<script type='text/javascript' src='" + path + "../src/" + scripts[i] + "'></script>");
for (var i = 0; i < scripts.length; i++) {
document.writeln("<script src='" + path + scripts[i] + "'></script>");
}
document.writeln('<script>L.Icon.Default.imagePath = "' + path + '../dist/images";</script>');
})();
function getRandomLatLng(map) {

View File

@ -28,6 +28,7 @@
var marker = new L.Marker(new L.LatLng(50.5, 30.505));
map.addLayer(marker);
marker.bindPopup("Hello World").openPopup();
var marker2 = new L.Marker(new L.LatLng(50.502, 30.515));
map.addLayer(marker2);
@ -41,6 +42,7 @@
});
map.addControl(layersControl);
map.addControl(new L.Control.Scale());
</script>
</body>

View File

@ -0,0 +1,35 @@
<!DOCTYPE html>
<html>
<head>
<title>Leaflet geolocation debug page</title>
<link rel="stylesheet" href="../../dist/leaflet.css" />
<!--[if lte IE 8]><link rel="stylesheet" href="../../dist/leaflet.ie.css" /><![endif]-->
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="../css/screen.css" />
<script src="../leaflet-include.js"></script>
</head>
<body>
<div id="map"></div>
<script type="text/javascript">
var cloudmadeUrl = 'http://{s}.tile.cloudmade.com/BC9A493B41014CAABB98F0471D759707/997/256/{z}/{x}/{y}.png',
cloudmadeAttribution = 'Map data &copy; 2011 OpenStreetMap contributors, Imagery &copy; 2011 CloudMade',
cloudmade = new L.TileLayer(cloudmadeUrl, {maxZoom: 18, attribution: cloudmadeAttribution});
var map = new L.Map('map', {zoom: 15, layers: [cloudmade]});
function logEvent(e) { console.log(e.type); }
map.on('locationerror', logEvent);
map.on('locationfound', logEvent);
map.locate({setView: true});
</script>
</body>
</html>

View File

@ -20,23 +20,15 @@
var cloudmadeUrl = 'http://{s}.tile.cloudmade.com/BC9A493B41014CAABB98F0471D759707/997/256/{z}/{x}/{y}.png',
cloudmadeAttribution = 'Map data &copy; 2011 OpenStreetMap contributors, Imagery &copy; 2011 CloudMade',
cloudmade = new L.TileLayer(cloudmadeUrl, {maxZoom: 18, attribution: cloudmadeAttribution});
cloudmade = new L.TileLayer(cloudmadeUrl, {maxZoom: 18, attribution: cloudmadeAttribution}),
latlng = new L.LatLng(50.5, 30.51);
var map = new L.Map('map').addLayer(cloudmade);
var map = new L.Map('map', {center: latlng, zoom: 15, layers: [cloudmade]});
map.on('locationfound', function(e) {
var marker = new L.Marker(e.latlng);
map.addLayer(marker);
var marker = new L.Marker(latlng);
map.addLayer(marker);
marker.bindPopup("<p>Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Donec odio. Quisque volutpat mattis eros. Nullam malesuada erat ut turpis. Suspendisse urna nibh, viverra non, semper suscipit, posuere a, pede.</p><p>Donec nec justo eget felis facilisis fermentum. Aliquam porttitor mauris sit amet orci. Aenean dignissim pellentesque felis.</p>");
});
map.on('locationerror', function(e) {
alert(e.message);
map.fitWorld();
});
map.locateAndSetView();
marker.bindPopup("<p>Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Donec odio. Quisque volutpat mattis eros. Nullam malesuada erat ut turpis. Suspendisse urna nibh, viverra non, semper suscipit, posuere a, pede.</p><p>Donec nec justo eget felis facilisis fermentum. Aliquam porttitor mauris sit amet orci. Aenean dignissim pellentesque felis.</p>");
</script>
</body>
</html>

View File

@ -14,7 +14,7 @@
</head>
<body>
<div id="map" style="width: 600px; height: 600px; border: 1px solid #ccc"></div>
<div id="map"></div>
<button id="populate">Populate with 10 markers</button>
<script type="text/javascript">
@ -26,6 +26,8 @@
var map = new L.Map('map', {center: latlng, zoom: 15, layers: [cloudmade]});
//map.on('click', function () { alert('hi'); });
var markers = new L.FeatureGroup();
function populate() {

View File

@ -0,0 +1,88 @@
<!DOCTYPE html>
<html>
<head>
<title>Leaflet debug page</title>
<link rel="stylesheet" href="../../dist/leaflet.css" />
<!--[if lte IE 8]><link rel="stylesheet" href="../../dist/leaflet.ie.css" /><![endif]-->
<link rel="stylesheet" href="../css/screen.css" />
<script src="../leaflet-include.js"></script>
</head>
<body>
<div id="map" style="width: 800px; height: 600px; border: 1px solid #ccc"></div>
<button onclick="boundsExtendBounds();">Extend the bounds of the center rectangle with the upper right rectangle</button>
<button onclick="boundsExtendLatLng()">Extend the bounds of the center rectangle with the lower left marker</button>
<script src="route.js"></script>
<script>
var cloudmadeUrl = 'http://{s}.tile.cloudmade.com/BC9A493B41014CAABB98F0471D759707/997/256/{z}/{x}/{y}.png',
cloudmade = new L.TileLayer(cloudmadeUrl, {maxZoom: 18});
var latLng = new L.LatLng(54.18815548107151, -7.657470703124999);
var bounds1 = new L.LatLngBounds(new L.LatLng(54.559322, -5.767822), new L.LatLng(56.1210604, -3.021240));
var bounds2 = new L.LatLngBounds(new L.LatLng(56.56023925701561, -2.076416015625), new L.LatLng(57.01158038001565, -0.9777832031250001));
var bounds3;
var map = new L.Map('map', {
layers: [cloudmade],
center: bounds1.getCenter(),
zoom: 7
});
var rectangle1 = new L.Rectangle(bounds1);
var rectangle2 = new L.Rectangle(bounds2);
var rectangle3;
var marker = new L.Marker(latLng);
map.addLayer(rectangle1).addLayer(rectangle2).addLayer(marker);
function boundsExtendBounds() {
if (rectangle3) {
map.removeLayer(rectangle3);
rectangle3 = null;
}
if (bounds3) {
bounds3 = null;
}
bounds3 = new L.LatLngBounds(bounds1.getSouthWest(), bounds1.getNorthEast());
bounds3.extend(bounds2);
rectangle3 = new L.Rectangle(bounds3, {
color: "#ff0000",
weight: 1,
opacity: 1,
fillOpacity: 0
});
map.addLayer(rectangle3);
}
function boundsExtendLatLng() {
if (rectangle3) {
map.removeLayer(rectangle3);
rectangle3 = null;
}
if (bounds3) {
bounds3 = null;
}
bounds3 = new L.LatLngBounds(bounds1.getSouthWest(), bounds1.getNorthEast());
bounds3.extend(marker.getLatLng());
rectangle3 = new L.Rectangle(bounds3, {
color: "#ff0000",
weight: 1,
opacity: 1,
fillOpacity: 0
});
map.addLayer(rectangle3);
}
</script>
</body>
</html>

View File

@ -5,9 +5,9 @@
<link rel="stylesheet" href="../../dist/leaflet.css" />
<!--[if lte IE 8]><link rel="stylesheet" href="../../dist/leaflet.ie.css" /><![endif]-->
<link rel="stylesheet" href="../css/screen.css" />
<script src="../leaflet-include.js"></script>
</head>
<body>
@ -17,27 +17,35 @@
<script>
var cloudmadeUrl = 'http://{s}.tile.cloudmade.com/BC9A493B41014CAABB98F0471D759707/997/256/{z}/{x}/{y}.png',
cloudmade = new L.TileLayer(cloudmadeUrl, {maxZoom: 18}),
map = new L.Map('map', {layers: [cloudmade], center: new L.LatLng(50.5, 30.5), zoom: 15});
var latlngs = [];
latlngs.push(getRandomLatLng(map));
latlngs.push(getRandomLatLng(map));
latlngs.push(getRandomLatLng(map));
var path = new L.Polygon(latlngs);
map = new L.Map('map', {layers: [cloudmade], center: new L.LatLng(51.505, -0.04), zoom: 13});
console.log(latlngs);
var marker = new L.Marker(latlngs[0], {draggable: true});
map.addLayer(marker);
marker.on('drag', function() {
latlngs[0] = marker.getLatLng();
path.setLatLngs(latlngs);
});
map.addLayer(path);
var polygon = new L.Polygon([
new L.LatLng(51.51, -0.1),
new L.LatLng(51.5, -0.06),
new L.LatLng(51.52, -0.03)
]);
polygon.editing.enable();
map.addLayer(polygon);
var polyline = new L.Polyline([
new L.LatLng(51.49, -0.02),
new L.LatLng(51.51, 0),
new L.LatLng(51.52, -0.02)
]);
polyline.editing.enable();
map.addLayer(polyline);
polygon.on('edit', function() {
console.log('Polygon was edited!');
});
polyline.on('edit', function() {
console.log('Polyline was edited!');
});
</script>
</body>
</html>
</html>

View File

@ -0,0 +1,94 @@
<!DOCTYPE html>
<html>
<head>
<title>Leaflet debug page</title>
<link rel="stylesheet" href="../../dist/leaflet.css" />
<!--[if lte IE 8]><link rel="stylesheet" href="../../dist/leaflet.ie.css" /><![endif]-->
<link rel="stylesheet" href="../css/screen.css" />
<script src="../leaflet-include.js"></script>
</head>
<body>
<div id="map" style="width: 600px; height: 600px; border: 1px solid #ccc"></div>
<button onclick="geojsonLayerBounds();">Show GeoJSON layer bounds</button>
<button onclick="featureGroupBounds();">Show feature group bounds</button>
<script type="text/javascript" src="geojson-sample.js"></script>
<script type="text/javascript">
var cloudmadeUrl = 'http://{s}.tile.cloudmade.com/BC9A493B41014CAABB98F0471D759707/997/256/{z}/{x}/{y}.png',
cloudmadeAttribution = 'Map data &copy; 2011 OpenStreetMap contributors, Imagery &copy; 2011 CloudMade',
cloudmade = new L.TileLayer(cloudmadeUrl, {maxZoom: 18, attribution: cloudmadeAttribution}),
rectangle,
featureGroup;
var map = new L.Map('map', {
center: new L.LatLng(0.78, 102.37),
zoom: 7,
layers: [cloudmade]
});
var geojson = new L.GeoJSON();
/* points are rendered as markers by default, but you can change this:
var geojson = new L.GeoJSON(null, {
pointToLayer: function(latlng) { return new L.CircleMarker(latlng); }
});
*/
geojson.on('featureparse', function(e) {
// you can style features depending on their properties, etc.
var popupText = 'geometry type: ' + e.geometryType;
if (e.layer.setStyle && e.properties && e.properties.color) {
e.layer.setStyle({color: e.properties.color});
popupText += '<br/>color: ' + e.properties.color;
}
e.layer.bindPopup(popupText);
});
geojson.addGeoJSON(geojsonSample);
geojson.addLayer(new L.Marker(new L.LatLng(2.745530718801952, 105.194091796875)))
var eye1 = new L.Marker(new L.LatLng(-0.7250783020332547, 101.8212890625));
var eye2 = new L.Marker(new L.LatLng(-0.7360637370492077, 103.2275390625));
var nose = new L.Marker(new L.LatLng(-1.3292264529974207, 102.5463867187));
var mouth = new L.Polyline([
new L.LatLng(-1.3841426927920029, 101.7333984375),
new L.LatLng(-1.6037944300589726, 101.964111328125),
new L.LatLng(-1.6806671337507222, 102.249755859375),
new L.LatLng(-1.7355743631421197, 102.67822265625),
new L.LatLng(-1.5928123762763, 103.0078125),
new L.LatLng(-1.3292264529974207, 103.3154296875)
]);
map.addLayer(eye1).addLayer(eye2).addLayer(nose).addLayer(mouth);
featureGroup = new L.FeatureGroup([eye1, eye2, nose, mouth]);
map.addLayer(geojson);
map.addLayer(featureGroup);
function geojsonLayerBounds() {
if (rectangle) {
rectangle.setBounds(geojson.getBounds());
} else {
rectangle = new L.Rectangle(geojson.getBounds());
map.addLayer(rectangle);
}
}
function featureGroupBounds() {
if (rectangle) {
rectangle.setBounds(featureGroup.getBounds());
} else {
rectangle = new L.Rectangle(featureGroup.getBounds());
map.addLayer(rectangle);
}
}
</script>
</body>
</html>

View File

@ -13,41 +13,39 @@
<body>
<div id="map" style="width: 500px; height: 500px;"></div>
<button onclick="javascript:capture1();">Capture Bounds 1</button>
<button onclick="javascript:capture2();">Capture Bounds 2</button>
<button onclick="javascript:testBounds();">Test Bounds</button>
<input type="button" value="Set blue rectangle bounds as current map extent." onclick="resetBounds();" />
<script type="text/javascript">
var cloudmadeUrl = 'http://{s}.tile.cloudmade.com/BC9A493B41014CAABB98F0471D759707/997/256/{z}/{x}/{y}.png',
cloudmadeAttribution = 'Map data &copy; 2011 OpenStreetMap contributors, Imagery &copy; 2011 CloudMade',
cloudmade = new L.TileLayer(cloudmadeUrl, {maxZoom: 18, attribution: cloudmadeAttribution}),
bounds = new L.LatLngBounds(new L.LatLng(49.5, -11.3), new L.LatLng(61.2, 2.5));
cloudmade = new L.TileLayer(cloudmadeUrl, {maxZoom: 18, attribution: cloudmadeAttribution});;
var bounds1, bounds2;
var bounds = new L.LatLngBounds(new L.LatLng(54.559322, -5.767822), new L.LatLng(56.1210604, -3.021240));
var bounds2 = new L.LatLngBounds(new L.LatLng(56.2124322195806, -3.427734375), new L.LatLng(56.307776937156945, -3.2560729980468746));
var rectangle = new L.Rectangle(bounds);
var styledRectangle = new L.Rectangle(bounds2, {
fillColor: "#ff7800",
color: "#000000",
opacity: 1,
weight: 2
});
rectangle.on("click", function () {
alert("you clicked a rectangle.")
});
var map = new L.Map('map', {
center: bounds.getCenter(),
zoom: 7,
layers: [cloudmade],
maxBounds: bounds
layers: [cloudmade]
});
function capture1 () {
bounds1 = map.getBounds();
}
function capture2() {
bounds2 = map.getBounds();
}
function testBounds() {
if (!(bounds1 && bounds2)) {
alert("Bounds 1 and 2 have not been set");
return;
}
alert("Bounds 1 " + (bounds1.equals(bounds2) ? "equals": "does not equal") + " bounds 2.")
map.addLayer(rectangle).addLayer(styledRectangle);
function resetBounds() {
rectangle.setBounds(map.getBounds());
}
</script>

View File

@ -0,0 +1,121 @@
<!DOCTYPE html>
<html>
<head>
<title>Leaflet debug page</title>
<link rel="stylesheet" href="../../dist/leaflet.css" />
<!--[if lte IE 8]><link rel="stylesheet" href="../../dist/leaflet.ie.css" /><![endif]-->
<link rel="stylesheet" href="../css/screen.css" />
<script src="../leaflet-include.js"></script>
</head>
<body>
<div id="map" style="width: 800px; height: 600px; border: 1px solid #ccc"></div>
<div style="background-color:chartreuse; width: 100px; height:100px; position: absolute; left: 850px; top: 10px" onclick="Hack1()">Hack1Touch</div>
<div style="background-color:coral; width: 100px; height:100px; position: absolute; left: 850px; top: 120px" onclick="Hack2()">Hack2Touch</div>
<script src="route.js"></script>
<script>
var cloudmadeUrl = 'http://{s}.tile.cloudmade.com/BC9A493B41014CAABB98F0471D759707/997/256/{z}/{x}/{y}.png',
cloudmade = new L.TileLayer(cloudmadeUrl, {maxZoom: 18}),
map = new L.Map('map', {layers: [cloudmade], center: new L.LatLng(51.505, -0.04), zoom: 13});
var polygon = new L.Polygon([
new L.LatLng(51.51, -0.1),
new L.LatLng(51.5, -0.06),
new L.LatLng(51.52, -0.03)
]);
polygon.editing.enable();
map.addLayer(polygon);
var polyline = new L.Polyline([
new L.LatLng(51.49, -0.02),
new L.LatLng(51.51, 0),
new L.LatLng(51.52, -0.02)
]);
polyline.editing.enable();
map.addLayer(polyline);
polygon.on('edit', function() {
console.log('Polygon was edited!');
});
polyline.on('edit', function() {
console.log('Polyline was edited!');
});
var _timerAt, _timer;
function Hack1() {
_timerAt = 0;
clearInterval(_timer);
_timer = setInterval(Hack1Timer, 1000);
}
function Hack1Timer() {
switch (_timerAt) {
case 0:
map.touchZoom._onTouchStart({ touches: [{ pageX: 405, pageY: 312 }, { pageX: 233, pageY: 321 }] });
break;
case 1:
map.touchZoom._onTouchMove({ touches: [{ pageX: 412, pageY: 312 }, { pageX: 236, pageY: 322 }] });
break;
case 2:
map.touchZoom._onTouchMove({ touches: [{ pageX: 423, pageY: 313 }, { pageX: 243, pageY: 321 }] });
break;
case 3:
map.touchZoom._onTouchMove({ touches: [{ pageX: 476, pageY: 326 }, { pageX: 299, pageY: 321 }] });
break;
case 4:
map.touchZoom._onTouchEnd();
break;
case 5:
clearInterval(_timer);
break;
}
_timerAt++;
}
function Hack2() {
map.touchZoom._onTouchStart({ touches: [{ pageX: 405, pageY: 312 }, { pageX: 233, pageY: 321 }] });
map.touchZoom._onTouchMove({ touches: [{ pageX: 476, pageY: 326 }, { pageX: 299, pageY: 321 }] });
//_timerAt = 0;
//clearInterval(_timer);
//_timer = setInterval(Hack2Timer, 100);
}
function Hack2Timer() {
switch (_timerAt) {
case 0:
map.touchZoom._onTouchStart({ touches: [{ pageX: 100, pageY: 100 }, { pageX: 50, pageY: 100}] });
break;
case 1:
map.touchZoom._onTouchMove({ touches: [{ pageX: 100, pageY: 100 }, { pageX: 50, pageY: 100 }] });
break;
case 2:
map.touchZoom._onTouchMove({ touches: [{ pageX: 110, pageY: 100 }, { pageX: 50, pageY: 100 }] });
break;
case 3:
map.touchZoom._onTouchMove({ touches: [{ pageX: 120, pageY: 100 }, { pageX: 50, pageY: 100 }] });
break;
case 4:
map.touchZoom._onTouchMove({ touches: [{ pageX: 130, pageY: 100 }, { pageX: 50, pageY: 100 }] });
break;
case 5:
map.touchZoom._onTouchMove({ touches: [{ pageX: 140, pageY: 100 }, { pageX: 50, pageY: 100 }] });
break;
case 6:
map.touchZoom._onTouchMove({ touches: [{ pageX: 150, pageY: 100 }, { pageX: 50, pageY: 100 }] });
break;
case 7:
map.touchZoom._onTouchEnd();
break;
case 8:
clearInterval(_timer);
break;
}
_timerAt++;
}
</script>
</body>
</html>

View File

@ -17,14 +17,14 @@
<script>
var map = new L.Map('map', {fadeAnimation: false});
var map = new L.Map('map');
var cloudmadeUrl = 'http://{s}.tile.cloudmade.com/BC9A493B41014CAABB98F0471D759707/997/256/{z}/{x}/{y}.png',
cloudmadeAttribution = 'Map data &copy; 2011 OpenStreetMap contributors, Imagery &copy; 2011 CloudMade',
cloudmade = new L.TileLayer(cloudmadeUrl, {
maxZoom: 18,
attribution: cloudmadeAttribution
});
var cloudmade = new L.TileLayer('http://{s}.tile.cloudmade.com/{key}/{styleId}/256/{z}/{x}/{y}.png', {
maxZoom: 18,
attribution: 'Map data &copy; 2011 OpenStreetMap contributors, Imagery &copy; 2011 CloudMade',
key: 'BC9A493B41014CAABB98F0471D759707',
styleId: 997
});
map.setView(new L.LatLng(51.505, -0.09), 13).addLayer(cloudmade);
@ -60,4 +60,4 @@
map.addLayer(polygon);
</script>
</body>
</html>
</html>

BIN
dist/images/marker.png vendored

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

2775
dist/leaflet-src.js vendored

File diff suppressed because it is too large Load Diff

142
dist/leaflet.css vendored
View File

@ -17,7 +17,11 @@
.leaflet-container {
overflow: hidden;
}
.leaflet-tile-pane, .leaflet-container {
.leaflet-tile-pane,
.leaflet-container,
.leaflet-corner,
.leaflet-popup {
/* TODO make this configurable */
-webkit-transform: translate3d(0,0,0);
}
.leaflet-tile,
@ -34,9 +38,22 @@
.leaflet-clickable {
cursor: pointer;
}
.leaflet-dragging {
cursor: move;
}
.leaflet-dragging .leaflet-clickable {
cursor: move;
}
.leaflet-container img {
max-width: none !important;
}
.leaflet-div-icon {
background: #fff;
border: 1px solid #666;
}
.leaflet-editing-icon {
border-radius: 2px;
}
.leaflet-tile-pane { z-index: 2; }
@ -52,7 +69,8 @@
}
.leaflet-tile {
visibility: hidden;
filter: inherit;
visibility: hidden;
}
.leaflet-tile-loaded {
visibility: inherit;
@ -105,7 +123,7 @@ a.leaflet-active {
margin-right: 10px;
}
.leaflet-control-zoom, .leaflet-control-layers {
.leaflet-control-zoom {
-moz-border-radius: 7px;
-webkit-border-radius: 7px;
border-radius: 7px;
@ -132,7 +150,7 @@ a.leaflet-active {
.leaflet-control-zoom a:hover {
background-color: #fff;
}
.leaflet-big-buttons .leaflet-control-zoom a {
.leaflet-touch .leaflet-control-zoom a {
width: 27px;
height: 27px;
}
@ -145,18 +163,18 @@ a.leaflet-active {
}
.leaflet-control-layers {
-moz-box-shadow: 0 0 7px #999;
-webkit-box-shadow: 0 0 7px #999;
box-shadow: 0 0 7px #999;
box-shadow: 0 1px 7px #999;
background: #f8f8f9;
-moz-border-radius: 8px;
-webkit-border-radius: 8px;
border-radius: 8px;
}
.leaflet-control-layers a {
background-image: url(images/layers.png);
width: 36px;
height: 36px;
}
.leaflet-big-buttons .leaflet-control-layers a {
.leaflet-touch .leaflet-control-layers a {
width: 44px;
height: 44px;
}
@ -189,23 +207,57 @@ a.leaflet-active {
}
.leaflet-container .leaflet-control-attribution {
margin: 0;
padding: 0 5px;
font: 11px/1.5 "Helvetica Neue", Arial, Helvetica, sans-serif;
color: #333;
background-color: rgba(255, 255, 255, 0.7);
box-shadow: 0 0 5px #bbb;
margin: 0;
}
-moz-box-shadow: 0 0 7px #ccc;
-webkit-box-shadow: 0 0 7px #ccc;
box-shadow: 0 0 7px #ccc;
.leaflet-control-attribution,
.leaflet-control-scale-line {
padding: 0 5px;
color: #333;
}
.leaflet-container .leaflet-control-attribution,
.leaflet-container .leaflet-control-scale {
font: 11px/1.5 "Helvetica Neue", Arial, Helvetica, sans-serif;
}
.leaflet-left .leaflet-control-scale {
margin-left: 5px;
}
.leaflet-bottom .leaflet-control-scale {
margin-bottom: 5px;
}
.leaflet-control-scale-line {
border: 2px solid #777;
border-top: none;
color: black;
line-height: 1;
font-size: 10px;
padding-bottom: 2px;
text-shadow: 1px 1px 1px #fff;
background-color: rgba(255, 255, 255, 0.5);
}
.leaflet-control-scale-line:nth-child(2) {
border-top: 2px solid #777;
padding-top: 1px;
border-bottom: none;
margin-top: -2px;
}
.leaflet-touch .leaflet-control-attribution, .leaflet-touch .leaflet-control-layers {
box-shadow: none;
}
.leaflet-touch .leaflet-control-layers {
border: 5px solid #bbb;
}
/* Fade animations */
.leaflet-fade-anim .leaflet-tile {
.leaflet-fade-anim .leaflet-tile, .leaflet-fade-anim .leaflet-popup {
opacity: 0;
-webkit-transition: opacity 0.2s linear;
@ -213,47 +265,48 @@ a.leaflet-active {
-o-transition: opacity 0.2s linear;
transition: opacity 0.2s linear;
}
.leaflet-fade-anim .leaflet-tile-loaded {
.leaflet-fade-anim .leaflet-tile-loaded, .leaflet-fade-anim .leaflet-map-pane .leaflet-popup {
opacity: 1;
}
.leaflet-fade-anim .leaflet-popup {
opacity: 0;
-webkit-transition: opacity 0.2s linear;
-moz-transition: opacity 0.2s linear;
-o-transition: opacity 0.2s linear;
transition: opacity 0.2s linear;
}
.leaflet-fade-anim .leaflet-map-pane .leaflet-popup {
opacity: 1;
}
.leaflet-zoom-anim .leaflet-tile {
.leaflet-zoom-anim .leaflet-tile, .leaflet-pan-anim .leaflet-tile {
-webkit-transition: none;
-moz-transition: none;
-o-transition: none;
transition: none;
}
.leaflet-zoom-anim .leaflet-objects-pane {
visibility: hidden;
.leaflet-zoom-anim .leaflet-zoom-animated {
-webkit-transition: -webkit-transform 0.25s cubic-bezier(0.25,0.1,0.25,0.75);
-moz-transition: -moz-transform 0.25s cubic-bezier(0.25,0.1,0.25,0.75);
-o-transition: -o-transform 0.25s cubic-bezier(0.25,0.1,0.25,0.75);
transition: transform 0.25s cubic-bezier(0.25,0.1,0.25,0.75);
}
.leaflet-touching .leaflet-zoom-animated {
-webkit-transition: none;
-moz-transition: none;
-o-transition: none;
transition: none;
}
.leaflet-zoom-anim .leaflet-zoom-hide {
visibility: hidden;
}
/* Popup layout */
.leaflet-popup {
position: absolute;
text-align: center;
-webkit-transform: translate3d(0,0,0);
}
.leaflet-popup-content-wrapper {
padding: 1px;
text-align: left;
}
.leaflet-popup-content {
margin: 19px;
margin: 14px 20px;
}
.leaflet-popup-tip-container {
margin: 0 auto;
@ -277,8 +330,8 @@ a.leaflet-active {
}
.leaflet-popup-close-button {
position: absolute;
top: 9px;
right: 9px;
top: 8px;
right: 8px;
width: 10px;
height: 10px;
@ -288,6 +341,11 @@ a.leaflet-active {
.leaflet-popup-content p {
margin: 18px 0;
}
.leaflet-popup-scrolled {
overflow: auto;
border-bottom: 1px solid #ddd;
border-top: 1px solid #ddd;
}
/* Visual appearance */
@ -306,9 +364,9 @@ a.leaflet-active {
.leaflet-popup-content-wrapper, .leaflet-popup-tip {
background: white;
box-shadow: 0 1px 10px #888;
-moz-box-shadow: 0 1px 10px #888;
-webkit-box-shadow: 0 1px 14px #999;
box-shadow: 0 3px 10px #888;
-moz-box-shadow: 0 3px 10px #888;
-webkit-box-shadow: 0 3px 14px #999;
}
.leaflet-popup-content-wrapper {
-moz-border-radius: 20px;

4
dist/leaflet.ie.css vendored
View File

@ -1,7 +1,3 @@
.leaflet-tile {
filter: inherit;
}
.leaflet-vml-shape {
width: 1px;
height: 1px;

2
dist/leaflet.js vendored

File diff suppressed because one or more lines are too long

View File

@ -1,3 +0,0 @@
compiler.jar
COPYING
README

View File

@ -2,24 +2,24 @@
<html>
<head>
<title>Jasmine Test Runner</title>
<link rel="stylesheet" type="text/css" href="../lib/jasmine/jasmine.css">
<script type="text/javascript" src="../lib/jasmine/jasmine.js"></script>
<script type="text/javascript" src="../lib/jasmine/jasmine-html.js"></script>
<link rel="stylesheet" type="text/css" href="jasmine/jasmine.css">
<script type="text/javascript" src="jasmine/jasmine.js"></script>
<script type="text/javascript" src="jasmine/jasmine-html.js"></script>
<!-- source files -->
<script type="text/javascript">
L = 'test'; //to test L#noConflict later
</script>
<script type="text/javascript" src="../debug/leaflet-include.js"></script>
<!-- spec files -->
<script type="text/javascript" src="suites/SpecHelper.js"></script>
<script type="text/javascript" src="suites/LeafletSpec.js"></script>
<!-- /core -->
<script type="text/javascript" src="suites/core/UtilSpec.js"></script>
<script type="text/javascript" src="suites/core/ClassSpec.js"></script>
@ -42,7 +42,7 @@
<!-- /layer -->
<script type="text/javascript" src="suites/layer/TileLayerSpec.js"></script>
<script type="text/javascript" src="suites/layer/vector/PolylineGeometrySpec.js"></script>
<!-- /map -->
<script type="text/javascript" src="suites/map/MapSpec.js"></script>
</head>

View File

@ -46,6 +46,7 @@ describe("Class", function() {
expect(method).toHaveBeenCalled();
});
/* superclass deprecated
it("should grant the ability to call parent methods, including constructor", function() {
var Klass2 = Klass.extend({
initialize: function() {},
@ -60,7 +61,7 @@ describe("Class", function() {
b.constructor.superclass.bar.call(this);
expect(method).toHaveBeenCalled();
});
}); */
it("should support static properties", function() {
expect(Klass.bla).toEqual(1);
@ -123,6 +124,7 @@ describe("Class", function() {
});
});
/* superclass deprecated
it("should have working superclass access with inheritance level > 2", function() {
var constructor2 = jasmine.createSpy("Klass2 constructor"),
constructor3 = jasmine.createSpy("Klass3 constructor");
@ -149,5 +151,6 @@ describe("Class", function() {
expect(constructor2).toHaveBeenCalled();
expect(constructor).toHaveBeenCalled();
});
*/
});
});

View File

@ -1,33 +1,18 @@
var L, originalL;
(function (root) {
root.L = {
VERSION: '0.4',
if (typeof exports !== 'undefined') {
L = exports;
} else {
L = {};
originalL = window.L;
ROOT_URL: root.L_ROOT_URL || (function () {
var scripts = document.getElementsByTagName('script'),
leafletRe = /\/?leaflet[\-\._]?([\w\-\._]*)\.js\??/;
var i, len, src, matches;
for (i = 0, len = scripts.length; i < len; i++) {
src = scripts[i].src;
matches = src.match(leafletRe);
if (matches) {
if (matches[1] === 'include') {
return '../../dist/';
}
return src.split(leafletRe)[0] + '/';
}
}
return '';
}()),
noConflict: function () {
root.L = this._originalL;
return this;
},
_originalL: root.L
L.noConflict = function () {
window.L = originalL;
return L;
};
}(this));
window.L = L;
}
L.version = '0.4';

View File

@ -1,52 +1,60 @@
L.Control.Attribution = L.Control.extend({
options: {
position: 'bottomright'
position: 'bottomright',
prefix: 'Powered by <a href="http://leaflet.cloudmade.com">Leaflet</a>'
},
initialize: function (prefix, options) {
initialize: function (options) {
L.Util.setOptions(this, options);
this._prefix = prefix || 'Powered by <a href="http://leaflet.cloudmade.com">Leaflet</a>';
this._attributions = {};
},
onAdd: function (map) {
this._container = L.DomUtil.create('div', 'leaflet-control-attribution');
L.DomEvent.disableClickPropagation(this._container);
this._map = map;
map
.on('layeradd', this._onLayerAdd, this)
.on('layerremove', this._onLayerRemove, this);
this._update();
return this._container;
},
onRemove: function (map) {
map
.off('layeradd', this._onLayerAdd)
.off('layerremove', this._onLayerRemove);
},
setPrefix: function (prefix) {
this._prefix = prefix;
this.options.prefix = prefix;
this._update();
},
addAttribution: function (text) {
if (!text) {
return;
}
if (!text) { return; }
if (!this._attributions[text]) {
this._attributions[text] = 0;
}
this._attributions[text]++;
this._update();
},
removeAttribution: function (text) {
if (!text) {
return;
}
if (!text) { return; }
this._attributions[text]--;
this._update();
},
_update: function () {
if (!this._map) {
return;
}
if (!this._map) { return; }
var attribs = [];
@ -57,13 +65,36 @@ L.Control.Attribution = L.Control.extend({
}
var prefixAndAttribs = [];
if (this._prefix) {
prefixAndAttribs.push(this._prefix);
if (this.options.prefix) {
prefixAndAttribs.push(this.options.prefix);
}
if (attribs.length) {
prefixAndAttribs.push(attribs.join(', '));
}
this._container.innerHTML = prefixAndAttribs.join(' &mdash; ');
},
_onLayerAdd: function (e) {
if (e.layer.getAttribution) {
this.addAttribution(e.layer.getAttribution());
}
},
_onLayerRemove: function (e) {
if (e.layer.getAttribution) {
this.removeAttribution(e.layer.getAttribution());
}
}
});
L.Map.mergeOptions({
attributionControl: true
});
L.Map.addInitHook(function () {
if (this.options.attributionControl) {
this.attributionControl = (new L.Control.Attribution()).addTo(this);
}
});

View File

@ -24,8 +24,6 @@ L.Control.Layers = L.Control.extend({
},
onAdd: function (map) {
this._map = map;
this._initLayout();
this._update();
@ -52,40 +50,47 @@ L.Control.Layers = L.Control.extend({
},
_initLayout: function () {
this._container = L.DomUtil.create('div', 'leaflet-control-layers');
var className = 'leaflet-control-layers',
container = this._container = L.DomUtil.create('div', className);
if (!L.Browser.touch) {
L.DomEvent.disableClickPropagation(this._container);
L.DomEvent.disableClickPropagation(container);
} else {
L.DomEvent.addListener(container, 'click', L.DomEvent.stopPropagation);
}
this._form = L.DomUtil.create('form', 'leaflet-control-layers-list');
var form = this._form = L.DomUtil.create('form', className + '-list');
if (this.options.collapsed) {
L.DomEvent.addListener(this._container, 'mouseover', this._expand, this);
L.DomEvent.addListener(this._container, 'mouseout', this._collapse, this);
L.DomEvent
.addListener(container, 'mouseover', this._expand, this)
.addListener(container, 'mouseout', this._collapse, this);
var link = this._layersLink = L.DomUtil.create('a', 'leaflet-control-layers-toggle');
var link = this._layersLink = L.DomUtil.create('a', className + '-toggle', container);
link.href = '#';
link.title = 'Layers';
if (L.Browser.touch) {
L.DomEvent.addListener(link, 'click', this._expand, this);
//L.DomEvent.disableClickPropagation(link);
} else {
L.DomEvent
.addListener(link, 'click', L.DomEvent.stopPropagation)
.addListener(link, 'click', L.DomEvent.preventDefault)
.addListener(link, 'click', this._expand, this);
}
else {
L.DomEvent.addListener(link, 'focus', this._expand, this);
}
this._map.on('movestart', this._collapse, this);
// TODO keyboard accessibility
this._container.appendChild(link);
} else {
this._expand();
}
this._baseLayersList = L.DomUtil.create('div', 'leaflet-control-layers-base', this._form);
this._separator = L.DomUtil.create('div', 'leaflet-control-layers-separator', this._form);
this._overlaysList = L.DomUtil.create('div', 'leaflet-control-layers-overlays', this._form);
this._baseLayersList = L.DomUtil.create('div', className + '-base', form);
this._separator = L.DomUtil.create('div', className + '-separator', form);
this._overlaysList = L.DomUtil.create('div', className + '-overlays', form);
this._container.appendChild(this._form);
container.appendChild(form);
},
_addLayer: function (layer, name, overlay) {
@ -128,8 +133,8 @@ L.Control.Layers = L.Control.extend({
input.name = 'leaflet-base-layers';
}
input.type = obj.overlay ? 'checkbox' : 'radio';
input.checked = this._map.hasLayer(obj.layer);
input.layerId = L.Util.stamp(obj.layer);
input.defaultChecked = this._map.hasLayer(obj.layer);
L.DomEvent.addListener(input, 'click', this._onInputClick, this);

View File

@ -0,0 +1,94 @@
L.Control.Scale = L.Control.extend({
options: {
position: 'bottomleft',
maxWidth: 100,
metric: true,
imperial: true,
updateWhenIdle: false
},
onAdd: function (map) {
this._map = map;
var className = 'leaflet-control-scale',
container = L.DomUtil.create('div', className),
options = this.options;
if (options.metric) {
this._mScale = L.DomUtil.create('div', className + '-line', container);
}
if (options.imperial) {
this._iScale = L.DomUtil.create('div', className + '-line', container);
}
map.on(options.updateWhenIdle ? 'moveend' : 'move', this._update, this);
this._update();
return container;
},
onRemove: function (map) {
map.off(this.options.updateWhenIdle ? 'moveend' : 'move', this._update, this);
},
_update: function () {
var bounds = this._map.getBounds(),
centerLat = bounds.getCenter().lat,
left = new L.LatLng(centerLat, bounds.getSouthWest().lng),
right = new L.LatLng(centerLat, bounds.getNorthEast().lng),
size = this._map.getSize(),
options = this.options,
maxMeters = left.distanceTo(right) * (options.maxWidth / size.x);
if (options.metric) {
this._updateMetric(maxMeters);
}
if (options.imperial) {
this._updateImperial(maxMeters);
}
},
_updateMetric: function (maxMeters) {
var meters = this._getRoundNum(maxMeters);
this._mScale.style.width = this._getScaleWidth(meters / maxMeters) + 'px';
this._mScale.innerHTML = meters < 1000 ? meters + ' m' : (meters / 1000) + ' km';
},
_updateImperial: function (maxMeters) {
var maxFeet = maxMeters * 3.2808399,
scale = this._iScale,
maxMiles, miles, feet;
if (maxFeet > 5280) {
maxMiles = maxFeet / 5280;
miles = this._getRoundNum(maxMiles);
scale.style.width = this._getScaleWidth(miles / maxMiles) + 'px';
scale.innerHTML = miles + ' mi';
} else {
feet = this._getRoundNum(maxFeet);
scale.style.width = this._getScaleWidth(feet / maxFeet) + 'px';
scale.innerHTML = feet + ' ft';
}
},
_getScaleWidth: function (ratio) {
return Math.round(this.options.maxWidth * ratio) - 10;
},
_getRoundNum: function (num) {
var pow10 = Math.pow(10, (Math.floor(num) + '').length - 1),
d = num / pow10;
d = d >= 10 ? 10 : d >= 5 ? 5 : d >= 2 ? 2 : 1;
return pow10 * d;
}
});

View File

@ -1,4 +1,3 @@
L.Control.Zoom = L.Control.extend({
options: {
position: 'topleft'
@ -6,28 +5,35 @@ L.Control.Zoom = L.Control.extend({
onAdd: function (map) {
var className = 'leaflet-control-zoom',
container = L.DomUtil.create('div', className),
zoomInButton = this._createButton('Zoom in', className + '-in', map.zoomIn, map),
zoomOutButton = this._createButton('Zoom out', className + '-out', map.zoomOut, map);
container = L.DomUtil.create('div', className);
container.appendChild(zoomInButton);
container.appendChild(zoomOutButton);
this._createButton('Zoom in', className + '-in', container, map.zoomIn, map);
this._createButton('Zoom out', className + '-out', container, map.zoomOut, map);
return container;
},
_createButton: function (title, className, fn, context) {
var link = document.createElement('a');
_createButton: function (title, className, container, fn, context) {
var link = L.DomUtil.create('a', className, container);
link.href = '#';
link.title = title;
link.className = className;
if (!L.Browser.touch) {
L.DomEvent.disableClickPropagation(link);
}
L.DomEvent.addListener(link, 'click', L.DomEvent.preventDefault);
L.DomEvent.addListener(link, 'click', fn, context);
L.DomEvent
.addListener(link, 'click', L.DomEvent.stopPropagation)
.addListener(link, 'click', L.DomEvent.preventDefault)
.addListener(link, 'click', fn, context);
return link;
}
});
L.Map.mergeOptions({
zoomControl: true
});
L.Map.addInitHook(function () {
if (this.options.zoomControl) {
this.zoomControl = new L.Control.Zoom();
this.addControl(this.zoomControl);
}
});

View File

@ -13,12 +13,49 @@ L.Control = L.Class.extend({
},
setPosition: function (position) {
var map = this._map;
if (map) {
map.removeControl(this);
}
this.options.position = position;
if (this._map) {
this._map.removeControl(this);
this._map.addControl(this);
if (map) {
map.addControl(this);
}
},
addTo: function (map) {
this._map = map;
var container = this._container = this.onAdd(map),
pos = this.getPosition(),
corner = map._controlCorners[pos];
L.DomUtil.addClass(container, 'leaflet-control');
if (pos.indexOf('bottom') !== -1) {
corner.insertBefore(container, corner.firstChild);
} else {
corner.appendChild(container);
}
return this;
},
removeFrom: function (map) {
var pos = this.getPosition(),
corner = map._controlCorners[pos];
corner.removeChild(this._container);
this._map = null;
if (this.onRemove) {
this.onRemove(map);
}
return this;
}
});

View File

@ -2,6 +2,7 @@
var ua = navigator.userAgent.toLowerCase(),
ie = !!window.ActiveXObject,
webkit = ua.indexOf("webkit") !== -1,
gecko = ua.indexOf("gecko") !== -1,
mobile = typeof orientation !== 'undefined' ? true : false,
android = ua.indexOf("android") !== -1,
opera = window.opera;
@ -13,9 +14,11 @@
webkit: webkit,
webkit3d: webkit && ('WebKitCSSMatrix' in window) && ('m11' in new window.WebKitCSSMatrix()),
gecko: ua.indexOf("gecko") !== -1,
gecko: gecko,
gecko3d: gecko && ('MozPerspective' in document.createElement('div').style),
opera: opera,
opera3d: opera && ('OTransition' in document.createElement('div').style),
android: android,
mobileWebkit: mobile && webkit,
@ -50,4 +53,6 @@
return touchSupported;
}())
};
L.Browser.any3d = !!L.Browser.webkit3d || !!L.Browser.gecko3d || !!L.Browser.opera3d;
}());

View File

@ -16,20 +16,15 @@ L.Class.extend = function (/*Object*/ props) /*-> Class*/ {
// instantiate class without calling constructor
var F = function () {};
F.prototype = this.prototype;
var proto = new F();
proto.constructor = NewClass;
NewClass.prototype = proto;
// add superclass access
NewClass.superclass = this.prototype;
// add class name
//proto.className = props;
//inherit parent's statics
for (var i in this) {
if (this.hasOwnProperty(i) && i !== 'prototype' && i !== 'superclass') {
if (this.hasOwnProperty(i) && i !== 'prototype') {
NewClass[i] = this[i];
}
}
@ -54,13 +49,15 @@ L.Class.extend = function (/*Object*/ props) /*-> Class*/ {
// mix given properties into the prototype
L.Util.extend(proto, props);
// allow inheriting further
NewClass.extend = L.Class.extend;
// method for adding properties to prototype
NewClass.include = function (props) {
L.Util.extend(this.prototype, props);
};
return NewClass;
};
// method for adding properties to prototype
L.Class.include = function (props) {
L.Util.extend(this.prototype, props);
};
L.Class.mergeOptions = function (options) {
L.Util.extend(this.prototype.options, options);
};

View File

@ -16,9 +16,10 @@ L.Util = {
return dest;
},
bind: function (/*Function*/ fn, /*Object*/ obj) /*-> Object*/ {
bind: function (fn, obj) { // (Function, Object) -> Function
var args = arguments.length > 2 ? Array.prototype.slice.call(arguments, 2) : null;
return function () {
return fn.apply(obj, arguments);
return fn.apply(obj, args || arguments);
};
},
@ -30,6 +31,9 @@ L.Util = {
};
}()),
// TODO refactor: remove repetition
requestAnimFrame: (function () {
function timeoutDefer(callback) {
window.setTimeout(callback, 1000 / 60);
@ -47,29 +51,48 @@ L.Util = {
if (immediate && requestFn === timeoutDefer) {
callback();
} else {
requestFn(callback, contextEl);
return requestFn.call(window, callback, contextEl);
}
};
}()),
cancelAnimFrame: (function () {
var requestFn = window.cancelAnimationFrame ||
window.webkitCancelRequestAnimationFrame ||
window.mozCancelRequestAnimationFrame ||
window.oCancelRequestAnimationFrame ||
window.msCancelRequestAnimationFrame ||
clearTimeout;
return function (handle) {
if (!handle) { return; }
return requestFn.call(window, handle);
};
}()),
limitExecByInterval: function (fn, time, context) {
var lock, execOnUnlock, args;
function exec() {
lock = false;
if (execOnUnlock) {
args.callee.apply(context, args);
execOnUnlock = false;
}
}
return function () {
args = arguments;
if (!lock) {
lock = true;
setTimeout(exec, time);
fn.apply(context, args);
} else {
var lock, execOnUnlock;
return function wrapperFn() {
var args = arguments;
if (lock) {
execOnUnlock = true;
return;
}
lock = true;
setTimeout(function () {
lock = false;
if (execOnUnlock) {
wrapperFn.apply(context, args);
execOnUnlock = false;
}
}, time);
fn.apply(context, args);
};
},
@ -84,6 +107,7 @@ L.Util = {
setOptions: function (obj, options) {
obj.options = L.Util.extend({}, obj.options, options);
return obj.options;
},
getParamString: function (obj) {
@ -104,5 +128,7 @@ L.Util = {
}
return value;
});
}
},
emptyImageUrl: 'data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs='
};

View File

@ -9,7 +9,7 @@ L.DomEvent = {
key = '_leaflet_' + type + id;
if (obj[key]) {
return;
return this;
}
var handler = function (e) {
@ -40,6 +40,8 @@ L.DomEvent = {
}
obj[key] = handler;
return this;
},
removeListener: function (/*HTMLElement*/ obj, /*String*/ type, /*Function*/ fn) {
@ -66,6 +68,8 @@ L.DomEvent = {
obj.detachEvent("on" + type, handler);
}
obj[key] = null;
return this;
},
_checkMouse: function (el, e) {
@ -109,12 +113,14 @@ L.DomEvent = {
} else {
e.cancelBubble = true;
}
return this;
},
disableClickPropagation: function (/*HTMLElement*/ el) {
L.DomEvent.addListener(el, L.Draggable.START, L.DomEvent.stopPropagation);
L.DomEvent.addListener(el, 'click', L.DomEvent.stopPropagation);
L.DomEvent.addListener(el, 'dblclick', L.DomEvent.stopPropagation);
return L.DomEvent
.addListener(el, L.Draggable.START, L.DomEvent.stopPropagation)
.addListener(el, 'click', L.DomEvent.stopPropagation)
.addListener(el, 'dblclick', L.DomEvent.stopPropagation);
},
preventDefault: function (/*Event*/ e) {
@ -123,11 +129,13 @@ L.DomEvent = {
} else {
e.returnValue = false;
}
return this;
},
stop: function (e) {
L.DomEvent.preventDefault(e);
L.DomEvent.stopPropagation(e);
return L.DomEvent
.preventDefault(e)
.stopPropagation(e);
},
getMousePosition: function (e, container) {
@ -136,6 +144,7 @@ L.DomEvent = {
y = e.pageY ? e.pageY : e.clientY +
document.body.scrollTop + document.documentElement.scrollTop,
pos = new L.Point(x, y);
return (container ?
pos.subtract(L.DomUtil.getViewportOffset(container)) : pos);
},

View File

@ -33,6 +33,12 @@ L.DomUtil = {
L.DomUtil.getStyle(el, 'position') === 'absolute') {
break;
}
if (L.DomUtil.getStyle(el, 'position') === 'fixed') {
top += docBody.scrollTop || 0;
left += docBody.scrollLeft || 0;
break;
}
el = el.offsetParent;
} while (el);
@ -98,7 +104,7 @@ L.DomUtil = {
setOpacity: function (el, value) {
if (L.Browser.ie) {
el.style.filter = 'alpha(opacity=' + Math.round(value * 100) + ')';
el.style.filter += value !== 1 ? 'alpha(opacity=' + Math.round(value * 100) + ')' : '';
} else {
el.style.opacity = value;
}
@ -133,13 +139,9 @@ L.DomUtil = {
setPosition: function (el, point) {
el._leaflet_pos = point;
if (L.Browser.webkit3d) {
if (L.Browser.any3d) {
el.style[L.DomUtil.TRANSFORM] = L.DomUtil.getTranslateString(point);
if (L.Browser.android) {
el.style['-webkit-perspective'] = '1000';
el.style['-webkit-backface-visibility'] = 'hidden';
}
el.style[L.DomUtil.BACKFACEVISIBILITY] = 'hidden';
} else {
el.style.left = point.x + 'px';
el.style.top = point.y + 'px';
@ -154,6 +156,7 @@ L.DomUtil = {
L.Util.extend(L.DomUtil, {
TRANSITION: L.DomUtil.testProp(['transition', 'webkitTransition', 'OTransition', 'MozTransition', 'msTransition']),
TRANSFORM: L.DomUtil.testProp(['transformProperty', 'WebkitTransform', 'OTransform', 'MozTransform', 'msTransform']),
BACKFACEVISIBILITY: L.DomUtil.testProp(['backfaceVisibility', 'WebkitBackfaceVisibility', 'OBackfaceVisibility', 'MozBackfaceVisibility', 'msBackfaceVisibility']),
TRANSLATE_OPEN: 'translate' + (L.Browser.webkit3d ? '3d(' : '('),
TRANSLATE_CLOSE: L.Browser.webkit3d ? ',0)' : ')'

View File

@ -31,6 +31,7 @@ L.Draggable = L.Class.extend({
}
L.DomEvent.removeListener(this._dragStartTarget, L.Draggable.START, this._onDown);
this._enabled = false;
this._moved = false;
},
_onDown: function (e) {
@ -38,7 +39,10 @@ L.Draggable = L.Class.extend({
return;
}
this._simulateClick = true;
if (e.touches && e.touches.length > 1) {
this._simulateClick = false;
return;
}
@ -86,7 +90,8 @@ L.Draggable = L.Class.extend({
var newPoint = new L.Point(first.clientX, first.clientY);
this._newPos = this._startPos.add(newPoint).subtract(this._startPoint);
L.Util.requestAnimFrame(this._updatePosition, this, true, this._dragStartTarget);
L.Util.cancelAnimFrame(this._animRequest);
this._animRequest = L.Util.requestAnimFrame(this._updatePosition, this, true, this._dragStartTarget);
},
_updatePosition: function () {
@ -96,7 +101,7 @@ L.Draggable = L.Class.extend({
},
_onUp: function (e) {
if (e.changedTouches) {
if (this._simulateClick && e.changedTouches) {
var first = e.changedTouches[0],
el = first.target,
dist = (this._newPos && this._newPos.distanceTo(this._startPos)) || 0;
@ -125,12 +130,11 @@ L.Draggable = L.Class.extend({
},
_setMovingCursor: function () {
this._bodyCursor = document.body.style.cursor;
document.body.style.cursor = 'move';
document.body.className += ' leaflet-dragging';
},
_restoreCursor: function () {
document.body.style.cursor = this._bodyCursor;
document.body.className = document.body.className.replace(/ leaflet-dragging/g, '');
},
_simulateEvent: function (type, e) {

View File

@ -20,7 +20,7 @@ L.Transition = L.Transition.extend({
// transition-property value to use with each particular custom property
CUSTOM_PROPS_PROPERTIES: {
position: L.Browser.webkit ? L.DomUtil.TRANSFORM : 'top, left'
position: L.Browser.any3d ? L.DomUtil.TRANSFORM : 'top, left'
}
};
}()),
@ -88,15 +88,18 @@ L.Transition = L.Transition.extend({
this.fire('step');
},
_onTransitionEnd: function () {
_onTransitionEnd: function (e) {
if (this._inProgress) {
this._inProgress = false;
clearInterval(this._timer);
this._el.style[L.Transition.PROPERTY] = 'none';
this._el.style[L.Transition.TRANSITION] = '';
this.fire('step');
this.fire('end');
if (e && e.type) {
this.fire('end');
}
}
}
});

View File

@ -13,17 +13,35 @@ L.LatLngBounds = L.Class.extend({
}
},
// extend the bounds to contain the given point
extend: function (/*LatLng*/ latlng) {
if (!this._southWest && !this._northEast) {
this._southWest = new L.LatLng(latlng.lat, latlng.lng, true);
this._northEast = new L.LatLng(latlng.lat, latlng.lng, true);
} else {
this._southWest.lat = Math.min(latlng.lat, this._southWest.lat);
this._southWest.lng = Math.min(latlng.lng, this._southWest.lng);
this._northEast.lat = Math.max(latlng.lat, this._northEast.lat);
this._northEast.lng = Math.max(latlng.lng, this._northEast.lng);
// extend the bounds to contain the given point or bounds
extend: function (/*LatLng or LatLngBounds*/ obj) {
if (obj instanceof L.LatLng) {
if (!this._southWest && !this._northEast) {
this._southWest = new L.LatLng(obj.lat, obj.lng, true);
this._northEast = new L.LatLng(obj.lat, obj.lng, true);
} else {
this._southWest.lat = Math.min(obj.lat, this._southWest.lat);
this._southWest.lng = Math.min(obj.lng, this._southWest.lng);
this._northEast.lat = Math.max(obj.lat, this._northEast.lat);
this._northEast.lng = Math.max(obj.lng, this._northEast.lng);
}
} else if (obj instanceof L.LatLngBounds) {
this.extend(obj._southWest);
this.extend(obj._northEast);
}
return this;
},
// extend the bounds by a percentage
pad: function (bufferRatio) { // (Number) -> LatLngBounds
var sw = this._southWest,
ne = this._northEast,
heightBuffer = Math.abs(sw.lat - ne.lat) * bufferRatio,
widthBuffer = Math.abs(sw.lng - ne.lng) * bufferRatio;
return new L.LatLngBounds(
new L.LatLng(sw.lat - heightBuffer, sw.lng - widthBuffer),
new L.LatLng(ne.lat + heightBuffer, ne.lng + widthBuffer));
},
getCenter: function () /*-> LatLng*/ {

View File

@ -3,6 +3,7 @@ L.CRS.EPSG3395 = L.Util.extend({}, L.CRS, {
code: 'EPSG:3395',
projection: L.Projection.Mercator,
transformation: (function () {
var m = L.Projection.Mercator,
r = m.R_MAJOR,

View File

@ -5,7 +5,7 @@ L.CRS.EPSG3857 = L.Util.extend({}, L.CRS, {
projection: L.Projection.SphericalMercator,
transformation: new L.Transformation(0.5 / Math.PI, 0.5, -0.5 / Math.PI, 0.5),
project: function (/*LatLng*/ latlng)/*-> Point*/ {
project: function (latlng) { // (LatLng) -> Point
var projectedPoint = this.projection.project(latlng),
earthRadius = 6378137;
return projectedPoint.multiplyBy(earthRadius);

View File

@ -0,0 +1,5 @@
L.CRS.Simple = L.Util.extend({}, L.CRS, {
projection: L.Projection.LonLat,
transformation: new L.Transformation(1, 0, 1, 0)
});

View File

@ -1,17 +1,25 @@
L.CRS = {
latLngToPoint: function (/*LatLng*/ latlng, /*Number*/ scale)/*-> Point*/ {
var projectedPoint = this.projection.project(latlng);
latLngToPoint: function (latlng, zoom) { // (LatLng, Number) -> Point
var projectedPoint = this.projection.project(latlng),
scale = this.scale(zoom);
return this.transformation._transform(projectedPoint, scale);
},
pointToLatLng: function (/*Point*/ point, /*Number*/ scale, /*(optional) Boolean*/ unbounded)/*-> LatLng*/ {
var untransformedPoint = this.transformation.untransform(point, scale);
pointToLatLng: function (point, zoom, unbounded) { // (Point, Number[, Boolean]) -> LatLng
var scale = this.scale(zoom),
untransformedPoint = this.transformation.untransform(point, scale);
return this.projection.unproject(untransformedPoint, unbounded);
//TODO get rid of 'unbounded' everywhere
},
project: function (latlng) {
return this.projection.project(latlng);
},
scale: function (zoom) {
return 256 * Math.pow(2, zoom);
}
};

View File

@ -5,7 +5,7 @@ L.Projection.Mercator = {
R_MINOR: 6356752.3142,
R_MAJOR: 6378137,
project: function (/*LatLng*/ latlng) /*-> Point*/ {
project: function (latlng) { // (LatLng) -> Point
var d = L.LatLng.DEG_TO_RAD,
max = this.MAX_LATITUDE,
lat = Math.max(Math.min(max, latlng.lat), -max),
@ -25,7 +25,7 @@ L.Projection.Mercator = {
return new L.Point(x, y);
},
unproject: function (/*Point*/ point, /*Boolean*/ unbounded) /*-> LatLng*/ {
unproject: function (point, unbounded) { // (Point, Boolean) -> LatLng
var d = L.LatLng.RAD_TO_DEG,
r = this.R_MAJOR,
r2 = this.R_MINOR,

View File

@ -2,7 +2,7 @@
L.Projection.SphericalMercator = {
MAX_LATITUDE: 85.0511287798,
project: function (/*LatLng*/ latlng) /*-> Point*/ {
project: function (latlng) { // (LatLng) -> Point
var d = L.LatLng.DEG_TO_RAD,
max = this.MAX_LATITUDE,
lat = Math.max(Math.min(max, latlng.lat), -max),
@ -13,7 +13,7 @@ L.Projection.SphericalMercator = {
return new L.Point(x, y);
},
unproject: function (/*Point*/ point, /*Boolean*/ unbounded) /*-> LatLng*/ {
unproject: function (point, unbounded) { // (Point, Boolean) -> LatLng
var d = L.LatLng.RAD_TO_DEG,
lng = point.x * d,
lat = (2 * Math.atan(Math.exp(point.y)) - (Math.PI / 2)) * d;

View File

@ -7,6 +7,7 @@ L.FeatureGroup = L.LayerGroup.extend({
addLayer: function (layer) {
this._initEvents(layer);
L.LayerGroup.prototype.addLayer.call(this, layer);
if (this._popupContent && layer.bindPopup) {
@ -16,7 +17,6 @@ L.FeatureGroup = L.LayerGroup.extend({
bindPopup: function (content) {
this._popupContent = content;
return this.invoke('bindPopup', content);
},
@ -24,17 +24,27 @@ L.FeatureGroup = L.LayerGroup.extend({
return this.invoke('setStyle', style);
},
_events: ['click', 'dblclick', 'mouseover', 'mouseout'],
getBounds: function () {
var bounds = new L.LatLngBounds();
this._iterateLayers(function (layer) {
bounds.extend(layer instanceof L.Marker ? layer.getLatLng() : layer.getBounds());
}, this);
return bounds;
},
_initEvents: function (layer) {
for (var i = 0, len = this._events.length; i < len; i++) {
layer.on(this._events[i], this._propagateEvent, this);
var events = ['click', 'dblclick', 'mouseover', 'mouseout'],
i, len;
for (i = 0, len = events.length; i < len; i++) {
layer.on(events[i], this._propagateEvent, this);
}
},
_propagateEvent: function (e) {
e.layer = e.target;
e.layer = e.target;
e.target = this;
this.fire(e.type, e);
}
});

View File

@ -1,7 +1,7 @@
L.GeoJSON = L.FeatureGroup.extend({
initialize: function (geojson, options) {
L.Util.setOptions(this, options);
this._geojson = geojson;
this._layers = {};
@ -11,23 +11,27 @@ L.GeoJSON = L.FeatureGroup.extend({
},
addGeoJSON: function (geojson) {
if (geojson.features) {
for (var i = 0, len = geojson.features.length; i < len; i++) {
this.addGeoJSON(geojson.features[i]);
var features = geojson.features,
i, len;
if (features) {
for (i = 0, len = features.length; i < len; i++) {
this.addGeoJSON(features[i]);
}
return;
}
var isFeature = (geojson.type === 'Feature'),
geometry = (isFeature ? geojson.geometry : geojson),
layer = L.GeoJSON.geometryToLayer(geometry, this.options.pointToLayer);
geometry = isFeature ? geojson.geometry : geojson,
layer = L.GeoJSON.geometryToLayer(geometry, this.options.pointToLayer);
this.fire('featureparse', {
layer: layer,
properties: geojson.properties,
geometryType: geometry.type,
bbox: geojson.bbox,
id: geojson.id
id: geojson.id,
geometry: geojson.geometry
});
this.addLayer(layer);
@ -37,10 +41,8 @@ L.GeoJSON = L.FeatureGroup.extend({
L.Util.extend(L.GeoJSON, {
geometryToLayer: function (geometry, pointToLayer) {
var coords = geometry.coordinates,
latlng, latlngs,
i, len,
layer,
layers = [];
layers = [],
latlng, latlngs, i, len, layer;
switch (geometry.type) {
case 'Point':
@ -83,22 +85,25 @@ L.Util.extend(L.GeoJSON, {
}
},
coordsToLatLng: function (/*Array*/ coords, /*Boolean*/ reverse)/*: LatLng*/ {
coordsToLatLng: function (coords, reverse) { // (Array, Boolean) -> LatLng
var lat = parseFloat(coords[reverse ? 0 : 1]),
lng = parseFloat(coords[reverse ? 1 : 0]);
lng = parseFloat(coords[reverse ? 1 : 0]);
return new L.LatLng(lat, lng, true);
},
coordsToLatLngs: function (/*Array*/ coords, /*Number*/ levelsDeep, /*Boolean*/ reverse)/*: Array*/ {
var latlng, latlngs = [],
i, len = coords.length;
coordsToLatLngs: function (coords, levelsDeep, reverse) { // (Array, Number, Boolean) -> Array
var latlng,
latlngs = [],
i, len;
for (i = 0; i < len; i++) {
for (i = 0, len = coords.length; i < len; i++) {
latlng = levelsDeep ?
this.coordsToLatLngs(coords[i], levelsDeep - 1, reverse) :
this.coordsToLatLng(coords[i], reverse);
latlngs.push(latlng);
}
return latlngs;
}
});

View File

@ -13,7 +13,7 @@ L.ImageOverlay = L.Class.extend({
this._initImage();
}
map.getPanes().overlayPane.appendChild(this._image);
map._panes.overlayPane.appendChild(this._image);
map.on('viewreset', this._reset, this);
this._reset();
@ -41,14 +41,14 @@ L.ImageOverlay = L.Class.extend({
},
_reset: function () {
var topLeft = this._map.latLngToLayerPoint(this._bounds.getNorthWest()),
bottomRight = this._map.latLngToLayerPoint(this._bounds.getSouthEast()),
size = bottomRight.subtract(topLeft);
var image = this._image,
topLeft = this._map.latLngToLayerPoint(this._bounds.getNorthWest()),
size = this._map.latLngToLayerPoint(this._bounds.getSouthEast()).subtract(topLeft);
L.DomUtil.setPosition(this._image, topLeft);
L.DomUtil.setPosition(image, topLeft);
this._image.style.width = size.x + 'px';
this._image.style.height = size.y + 'px';
image.style.width = size.x + 'px';
image.style.height = size.y + 'px';
},
_onImageLoad: function () {

View File

@ -6,8 +6,10 @@ L.LayerGroup = L.Class.extend({
initialize: function (layers) {
this._layers = {};
var i, len;
if (layers) {
for (var i = 0, len = layers.length; i < len; i++) {
for (i = 0, len = layers.length; i < len; i++) {
this.addLayer(layers[i]);
}
}
@ -15,21 +17,25 @@ L.LayerGroup = L.Class.extend({
addLayer: function (layer) {
var id = L.Util.stamp(layer);
this._layers[id] = layer;
if (this._map) {
this._map.addLayer(layer);
}
return this;
},
removeLayer: function (layer) {
var id = L.Util.stamp(layer);
delete this._layers[id];
if (this._map) {
this._map.removeLayer(layer);
}
return this;
},
@ -51,6 +57,7 @@ L.LayerGroup = L.Class.extend({
}
}
}
return this;
},
@ -61,7 +68,7 @@ L.LayerGroup = L.Class.extend({
onRemove: function (map) {
this._iterateLayers(map.removeLayer, map);
delete this._map;
this._map = null;
},
_iterateLayers: function (method, context) {

View File

@ -1,10 +1,15 @@
L.Map.mergeOptions({
closePopupOnClick: true
});
L.Popup = L.Class.extend({
includes: L.Mixin.Events,
options: {
minWidth: 50,
maxWidth: 300,
maxHeight: null,
autoPan: true,
closeButton: true,
offset: new L.Point(0, 2),
@ -20,79 +25,93 @@ L.Popup = L.Class.extend({
onAdd: function (map) {
this._map = map;
if (!this._container) {
this._initLayout();
}
this._updateContent();
this._container.style.opacity = '0';
map._panes.popupPane.appendChild(this._container);
this._map._panes.popupPane.appendChild(this._container);
this._map.on('viewreset', this._updatePosition, this);
map.on('viewreset', this._updatePosition, this);
if (this._map.options.closePopupOnClick) {
this._map.on('preclick', this._close, this);
if (L.Browser.any3d) {
map.on('zoomanim', this._zoomAnimation, this);
}
if (map.options.closePopupOnClick) {
map.on('preclick', this._close, this);
}
this._update();
this._container.style.opacity = '1'; //TODO fix ugly opacity hack
this._opened = true;
},
onRemove: function (map) {
map._panes.popupPane.removeChild(this._container);
L.Util.falseFn(this._container.offsetWidth);
map.off('viewreset', this._updatePosition, this);
map.off('click', this._close, this);
map.off('viewreset', this._updatePosition, this)
.off('preclick', this._close, this)
.off('zoomanim', this._zoomAnimation, this);
this._container.style.opacity = '0';
this._opened = false;
this._map = null;
},
setLatLng: function (latlng) {
this._latlng = latlng;
if (this._opened) {
this._update();
}
this._update();
return this;
},
setContent: function (content) {
this._content = content;
if (this._opened) {
this._update();
}
this._update();
return this;
},
_close: function () {
if (this._opened) {
this._map.closePopup();
var map = this._map;
if (map) {
map._popup = null;
map
.removeLayer(this)
.fire('popupclose', {popup: this});
}
},
_initLayout: function () {
this._container = L.DomUtil.create('div', 'leaflet-popup ' + this.options.className);
var prefix = 'leaflet-popup',
container = this._container = L.DomUtil.create('div', prefix + ' ' + this.options.className + ' leaflet-zoom-animated'),
closeButton;
if (this.options.closeButton) {
this._closeButton = L.DomUtil.create('a', 'leaflet-popup-close-button', this._container);
this._closeButton.href = '#close';
L.DomEvent.addListener(this._closeButton, 'click', this._onCloseButtonClick, this);
closeButton = this._closeButton = L.DomUtil.create('a', prefix + '-close-button', container);
closeButton.href = '#close';
L.DomEvent.addListener(closeButton, 'click', this._onCloseButtonClick, this);
}
this._wrapper = L.DomUtil.create('div', 'leaflet-popup-content-wrapper', this._container);
L.DomEvent.disableClickPropagation(this._wrapper);
this._contentNode = L.DomUtil.create('div', 'leaflet-popup-content', this._wrapper);
var wrapper = this._wrapper = L.DomUtil.create('div', prefix + '-content-wrapper', container);
L.DomEvent.disableClickPropagation(wrapper);
this._tipContainer = L.DomUtil.create('div', 'leaflet-popup-tip-container', this._container);
this._tip = L.DomUtil.create('div', 'leaflet-popup-tip', this._tipContainer);
this._contentNode = L.DomUtil.create('div', prefix + '-content', wrapper);
L.DomEvent.addListener(this._contentNode, 'mousewheel', L.DomEvent.stopPropagation);
this._tipContainer = L.DomUtil.create('div', prefix + '-tip-container', container);
this._tip = L.DomUtil.create('div', prefix + '-tip', this._tipContainer);
},
_update: function () {
if (!this._map) { return; }
this._container.style.visibility = 'hidden';
this._updateContent();
@ -105,60 +124,85 @@ L.Popup = L.Class.extend({
},
_updateContent: function () {
if (!this._content) {
return;
}
if (!this._content) { return; }
if (typeof this._content === 'string') {
this._contentNode.innerHTML = this._content;
} else {
this._contentNode.innerHTML = '';
while (this._contentNode.hasChildNodes()) {
this._contentNode.removeChild(this._contentNode.firstChild);
}
this._contentNode.appendChild(this._content);
}
this.fire('contentupdate');
},
_updateLayout: function () {
this._container.style.width = '';
this._container.style.whiteSpace = 'nowrap';
var container = this._contentNode;
var width = this._container.offsetWidth;
container.style.width = '';
container.style.whiteSpace = 'nowrap';
this._container.style.width = (width > this.options.maxWidth ?
this.options.maxWidth : (width < this.options.minWidth ? this.options.minWidth : width)) + 'px';
this._container.style.whiteSpace = '';
var width = container.offsetWidth;
width = Math.min(width, this.options.maxWidth);
width = Math.max(width, this.options.minWidth);
container.style.width = (width + 1) + 'px';
container.style.whiteSpace = '';
container.style.height = '';
var height = container.offsetHeight,
maxHeight = this.options.maxHeight,
scrolledClass = ' leaflet-popup-scrolled';
if (maxHeight && height > maxHeight) {
container.style.height = maxHeight + 'px';
container.className += scrolledClass;
} else {
container.className = container.className.replace(scrolledClass, '');
}
this._containerWidth = this._container.offsetWidth;
this._containerBottom = -this.options.offset.y;
this._containerLeft = -Math.round(this._containerWidth / 2) + this.options.offset.x;
},
_updatePosition: function () {
var pos = this._map.latLngToLayerPoint(this._latlng);
this._containerBottom = -pos.y - this.options.offset.y;
this._containerLeft = pos.x - Math.round(this._containerWidth / 2) + this.options.offset.x;
this._container.style.bottom = this._containerBottom + 'px';
this._container.style.left = this._containerLeft + 'px';
L.DomUtil.setPosition(this._container, pos);
},
_zoomAnimation: function (opt) {
var pos = this._map._latLngToNewLayerPoint(this._latlng, opt.zoom, opt.center)._round();
L.DomUtil.setPosition(this._container, pos);
},
_adjustPan: function () {
if (!this.options.autoPan) {
return;
}
if (!this.options.autoPan) { return; }
var containerHeight = this._container.offsetHeight,
layerPos = new L.Point(
this._containerLeft,
-containerHeight - this._containerBottom),
containerPos = this._map.layerPointToContainerPoint(layerPos),
var map = this._map,
containerHeight = this._container.offsetHeight,
containerWidth = this._containerWidth,
layerPos = L.DomUtil.getPosition(this._container).add(
new L.Point(this._containerLeft, -containerHeight - this._containerBottom)),
containerPos = map.layerPointToContainerPoint(layerPos),
adjustOffset = new L.Point(0, 0),
padding = this.options.autoPanPadding,
size = this._map.getSize();
padding = this.options.autoPanPadding,
size = map.getSize();
if (containerPos.x < 0) {
adjustOffset.x = containerPos.x - padding.x;
}
if (containerPos.x + this._containerWidth > size.x) {
adjustOffset.x = containerPos.x + this._containerWidth - size.x + padding.x;
if (containerPos.x + containerWidth > size.x) {
adjustOffset.x = containerPos.x + containerWidth - size.x + padding.x;
}
if (containerPos.y < 0) {
adjustOffset.y = containerPos.y - padding.y;
@ -168,7 +212,7 @@ L.Popup = L.Class.extend({
}
if (adjustOffset.x || adjustOffset.y) {
this._map.panBy(adjustOffset);
map.panBy(adjustOffset);
}
},

View File

@ -0,0 +1,20 @@
L.DivIcon = L.Icon.extend({
options: {
iconSize: new L.Point(12, 12), // also can be set through CSS
/*
iconAnchor: (Point)
popupAnchor: (Point)
*/
className: 'leaflet-div-icon'
},
createIcon: function () {
var div = document.createElement('div');
this._setIconStyles(div, 'icon');
return div;
},
createShadow: function () {
return null;
}
});

View File

@ -1,17 +1,18 @@
L.Icon = L.Class.extend({
iconUrl: L.ROOT_URL + 'images/marker.png',
shadowUrl: L.ROOT_URL + 'images/marker-shadow.png',
options: {
/*
iconUrl: (String) (required)
iconSize: (Point) (can be set through CSS)
iconAnchor: (Point) (centered by default if size is specified, can be set in CSS with negative margins)
popupAnchor: (Point) (if not specified, popup opens in the anchor point)
shadowUrl: (Point) (no shadow by default)
shadowSize: (Point)
*/
className: ''
},
iconSize: new L.Point(25, 41),
shadowSize: new L.Point(41, 41),
iconAnchor: new L.Point(13, 41),
popupAnchor: new L.Point(0, -33),
initialize: function (iconUrl) {
if (iconUrl) {
this.iconUrl = iconUrl;
}
initialize: function (options) {
L.Util.setOptions(this, options);
},
createIcon: function () {
@ -23,35 +24,45 @@ L.Icon = L.Class.extend({
},
_createIcon: function (name) {
var size = this[name + 'Size'],
src = this[name + 'Url'];
if (!src && name === 'shadow') {
return null;
}
var src = this._getIconUrl(name);
var img;
if (!src) {
img = this._createDiv();
}
else {
img = this._createImg(src);
}
img.className = 'leaflet-marker-' + name;
img.style.marginLeft = (-this.iconAnchor.x) + 'px';
img.style.marginTop = (-this.iconAnchor.y) + 'px';
if (size) {
img.style.width = size.x + 'px';
img.style.height = size.y + 'px';
}
if (!src) { return null; }
var img = this._createImg(src);
this._setIconStyles(img, name);
return img;
},
_setIconStyles: function (img, name) {
var options = this.options,
size = options[name + 'Size'],
anchor = options.iconAnchor;
if (!anchor && size) {
anchor = size.divideBy(2, true);
}
if (name === 'shadow' && anchor && options.shadowOffset) {
anchor._add(options.shadowOffset);
}
img.className = 'leaflet-marker-' + name + ' ' + options.className + ' leaflet-zoom-animated';
if (anchor) {
img.style.marginLeft = (-anchor.x) + 'px';
img.style.marginTop = (-anchor.y) + 'px';
}
if (size) {
img.style.width = size.x + 'px';
img.style.height = size.y + 'px';
}
},
_createImg: function (src) {
var el;
if (!L.Browser.ie6) {
el = document.createElement('img');
el.src = src;
@ -62,7 +73,45 @@ L.Icon = L.Class.extend({
return el;
},
_createDiv: function () {
return document.createElement('div');
_getIconUrl: function (name) {
return this.options[name + 'Url'];
}
});
// TODO move to a separate file
L.Icon.Default = L.Icon.extend({
options: {
iconSize: new L.Point(25, 41),
iconAnchor: new L.Point(13, 41),
popupAnchor: new L.Point(0, -33),
shadowSize: new L.Point(41, 41)
},
_getIconUrl: function (name) {
var path = L.Icon.Default.imagePath;
if (!path) {
throw new Error("Couldn't autodetect L.Icon.Default.imagePath, set it manually.");
}
return path + '/marker-' + name + '.png';
}
});
L.Icon.Default.imagePath = (function () {
var scripts = document.getElementsByTagName('script'),
leafletRe = /\/?leaflet[\-\._]?([\w\-\._]*)\.js\??/;
var i, len, src, matches;
for (i = 0, len = scripts.length; i < len; i++) {
src = scripts[i].src;
matches = src.match(leafletRe);
if (matches) {
return src.split(leafletRe)[0] + '/images';
}
}
}());

View File

@ -10,9 +10,7 @@ L.Handler.MarkerDrag = L.Handler.extend({
addHooks: function () {
var icon = this._marker._icon;
if (!this._draggable) {
this._draggable = new L.Draggable(icon, icon);
this._draggable
this._draggable = new L.Draggable(icon, icon)
.on('dragstart', this._onDragStart, this)
.on('drag', this._onDrag, this)
.on('dragend', this._onDragEnd, this);

View File

@ -4,8 +4,8 @@
L.Marker.include({
openPopup: function () {
this._popup.setLatLng(this._latlng);
if (this._map) {
if (this._popup && this._map) {
this._popup.setLatLng(this._latlng);
this._map.openPopup(this._popup);
}
@ -20,14 +20,20 @@ L.Marker.include({
},
bindPopup: function (content, options) {
options = L.Util.extend({offset: this.options.icon.popupAnchor}, options);
var anchor = this.options.icon.options.popupAnchor || new L.Point(0, 0);
if (options && options.offset) {
anchor = anchor.add(options.offset);
}
options = L.Util.extend({offset: anchor}, options);
if (!this._popup) {
this.on('click', this.openPopup, this);
}
this._popup = new L.Popup(options, this);
this._popup.setContent(content);
this._popup = new L.Popup(options, this)
.setContent(content);
return this;
},

View File

@ -7,11 +7,12 @@ L.Marker = L.Class.extend({
includes: L.Mixin.Events,
options: {
icon: new L.Icon(),
icon: new L.Icon.Default(),
title: '',
clickable: true,
draggable: false,
zIndexOffset: 0
zIndexOffset: 0,
opacity: 1
},
initialize: function (latlng, options) {
@ -22,9 +23,13 @@ L.Marker = L.Class.extend({
onAdd: function (map) {
this._map = map;
this._initIcon();
map.on('viewreset', this._reset, this);
if (map.options.zoomAnimation && map.options.animateMarkerZoom) {
map.on('zoomanim', this._zoomAnimation, this);
}
this._initIcon();
this._reset();
},
@ -36,9 +41,10 @@ L.Marker = L.Class.extend({
this.closePopup();
}
this._map = null;
map.off('viewreset', this._reset, this)
.off('zoomanim', this._zoomAnimation, this);
map.off('viewreset', this._reset, this);
this._map = null;
},
getLatLng: function () {
@ -47,20 +53,17 @@ L.Marker = L.Class.extend({
setLatLng: function (latlng) {
this._latlng = latlng;
if (this._icon) {
this._reset();
if (this._popup) {
this._popup.setLatLng(this._latlng);
}
this._reset();
if (this._popup) {
this._popup.setLatLng(latlng);
}
},
setZIndexOffset: function (offset) {
this.options.zIndexOffset = offset;
if (this._icon) {
this._reset();
}
this._reset();
},
setIcon: function (icon) {
@ -77,37 +80,53 @@ L.Marker = L.Class.extend({
},
_initIcon: function () {
if (!this._icon) {
this._icon = this.options.icon.createIcon();
var options = this.options;
if (this.options.title) {
this._icon.title = this.options.title;
if (!this._icon) {
this._icon = options.icon.createIcon();
if (options.title) {
this._icon.title = options.title;
}
this._initInteraction();
this._updateOpacity();
}
if (!this._shadow) {
this._shadow = this.options.icon.createShadow();
this._shadow = options.icon.createShadow();
}
this._map._panes.markerPane.appendChild(this._icon);
var panes = this._map._panes;
panes.markerPane.appendChild(this._icon);
if (this._shadow) {
this._map._panes.shadowPane.appendChild(this._shadow);
panes.shadowPane.appendChild(this._shadow);
}
},
_removeIcon: function () {
this._map._panes.markerPane.removeChild(this._icon);
var panes = this._map._panes;
panes.markerPane.removeChild(this._icon);
if (this._shadow) {
this._map._panes.shadowPane.removeChild(this._shadow);
panes.shadowPane.removeChild(this._shadow);
}
this._icon = this._shadow = null;
},
_reset: function () {
var pos = this._map.latLngToLayerPoint(this._latlng).round();
if (!this._icon) { return; }
var pos = this._map.latLngToLayerPoint(this._latlng).round();
this._setPos(pos);
},
_setPos: function (pos) {
L.DomUtil.setPosition(this._icon, pos);
if (this._shadow) {
L.DomUtil.setPosition(this._shadow, pos);
}
@ -115,16 +134,25 @@ L.Marker = L.Class.extend({
this._icon.style.zIndex = pos.y + this.options.zIndexOffset;
},
_zoomAnimation: function (opt) {
var pos = this._map._latLngToNewLayerPoint(this._latlng, opt.zoom, opt.center);
this._setPos(pos);
},
_initInteraction: function () {
if (this.options.clickable) {
this._icon.className += ' leaflet-clickable';
if (!this.options.clickable) {
return;
}
L.DomEvent.addListener(this._icon, 'click', this._onMouseClick, this);
var icon = this._icon,
events = ['dblclick', 'mousedown', 'mouseover', 'mouseout'];
var events = ['dblclick', 'mousedown', 'mouseover', 'mouseout'];
for (var i = 0; i < events.length; i++) {
L.DomEvent.addListener(this._icon, events[i], this._fireMouseEvent, this);
}
icon.className += ' leaflet-clickable';
L.DomEvent.addListener(icon, 'click', this._onMouseClick, this);
for (var i = 0; i < events.length; i++) {
L.DomEvent.addListener(icon, events[i], this._fireMouseEvent, this);
}
if (L.Handler.MarkerDrag) {
@ -139,11 +167,29 @@ L.Marker = L.Class.extend({
_onMouseClick: function (e) {
L.DomEvent.stopPropagation(e);
if (this.dragging && this.dragging.moved()) { return; }
this.fire(e.type);
if (this._map.dragging && this._map.dragging.moved()) { return; }
this.fire(e.type, {
originalEvent: e
});
},
_fireMouseEvent: function (e) {
this.fire(e.type);
L.DomEvent.stopPropagation(e);
this.fire(e.type, {
originalEvent: e
});
if (e.type !== 'mousedown') {
L.DomEvent.stopPropagation(e);
}
},
setOpacity: function (opacity) {
this.options.opacity = opacity;
if (this._map) {
this._updateOpacity();
}
},
_updateOpacity: function (opacity) {
L.DomUtil.setOpacity(this._icon, this.options.opacity);
}
});

View File

@ -8,9 +8,13 @@ L.TileLayer.Canvas = L.TileLayer.extend({
},
redraw: function () {
for (var i in this._tiles) {
var tile = this._tiles[i];
this._redrawTile(tile);
var i,
tiles = this._tiles;
for (i in tiles) {
if (tiles.hasOwnProperty(i)) {
this._redrawTile(tiles[i]);
}
}
},
@ -19,11 +23,11 @@ L.TileLayer.Canvas = L.TileLayer.extend({
},
_createTileProto: function () {
this._canvasProto = L.DomUtil.create('canvas', 'leaflet-tile');
var proto = this._canvasProto = L.DomUtil.create('canvas', 'leaflet-tile');
var tileSize = this.options.tileSize;
this._canvasProto.width = tileSize;
this._canvasProto.height = tileSize;
proto.width = tileSize;
proto.height = tileSize;
},
_createTile: function () {

View File

@ -9,37 +9,46 @@ L.TileLayer.WMS = L.TileLayer.extend({
transparent: false
},
initialize: function (/*String*/ url, /*Object*/ options) {
initialize: function (url, options) { // (String, Object)
this._url = url;
this.wmsParams = L.Util.extend({}, this.defaultWmsParams);
this.wmsParams.width = this.wmsParams.height = this.options.tileSize;
var wmsParams = L.Util.extend({}, this.defaultWmsParams);
wmsParams.width = wmsParams.height = this.options.tileSize;
for (var i in options) {
// all keys that are not TileLayer options go to WMS params
if (!this.options.hasOwnProperty(i)) {
this.wmsParams[i] = options[i];
wmsParams[i] = options[i];
}
}
this.wmsParams = wmsParams;
L.Util.setOptions(this, options);
},
onAdd: function (map, insertAtTheBottom) {
var projectionKey = (parseFloat(this.wmsParams.version) >= 1.3 ? 'crs' : 'srs');
var projectionKey = parseFloat(this.wmsParams.version) >= 1.3 ? 'crs' : 'srs';
this.wmsParams[projectionKey] = map.options.crs.code;
L.TileLayer.prototype.onAdd.call(this, map, insertAtTheBottom);
},
getTileUrl: function (/*Point*/ tilePoint, /*Number*/ zoom)/*-> String*/ {
var tileSize = this.options.tileSize,
getTileUrl: function (tilePoint, zoom) { // (Point, Number) -> String
var map = this._map,
crs = map.options.crs,
tileSize = this.options.tileSize,
nwPoint = tilePoint.multiplyBy(tileSize),
sePoint = nwPoint.add(new L.Point(tileSize, tileSize)),
nwMap = this._map.unproject(nwPoint, this._zoom, true),
seMap = this._map.unproject(sePoint, this._zoom, true),
nw = this._map.options.crs.project(nwMap),
se = this._map.options.crs.project(seMap),
nwMap = map.unproject(nwPoint, zoom, true),
seMap = map.unproject(sePoint, zoom, true),
nw = crs.project(nwMap),
se = crs.project(seMap),
bbox = [nw.x, se.y, se.x, nw.y].join(',');
return this._url + L.Util.getParamString(this.wmsParams) + "&bbox=" + bbox;

View File

@ -18,20 +18,34 @@ L.TileLayer = L.Class.extend({
noWrap: false,
zoomOffset: 0,
zoomReverse: false,
detectRetina: false,
unloadInvisibleTiles: L.Browser.mobile,
updateWhenIdle: L.Browser.mobile,
reuseTiles: false
},
initialize: function (url, options, urlParams) {
L.Util.setOptions(this, options);
initialize: function (url, options) {
options = L.Util.setOptions(this, options);
// detecting retina displays, adjusting tileSize and zoom levels
if (options.detectRetina && window.devicePixelRatio > 1 && options.maxZoom > 0) {
options.tileSize = Math.floor(options.tileSize / 2);
options.zoomOffset++;
if (options.minZoom > 0) {
options.minZoom--;
}
this.options.maxZoom--;
}
this._url = url;
this._urlParams = urlParams;
if (typeof this.options.subdomains === 'string') {
this.options.subdomains = this.options.subdomains.split('');
var subdomains = this.options.subdomains;
if (typeof subdomains === 'string') {
this.options.subdomains = subdomains.split('');
}
},
@ -47,10 +61,9 @@ L.TileLayer = L.Class.extend({
// set up events
map.on('viewreset', this._resetCallback, this);
map.on('moveend', this._update, this);
if (this.options.updateWhenIdle) {
map.on('moveend', this._update, this);
} else {
if (!this.options.updateWhenIdle) {
this._limitedUpdate = L.Util.limitExecByInterval(this._update, 150, this);
map.on('move', this._limitedUpdate, this);
}
@ -60,16 +73,17 @@ L.TileLayer = L.Class.extend({
},
onRemove: function (map) {
this._map.getPanes().tilePane.removeChild(this._container);
this._container = null;
map._panes.tilePane.removeChild(this._container);
this._map.off('viewreset', this._resetCallback, this);
map.off('viewreset', this._resetCallback, this);
map.off('moveend', this._update, this);
if (this.options.updateWhenIdle) {
this._map.off('moveend', this._update, this);
} else {
this._map.off('move', this._limitedUpdate, this);
if (!this.options.updateWhenIdle) {
map.off('move', this._limitedUpdate, this);
}
this._container = null;
this._map = null;
},
getAttribution: function () {
@ -79,26 +93,29 @@ L.TileLayer = L.Class.extend({
setOpacity: function (opacity) {
this.options.opacity = opacity;
this._setOpacity(opacity);
if (this._map) {
this._updateOpacity();
}
// stupid webkit hack to force redrawing of tiles
var i,
tiles = this._tiles;
if (L.Browser.webkit) {
for (var i in this._tiles) {
if (this._tiles.hasOwnProperty(i)) {
this._tiles[i].style.webkitTransform += ' translate(0,0)';
for (i in tiles) {
if (tiles.hasOwnProperty(i)) {
tiles[i].style.webkitTransform += ' translate(0,0)';
}
}
}
},
_setOpacity: function (opacity) {
if (opacity < 1) {
L.DomUtil.setOpacity(this._container, opacity);
}
_updateOpacity: function () {
L.DomUtil.setOpacity(this._container, this.options.opacity);
},
_initContainer: function () {
var tilePane = this._map.getPanes().tilePane,
var tilePane = this._map._panes.tilePane,
first = tilePane.firstChild;
if (!this._container || tilePane.empty) {
@ -110,7 +127,9 @@ L.TileLayer = L.Class.extend({
tilePane.appendChild(this._container);
}
this._setOpacity(this.options.opacity);
if (this.options.opacity < 1) {
this._updateOpacity();
}
}
},
@ -119,12 +138,15 @@ L.TileLayer = L.Class.extend({
},
_reset: function (clearOldContainer) {
var key;
for (key in this._tiles) {
if (this._tiles.hasOwnProperty(key)) {
this.fire("tileunload", {tile: this._tiles[key]});
var key,
tiles = this._tiles;
for (key in tiles) {
if (tiles.hasOwnProperty(key)) {
this.fire('tileunload', {tile: tiles[key]});
}
}
this._tiles = {};
if (this.options.reuseTiles) {
@ -134,13 +156,16 @@ L.TileLayer = L.Class.extend({
if (clearOldContainer && this._container) {
this._container.innerHTML = "";
}
this._initContainer();
},
_update: function () {
var bounds = this._map.getPixelBounds(),
zoom = this._map.getZoom(),
tileSize = this.options.tileSize;
_update: function (e) {
if (this._map._panTransition && this._map._panTransition._inProgress) { return; }
var bounds = this._map.getPixelBounds(),
zoom = this._map.getZoom(),
tileSize = this.options.tileSize;
if (zoom > this.options.maxZoom || zoom < this.options.minZoom) {
return;
@ -165,12 +190,12 @@ L.TileLayer = L.Class.extend({
var queue = [],
center = bounds.getCenter();
for (var j = bounds.min.y; j <= bounds.max.y; j++) {
for (var i = bounds.min.x; i <= bounds.max.x; i++) {
if ((i + ':' + j) in this._tiles) {
continue;
var j, i;
for (j = bounds.min.y; j <= bounds.max.y; j++) {
for (i = bounds.min.x; i <= bounds.max.x; i++) {
if (!((i + ':' + j) in this._tiles)) {
queue.push(new L.Point(i, j));
}
queue.push(new L.Point(i, j));
}
}
@ -182,7 +207,9 @@ L.TileLayer = L.Class.extend({
var fragment = document.createDocumentFragment();
this._tilesToLoad = queue.length;
for (var k = 0, len = this._tilesToLoad; k < len; k++) {
var k, len;
for (k = 0, len = this._tilesToLoad; k < len; k++) {
this._addTile(queue[k], fragment);
}
@ -200,40 +227,45 @@ L.TileLayer = L.Class.extend({
// remove tile if it's out of bounds
if (x < bounds.min.x || x > bounds.max.x || y < bounds.min.y || y > bounds.max.y) {
tile = this._tiles[key];
this.fire("tileunload", {tile: tile, url: tile.src});
if (tile.parentNode === this._container) {
this._container.removeChild(tile);
}
if (this.options.reuseTiles) {
this._unusedTiles.push(this._tiles[key]);
}
tile.src = 'data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=';
delete this._tiles[key];
this._removeTile(key);
}
}
}
},
_removeTile: function (key) {
var tile = this._tiles[key];
this.fire("tileunload", {tile: tile, url: tile.src});
if (tile.parentNode === this._container) {
this._container.removeChild(tile);
}
if (this.options.reuseTiles) {
this._unusedTiles.push(tile);
}
tile.src = L.Util.emptyImageUrl;
delete this._tiles[key];
},
_addTile: function (tilePoint, container) {
var tilePos = this._getTilePos(tilePoint),
zoom = this._map.getZoom(),
key = tilePoint.x + ':' + tilePoint.y,
tileLimit = Math.pow(2, this._getOffsetZoom(zoom));
key = tilePoint.x + ':' + tilePoint.y,
limit = Math.pow(2, this._getOffsetZoom(zoom));
// wrap tile coordinates
if (!this.options.continuousWorld) {
if (!this.options.noWrap) {
tilePoint.x = ((tilePoint.x % tileLimit) + tileLimit) % tileLimit;
} else if (tilePoint.x < 0 || tilePoint.x >= tileLimit) {
tilePoint.x = ((tilePoint.x % limit) + limit) % limit;
} else if (tilePoint.x < 0 || tilePoint.x >= limit) {
this._tilesToLoad--;
return;
}
if (tilePoint.y < 0 || tilePoint.y >= tileLimit) {
if (tilePoint.y < 0 || tilePoint.y >= limit) {
this._tilesToLoad--;
return;
}
@ -246,7 +278,7 @@ L.TileLayer = L.Class.extend({
this._tiles[key] = tile;
if (this.options.scheme === 'tms') {
tilePoint.y = tileLimit - tilePoint.y - 1;
tilePoint.y = limit - tilePoint.y - 1;
}
this._loadTile(tile, tilePoint, zoom);
@ -255,8 +287,9 @@ L.TileLayer = L.Class.extend({
},
_getOffsetZoom: function (zoom) {
zoom = this.options.zoomReverse ? this.options.maxZoom - zoom : zoom;
return zoom + this.options.zoomOffset;
var options = this.options;
zoom = options.zoomReverse ? options.maxZoom - zoom : zoom;
return zoom + options.zoomOffset;
},
_getTilePos: function (tilePoint) {
@ -270,23 +303,24 @@ L.TileLayer = L.Class.extend({
getTileUrl: function (tilePoint, zoom) {
var subdomains = this.options.subdomains,
s = this.options.subdomains[(tilePoint.x + tilePoint.y) % subdomains.length];
index = (tilePoint.x + tilePoint.y) % subdomains.length,
s = this.options.subdomains[index];
return L.Util.template(this._url, L.Util.extend({
s: s,
z: this._getOffsetZoom(zoom),
x: tilePoint.x,
y: tilePoint.y
}, this._urlParams));
}, this.options));
},
_createTileProto: function () {
this._tileImg = L.DomUtil.create('img', 'leaflet-tile');
this._tileImg.galleryimg = 'no';
var img = this._tileImg = L.DomUtil.create('img', 'leaflet-tile');
img.galleryimg = 'no';
var tileSize = this.options.tileSize;
this._tileImg.style.width = tileSize + 'px';
this._tileImg.style.height = tileSize + 'px';
img.style.width = tileSize + 'px';
img.style.height = tileSize + 'px';
},
_getTile: function () {
@ -309,33 +343,46 @@ L.TileLayer = L.Class.extend({
},
_loadTile: function (tile, tilePoint, zoom) {
tile._layer = this;
tile.onload = this._tileOnLoad;
tile._layer = this;
tile.onload = this._tileOnLoad;
tile.onerror = this._tileOnError;
tile.src = this.getTileUrl(tilePoint, zoom);
tile.src = this.getTileUrl(tilePoint, zoom);
},
_tileLoaded: function () {
this._tilesToLoad--;
if (!this._tilesToLoad) {
this.fire('load');
}
},
_tileOnLoad: function (e) {
var layer = this._layer;
this.className += ' leaflet-tile-loaded';
layer.fire('tileload', {tile: this, url: this.src});
layer.fire('tileload', {
tile: this,
url: this.src
});
layer._tilesToLoad--;
if (!layer._tilesToLoad) {
layer.fire('load');
}
layer._tileLoaded();
},
_tileOnError: function (e) {
var layer = this._layer;
layer.fire('tileerror', {tile: this, url: this.src});
layer.fire('tileerror', {
tile: this,
url: this.src
});
var newUrl = layer.options.errorTileUrl;
if (newUrl) {
this.src = newUrl;
}
}
layer._tileLoaded();
}
});

View File

@ -16,26 +16,38 @@ L.Circle = L.Path.extend({
setLatLng: function (latlng) {
this._latlng = latlng;
this._redraw();
return this;
return this.redraw();
},
setRadius: function (radius) {
this._mRadius = radius;
this._redraw();
return this;
return this.redraw();
},
projectLatlngs: function () {
var equatorLength = 40075017,
hLength = equatorLength * Math.cos(L.LatLng.DEG_TO_RAD * this._latlng.lat);
var lngSpan = (this._mRadius / hLength) * 360,
latlng2 = new L.LatLng(this._latlng.lat, this._latlng.lng - lngSpan, true),
var lngRadius = this._getLngRadius(),
latlng2 = new L.LatLng(this._latlng.lat, this._latlng.lng - lngRadius, true),
point2 = this._map.latLngToLayerPoint(latlng2);
this._point = this._map.latLngToLayerPoint(this._latlng);
this._radius = Math.round(this._point.x - point2.x);
this._radius = Math.max(Math.round(this._point.x - point2.x), 1);
},
getBounds: function () {
var map = this._map,
delta = this._radius * Math.cos(Math.PI / 4),
point = map.project(this._latlng),
swPoint = new L.Point(point.x - delta, point.y + delta),
nePoint = new L.Point(point.x + delta, point.y - delta),
zoom = map.getZoom(),
sw = map.unproject(swPoint, zoom, true),
ne = map.unproject(nePoint, zoom, true);
return new L.LatLngBounds(sw, ne);
},
getLatLng: function () {
return this._latlng;
},
getPathString: function () {
@ -56,8 +68,22 @@ L.Circle = L.Path.extend({
return "AL " + p.x + "," + p.y + " " + r + "," + r + " 0," + (65535 * 360);
}
},
getRadius: function () {
return this._mRadius;
},
_getLngRadius: function () {
var equatorLength = 40075017,
hLength = equatorLength * Math.cos(L.LatLng.DEG_TO_RAD * this._latlng.lat);
return (this._mRadius / hLength) * 360;
},
_checkIfEmpty: function () {
if (!this._map) {
return false;
}
var vp = this._map._pathViewport,
r = this._radius,
p = this._point;

View File

@ -19,7 +19,6 @@ L.CircleMarker = L.Circle.extend({
setRadius: function (radius) {
this._radius = radius;
this._redraw();
return this;
return this.redraw();
}
});

View File

@ -25,6 +25,8 @@
while (i < len) {
this.addLayer(new Klass(latlngs[i++], this._options));
}
return this;
}
});
}

View File

@ -4,16 +4,17 @@ L.Browser.svg = !!(document.createElementNS && document.createElementNS(L.Path.S
L.Path = L.Path.extend({
statics: {
SVG: L.Browser.svg,
_createElement: function (name) {
return document.createElementNS(L.Path.SVG_NS, name);
}
SVG: L.Browser.svg
},
getPathString: function () {
// form path string here
},
_createElement: function (name) {
return document.createElementNS(L.Path.SVG_NS, name);
},
_initElements: function () {
this._map._initPathRoot();
this._initPath();
@ -21,9 +22,9 @@ L.Path = L.Path.extend({
},
_initPath: function () {
this._container = L.Path._createElement('g');
this._container = this._createElement('g');
this._path = L.Path._createElement('path');
this._path = this._createElement('path');
this._container.appendChild(this._path);
this._map._pathRoot.appendChild(this._container);
@ -36,8 +37,6 @@ L.Path = L.Path.extend({
}
if (this.options.fill) {
this._path.setAttribute('fill-rule', 'evenodd');
} else {
this._path.setAttribute('fill', 'none');
}
this._updateStyle();
},
@ -47,10 +46,14 @@ L.Path = L.Path.extend({
this._path.setAttribute('stroke', this.options.color);
this._path.setAttribute('stroke-opacity', this.options.opacity);
this._path.setAttribute('stroke-width', this.options.weight);
} else {
this._path.setAttribute('stroke', 'none');
}
if (this.options.fill) {
this._path.setAttribute('fill', this.options.fillColor || this.options.color);
this._path.setAttribute('fill-opacity', this.options.fillOpacity);
} else {
this._path.setAttribute('fill', 'none');
}
},
@ -66,13 +69,13 @@ L.Path = L.Path.extend({
// TODO remove duplication with L.Map
_initEvents: function () {
if (this.options.clickable) {
if (!L.Browser.vml) {
if (L.Browser.svg || !L.Browser.vml) {
this._path.setAttribute('class', 'leaflet-clickable');
}
L.DomEvent.addListener(this._container, 'click', this._onMouseClick, this);
var events = ['dblclick', 'mousedown', 'mouseover', 'mouseout', 'mousemove'];
var events = ['dblclick', 'mousedown', 'mouseover', 'mouseout', 'mousemove', 'contextmenu'];
for (var i = 0; i < events.length; i++) {
L.DomEvent.addListener(this._container, events[i], this._fireMouseEvent, this);
}
@ -83,6 +86,11 @@ L.Path = L.Path.extend({
if (this._map.dragging && this._map.dragging.moved()) {
return;
}
if (e.type === 'contextmenu') {
L.DomEvent.preventDefault(e);
}
this._fireMouseEvent(e);
},
@ -90,10 +98,18 @@ L.Path = L.Path.extend({
if (!this.hasEventListeners(e.type)) {
return;
}
var map = this._map,
containerPoint = map.mouseEventToContainerPoint(e),
layerPoint = map.containerPointToLayerPoint(containerPoint),
latlng = map.layerPointToLatLng(layerPoint);
this.fire(e.type, {
latlng: this._map.mouseEventToLatLng(e),
layerPoint: this._map.mouseEventToLayerPoint(e)
latlng: latlng,
layerPoint: layerPoint,
containerPoint: containerPoint,
originalEvent: e
});
L.DomEvent.stopPropagation(e);
}
});
@ -101,15 +117,45 @@ L.Path = L.Path.extend({
L.Map.include({
_initPathRoot: function () {
if (!this._pathRoot) {
this._pathRoot = L.Path._createElement('svg');
this._pathRoot = L.Path.prototype._createElement('svg');
this._panes.overlayPane.appendChild(this._pathRoot);
if (this.options.zoomAnimation) {
this._pathRoot.setAttribute('class', ' leaflet-zoom-animated');
this.on('zoomanim', this._animatePathZoom);
this.on('zoomend', this._endPathZoom);
}
this.on('moveend', this._updateSvgViewport);
this._updateSvgViewport();
}
},
_animatePathZoom: function (opt) {
var centerOffset = this._getNewTopLeftPoint(opt.center).subtract(this._getTopLeftPoint()),
scale = Math.pow(2, opt.zoom - this._zoom),
offset = centerOffset.divideBy(1 - 1 / scale),
centerPoint = this.containerPointToLayerPoint(this.getSize().divideBy(-2)),
origin = centerPoint.add(offset).round(),
pathRootStyle = this._pathRoot.style;
pathRootStyle[L.DomUtil.TRANSFORM] = L.DomUtil.getTranslateString((origin.multiplyBy(-1).add(L.DomUtil.getPosition(this._pathRoot)).multiplyBy(scale).add(origin))) + ' scale(' + scale + ') ';
this._pathZooming = true;
},
_endPathZoom: function () {
this._pathZooming = false;
},
_updateSvgViewport: function () {
if (this._pathZooming) {
//Do not update SVGs while a zoom animation is going on otherwise the animation will break.
//When the zoom animation ends we will be updated again anyway
//This fixes the case where you do a momentum move and zoom while the move is still ongoing.
return;
}
this._updatePathViewport();
var vp = this._pathViewport,
@ -122,7 +168,7 @@ L.Map.include({
// Hack to make flicker on drag end on mobile webkit less irritating
// Unfortunately I haven't found a good workaround for this yet
if (L.Browser.webkit) {
if (L.Browser.mobileWebkit) {
pane.removeChild(root);
}
@ -131,7 +177,7 @@ L.Map.include({
root.setAttribute('height', height);
root.setAttribute('viewBox', [min.x, min.y, width, height].join(' '));
if (L.Browser.webkit) {
if (L.Browser.mobileWebkit) {
pane.appendChild(root);
}
}

View File

@ -4,90 +4,104 @@
*/
L.Browser.vml = (function () {
var d = document.createElement('div'), s;
d.innerHTML = '<v:shape adj="1"/>';
s = d.firstChild;
s.style.behavior = 'url(#default#VML)';
var div = document.createElement('div');
div.innerHTML = '<v:shape adj="1"/>';
return (s && (typeof s.adj === 'object'));
var shape = div.firstChild;
shape.style.behavior = 'url(#default#VML)';
return shape && (typeof shape.adj === 'object');
}());
L.Path = L.Browser.svg || !L.Browser.vml ? L.Path : L.Path.extend({
statics: {
VML: true,
CLIP_PADDING: 0.02,
_createElement: (function () {
try {
document.namespaces.add('lvml', 'urn:schemas-microsoft-com:vml');
return function (name) {
return document.createElement('<lvml:' + name + ' class="lvml">');
};
} catch (e) {
return function (name) {
return document.createElement('<' + name + ' xmlns="urn:schemas-microsoft.com:vml" class="lvml">');
};
}
}())
CLIP_PADDING: 0.02
},
_createElement: (function () {
try {
document.namespaces.add('lvml', 'urn:schemas-microsoft-com:vml');
return function (name) {
return document.createElement('<lvml:' + name + ' class="lvml">');
};
} catch (e) {
return function (name) {
return document.createElement('<' + name + ' xmlns="urn:schemas-microsoft.com:vml" class="lvml">');
};
}
}()),
_initPath: function () {
this._container = L.Path._createElement('shape');
this._container.className += ' leaflet-vml-shape' +
var container = this._container = this._createElement('shape');
container.className += ' leaflet-vml-shape' +
(this.options.clickable ? ' leaflet-clickable' : '');
this._container.coordsize = '1 1';
container.coordsize = '1 1';
this._path = L.Path._createElement('path');
this._container.appendChild(this._path);
this._path = this._createElement('path');
container.appendChild(this._path);
this._map._pathRoot.appendChild(this._container);
this._map._pathRoot.appendChild(container);
},
_initStyle: function () {
var container = this._container,
stroke,
fill;
if (this.options.stroke) {
this._stroke = L.Path._createElement('stroke');
this._stroke.endcap = 'round';
this._container.appendChild(this._stroke);
} else {
this._container.stroked = false;
stroke = this._stroke = this._createElement('stroke');
stroke.endcap = 'round';
container.appendChild(stroke);
}
if (this.options.fill) {
this._container.filled = true;
this._fill = L.Path._createElement('fill');
this._container.appendChild(this._fill);
} else {
this._container.filled = false;
fill = this._fill = this._createElement('fill');
container.appendChild(fill);
}
this._updateStyle();
},
_updateStyle: function () {
if (this.options.stroke) {
this._stroke.weight = this.options.weight + 'px';
this._stroke.color = this.options.color;
this._stroke.opacity = this.options.opacity;
var stroke = this._stroke,
fill = this._fill,
options = this.options,
container = this._container;
container.stroked = options.stroke;
container.filled = options.fill;
if (options.stroke) {
stroke.weight = options.weight + 'px';
stroke.color = options.color;
stroke.opacity = options.opacity;
}
if (this.options.fill) {
this._fill.color = this.options.fillColor || this.options.color;
this._fill.opacity = this.options.fillOpacity;
if (options.fill) {
fill.color = options.fillColor || options.color;
fill.opacity = options.fillOpacity;
}
},
_updatePath: function () {
this._container.style.display = 'none';
var style = this._container.style;
style.display = 'none';
this._path.v = this.getPathString() + ' '; // the space fixes IE empty path string bug
this._container.style.display = '';
style.display = '';
}
});
L.Map.include(L.Browser.svg || !L.Browser.vml ? {} : {
_initPathRoot: function () {
if (!this._pathRoot) {
this._pathRoot = document.createElement('div');
this._pathRoot.className = 'leaflet-vml-container';
this._panes.overlayPane.appendChild(this._pathRoot);
if (this._pathRoot) { return; }
this.on('moveend', this._updatePathViewport);
this._updatePathViewport();
}
var root = this._pathRoot = document.createElement('div');
root.className = 'leaflet-vml-container';
this._panes.overlayPane.appendChild(root);
this.on('moveend', this._updatePathViewport);
this._updatePathViewport();
}
});

View File

@ -21,10 +21,7 @@ L.Path = L.Class.extend({
fillColor: null, //same as color by default
fillOpacity: 0.2,
clickable: true,
// TODO remove this, as all paths now update on moveend
updateOnMoveEnd: true
clickable: true
},
initialize: function (options) {
@ -39,10 +36,9 @@ L.Path = L.Class.extend({
this.projectLatlngs();
this._updatePath();
map.on('viewreset', this.projectLatlngs, this);
this._updateTrigger = this.options.updateOnMoveEnd ? 'moveend' : 'viewreset';
map.on(this._updateTrigger, this._updatePath, this);
map
.on('viewreset', this.projectLatlngs, this)
.on('moveend', this._updatePath, this);
},
onRemove: function (map) {
@ -50,8 +46,9 @@ L.Path = L.Class.extend({
map._pathRoot.removeChild(this._container);
map.off('viewreset', this.projectLatlngs, this);
map.off(this._updateTrigger, this._updatePath, this);
map
.off('viewreset', this.projectLatlngs, this)
.off('moveend', this._updatePath, this);
},
projectLatlngs: function () {
@ -60,17 +57,20 @@ L.Path = L.Class.extend({
setStyle: function (style) {
L.Util.setOptions(this, style);
if (this._container) {
this._updateStyle();
}
return this;
},
_redraw: function () {
redraw: function () {
if (this._map) {
this.projectLatlngs();
this._updatePath();
}
return this;
}
});
@ -78,9 +78,8 @@ L.Map.include({
_updatePathViewport: function () {
var p = L.Path.CLIP_PADDING,
size = this.getSize(),
//TODO this._map._getMapPanePos()
panePos = L.DomUtil.getPosition(this._mapPane),
min = panePos.multiplyBy(-1).subtract(size.multiplyBy(p)),
min = panePos.multiplyBy(-1)._subtract(size.multiplyBy(p)),
max = min.add(size.multiplyBy(1 + p * 2));
this._pathViewport = new L.Bounds(min, max);

View File

@ -0,0 +1,210 @@
L.Handler.PolyEdit = L.Handler.extend({
options: {
icon: new L.DivIcon({
iconSize: new L.Point(8, 8),
className: 'leaflet-div-icon leaflet-editing-icon'
})
},
initialize: function (poly, options) {
this._poly = poly;
L.Util.setOptions(this, options);
},
addHooks: function () {
if (this._poly._map) {
if (!this._markerGroup) {
this._initMarkers();
}
this._poly._map.addLayer(this._markerGroup);
}
},
removeHooks: function () {
if (this._poly._map) {
this._poly._map.removeLayer(this._markerGroup);
delete this._markerGroup;
delete this._markers;
}
},
updateMarkers: function () {
this._markerGroup.clearLayers();
this._initMarkers();
},
_initMarkers: function () {
if (!this._markerGroup) {
this._markerGroup = new L.LayerGroup();
}
this._markers = [];
var latlngs = this._poly._latlngs,
i, j, len, marker;
// TODO refactor holes implementation in Polygon to support it here
for (i = 0, len = latlngs.length; i < len; i++) {
marker = this._createMarker(latlngs[i], i);
marker.on('click', this._onMarkerClick, this);
this._markers.push(marker);
}
var markerLeft, markerRight;
for (i = 0, j = len - 1; i < len; j = i++) {
if (i === 0 && !(L.Polygon && (this._poly instanceof L.Polygon))) {
continue;
}
markerLeft = this._markers[j];
markerRight = this._markers[i];
this._createMiddleMarker(markerLeft, markerRight);
this._updatePrevNext(markerLeft, markerRight);
}
},
_createMarker: function (latlng, index) {
var marker = new L.Marker(latlng, {
draggable: true,
icon: this.options.icon
});
marker._origLatLng = latlng;
marker._index = index;
marker.on('drag', this._onMarkerDrag, this);
marker.on('dragend', this._fireEdit, this);
this._markerGroup.addLayer(marker);
return marker;
},
_fireEdit: function () {
this._poly.fire('edit');
},
_onMarkerDrag: function (e) {
var marker = e.target;
L.Util.extend(marker._origLatLng, marker._latlng);
if (marker._middleLeft) {
marker._middleLeft.setLatLng(this._getMiddleLatLng(marker._prev, marker));
}
if (marker._middleRight) {
marker._middleRight.setLatLng(this._getMiddleLatLng(marker, marker._next));
}
this._poly.redraw();
},
_onMarkerClick: function (e) {
// Default action on marker click is to remove that marker, but if we remove the marker when latlng count < 3, we don't have a valid polyline anymore
if (this._poly._latlngs.length < 3) {
return;
}
var marker = e.target,
i = marker._index;
// Check existence of previous and next markers since they wouldn't exist for edge points on the polyline
if (marker._prev && marker._next) {
this._createMiddleMarker(marker._prev, marker._next);
this._updatePrevNext(marker._prev, marker._next);
}
// The marker itself is guaranteed to exist and present in the layer, since we managed to click on it
this._markerGroup.removeLayer(marker);
// Check for the existence of middle left or middle right
if (marker._middleLeft) {
this._markerGroup.removeLayer(marker._middleLeft);
}
if (marker._middleRight) {
this._markerGroup.removeLayer(marker._middleRight);
}
this._markers.splice(i, 1);
this._poly.spliceLatLngs(i, 1);
this._updateIndexes(i, -1);
this._poly.fire('edit');
},
_updateIndexes: function (index, delta) {
this._markerGroup._iterateLayers(function (marker) {
if (marker._index > index) {
marker._index += delta;
}
});
},
_createMiddleMarker: function (marker1, marker2) {
var latlng = this._getMiddleLatLng(marker1, marker2),
marker = this._createMarker(latlng),
onClick,
onDragStart,
onDragEnd;
marker.setOpacity(0.6);
marker1._middleRight = marker2._middleLeft = marker;
onDragStart = function () {
var i = marker2._index;
marker._index = i;
marker
.off('click', onClick)
.on('click', this._onMarkerClick, this);
latlng.lat = marker.getLatLng().lat;
latlng.lng = marker.getLatLng().lng;
this._poly.spliceLatLngs(i, 0, latlng);
this._markers.splice(i, 0, marker);
marker.setOpacity(1);
this._updateIndexes(i, 1);
marker2._index++;
this._updatePrevNext(marker1, marker);
this._updatePrevNext(marker, marker2);
};
onDragEnd = function () {
marker.off('dragstart', onDragStart, this);
marker.off('dragend', onDragEnd, this);
this._createMiddleMarker(marker1, marker);
this._createMiddleMarker(marker, marker2);
};
onClick = function () {
onDragStart.call(this);
onDragEnd.call(this);
this._poly.fire('edit');
};
marker
.on('click', onClick, this)
.on('dragstart', onDragStart, this)
.on('dragend', onDragEnd, this);
this._markerGroup.addLayer(marker);
},
_updatePrevNext: function (marker1, marker2) {
marker1._next = marker2;
marker2._prev = marker1;
},
_getMiddleLatLng: function (marker1, marker2) {
var map = this._poly._map,
p1 = map.latLngToLayerPoint(marker1.getLatLng()),
p2 = map.latLngToLayerPoint(marker2.getLatLng());
return map.layerPointToLatLng(p1._add(p2).divideBy(2));
}
});

View File

@ -1,17 +1,23 @@
L.Polyline = L.Path.extend({
initialize: function (latlngs, options) {
L.Path.prototype.initialize.call(this, options);
this._latlngs = latlngs;
// TODO refactor: move to Polyline.Edit.js
if (L.Handler.PolyEdit) {
this.editing = new L.Handler.PolyEdit(this);
if (this.options.editable) {
this.editing.enable();
}
}
},
options: {
// how much to simplify the polyline on each zoom level
// more = better performance and smoother look, less = more accurate
smoothFactor: 1.0,
noClip: false,
updateOnMoveEnd: true
noClip: false
},
projectLatlngs: function () {
@ -35,19 +41,17 @@ L.Polyline = L.Path.extend({
setLatLngs: function (latlngs) {
this._latlngs = latlngs;
this._redraw();
return this;
return this.redraw();
},
addLatLng: function (latlng) {
this._latlngs.push(latlng);
this._redraw();
return this;
return this.redraw();
},
spliceLatLngs: function (index, howMany) {
var removed = [].splice.apply(this._latlngs, arguments);
this._redraw();
this.redraw();
return removed;
},
@ -81,6 +85,27 @@ L.Polyline = L.Path.extend({
return b;
},
// TODO refactor: move to Polyline.Edit.js
onAdd: function (map) {
L.Path.prototype.onAdd.call(this, map);
if (this.editing && this.editing.enabled()) {
this.editing.addHooks();
}
},
onRemove: function (map) {
if (this.editing && this.editing.enabled()) {
this.editing.removeHooks();
}
L.Path.prototype.onRemove.call(this, map);
},
_initEvents: function () {
L.Path.prototype._initEvents.call(this);
},
_getPathPartStr: function (points) {
var round = L.Path.VML;

View File

@ -0,0 +1,23 @@
/*
* L.Rectangle extends Polygon and creates a rectangle when passed a LatLngBounds
*/
L.Rectangle = L.Polygon.extend({
initialize: function (latLngBounds, options) {
L.Polygon.prototype.initialize.call(this, this._boundsToLatLngs(latLngBounds), options);
},
setBounds: function (latLngBounds) {
this.setLatLngs(this._boundsToLatLngs(latLngBounds));
},
_boundsToLatLngs: function (latLngBounds) {
return [
latLngBounds.getSouthWest(),
latLngBounds.getNorthWest(),
latLngBounds.getNorthEast(),
latLngBounds.getSouthEast(),
latLngBounds.getSouthWest()
];
}
});

View File

@ -6,7 +6,7 @@ L.Circle.include(!L.Path.CANVAS ? {} : {
_drawPath: function () {
var p = this._point;
this._ctx.beginPath();
this._ctx.arc(p.x, p.y, this._radius, 0, Math.PI * 2);
this._ctx.arc(p.x, p.y, this._radius, 0, Math.PI * 2, false);
},
_containsPoint: function (p) {

View File

@ -13,22 +13,20 @@ L.Path = (L.Path.SVG && !window.L_PREFER_CANVAS) || !L.Browser.canvas ? L.Path :
SVG: false
},
options: {
updateOnMoveEnd: true
},
_initElements: function () {
this._map._initPathRoot();
this._ctx = this._map._canvasCtx;
},
_updateStyle: function () {
if (this.options.stroke) {
this._ctx.lineWidth = this.options.weight;
this._ctx.strokeStyle = this.options.color;
var options = this.options;
if (options.stroke) {
this._ctx.lineWidth = options.weight;
this._ctx.strokeStyle = options.color;
}
if (this.options.fill) {
this._ctx.fillStyle = this.options.fillColor || this.options.color;
if (options.fill) {
this._ctx.fillStyle = options.fillColor || options.color;
}
},
@ -56,34 +54,30 @@ L.Path = (L.Path.SVG && !window.L_PREFER_CANVAS) || !L.Browser.canvas ? L.Path :
},
_updatePath: function () {
if (this._checkIfEmpty()) {
return;
}
if (this._checkIfEmpty()) { return; }
var ctx = this._ctx,
options = this.options;
this._drawPath();
this._ctx.save();
ctx.save();
this._updateStyle();
var opacity = this.options.opacity,
fillOpacity = this.options.fillOpacity;
if (this.options.fill) {
if (fillOpacity < 1) {
this._ctx.globalAlpha = fillOpacity;
if (options.fill) {
if (options.fillOpacity < 1) {
ctx.globalAlpha = options.fillOpacity;
}
this._ctx.fill();
ctx.fill();
}
if (this.options.stroke) {
if (opacity < 1) {
this._ctx.globalAlpha = opacity;
if (options.stroke) {
if (options.opacity < 1) {
ctx.globalAlpha = options.opacity;
}
this._ctx.stroke();
ctx.stroke();
}
this._ctx.restore();
ctx.restore();
// TODO optimization: 1 fill/stroke for all features with equal style instead of 1 for each feature
},
@ -103,9 +97,10 @@ L.Path = (L.Path.SVG && !window.L_PREFER_CANVAS) || !L.Browser.canvas ? L.Path :
},
onRemove: function (map) {
map.off('viewreset', this._projectLatlngs, this);
map.off(this._updateTrigger, this._updatePath, this);
map.fire(this._updateTrigger);
map
.off('viewreset', this._projectLatlngs, this)
.off('moveend', this._updatePath, this)
.fire('moveend');
}
});
@ -124,12 +119,21 @@ L.Map.include((L.Path.SVG && !window.L_PREFER_CANVAS) || !L.Browser.canvas ? {}
this._panes.overlayPane.appendChild(root);
if (this.options.zoomAnimation) {
this._pathRoot.className = 'leaflet-zoom-animated';
this.on('zoomanim', this._animatePathZoom);
this.on('zoomend', this._endPathZoom);
}
this.on('moveend', this._updateCanvasViewport);
this._updateCanvasViewport();
}
},
_updateCanvasViewport: function () {
if (this._pathZooming) {
//Don't redraw while zooming. See _updateSvgViewport for more details
return;
}
this._updatePathViewport();
var vp = this._pathViewport,
@ -137,7 +141,7 @@ L.Map.include((L.Path.SVG && !window.L_PREFER_CANVAS) || !L.Browser.canvas ? {}
size = vp.max.subtract(min),
root = this._pathRoot;
//TODO check if it's works properly on mobile webkit
//TODO check if this works properly on mobile webkit
L.DomUtil.setPosition(root, min);
root.width = size.x;
root.height = size.y;

View File

@ -6,78 +6,36 @@ L.Map = L.Class.extend({
includes: L.Mixin.Events,
options: {
// projection
crs: L.CRS.EPSG3857 || L.CRS.EPSG4326,
scale: function (zoom) {
return 256 * Math.pow(2, zoom);
},
crs: L.CRS.EPSG3857,
// state
center: null,
zoom: null,
layers: [],
/*
center: LatLng,
zoom: Number,
layers: Array,
*/
// interaction
dragging: true,
touchZoom: L.Browser.touch && !L.Browser.android,
scrollWheelZoom: !L.Browser.touch,
doubleClickZoom: true,
boxZoom: true,
// controls
zoomControl: true,
attributionControl: true,
// animation
fadeAnimation: L.DomUtil.TRANSITION && !L.Browser.android,
zoomAnimation: L.DomUtil.TRANSITION && !L.Browser.android && !L.Browser.mobileOpera,
// misc
trackResize: true,
closePopupOnClick: true,
worldCopyJump: true
animateMarkerZoom: true
},
// constructor
initialize: function (id, options) { // (HTMLElement or String, Object)
L.Util.setOptions(this, options);
this._container = L.DomUtil.get(id);
if (this._container._leaflet) {
throw new Error("Map container is already initialized.");
}
this._container._leaflet = true;
options = L.Util.setOptions(this, options);
this._initContainer(id);
this._initLayout();
this._initHooks();
this._initEvents();
if (L.DomEvent) {
this._initEvents();
if (L.Handler) {
this._initInteraction();
}
if (L.Control) {
this._initControls();
}
if (options.maxBounds) {
this.setMaxBounds(options.maxBounds);
}
if (this.options.maxBounds) {
this.setMaxBounds(this.options.maxBounds);
if (options.center && typeof options.zoom !== 'undefined') {
this.setView(options.center, options.zoom, true);
}
var center = this.options.center,
zoom = this.options.zoom;
if (center !== null && zoom !== null) {
this.setView(center, zoom, true);
}
var layers = this.options.layers;
layers = (layers instanceof Array ? layers : [layers]);
this._tileLayersNum = 0;
this._initLayers(layers);
this._initLayers(options.layers);
},
@ -85,7 +43,6 @@ L.Map = L.Class.extend({
// replaced by animation-powered implementation in Map.PanAnimation.js
setView: function (center, zoom) {
// reset the map view
this._resetView(center, this._limitZoom(zoom));
return this;
},
@ -109,7 +66,8 @@ L.Map = L.Class.extend({
fitWorld: function () {
var sw = new L.LatLng(-60, -170),
ne = new L.LatLng(85, 179);
ne = new L.LatLng(85, 179);
return this.fitBounds(new L.LatLngBounds(sw, ne));
},
@ -124,9 +82,7 @@ L.Map = L.Class.extend({
this._rawPanBy(offset);
this.fire('move');
this.fire('moveend');
return this;
return this.fire('moveend');
},
setMaxBounds: function (bounds) {
@ -148,17 +104,18 @@ L.Map = L.Class.extend({
this.panInsideBounds(bounds);
}
}
return this;
},
panInsideBounds: function (bounds) {
var viewBounds = this.getBounds(),
viewSw = this.project(viewBounds.getSouthWest()),
viewNe = this.project(viewBounds.getNorthEast()),
sw = this.project(bounds.getSouthWest()),
ne = this.project(bounds.getNorthEast()),
dx = 0,
dy = 0;
viewSw = this.project(viewBounds.getSouthWest()),
viewNe = this.project(viewBounds.getNorthEast()),
sw = this.project(bounds.getSouthWest()),
ne = this.project(bounds.getNorthEast()),
dx = 0,
dy = 0;
if (viewNe.y < ne.y) { // north
dy = ne.y - viewNe.y;
@ -177,28 +134,27 @@ L.Map = L.Class.extend({
},
addLayer: function (layer, insertAtTheBottom) {
// TODO method is too big, refactor
var id = L.Util.stamp(layer);
if (this._layers[id]) {
return this;
}
if (this._layers[id]) { return this; }
this._layers[id] = layer;
// TODO getMaxZoom, getMinZoom in ILayer (instead of options)
if (layer.options && !isNaN(layer.options.maxZoom)) {
this._layersMaxZoom = Math.max(this._layersMaxZoom || 0, layer.options.maxZoom);
}
if (layer.options && !isNaN(layer.options.minZoom)) {
this._layersMinZoom = Math.min(this._layersMinZoom || Infinity, layer.options.minZoom);
}
//TODO getMaxZoom, getMinZoom in ILayer (instead of options)
// TODO looks ugly, refactor!!!
if (this.options.zoomAnimation && L.TileLayer && (layer instanceof L.TileLayer)) {
this._tileLayersNum++;
layer.on('load', this._onTileLayerLoad, this);
}
if (this.attributionControl && layer.getAttribution) {
this.attributionControl.addAttribution(layer.getAttribution());
this._tileLayersToLoad++;
layer.on('load', this._onTileLayerLoad, this);
}
var onMapLoad = function () {
@ -218,21 +174,20 @@ L.Map = L.Class.extend({
removeLayer: function (layer) {
var id = L.Util.stamp(layer);
if (this._layers[id]) {
layer.onRemove(this);
delete this._layers[id];
if (!this._layers[id]) { return; }
if (this.options.zoomAnimation && L.TileLayer && (layer instanceof L.TileLayer)) {
this._tileLayersNum--;
layer.off('load', this._onTileLayerLoad, this);
}
if (this.attributionControl && layer.getAttribution) {
this.attributionControl.removeAttribution(layer.getAttribution());
}
layer.onRemove(this);
this.fire('layerremove', {layer: layer});
delete this._layers[id];
// TODO looks ugly, refactor
if (this.options.zoomAnimation && L.TileLayer && (layer instanceof L.TileLayer)) {
this._tileLayersNum--;
this._tileLayersToLoad--;
layer.off('load', this._onTileLayerLoad, this);
}
return this;
return this.fire('layerremove', {layer: layer});
},
hasLayer: function (layer) {
@ -249,18 +204,28 @@ L.Map = L.Class.extend({
this.setMaxBounds(this.options.maxBounds);
}
if (!this._loaded) {
return this;
}
if (!this._loaded) { return this; }
this._rawPanBy(oldSize.subtract(this.getSize()).divideBy(2, true));
var offset = oldSize.subtract(this.getSize()).divideBy(2, true);
this._rawPanBy(offset);
this.fire('move');
clearTimeout(this._sizeTimer);
this._sizeTimer = setTimeout(L.Util.bind(function () {
this.fire('moveend');
}, this), 200);
this._sizeTimer = setTimeout(L.Util.bind(this.fire, this, 'moveend'), 200);
return this;
},
// TODO handler.addTo
addHandler: function (name, HandlerClass) {
if (!HandlerClass) { return; }
this[name] = new HandlerClass(this);
if (this.options[name]) {
this[name].enable();
}
return this;
},
@ -268,9 +233,10 @@ L.Map = L.Class.extend({
// public methods for getting map state
getCenter: function (unbounded) { // (Boolean)
getCenter: function (unbounded) { // (Boolean) -> LatLng
var viewHalf = this.getSize().divideBy(2),
centerPoint = this._getTopLeftPoint().add(viewHalf);
centerPoint = this._getTopLeftPoint().add(viewHalf);
return this.unproject(centerPoint, this._zoom, unbounded);
},
@ -280,36 +246,37 @@ L.Map = L.Class.extend({
getBounds: function () {
var bounds = this.getPixelBounds(),
sw = this.unproject(new L.Point(bounds.min.x, bounds.max.y), this._zoom, true),
ne = this.unproject(new L.Point(bounds.max.x, bounds.min.y), this._zoom, true);
sw = this.unproject(new L.Point(bounds.min.x, bounds.max.y), this._zoom, true),
ne = this.unproject(new L.Point(bounds.max.x, bounds.min.y), this._zoom, true);
return new L.LatLngBounds(sw, ne);
},
getMinZoom: function () {
var z1 = this.options.minZoom || 0,
z2 = this._layersMinZoom || 0,
z3 = this._boundsMinZoom || 0;
z2 = this._layersMinZoom || 0,
z3 = this._boundsMinZoom || 0;
return Math.max(z1, z2, z3);
},
getMaxZoom: function () {
var z1 = isNaN(this.options.maxZoom) ? Infinity : this.options.maxZoom,
z2 = this._layersMaxZoom || Infinity;
var z1 = typeof this.options.maxZoom === 'undefined' ? Infinity : this.options.maxZoom,
z2 = typeof this._layersMaxZoom === 'undefined' ? Infinity : this._layersMaxZoom;
return Math.min(z1, z2);
},
getBoundsZoom: function (bounds, inside) { // (LatLngBounds)
getBoundsZoom: function (bounds, inside) { // (LatLngBounds, Boolean) -> Number
var size = this.getSize(),
zoom = this.options.minZoom || 0,
maxZoom = this.getMaxZoom(),
ne = bounds.getNorthEast(),
sw = bounds.getSouthWest(),
boundsSize,
nePoint,
swPoint,
zoomNotFound = true;
zoom = this.options.minZoom || 0,
maxZoom = this.getMaxZoom(),
ne = bounds.getNorthEast(),
sw = bounds.getSouthWest(),
boundsSize,
nePoint,
swPoint,
zoomNotFound = true;
if (inside) {
zoom--;
@ -322,11 +289,11 @@ L.Map = L.Class.extend({
boundsSize = new L.Point(nePoint.x - swPoint.x, swPoint.y - nePoint.y);
if (!inside) {
zoomNotFound = (boundsSize.x <= size.x) && (boundsSize.y <= size.y);
zoomNotFound = boundsSize.x <= size.x && boundsSize.y <= size.y;
} else {
zoomNotFound = (boundsSize.x < size.x) || (boundsSize.y < size.y);
zoomNotFound = boundsSize.x < size.x || boundsSize.y < size.y;
}
} while (zoomNotFound && (zoom <= maxZoom));
} while (zoomNotFound && zoom <= maxZoom);
if (zoomNotFound && inside) {
return null;
@ -337,16 +304,18 @@ L.Map = L.Class.extend({
getSize: function () {
if (!this._size || this._sizeChanged) {
this._size = new L.Point(this._container.clientWidth, this._container.clientHeight);
this._size = new L.Point(
this._container.clientWidth,
this._container.clientHeight);
this._sizeChanged = false;
}
return this._size;
},
getPixelBounds: function () {
var topLeftPoint = this._getTopLeftPoint(),
size = this.getSize();
return new L.Bounds(topLeftPoint, topLeftPoint.add(size));
var topLeftPoint = this._getTopLeftPoint();
return new L.Bounds(topLeftPoint, topLeftPoint.add(this.getSize()));
},
getPixelOrigin: function () {
@ -356,6 +325,10 @@ L.Map = L.Class.extend({
getPanes: function () {
return this._panes;
},
getContainer: function () {
return this._container;
},
// conversion methods
@ -388,32 +361,55 @@ L.Map = L.Class.extend({
return this.project(latlng)._round()._subtract(this._initialTopLeftPoint);
},
containerPointToLatLng: function (point) {
return this.layerPointToLatLng(this.containerPointToLayerPoint(point));
},
latLngToContainerPoint: function (latlng) {
return this.layerPointToContainerPoint(this.latLngToLayerPoint(latlng));
},
project: function (latlng, zoom) { // (LatLng[, Number]) -> Point
zoom = (typeof zoom === 'undefined' ? this._zoom : zoom);
return this.options.crs.latLngToPoint(latlng, this.options.scale(zoom));
zoom = typeof zoom === 'undefined' ? this._zoom : zoom;
return this.options.crs.latLngToPoint(latlng, zoom);
},
unproject: function (point, zoom, unbounded) { // (Point[, Number, Boolean]) -> LatLng
zoom = (typeof zoom === 'undefined' ? this._zoom : zoom);
return this.options.crs.pointToLatLng(point, this.options.scale(zoom), unbounded);
// TODO remove unbounded, making it true all the time?
zoom = typeof zoom === 'undefined' ? this._zoom : zoom;
return this.options.crs.pointToLatLng(point, zoom, unbounded);
},
// private methods that modify map state
_initContainer: function (id) {
var container = this._container = L.DomUtil.get(id);
if (container._leaflet) {
throw new Error("Map container is already initialized.");
}
container._leaflet = true;
},
_initLayout: function () {
var container = this._container;
container.innerHTML = '';
container.className += ' leaflet-container';
if (L.Browser.touch) {
container.className += ' leaflet-touch';
}
if (this.options.fadeAnimation) {
container.className += ' leaflet-fade-anim';
}
var position = L.DomUtil.getStyle(container, 'position');
if (position !== 'absolute' && position !== 'relative') {
if (position !== 'absolute' && position !== 'relative' && position !== 'fixed') {
container.style.position = 'relative';
}
@ -436,13 +432,29 @@ L.Map = L.Class.extend({
panes.overlayPane = this._createPane('leaflet-overlay-pane');
panes.markerPane = this._createPane('leaflet-marker-pane');
panes.popupPane = this._createPane('leaflet-popup-pane');
if (!this.options.animateMarkerZoom) {
panes.markerPane.className += ' leaflet-zoom-hide';
panes.shadowPane.className += ' leaflet-zoom-hide';
panes.popupPane.className += ' leaflet-zoom-hide';
}
},
_createPane: function (className, container) {
return L.DomUtil.create('div', className, container || this._objectsPane);
},
_initializers: [],
_initHooks: function () {
var i, len;
for (i = 0, len = this._initializers.length; i < len; i++) {
this._initializers[i].call(this);
}
},
_resetView: function (center, zoom, preserveMapOffset, afterZoomAnim) {
var zoomChanged = (this._zoom !== zoom);
if (!afterZoomAnim) {
@ -460,17 +472,19 @@ L.Map = L.Class.extend({
if (!preserveMapOffset) {
L.DomUtil.setPosition(this._mapPane, new L.Point(0, 0));
} else {
var offset = L.DomUtil.getPosition(this._mapPane);
this._initialTopLeftPoint._add(offset);
this._initialTopLeftPoint._add(L.DomUtil.getPosition(this._mapPane));
}
this._tileLayersToLoad = this._tileLayersNum;
this.fire('viewreset', {hard: !preserveMapOffset});
this.fire('move');
if (zoomChanged || afterZoomAnim) {
this.fire('zoomend');
}
this.fire('moveend');
if (!this._loaded) {
@ -480,7 +494,10 @@ L.Map = L.Class.extend({
},
_initLayers: function (layers) {
layers = layers ? (layers instanceof Array ? layers : [layers]) : [];
this._layers = {};
this._tileLayersNum = 0;
var i, len;
@ -489,28 +506,17 @@ L.Map = L.Class.extend({
}
},
_initControls: function () {
// TODO refactor, this should happen automatically
if (this.options.zoomControl) {
this.zoomControl = new L.Control.Zoom();
this.addControl(this.zoomControl);
}
if (this.options.attributionControl) {
this.attributionControl = new L.Control.Attribution();
this.addControl(this.attributionControl);
}
},
_rawPanBy: function (offset) {
var mapPaneOffset = L.DomUtil.getPosition(this._mapPane);
L.DomUtil.setPosition(this._mapPane, mapPaneOffset.subtract(offset));
var newPos = L.DomUtil.getPosition(this._mapPane).subtract(offset);
L.DomUtil.setPosition(this._mapPane, newPos);
},
// map events
_initEvents: function () {
if (!L.DomEvent) { return; }
L.DomEvent.addListener(this._container, 'click', this._onMouseClick, this);
var events = ['dblclick', 'mousedown', 'mouseenter', 'mouseleave', 'mousemove', 'contextmenu'];
@ -531,58 +537,39 @@ L.Map = L.Class.extend({
},
_onMouseClick: function (e) {
if (!this._loaded || (this.dragging && this.dragging.moved())) {
return;
}
if (!this._loaded || (this.dragging && this.dragging.moved())) { return; }
this.fire('pre' + e.type);
this._fireMouseEvent(e);
},
_fireMouseEvent: function (e) {
if (!this._loaded) {
return;
}
if (!this._loaded) { return; }
var type = e.type;
type = (type === 'mouseenter' ? 'mouseover' : (type === 'mouseleave' ? 'mouseout' : type));
if (!this.hasEventListeners(type)) {
return;
}
if (!this.hasEventListeners(type)) { return; }
if (type === 'contextmenu') {
L.DomEvent.preventDefault(e);
}
var containerPoint = this.mouseEventToContainerPoint(e),
layerPoint = this.containerPointToLayerPoint(containerPoint),
latlng = this.layerPointToLatLng(layerPoint);
this.fire(type, {
latlng: this.mouseEventToLatLng(e),
layerPoint: this.mouseEventToLayerPoint(e)
latlng: latlng,
layerPoint: layerPoint,
containerPoint: containerPoint,
originalEvent: e
});
},
_initInteraction: function () {
var handlers = {
dragging: L.Map.Drag,
touchZoom: L.Map.TouchZoom,
doubleClickZoom: L.Map.DoubleClickZoom,
scrollWheelZoom: L.Map.ScrollWheelZoom,
boxZoom: L.Map.BoxZoom
};
var i;
for (i in handlers) {
if (handlers.hasOwnProperty(i) && handlers[i]) {
this[i] = new handlers[i](this);
if (this.options[i]) {
this[i].enable();
}
// TODO move enabling to handler contructor
}
}
},
_onTileLayerLoad: function () {
// TODO super-ugly, refactor!!!
// clear scaled tiles after all new tiles are loaded (for performance)
this._tileLayersToLoad--;
if (this._tileLayersNum && !this._tileLayersToLoad && this._tileBg) {
@ -599,18 +586,37 @@ L.Map = L.Class.extend({
throw new Error('Set map center and zoom first.');
}
var offset = L.DomUtil.getPosition(this._mapPane);
return this._initialTopLeftPoint.subtract(offset);
var mapPanePos = L.DomUtil.getPosition(this._mapPane);
return this._initialTopLeftPoint.subtract(mapPanePos);
},
_getNewTopLeftPoint: function (center) {
_getNewTopLeftPoint: function (center, zoom) {
var viewHalf = this.getSize().divideBy(2);
return this.project(center).subtract(viewHalf).round();
// TODO round on display, not calculation to increase precision?
return this.project(center, zoom)._subtract(viewHalf)._round();
},
_latLngToNewLayerPoint: function (latlng, newZoom, newCenter) {
var mapPaneOffset = L.DomUtil.getPosition(this._mapPane),
topLeft = this._getNewTopLeftPoint(newCenter, newZoom).add(mapPaneOffset);
return this.project(latlng, newZoom)._round()._subtract(topLeft);
},
_limitZoom: function (zoom) {
var min = this.getMinZoom();
var max = this.getMaxZoom();
var min = this.getMinZoom(),
max = this.getMaxZoom();
return Math.max(min, Math.min(max, zoom));
}
});
L.Map.addInitHook = function (fn) {
var args = Array.prototype.slice.call(arguments, 1);
var init = typeof fn === 'function' ? fn : function () {
this[fn].apply(this, args);
};
this.prototype._initializers.push(init);
};

View File

@ -1,6 +1,8 @@
L.Map.include(!(L.Transition && L.Transition.implemented()) ? {} : {
setView: function (center, zoom, forceReset) {
zoom = this._limitZoom(zoom);
var zoomChanged = (this._zoom !== zoom);
if (this._loaded && !forceReset && this._layers) {
@ -10,8 +12,8 @@ L.Map.include(!(L.Transition && L.Transition.implemented()) ? {} : {
center = new L.LatLng(center.lat, center.lng);
var done = (zoomChanged ?
!!this._zoomToIfCenterInView && this._zoomToIfCenterInView(center, zoom, offset) :
this._panByIfClose(offset));
this._zoomToIfCenterInView && this._zoomToIfCenterInView(center, zoom, offset) :
this._panByIfClose(offset));
// exit if animated pan or zoom started
if (done) {
@ -25,19 +27,24 @@ L.Map.include(!(L.Transition && L.Transition.implemented()) ? {} : {
return this;
},
panBy: function (offset) {
panBy: function (offset, options) {
if (!(offset.x || offset.y)) {
return this;
}
if (!this._panTransition) {
this._panTransition = new L.Transition(this._mapPane, {duration: 0.3});
this._panTransition = new L.Transition(this._mapPane);
this._panTransition.on('step', this._onPanTransitionStep, this);
this._panTransition.on('end', this._onPanTransitionEnd, this);
}
L.Util.setOptions(this._panTransition, L.Util.extend({duration: 0.25}, options));
this.fire('movestart');
this._mapPane.className += ' leaflet-pan-anim';
this._panTransition.run({
position: L.DomUtil.getPosition(this._mapPane).subtract(offset)
});
@ -50,6 +57,7 @@ L.Map.include(!(L.Transition && L.Transition.implemented()) ? {} : {
},
_onPanTransitionEnd: function () {
this._mapPane.className = this._mapPane.className.replace(/ leaflet-pan-anim/g, '');
this.fire('moveend');
},
@ -64,6 +72,7 @@ L.Map.include(!(L.Transition && L.Transition.implemented()) ? {} : {
_offsetIsWithinView: function (offset, multiplyFactor) {
var m = multiplyFactor || 1,
size = this.getSize();
return (Math.abs(offset.x) <= size.x * m) &&
(Math.abs(offset.y) <= size.y * m);
}

View File

@ -1,3 +1,7 @@
L.Map.mergeOptions({
zoomAnimation: L.DomUtil.TRANSITION && !L.Browser.android && !L.Browser.mobileOpera
});
L.Map.include(!L.DomUtil.TRANSITION ? {} : {
_zoomToIfCenterInView: function (center, zoom, centerOffset) {
@ -8,107 +12,146 @@ L.Map.include(!L.DomUtil.TRANSITION ? {} : {
return false;
}
var zoomDelta = zoom - this._zoom,
scale = Math.pow(2, zoomDelta),
var scale = Math.pow(2, zoom - this._zoom),
offset = centerOffset.divideBy(1 - 1 / scale);
//if offset does not exceed half of the view
// if offset does not exceed half of the view
if (!this._offsetIsWithinView(offset, 1)) {
return false;
}
this._mapPane.className += ' leaflet-zoom-anim';
this
this
.fire('movestart')
.fire('zoomstart');
//Hack: Disable this for android due to it not supporting double translate (mentioned in _runAnimation below)
//if Foreground layer doesn't have many tiles but bg layer does, keep the existing bg layer
if (!L.Browser.android && this._tileBg && this._getLoadedTilesPercentage(this._tileBg) > 0.5 && this._getLoadedTilesPercentage(this._tilePane) < 0.5) {
//Leave current bg and just zoom it some more
this._tilePane.style.visibility = 'hidden';
this._tilePane.empty = true;
this._stopLoadingImages(this._tilePane);
} else {
this._prepareTileBg();
}
var centerPoint = this.containerPointToLayerPoint(this.getSize().divideBy(2)),
origin = centerPoint.add(offset);
this._prepareTileBg();
this.fire('zoomanim', {
center: center,
zoom: zoom
});
this._runAnimation(center, zoom, scale, origin);
return true;
},
_runAnimation: function (center, zoom, scale, origin) {
_runAnimation: function (center, zoom, scale, origin, backwardsTransform) {
this._animatingZoom = true;
this._animateToCenter = center;
this._animateToZoom = zoom;
var transform = L.DomUtil.TRANSFORM;
var transform = L.DomUtil.TRANSFORM,
tileBg = this._tileBg;
clearTimeout(this._clearTileBgTimer);
//dumb FireFox hack, I have no idea why this magic zero translate fixes the scale transition problem
if (L.Browser.gecko || window.opera) {
this._tileBg.style[transform] += ' translate(0,0)';
tileBg.style[transform] += ' translate(0,0)';
}
var scaleStr;
// Android doesn't like translate/scale chains, transformOrigin + scale works better but
// Android 2.* doesn't like translate/scale chains, transformOrigin + scale works better but
// it breaks touch zoom which Anroid doesn't support anyway, so that's a really ugly hack
// TODO work around this prettier
if (L.Browser.android) {
this._tileBg.style[transform + 'Origin'] = origin.x + 'px ' + origin.y + 'px';
tileBg.style[transform + 'Origin'] = origin.x + 'px ' + origin.y + 'px';
scaleStr = 'scale(' + scale + ')';
} else {
scaleStr = L.DomUtil.getScaleString(scale, origin);
}
L.Util.falseFn(this._tileBg.offsetWidth); //hack to make sure transform is updated before running animation
L.Util.falseFn(tileBg.offsetWidth); //hack to make sure transform is updated before running animation
var options = {};
options[transform] = this._tileBg.style[transform] + ' ' + scaleStr;
this._tileBg.transition.run(options);
if (backwardsTransform) {
options[transform] = tileBg.style[transform] + ' ' + scaleStr;
} else {
options[transform] = scaleStr + ' ' + tileBg.style[transform];
}
tileBg.transition.run(options);
},
_prepareTileBg: function () {
if (!this._tileBg) {
this._tileBg = this._createPane('leaflet-tile-pane', this._mapPane);
this._tileBg.style.zIndex = 1;
}
var tilePane = this._tilePane,
tileBg = this._tileBg;
if (!tileBg) {
tileBg = this._tileBg = this._createPane('leaflet-tile-pane', this._mapPane);
tileBg.style.zIndex = 1;
}
// prepare the background pane to become the main tile pane
//tileBg.innerHTML = '';
tileBg.style[L.DomUtil.TRANSFORM] = '';
tileBg.style.visibility = 'hidden';
// tells tile layers to reinitialize their containers
tileBg.empty = true;
tilePane.empty = false;
tileBg.empty = true; //new FG
tilePane.empty = false; //new BG
//Switch out the current layer to be the new bg layer (And vice-versa)
this._tilePane = this._panes.tilePane = tileBg;
this._tileBg = tilePane;
var newTileBg = this._tileBg = tilePane;
if (!this._tileBg.transition) {
this._tileBg.transition = new L.Transition(this._tileBg, {duration: 0.3, easing: 'cubic-bezier(0.25,0.1,0.25,0.75)'});
this._tileBg.transition.on('end', this._onZoomTransitionEnd, this);
if (!newTileBg.transition) {
// TODO move to Map options
newTileBg.transition = new L.Transition(newTileBg, {
duration: 0.25,
easing: 'cubic-bezier(0.25,0.1,0.25,0.75)'
});
newTileBg.transition.on('end', this._onZoomTransitionEnd, this);
}
this._stopLoadingBgTiles();
this._stopLoadingImages(newTileBg);
},
_getLoadedTilesPercentage: function (container) {
var tiles = Array.prototype.slice.call(container.getElementsByTagName('img')),
i, len, count = 0;
for (i = 0, len = tiles.length; i < len; i++) {
if (tiles[i].complete) {
count++;
}
}
return count / len;
},
// stops loading all tiles in the background layer
_stopLoadingBgTiles: function () {
var tiles = [].slice.call(this._tileBg.getElementsByTagName('img'));
_stopLoadingImages: function (container) {
var tiles = Array.prototype.slice.call(container.getElementsByTagName('img')),
i, len, tile;
for (var i = 0, len = tiles.length; i < len; i++) {
if (!tiles[i].complete) {
tiles[i].onload = L.Util.falseFn;
tiles[i].onerror = L.Util.falseFn;
tiles[i].src = 'data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=';
for (i = 0, len = tiles.length; i < len; i++) {
tile = tiles[i];
tiles[i].parentNode.removeChild(tiles[i]);
tiles[i] = null;
if (!tile.complete) {
tile.onload = L.Util.falseFn;
tile.onerror = L.Util.falseFn;
tile.src = L.Util.emptyImageUrl;
tile.parentNode.removeChild(tile);
}
}
},

View File

@ -1,52 +1,30 @@
L.Map.include({
addControl: function (control) {
var container = control.onAdd(this);
control._container = container;
control._map = this;
var pos = control.getPosition(),
corner = this._controlCorners[pos];
L.DomUtil.addClass(container, 'leaflet-control');
if (pos.indexOf('bottom') !== -1) {
corner.insertBefore(container, corner.firstChild);
} else {
corner.appendChild(container);
}
control.addTo(this);
return this;
},
removeControl: function (control) {
var pos = control.getPosition(),
corner = this._controlCorners[pos];
corner.removeChild(control._container);
control._map = null;
if (control.onRemove) {
control.onRemove(this);
}
control.removeFrom(this);
return this;
},
_initControlPos: function () {
var corners = this._controlCorners = {},
classPart = 'leaflet-',
top = classPart + 'top',
bottom = classPart + 'bottom',
left = classPart + 'left',
right = classPart + 'right',
controlContainer = L.DomUtil.create('div', classPart + 'control-container', this._container);
l = 'leaflet-',
container = this._controlContainer =
L.DomUtil.create('div', l + 'control-container', this._container);
if (L.Browser.touch) {
controlContainer.className += ' ' + classPart + 'big-buttons';
function createCorner(vSide, hSide) {
var className = l + vSide + ' ' + l + hSide;
corners[vSide + hSide] =
L.DomUtil.create('div', className, container);
}
corners.topleft = L.DomUtil.create('div', top + ' ' + left, controlContainer);
corners.topright = L.DomUtil.create('div', top + ' ' + right, controlContainer);
corners.bottomleft = L.DomUtil.create('div', bottom + ' ' + left, controlContainer);
corners.bottomright = L.DomUtil.create('div', bottom + ' ' + right, controlContainer);
createCorner('top', 'left');
createCorner('top', 'right');
createCorner('bottom', 'left');
createCorner('bottom', 'right');
}
});

View File

@ -3,16 +3,18 @@
*/
L.Map.include({
_defaultLocateOptions: {
watch: false,
setView: false,
maxZoom: Infinity,
timeout: 10000,
maximumAge: 0,
enableHighAccuracy: false
},
locate: function (/*Object*/ options) {
this._locationOptions = options = L.Util.extend({
watch: false,
setView: false,
maxZoom: Infinity,
timeout: 10000,
maximumAge: 0,
enableHighAccuracy: false
}, options);
options = this._locationOptions = L.Util.extend(this._defaultLocateOptions, options);
if (!navigator.geolocation) {
return this.fire('locationerror', {
@ -36,19 +38,13 @@ L.Map.include({
if (navigator.geolocation) {
navigator.geolocation.clearWatch(this._locationWatchId);
}
},
locateAndSetView: function (maxZoom, options) {
options = L.Util.extend({
maxZoom: maxZoom || Infinity,
setView: true
}, options);
return this.locate(options);
return this;
},
_handleGeolocationError: function (error) {
var c = error.code,
message = (c === 1 ? "permission denied" :
message =
(c === 1 ? "permission denied" :
(c === 2 ? "position unavailable" : "timeout"));
if (this._locationOptions.setView && !this._loaded) {
@ -64,16 +60,19 @@ L.Map.include({
_handleGeolocationResponse: function (pos) {
var latAccuracy = 180 * pos.coords.accuracy / 4e7,
lngAccuracy = latAccuracy * 2,
lat = pos.coords.latitude,
lng = pos.coords.longitude,
latlng = new L.LatLng(lat, lng);
latlng = new L.LatLng(lat, lng),
var sw = new L.LatLng(lat - latAccuracy, lng - lngAccuracy),
sw = new L.LatLng(lat - latAccuracy, lng - lngAccuracy),
ne = new L.LatLng(lat + latAccuracy, lng + lngAccuracy),
bounds = new L.LatLngBounds(sw, ne);
bounds = new L.LatLngBounds(sw, ne),
if (this._locationOptions.setView) {
var zoom = Math.min(this.getBoundsZoom(bounds), this._locationOptions.maxZoom);
options = this._locationOptions;
if (options.setView) {
var zoom = Math.min(this.getBoundsZoom(bounds), options.maxZoom);
this.setView(latlng, zoom);
}

View File

@ -2,19 +2,18 @@
L.Map.include({
openPopup: function (popup) {
this.closePopup();
this._popup = popup;
this.addLayer(popup);
this.fire('popupopen', { popup: this._popup });
return this;
return this
.addLayer(popup)
.fire('popupopen', {popup: this._popup});
},
closePopup: function () {
if (this._popup) {
this.removeLayer(this._popup);
this.fire('popupclose', { popup: this._popup });
this._popup = null;
this._popup._close();
}
return this;
}
});
});

View File

@ -2,6 +2,10 @@
* L.Handler.ShiftDragZoom is used internally by L.Map to add shift-drag zoom (zoom to a selected bounding box).
*/
L.Map.mergeOptions({
boxZoom: true
});
L.Map.BoxZoom = L.Handler.extend({
initialize: function (map) {
this._map = map;
@ -18,9 +22,7 @@ L.Map.BoxZoom = L.Handler.extend({
},
_onMouseDown: function (e) {
if (!e.shiftKey || ((e.which !== 1) && (e.button !== 1))) {
return false;
}
if (!e.shiftKey || ((e.which !== 1) && (e.button !== 1))) { return false; }
L.DomUtil.disableTextSelection();
@ -29,28 +31,33 @@ L.Map.BoxZoom = L.Handler.extend({
this._box = L.DomUtil.create('div', 'leaflet-zoom-box', this._pane);
L.DomUtil.setPosition(this._box, this._startLayerPoint);
//TODO move cursor to styles
//TODO refactor: move cursor to styles
this._container.style.cursor = 'crosshair';
L.DomEvent.addListener(document, 'mousemove', this._onMouseMove, this);
L.DomEvent.addListener(document, 'mouseup', this._onMouseUp, this);
L.DomEvent.preventDefault(e);
L.DomEvent
.addListener(document, 'mousemove', this._onMouseMove, this)
.addListener(document, 'mouseup', this._onMouseUp, this)
.preventDefault(e);
this._map.fire("boxzoomstart");
},
_onMouseMove: function (e) {
var layerPoint = this._map.mouseEventToLayerPoint(e),
dx = layerPoint.x - this._startLayerPoint.x,
dy = layerPoint.y - this._startLayerPoint.y;
var startPoint = this._startLayerPoint,
box = this._box,
var newX = Math.min(layerPoint.x, this._startLayerPoint.x),
newY = Math.min(layerPoint.y, this._startLayerPoint.y),
newPos = new L.Point(newX, newY);
layerPoint = this._map.mouseEventToLayerPoint(e),
offset = layerPoint.subtract(startPoint),
L.DomUtil.setPosition(this._box, newPos);
newPos = new L.Point(
Math.min(layerPoint.x, startPoint.x),
Math.min(layerPoint.y, startPoint.y));
this._box.style.width = (Math.abs(dx) - 4) + 'px';
this._box.style.height = (Math.abs(dy) - 4) + 'px';
L.DomUtil.setPosition(box, newPos);
// TODO refactor: remove hardcoded 4 pixels
box.style.width = (Math.abs(offset.x) - 4) + 'px';
box.style.height = (Math.abs(offset.y) - 4) + 'px';
},
_onMouseUp: function (e) {
@ -59,15 +66,23 @@ L.Map.BoxZoom = L.Handler.extend({
L.DomUtil.enableTextSelection();
L.DomEvent.removeListener(document, 'mousemove', this._onMouseMove);
L.DomEvent.removeListener(document, 'mouseup', this._onMouseUp);
L.DomEvent
.removeListener(document, 'mousemove', this._onMouseMove)
.removeListener(document, 'mouseup', this._onMouseUp);
var layerPoint = this._map.mouseEventToLayerPoint(e);
var map = this._map,
layerPoint = map.mouseEventToLayerPoint(e);
var bounds = new L.LatLngBounds(
this._map.layerPointToLatLng(this._startLayerPoint),
this._map.layerPointToLatLng(layerPoint));
map.layerPointToLatLng(this._startLayerPoint),
map.layerPointToLatLng(layerPoint));
this._map.fitBounds(bounds);
map.fitBounds(bounds);
map.fire("boxzoomend", {
boxZoomBounds: bounds
});
}
});
L.Map.addInitHook('addHandler', 'boxZoom', L.Map.BoxZoom);

View File

@ -2,10 +2,13 @@
* L.Handler.DoubleClickZoom is used internally by L.Map to add double-click zooming.
*/
L.Map.mergeOptions({
doubleClickZoom: true
});
L.Map.DoubleClickZoom = L.Handler.extend({
addHooks: function () {
this._map.on('dblclick', this._onDoubleClick);
// TODO remove 3d argument?
},
removeHooks: function () {
@ -16,3 +19,5 @@ L.Map.DoubleClickZoom = L.Handler.extend({
this.setView(e.latlng, this._zoom + 1);
}
});
L.Map.addInitHook('addHandler', 'doubleClickZoom', L.Map.DoubleClickZoom);

View File

@ -2,6 +2,19 @@
* L.Handler.MapDrag is used internally by L.Map to make the map draggable.
*/
L.Map.mergeOptions({
dragging: true,
inertia: !L.Browser.android,
inertiaDeceleration: L.Browser.touch ? 3000 : 2000, // px/s^2
inertiaMaxSpeed: L.Browser.touch ? 1500 : 1000, // px/s
inertiaThreshold: L.Browser.touch ? 32 : 16, // ms
// TODO refactor, move to CRS
worldCopyJump: true,
continuousWorld: false
});
L.Map.Drag = L.Handler.extend({
addHooks: function () {
if (!this._draggable) {
@ -31,12 +44,36 @@ L.Map.Drag = L.Handler.extend({
},
_onDragStart: function () {
this._map
var map = this._map;
map
.fire('movestart')
.fire('dragstart');
if (map._panTransition) {
map._panTransition._onTransitionEnd(true);
}
if (map.options.inertia) {
this._positions = [];
this._times = [];
}
},
_onDrag: function () {
if (this._map.options.inertia) {
var time = this._lastTime = +new Date(),
pos = this._lastPos = this._draggable._newPos;
this._positions.push(pos);
this._times.push(time);
if (time - this._times[0] > 200) {
this._positions.shift();
this._times.shift();
}
}
this._map
.fire('move')
.fire('drag');
@ -51,7 +88,7 @@ L.Map.Drag = L.Handler.extend({
_onPreDrag: function () {
var map = this._map,
worldWidth = map.options.scale(map.getZoom()),
worldWidth = map.options.crs.scale(map.getZoom()),
halfWidth = Math.round(worldWidth / 2),
dx = this._initialWorldOffset.x,
x = this._draggable._newPos.x,
@ -63,13 +100,44 @@ L.Map.Drag = L.Handler.extend({
},
_onDragEnd: function () {
var map = this._map;
var map = this._map,
options = map.options,
delay = +new Date() - this._lastTime,
map
.fire('moveend')
.fire('dragend');
noInertia = !options.inertia ||
delay > options.inertiaThreshold ||
typeof this._positions[0] === 'undefined';
if (map.options.maxBounds) {
if (noInertia) {
map.fire('moveend');
} else {
var direction = this._lastPos.subtract(this._positions[0]),
duration = (this._lastTime + delay - this._times[0]) / 1000,
speedVector = direction.multiplyBy(0.58 / duration),
speed = speedVector.distanceTo(new L.Point(0, 0)),
limitedSpeed = Math.min(options.inertiaMaxSpeed, speed),
limitedSpeedVector = speedVector.multiplyBy(limitedSpeed / speed),
decelerationDuration = limitedSpeed / options.inertiaDeceleration,
offset = limitedSpeedVector.multiplyBy(-decelerationDuration / 2).round();
var panOptions = {
duration: decelerationDuration,
easing: 'ease-out'
};
L.Util.requestAnimFrame(L.Util.bind(function () {
this._map.panBy(offset, panOptions);
}, this));
}
map.fire('dragend');
if (options.maxBounds) {
// TODO predrag validation instead of animation
L.Util.requestAnimFrame(this._panInsideMaxBounds, map, true, map._container);
}
@ -79,3 +147,5 @@ L.Map.Drag = L.Handler.extend({
this.panInsideBounds(this.options.maxBounds);
}
});
L.Map.addInitHook('addHandler', 'dragging', L.Map.Drag);

View File

@ -2,6 +2,10 @@
* L.Handler.ScrollWheelZoom is used internally by L.Map to enable mouse scroll wheel zooming on the map.
*/
L.Map.mergeOptions({
scrollWheelZoom: !L.Browser.touch
});
L.Map.ScrollWheelZoom = L.Handler.extend({
addHooks: function () {
L.DomEvent.addListener(this._map._container, 'mousewheel', this._onWheelScroll, this);
@ -14,6 +18,7 @@ L.Map.ScrollWheelZoom = L.Handler.extend({
_onWheelScroll: function (e) {
var delta = L.DomEvent.getWheelDelta(e);
this._delta += delta;
this._lastMousePos = this._map.mouseEventToContainerPoint(e);
@ -33,9 +38,7 @@ L.Map.ScrollWheelZoom = L.Handler.extend({
this._delta = 0;
if (!delta) {
return;
}
if (!delta) { return; }
var newCenter = this._getCenterForScrollWheelZoom(this._lastMousePos, delta),
newZoom = zoom + delta;
@ -53,3 +56,5 @@ L.Map.ScrollWheelZoom = L.Handler.extend({
return map.unproject(newCenterPoint, map._zoom, true);
}
});
L.Map.addInitHook('addHandler', 'scrollWheelZoom', L.Map.ScrollWheelZoom);

View File

@ -2,6 +2,10 @@
* L.Handler.TouchZoom is used internally by L.Map to add touch-zooming on Webkit-powered mobile browsers.
*/
L.Map.mergeOptions({
touchZoom: L.Browser.touch && !L.Browser.android
});
L.Map.TouchZoom = L.Handler.extend({
addHooks: function () {
L.DomEvent.addListener(this._map._container, 'touchstart', this._onTouchStart, this);
@ -12,82 +16,101 @@ L.Map.TouchZoom = L.Handler.extend({
},
_onTouchStart: function (e) {
if (!e.touches || e.touches.length !== 2 || this._map._animatingZoom) {
return;
}
var map = this._map;
var p1 = this._map.mouseEventToLayerPoint(e.touches[0]),
p2 = this._map.mouseEventToLayerPoint(e.touches[1]),
viewCenter = this._map.containerPointToLayerPoint(this._map.getSize().divideBy(2));
if (!e.touches || e.touches.length !== 2 || map._animatingZoom || this._zooming) { return; }
var p1 = map.mouseEventToLayerPoint(e.touches[0]),
p2 = map.mouseEventToLayerPoint(e.touches[1]),
viewCenter = map.containerPointToLayerPoint(map.getSize().divideBy(2));
this._startCenter = p1.add(p2).divideBy(2, true);
this._startDist = p1.distanceTo(p2);
//this._startTransform = this._map._mapPane.style.webkitTransform;
this._moved = false;
this._zooming = true;
this._centerOffset = viewCenter.subtract(this._startCenter);
L.DomEvent.addListener(document, 'touchmove', this._onTouchMove, this);
L.DomEvent.addListener(document, 'touchend', this._onTouchEnd, this);
L.DomEvent
.addListener(document, 'touchmove', this._onTouchMove, this)
.addListener(document, 'touchend', this._onTouchEnd, this);
L.DomEvent.preventDefault(e);
},
_onTouchMove: function (e) {
if (!e.touches || e.touches.length !== 2) {
return;
}
if (!e.touches || e.touches.length !== 2) { return; }
var map = this._map;
var p1 = map.mouseEventToLayerPoint(e.touches[0]),
p2 = map.mouseEventToLayerPoint(e.touches[1]);
this._scale = p1.distanceTo(p2) / this._startDist;
this._delta = p1.add(p2).divideBy(2, true).subtract(this._startCenter);
if (this._scale === 1) { return; }
var zoom = this._map._zoom + Math.log(this._scale) / Math.LN2;
var centerOffset = this._centerOffset.subtract(this._delta).divideBy(this._scale),
centerPoint = this._map.getPixelOrigin().add(this._startCenter).add(centerOffset),
center = this._map.unproject(centerPoint);
if (!this._moved) {
this._map._mapPane.className += ' leaflet-zoom-anim';
map._mapPane.className += ' leaflet-zoom-anim leaflet-touching';
this._map
.fire('zoomstart')
map
.fire('movestart')
.fire('zoomstart')
._prepareTileBg();
this._moved = true;
}
var p1 = this._map.mouseEventToLayerPoint(e.touches[0]),
p2 = this._map.mouseEventToLayerPoint(e.touches[1]);
this._scale = p1.distanceTo(p2) / this._startDist;
this._delta = p1.add(p2).divideBy(2, true).subtract(this._startCenter);
map.fire('zoomanim', {
center: center,
zoom: zoom
});
// Used 2 translates instead of transform-origin because of a very strange bug -
// it didn't count the origin on the first touch-zoom but worked correctly afterwards
this._map._tileBg.style.webkitTransform = [
L.DomUtil.getTranslateString(this._delta),
L.DomUtil.getScaleString(this._scale, this._startCenter)
].join(" ");
map._tileBg.style[L.DomUtil.TRANSFORM] =
L.DomUtil.getTranslateString(this._delta) + ' ' +
L.DomUtil.getScaleString(this._scale, this._startCenter);
L.DomEvent.preventDefault(e);
},
_onTouchEnd: function (e) {
if (!this._moved || !this._zooming) {
return;
}
this._zooming = false;
if (!this._moved || !this._zooming) { return; }
var oldZoom = this._map.getZoom(),
this._zooming = false;
this._map._mapPane.className = this._map._mapPane.className.replace(' leaflet-touching', ''); //TODO toggleClass util
L.DomEvent
.removeListener(document, 'touchmove', this._onTouchMove)
.removeListener(document, 'touchend', this._onTouchEnd);
var centerOffset = this._centerOffset.subtract(this._delta).divideBy(this._scale),
centerPoint = this._map.getPixelOrigin().add(this._startCenter).add(centerOffset),
center = this._map.unproject(centerPoint),
oldZoom = this._map.getZoom(),
floatZoomDelta = Math.log(this._scale) / Math.LN2,
roundZoomDelta = (floatZoomDelta > 0 ? Math.ceil(floatZoomDelta) : Math.floor(floatZoomDelta)),
zoom = this._map._limitZoom(oldZoom + roundZoomDelta),
zoomDelta = zoom - oldZoom,
centerOffset = this._centerOffset.subtract(this._delta).divideBy(this._scale),
centerPoint = this._map.getPixelOrigin().add(this._startCenter).add(centerOffset),
center = this._map.unproject(centerPoint);
finalScale = Math.pow(2, zoom - oldZoom);
L.DomEvent.removeListener(document, 'touchmove', this._onTouchMove);
L.DomEvent.removeListener(document, 'touchend', this._onTouchEnd);
this._map.fire('zoomanim', {
center: center,
zoom: zoom
});
var finalScale = Math.pow(2, zoomDelta);
this._map._runAnimation(center, zoom, finalScale / this._scale, this._startCenter.add(centerOffset));
this._map._runAnimation(center, zoom, finalScale / this._scale, this._startCenter.add(centerOffset), true);
}
});
L.Map.addInitHook('addHandler', 'touchZoom', L.Map.TouchZoom);