hdalab/lib/leaflet/leaflet-src.js
changeset 118 fdf808d7d374
equal deleted inserted replaced
117:dc6c3ac62efa 118:fdf808d7d374
       
     1 /*
       
     2  Copyright (c) 2010-2011, CloudMade, Vladimir Agafonkin
       
     3  Leaflet is a modern open-source JavaScript library for interactive maps.
       
     4  http://leaflet.cloudmade.com
       
     5 */
       
     6 
       
     7 (function (root) {
       
     8 	root.L = {
       
     9 		VERSION: '0.3',
       
    10 
       
    11 		ROOT_URL: root.L_ROOT_URL || (function () {
       
    12 			var scripts = document.getElementsByTagName('script'),
       
    13 				leafletRe = /\/?leaflet[\-\._]?([\w\-\._]*)\.js\??/;
       
    14 
       
    15 			var i, len, src, matches;
       
    16 
       
    17 			for (i = 0, len = scripts.length; i < len; i++) {
       
    18 				src = scripts[i].src;
       
    19 				matches = src.match(leafletRe);
       
    20 
       
    21 				if (matches) {
       
    22 					if (matches[1] === 'include') {
       
    23 						return '../../dist/';
       
    24 					}
       
    25 					return src.replace(leafletRe, '') + '/';
       
    26 				}
       
    27 			}
       
    28 			return '';
       
    29 		}()),
       
    30 
       
    31 		noConflict: function () {
       
    32 			root.L = this._originalL;
       
    33 			return this;
       
    34 		},
       
    35 
       
    36 		_originalL: root.L
       
    37 	};
       
    38 }(this));
       
    39 
       
    40 
       
    41 /*
       
    42  * L.Util is a namespace for various utility functions.
       
    43  */
       
    44 
       
    45 L.Util = {
       
    46 	extend: function (/*Object*/ dest) /*-> Object*/ {	// merge src properties into dest
       
    47 		var sources = Array.prototype.slice.call(arguments, 1);
       
    48 		for (var j = 0, len = sources.length, src; j < len; j++) {
       
    49 			src = sources[j] || {};
       
    50 			for (var i in src) {
       
    51 				if (src.hasOwnProperty(i)) {
       
    52 					dest[i] = src[i];
       
    53 				}
       
    54 			}
       
    55 		}
       
    56 		return dest;
       
    57 	},
       
    58 
       
    59 	bind: function (/*Function*/ fn, /*Object*/ obj) /*-> Object*/ {
       
    60 		return function () {
       
    61 			return fn.apply(obj, arguments);
       
    62 		};
       
    63 	},
       
    64 
       
    65 	stamp: (function () {
       
    66 		var lastId = 0, key = '_leaflet_id';
       
    67 		return function (/*Object*/ obj) {
       
    68 			obj[key] = obj[key] || ++lastId;
       
    69 			return obj[key];
       
    70 		};
       
    71 	}()),
       
    72 
       
    73 	requestAnimFrame: (function () {
       
    74 		function timeoutDefer(callback) {
       
    75 			window.setTimeout(callback, 1000 / 60);
       
    76 		}
       
    77 
       
    78 		var requestFn = window.requestAnimationFrame ||
       
    79 			window.webkitRequestAnimationFrame ||
       
    80 			window.mozRequestAnimationFrame ||
       
    81 			window.oRequestAnimationFrame ||
       
    82 			window.msRequestAnimationFrame ||
       
    83 			timeoutDefer;
       
    84 
       
    85 		return function (callback, context, immediate, contextEl) {
       
    86 			callback = context ? L.Util.bind(callback, context) : callback;
       
    87 			if (immediate && requestFn === timeoutDefer) {
       
    88 				callback();
       
    89 			} else {
       
    90 				requestFn(callback, contextEl);
       
    91 			}
       
    92 		};
       
    93 	}()),
       
    94 
       
    95 	limitExecByInterval: function (fn, time, context) {
       
    96 		var lock, execOnUnlock, args;
       
    97 		function exec() {
       
    98 			lock = false;
       
    99 			if (execOnUnlock) {
       
   100 				args.callee.apply(context, args);
       
   101 				execOnUnlock = false;
       
   102 			}
       
   103 		}
       
   104 		return function () {
       
   105 			args = arguments;
       
   106 			if (!lock) {
       
   107 				lock = true;
       
   108 				setTimeout(exec, time);
       
   109 				fn.apply(context, args);
       
   110 			} else {
       
   111 				execOnUnlock = true;
       
   112 			}
       
   113 		};
       
   114 	},
       
   115 
       
   116 	falseFn: function () {
       
   117 		return false;
       
   118 	},
       
   119 
       
   120 	formatNum: function (num, digits) {
       
   121 		var pow = Math.pow(10, digits || 5);
       
   122 		return Math.round(num * pow) / pow;
       
   123 	},
       
   124 
       
   125 	setOptions: function (obj, options) {
       
   126 		obj.options = L.Util.extend({}, obj.options, options);
       
   127 	},
       
   128 
       
   129 	getParamString: function (obj) {
       
   130 		var params = [];
       
   131 		for (var i in obj) {
       
   132 			if (obj.hasOwnProperty(i)) {
       
   133 				params.push(i + '=' + obj[i]);
       
   134 			}
       
   135 		}
       
   136 		return '?' + params.join('&');
       
   137 	},
       
   138 
       
   139 	template: function (str, data) {
       
   140 		return str.replace(/\{ *([\w_]+) *\}/g, function (str, key) {
       
   141 			var value = data[key];
       
   142 			if (!data.hasOwnProperty(key)) {
       
   143 				throw new Error('No value provided for variable ' + str);
       
   144 			}
       
   145 			return value;
       
   146 		});
       
   147 	}
       
   148 };
       
   149 
       
   150 
       
   151 /*
       
   152  * Class powers the OOP facilities of the library. Thanks to John Resig and Dean Edwards for inspiration!
       
   153  */
       
   154 
       
   155 L.Class = function () {};
       
   156 
       
   157 L.Class.extend = function (/*Object*/ props) /*-> Class*/ {
       
   158 
       
   159 	// extended class with the new prototype
       
   160 	var NewClass = function () {
       
   161 		if (this.initialize) {
       
   162 			this.initialize.apply(this, arguments);
       
   163 		}
       
   164 	};
       
   165 
       
   166 	// instantiate class without calling constructor
       
   167 	var F = function () {};
       
   168 	F.prototype = this.prototype;
       
   169 	var proto = new F();
       
   170 
       
   171 	proto.constructor = NewClass;
       
   172 	NewClass.prototype = proto;
       
   173 
       
   174 	// add superclass access
       
   175 	NewClass.superclass = this.prototype;
       
   176 
       
   177 	// add class name
       
   178 	//proto.className = props;
       
   179 
       
   180 	//inherit parent's statics
       
   181 	for (var i in this) {
       
   182 		if (this.hasOwnProperty(i) && i !== 'prototype' && i !== 'superclass') {
       
   183 			NewClass[i] = this[i];
       
   184 		}
       
   185 	}
       
   186 
       
   187 	// mix static properties into the class
       
   188 	if (props.statics) {
       
   189 		L.Util.extend(NewClass, props.statics);
       
   190 		delete props.statics;
       
   191 	}
       
   192 
       
   193 	// mix includes into the prototype
       
   194 	if (props.includes) {
       
   195 		L.Util.extend.apply(null, [proto].concat(props.includes));
       
   196 		delete props.includes;
       
   197 	}
       
   198 
       
   199 	// merge options
       
   200 	if (props.options && proto.options) {
       
   201 		props.options = L.Util.extend({}, proto.options, props.options);
       
   202 	}
       
   203 
       
   204 	// mix given properties into the prototype
       
   205 	L.Util.extend(proto, props);
       
   206 
       
   207 	// allow inheriting further
       
   208 	NewClass.extend = L.Class.extend;
       
   209 
       
   210 	// method for adding properties to prototype
       
   211 	NewClass.include = function (props) {
       
   212 		L.Util.extend(this.prototype, props);
       
   213 	};
       
   214 
       
   215 	return NewClass;
       
   216 };
       
   217 
       
   218 
       
   219 /*
       
   220  * L.Mixin.Events adds custom events functionality to Leaflet classes
       
   221  */
       
   222 
       
   223 L.Mixin = {};
       
   224 
       
   225 L.Mixin.Events = {
       
   226 	addEventListener: function (/*String*/ type, /*Function*/ fn, /*(optional) Object*/ context) {
       
   227 		var events = this._leaflet_events = this._leaflet_events || {};
       
   228 		events[type] = events[type] || [];
       
   229 		events[type].push({
       
   230 			action: fn,
       
   231 			context: context || this
       
   232 		});
       
   233 		return this;
       
   234 	},
       
   235 
       
   236 	hasEventListeners: function (/*String*/ type) /*-> Boolean*/ {
       
   237 		var k = '_leaflet_events';
       
   238 		return (k in this) && (type in this[k]) && (this[k][type].length > 0);
       
   239 	},
       
   240 
       
   241 	removeEventListener: function (/*String*/ type, /*Function*/ fn, /*(optional) Object*/ context) {
       
   242 		if (!this.hasEventListeners(type)) {
       
   243 			return this;
       
   244 		}
       
   245 
       
   246 		for (var i = 0, events = this._leaflet_events, len = events[type].length; i < len; i++) {
       
   247 			if (
       
   248 				(events[type][i].action === fn) &&
       
   249 				(!context || (events[type][i].context === context))
       
   250 			) {
       
   251 				events[type].splice(i, 1);
       
   252 				return this;
       
   253 			}
       
   254 		}
       
   255 		return this;
       
   256 	},
       
   257 
       
   258 	fireEvent: function (/*String*/ type, /*(optional) Object*/ data) {
       
   259 		if (!this.hasEventListeners(type)) {
       
   260 			return this;
       
   261 		}
       
   262 
       
   263 		var event = L.Util.extend({
       
   264 			type: type,
       
   265 			target: this
       
   266 		}, data);
       
   267 
       
   268 		var listeners = this._leaflet_events[type].slice();
       
   269 
       
   270 		for (var i = 0, len = listeners.length; i < len; i++) {
       
   271 			listeners[i].action.call(listeners[i].context || this, event);
       
   272 		}
       
   273 
       
   274 		return this;
       
   275 	}
       
   276 };
       
   277 
       
   278 L.Mixin.Events.on = L.Mixin.Events.addEventListener;
       
   279 L.Mixin.Events.off = L.Mixin.Events.removeEventListener;
       
   280 L.Mixin.Events.fire = L.Mixin.Events.fireEvent;
       
   281 
       
   282 
       
   283 (function () {
       
   284 	var ua = navigator.userAgent.toLowerCase(),
       
   285 		ie = !!window.ActiveXObject,
       
   286 		webkit = ua.indexOf("webkit") !== -1,
       
   287 		mobile = typeof orientation !== 'undefined' ? true : false,
       
   288 		android = ua.indexOf("android") !== -1,
       
   289 		opera = window.opera;
       
   290 
       
   291 	L.Browser = {
       
   292 		ie: ie,
       
   293 		ie6: ie && !window.XMLHttpRequest,
       
   294 
       
   295 		webkit: webkit,
       
   296 		webkit3d: webkit && ('WebKitCSSMatrix' in window) && ('m11' in new window.WebKitCSSMatrix()),
       
   297 
       
   298 		gecko: ua.indexOf("gecko") !== -1,
       
   299 
       
   300 		opera: opera,
       
   301 
       
   302 		android: android,
       
   303 		mobileWebkit: mobile && webkit,
       
   304 		mobileOpera: mobile && opera,
       
   305 
       
   306 		mobile: mobile,
       
   307 		touch: (function () {
       
   308 			var touchSupported = false,
       
   309 				startName = 'ontouchstart';
       
   310 
       
   311 			// WebKit, etc
       
   312 			if (startName in document.documentElement) {
       
   313 				return true;
       
   314 			}
       
   315 
       
   316 			// Firefox/Gecko
       
   317 			var e = document.createElement('div');
       
   318 
       
   319 			// If no support for basic event stuff, unlikely to have touch support
       
   320 			if (!e.setAttribute || !e.removeAttribute) {
       
   321 				return false;
       
   322 			}
       
   323 
       
   324 			e.setAttribute(startName, 'return;');
       
   325 			if (typeof e[startName] === 'function') {
       
   326 				touchSupported = true;
       
   327 			}
       
   328 
       
   329 			e.removeAttribute(startName);
       
   330 			e = null;
       
   331 
       
   332 			return touchSupported;
       
   333 		}())
       
   334 	};
       
   335 }());
       
   336 
       
   337 
       
   338 /*
       
   339  * L.Point represents a point with x and y coordinates.
       
   340  */
       
   341 
       
   342 L.Point = function (/*Number*/ x, /*Number*/ y, /*Boolean*/ round) {
       
   343 	this.x = (round ? Math.round(x) : x);
       
   344 	this.y = (round ? Math.round(y) : y);
       
   345 };
       
   346 
       
   347 L.Point.prototype = {
       
   348 	add: function (point) {
       
   349 		return this.clone()._add(point);
       
   350 	},
       
   351 
       
   352 	_add: function (point) {
       
   353 		this.x += point.x;
       
   354 		this.y += point.y;
       
   355 		return this;
       
   356 	},
       
   357 
       
   358 	subtract: function (point) {
       
   359 		return this.clone()._subtract(point);
       
   360 	},
       
   361 
       
   362 	// destructive subtract (faster)
       
   363 	_subtract: function (point) {
       
   364 		this.x -= point.x;
       
   365 		this.y -= point.y;
       
   366 		return this;
       
   367 	},
       
   368 
       
   369 	divideBy: function (num, round) {
       
   370 		return new L.Point(this.x / num, this.y / num, round);
       
   371 	},
       
   372 
       
   373 	multiplyBy: function (num) {
       
   374 		return new L.Point(this.x * num, this.y * num);
       
   375 	},
       
   376 
       
   377 	distanceTo: function (point) {
       
   378 		var x = point.x - this.x,
       
   379 			y = point.y - this.y;
       
   380 		return Math.sqrt(x * x + y * y);
       
   381 	},
       
   382 
       
   383 	round: function () {
       
   384 		return this.clone()._round();
       
   385 	},
       
   386 
       
   387 	// destructive round
       
   388 	_round: function () {
       
   389 		this.x = Math.round(this.x);
       
   390 		this.y = Math.round(this.y);
       
   391 		return this;
       
   392 	},
       
   393 
       
   394 	clone: function () {
       
   395 		return new L.Point(this.x, this.y);
       
   396 	},
       
   397 
       
   398 	toString: function () {
       
   399 		return 'Point(' +
       
   400 				L.Util.formatNum(this.x) + ', ' +
       
   401 				L.Util.formatNum(this.y) + ')';
       
   402 	}
       
   403 };
       
   404 
       
   405 
       
   406 /*
       
   407  * L.Bounds represents a rectangular area on the screen in pixel coordinates.
       
   408  */
       
   409 
       
   410 L.Bounds = L.Class.extend({
       
   411 	initialize: function (min, max) {	//(Point, Point) or Point[]
       
   412 		if (!min) {
       
   413 			return;
       
   414 		}
       
   415 		var points = (min instanceof Array ? min : [min, max]);
       
   416 		for (var i = 0, len = points.length; i < len; i++) {
       
   417 			this.extend(points[i]);
       
   418 		}
       
   419 	},
       
   420 
       
   421 	// extend the bounds to contain the given point
       
   422 	extend: function (/*Point*/ point) {
       
   423 		if (!this.min && !this.max) {
       
   424 			this.min = new L.Point(point.x, point.y);
       
   425 			this.max = new L.Point(point.x, point.y);
       
   426 		} else {
       
   427 			this.min.x = Math.min(point.x, this.min.x);
       
   428 			this.max.x = Math.max(point.x, this.max.x);
       
   429 			this.min.y = Math.min(point.y, this.min.y);
       
   430 			this.max.y = Math.max(point.y, this.max.y);
       
   431 		}
       
   432 	},
       
   433 
       
   434 	getCenter: function (round)/*->Point*/ {
       
   435 		return new L.Point(
       
   436 				(this.min.x + this.max.x) / 2,
       
   437 				(this.min.y + this.max.y) / 2, round);
       
   438 	},
       
   439 
       
   440 	contains: function (/*Bounds or Point*/ obj)/*->Boolean*/ {
       
   441 		var min, max;
       
   442 
       
   443 		if (obj instanceof L.Bounds) {
       
   444 			min = obj.min;
       
   445 			max = obj.max;
       
   446 		} else {
       
   447 			min = max = obj;
       
   448 		}
       
   449 
       
   450 		return (min.x >= this.min.x) &&
       
   451 				(max.x <= this.max.x) &&
       
   452 				(min.y >= this.min.y) &&
       
   453 				(max.y <= this.max.y);
       
   454 	},
       
   455 
       
   456 	intersects: function (/*Bounds*/ bounds) {
       
   457 		var min = this.min,
       
   458 			max = this.max,
       
   459 			min2 = bounds.min,
       
   460 			max2 = bounds.max;
       
   461 
       
   462 		var xIntersects = (max2.x >= min.x) && (min2.x <= max.x),
       
   463 			yIntersects = (max2.y >= min.y) && (min2.y <= max.y);
       
   464 
       
   465 		return xIntersects && yIntersects;
       
   466 	}
       
   467 
       
   468 });
       
   469 
       
   470 
       
   471 /*
       
   472  * L.Transformation is an utility class to perform simple point transformations through a 2d-matrix.
       
   473  */
       
   474 
       
   475 L.Transformation = L.Class.extend({
       
   476 	initialize: function (/*Number*/ a, /*Number*/ b, /*Number*/ c, /*Number*/ d) {
       
   477 		this._a = a;
       
   478 		this._b = b;
       
   479 		this._c = c;
       
   480 		this._d = d;
       
   481 	},
       
   482 
       
   483 	transform: function (point, scale) {
       
   484 		return this._transform(point.clone(), scale);
       
   485 	},
       
   486 
       
   487 	// destructive transform (faster)
       
   488 	_transform: function (/*Point*/ point, /*Number*/ scale) /*-> Point*/ {
       
   489 		scale = scale || 1;
       
   490 		point.x = scale * (this._a * point.x + this._b);
       
   491 		point.y = scale * (this._c * point.y + this._d);
       
   492 		return point;
       
   493 	},
       
   494 
       
   495 	untransform: function (/*Point*/ point, /*Number*/ scale) /*-> Point*/ {
       
   496 		scale = scale || 1;
       
   497 		return new L.Point(
       
   498 			(point.x / scale - this._b) / this._a,
       
   499 			(point.y / scale - this._d) / this._c);
       
   500 	}
       
   501 });
       
   502 
       
   503 
       
   504 /*
       
   505  * L.DomUtil contains various utility functions for working with DOM
       
   506  */
       
   507 
       
   508 L.DomUtil = {
       
   509 	get: function (id) {
       
   510 		return (typeof id === 'string' ? document.getElementById(id) : id);
       
   511 	},
       
   512 
       
   513 	getStyle: function (el, style) {
       
   514 		var value = el.style[style];
       
   515 		if (!value && el.currentStyle) {
       
   516 			value = el.currentStyle[style];
       
   517 		}
       
   518 		if (!value || value === 'auto') {
       
   519 			var css = document.defaultView.getComputedStyle(el, null);
       
   520 			value = css ? css[style] : null;
       
   521 		}
       
   522 		return (value === 'auto' ? null : value);
       
   523 	},
       
   524 
       
   525 	getViewportOffset: function (element) {
       
   526 		var top = 0,
       
   527 			left = 0,
       
   528 			el = element,
       
   529 			docBody = document.body;
       
   530 
       
   531 		do {
       
   532 			top += el.offsetTop || 0;
       
   533 			left += el.offsetLeft || 0;
       
   534 
       
   535 			if (el.offsetParent === docBody &&
       
   536 					L.DomUtil.getStyle(el, 'position') === 'absolute') {
       
   537 				break;
       
   538 			}
       
   539 			el = el.offsetParent;
       
   540 		} while (el);
       
   541 
       
   542 		el = element;
       
   543 
       
   544 		do {
       
   545 			if (el === docBody) {
       
   546 				break;
       
   547 			}
       
   548 
       
   549 			top -= el.scrollTop || 0;
       
   550 			left -= el.scrollLeft || 0;
       
   551 
       
   552 			el = el.parentNode;
       
   553 		} while (el);
       
   554 
       
   555 		return new L.Point(left, top);
       
   556 	},
       
   557 
       
   558 	create: function (tagName, className, container) {
       
   559 		var el = document.createElement(tagName);
       
   560 		el.className = className;
       
   561 		if (container) {
       
   562 			container.appendChild(el);
       
   563 		}
       
   564 		return el;
       
   565 	},
       
   566 
       
   567 	disableTextSelection: function () {
       
   568 		if (document.selection && document.selection.empty) {
       
   569 			document.selection.empty();
       
   570 		}
       
   571 		if (!this._onselectstart) {
       
   572 			this._onselectstart = document.onselectstart;
       
   573 			document.onselectstart = L.Util.falseFn;
       
   574 		}
       
   575 	},
       
   576 
       
   577 	enableTextSelection: function () {
       
   578 		document.onselectstart = this._onselectstart;
       
   579 		this._onselectstart = null;
       
   580 	},
       
   581 
       
   582 	hasClass: function (el, name) {
       
   583 		return (el.className.length > 0) &&
       
   584 				new RegExp("(^|\\s)" + name + "(\\s|$)").test(el.className);
       
   585 	},
       
   586 
       
   587 	addClass: function (el, name) {
       
   588 		if (!L.DomUtil.hasClass(el, name)) {
       
   589 			el.className += (el.className ? ' ' : '') + name;
       
   590 		}
       
   591 	},
       
   592 
       
   593 	removeClass: function (el, name) {
       
   594 		el.className = el.className.replace(/(\S+)\s*/g, function (w, match) {
       
   595 			if (match === name) {
       
   596 				return '';
       
   597 			}
       
   598 			return w;
       
   599 		}).replace(/^\s+/, '');
       
   600 	},
       
   601 
       
   602 	setOpacity: function (el, value) {
       
   603 		if (L.Browser.ie) {
       
   604 			el.style.filter = 'alpha(opacity=' + Math.round(value * 100) + ')';
       
   605 		} else {
       
   606 			el.style.opacity = value;
       
   607 		}
       
   608 	},
       
   609 
       
   610 	//TODO refactor away this ugly translate/position mess
       
   611 
       
   612 	testProp: function (props) {
       
   613 		var style = document.documentElement.style;
       
   614 
       
   615 		for (var i = 0; i < props.length; i++) {
       
   616 			if (props[i] in style) {
       
   617 				return props[i];
       
   618 			}
       
   619 		}
       
   620 		return false;
       
   621 	},
       
   622 
       
   623 	getTranslateString: function (point) {
       
   624 		return L.DomUtil.TRANSLATE_OPEN +
       
   625 				point.x + 'px,' + point.y + 'px' +
       
   626 				L.DomUtil.TRANSLATE_CLOSE;
       
   627 	},
       
   628 
       
   629 	getScaleString: function (scale, origin) {
       
   630 		var preTranslateStr = L.DomUtil.getTranslateString(origin),
       
   631 			scaleStr = ' scale(' + scale + ') ',
       
   632 			postTranslateStr = L.DomUtil.getTranslateString(origin.multiplyBy(-1));
       
   633 
       
   634 		return preTranslateStr + scaleStr + postTranslateStr;
       
   635 	},
       
   636 
       
   637 	setPosition: function (el, point) {
       
   638 		el._leaflet_pos = point;
       
   639 		if (L.Browser.webkit) {
       
   640 			el.style[L.DomUtil.TRANSFORM] =  L.DomUtil.getTranslateString(point);
       
   641 		} else {
       
   642 			el.style.left = point.x + 'px';
       
   643 			el.style.top = point.y + 'px';
       
   644 		}
       
   645 	},
       
   646 
       
   647 	getPosition: function (el) {
       
   648 		return el._leaflet_pos;
       
   649 	}
       
   650 };
       
   651 
       
   652 L.Util.extend(L.DomUtil, {
       
   653 	TRANSITION: L.DomUtil.testProp(['transition', 'webkitTransition', 'OTransition', 'MozTransition', 'msTransition']),
       
   654 	TRANSFORM: L.DomUtil.testProp(['transformProperty', 'WebkitTransform', 'OTransform', 'MozTransform', 'msTransform']),
       
   655 
       
   656 	TRANSLATE_OPEN: 'translate' + (L.Browser.webkit3d ? '3d(' : '('),
       
   657 	TRANSLATE_CLOSE: L.Browser.webkit3d ? ',0)' : ')'
       
   658 });
       
   659 
       
   660 
       
   661 /*
       
   662 	CM.LatLng represents a geographical point with latitude and longtitude coordinates.
       
   663 */
       
   664 
       
   665 L.LatLng = function (/*Number*/ rawLat, /*Number*/ rawLng, /*Boolean*/ noWrap) {
       
   666 	var lat = parseFloat(rawLat),
       
   667 		lng = parseFloat(rawLng);
       
   668 
       
   669 	if (isNaN(lat) || isNaN(lng)) {
       
   670 		throw new Error('Invalid LatLng object: (' + rawLat + ', ' + rawLng + ')');
       
   671 	}
       
   672 
       
   673 	if (noWrap !== true) {
       
   674 		lat = Math.max(Math.min(lat, 90), -90);					// clamp latitude into -90..90
       
   675 		lng = (lng + 180) % 360 + ((lng < -180 || lng === 180) ? 180 : -180);	// wrap longtitude into -180..180
       
   676 	}
       
   677 
       
   678 	//TODO change to lat() & lng()
       
   679 	this.lat = lat;
       
   680 	this.lng = lng;
       
   681 };
       
   682 
       
   683 L.Util.extend(L.LatLng, {
       
   684 	DEG_TO_RAD: Math.PI / 180,
       
   685 	RAD_TO_DEG: 180 / Math.PI,
       
   686 	MAX_MARGIN: 1.0E-9 // max margin of error for the "equals" check
       
   687 });
       
   688 
       
   689 L.LatLng.prototype = {
       
   690 	equals: function (/*LatLng*/ obj) {
       
   691 		if (!(obj instanceof L.LatLng)) {
       
   692 			return false;
       
   693 		}
       
   694 
       
   695 		var margin = Math.max(Math.abs(this.lat - obj.lat), Math.abs(this.lng - obj.lng));
       
   696 		return margin <= L.LatLng.MAX_MARGIN;
       
   697 	},
       
   698 
       
   699 	toString: function () {
       
   700 		return 'LatLng(' +
       
   701 				L.Util.formatNum(this.lat) + ', ' +
       
   702 				L.Util.formatNum(this.lng) + ')';
       
   703 	},
       
   704 
       
   705 	// Haversine distance formula, see http://en.wikipedia.org/wiki/Haversine_formula
       
   706 	distanceTo: function (/*LatLng*/ other)/*->Double*/ {
       
   707 		var R = 6378137, // earth radius in meters
       
   708 			d2r = L.LatLng.DEG_TO_RAD,
       
   709 			dLat = (other.lat - this.lat) * d2r,
       
   710 			dLon = (other.lng - this.lng) * d2r,
       
   711 			lat1 = this.lat * d2r,
       
   712 			lat2 = other.lat * d2r,
       
   713 			sin1 = Math.sin(dLat / 2),
       
   714 			sin2 = Math.sin(dLon / 2);
       
   715 
       
   716 		var a = sin1 * sin1 + sin2 * sin2 * Math.cos(lat1) * Math.cos(lat2);
       
   717 
       
   718 		return R * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
       
   719 	}
       
   720 };
       
   721 
       
   722 
       
   723 /*
       
   724  * L.LatLngBounds represents a rectangular area on the map in geographical coordinates.
       
   725  */
       
   726 
       
   727 L.LatLngBounds = L.Class.extend({
       
   728 	initialize: function (southWest, northEast) {	// (LatLng, LatLng) or (LatLng[])
       
   729 		if (!southWest) {
       
   730 			return;
       
   731 		}
       
   732 		var latlngs = (southWest instanceof Array ? southWest : [southWest, northEast]);
       
   733 		for (var i = 0, len = latlngs.length; i < len; i++) {
       
   734 			this.extend(latlngs[i]);
       
   735 		}
       
   736 	},
       
   737 
       
   738 	// extend the bounds to contain the given point
       
   739 	extend: function (/*LatLng*/ latlng) {
       
   740 		if (!this._southWest && !this._northEast) {
       
   741 			this._southWest = new L.LatLng(latlng.lat, latlng.lng, true);
       
   742 			this._northEast = new L.LatLng(latlng.lat, latlng.lng, true);
       
   743 		} else {
       
   744 			this._southWest.lat = Math.min(latlng.lat, this._southWest.lat);
       
   745 			this._southWest.lng = Math.min(latlng.lng, this._southWest.lng);
       
   746 			this._northEast.lat = Math.max(latlng.lat, this._northEast.lat);
       
   747 			this._northEast.lng = Math.max(latlng.lng, this._northEast.lng);
       
   748 		}
       
   749 	},
       
   750 
       
   751 	getCenter: function () /*-> LatLng*/ {
       
   752 		return new L.LatLng(
       
   753 				(this._southWest.lat + this._northEast.lat) / 2,
       
   754 				(this._southWest.lng + this._northEast.lng) / 2);
       
   755 	},
       
   756 
       
   757 	getSouthWest: function () {
       
   758 		return this._southWest;
       
   759 	},
       
   760 
       
   761 	getNorthEast: function () {
       
   762 		return this._northEast;
       
   763 	},
       
   764 
       
   765 	getNorthWest: function () {
       
   766 		return new L.LatLng(this._northEast.lat, this._southWest.lng, true);
       
   767 	},
       
   768 
       
   769 	getSouthEast: function () {
       
   770 		return new L.LatLng(this._southWest.lat, this._northEast.lng, true);
       
   771 	},
       
   772 
       
   773 	contains: function (/*LatLngBounds or LatLng*/ obj) /*-> Boolean*/ {
       
   774 		var sw = this._southWest,
       
   775 			ne = this._northEast,
       
   776 			sw2, ne2;
       
   777 
       
   778 		if (obj instanceof L.LatLngBounds) {
       
   779 			sw2 = obj.getSouthWest();
       
   780 			ne2 = obj.getNorthEast();
       
   781 		} else {
       
   782 			sw2 = ne2 = obj;
       
   783 		}
       
   784 
       
   785 		return (sw2.lat >= sw.lat) && (ne2.lat <= ne.lat) &&
       
   786 				(sw2.lng >= sw.lng) && (ne2.lng <= ne.lng);
       
   787 	},
       
   788 
       
   789 	intersects: function (/*LatLngBounds*/ bounds) {
       
   790 		var sw = this._southWest,
       
   791 			ne = this._northEast,
       
   792 			sw2 = bounds.getSouthWest(),
       
   793 			ne2 = bounds.getNorthEast();
       
   794 
       
   795 		var latIntersects = (ne2.lat >= sw.lat) && (sw2.lat <= ne.lat),
       
   796 			lngIntersects = (ne2.lng >= sw.lng) && (sw2.lng <= ne.lng);
       
   797 
       
   798 		return latIntersects && lngIntersects;
       
   799 	},
       
   800 
       
   801 	toBBoxString: function () {
       
   802 		var sw = this._southWest,
       
   803 			ne = this._northEast;
       
   804 		return [sw.lng, sw.lat, ne.lng, ne.lat].join(',');
       
   805 	}
       
   806 });
       
   807 
       
   808 //TODO International date line?
       
   809 
       
   810 
       
   811 /*
       
   812  * L.Projection contains various geographical projections used by CRS classes.
       
   813  */
       
   814 
       
   815 L.Projection = {};
       
   816 
       
   817 
       
   818 
       
   819 L.Projection.SphericalMercator = {
       
   820 	MAX_LATITUDE: 85.0511287798,
       
   821 
       
   822 	project: function (/*LatLng*/ latlng) /*-> Point*/ {
       
   823 		var d = L.LatLng.DEG_TO_RAD,
       
   824 			max = this.MAX_LATITUDE,
       
   825 			lat = Math.max(Math.min(max, latlng.lat), -max),
       
   826 			x = latlng.lng * d,
       
   827 			y = lat * d;
       
   828 		y = Math.log(Math.tan((Math.PI / 4) + (y / 2)));
       
   829 
       
   830 		return new L.Point(x, y);
       
   831 	},
       
   832 
       
   833 	unproject: function (/*Point*/ point, /*Boolean*/ unbounded) /*-> LatLng*/ {
       
   834 		var d = L.LatLng.RAD_TO_DEG,
       
   835 			lng = point.x * d,
       
   836 			lat = (2 * Math.atan(Math.exp(point.y)) - (Math.PI / 2)) * d;
       
   837 
       
   838 		return new L.LatLng(lat, lng, unbounded);
       
   839 	}
       
   840 };
       
   841 
       
   842 
       
   843 
       
   844 L.Projection.LonLat = {
       
   845 	project: function (latlng) {
       
   846 		return new L.Point(latlng.lng, latlng.lat);
       
   847 	},
       
   848 
       
   849 	unproject: function (point, unbounded) {
       
   850 		return new L.LatLng(point.y, point.x, unbounded);
       
   851 	}
       
   852 };
       
   853 
       
   854 
       
   855 
       
   856 L.CRS = {
       
   857 	latLngToPoint: function (/*LatLng*/ latlng, /*Number*/ scale)/*-> Point*/ {
       
   858 		var projectedPoint = this.projection.project(latlng);
       
   859 		return this.transformation._transform(projectedPoint, scale);
       
   860 	},
       
   861 
       
   862 	pointToLatLng: function (/*Point*/ point, /*Number*/ scale, /*(optional) Boolean*/ unbounded)/*-> LatLng*/ {
       
   863 		var untransformedPoint = this.transformation.untransform(point, scale);
       
   864 		return this.projection.unproject(untransformedPoint, unbounded);
       
   865 		//TODO get rid of 'unbounded' everywhere
       
   866 	},
       
   867 
       
   868 	project: function (latlng) {
       
   869 		return this.projection.project(latlng);
       
   870 	}
       
   871 };
       
   872 
       
   873 
       
   874 
       
   875 L.CRS.EPSG3857 = L.Util.extend({}, L.CRS, {
       
   876 	code: 'EPSG:3857',
       
   877 
       
   878 	projection: L.Projection.SphericalMercator,
       
   879 	transformation: new L.Transformation(0.5 / Math.PI, 0.5, -0.5 / Math.PI, 0.5),
       
   880 
       
   881 	project: function (/*LatLng*/ latlng)/*-> Point*/ {
       
   882 		var projectedPoint = this.projection.project(latlng),
       
   883 			earthRadius = 6378137;
       
   884 		return projectedPoint.multiplyBy(earthRadius);
       
   885 	}
       
   886 });
       
   887 
       
   888 L.CRS.EPSG900913 = L.Util.extend({}, L.CRS.EPSG3857, {
       
   889 	code: 'EPSG:900913'
       
   890 });
       
   891 
       
   892 
       
   893 
       
   894 L.CRS.EPSG4326 = L.Util.extend({}, L.CRS, {
       
   895 	code: 'EPSG:4326',
       
   896 
       
   897 	projection: L.Projection.LonLat,
       
   898 	transformation: new L.Transformation(1 / 360, 0.5, -1 / 360, 0.5)
       
   899 });
       
   900 
       
   901 
       
   902 /*
       
   903  * L.Map is the central class of the API - it is used to create a map.
       
   904  */
       
   905 
       
   906 L.Map = L.Class.extend({
       
   907 	includes: L.Mixin.Events,
       
   908 
       
   909 	options: {
       
   910 		// projection
       
   911 		crs: L.CRS.EPSG3857 || L.CRS.EPSG4326,
       
   912 		scale: function (zoom) {
       
   913 			return 256 * Math.pow(2, zoom);
       
   914 		},
       
   915 
       
   916 		// state
       
   917 		center: null,
       
   918 		zoom: null,
       
   919 		layers: [],
       
   920 
       
   921 		// interaction
       
   922 		dragging: true,
       
   923 		touchZoom: L.Browser.touch && !L.Browser.android,
       
   924 		scrollWheelZoom: !L.Browser.touch,
       
   925 		doubleClickZoom: true,
       
   926 		boxZoom: true,
       
   927 
       
   928 		// controls
       
   929 		zoomControl: true,
       
   930 		attributionControl: true,
       
   931 
       
   932 		// animation
       
   933 		fadeAnimation: L.DomUtil.TRANSITION && !L.Browser.android,
       
   934 		zoomAnimation: L.DomUtil.TRANSITION && !L.Browser.android && !L.Browser.mobileOpera,
       
   935 
       
   936 		// misc
       
   937 		trackResize: true,
       
   938 		closePopupOnClick: true,
       
   939 		worldCopyJump: true
       
   940 	},
       
   941 
       
   942 
       
   943 	// constructor
       
   944 
       
   945 	initialize: function (id, options) { // (HTMLElement or String, Object)
       
   946 		L.Util.setOptions(this, options);
       
   947 
       
   948 		this._container = L.DomUtil.get(id);
       
   949 
       
   950 		if (this._container._leaflet) {
       
   951 			throw new Error("Map container is already initialized.");
       
   952 		}
       
   953 		this._container._leaflet = true;
       
   954 
       
   955 		this._initLayout();
       
   956 
       
   957 		if (L.DomEvent) {
       
   958 			this._initEvents();
       
   959 			if (L.Handler) {
       
   960 				this._initInteraction();
       
   961 			}
       
   962 			if (L.Control) {
       
   963 				this._initControls();
       
   964 			}
       
   965 		}
       
   966 
       
   967 		if (this.options.maxBounds) {
       
   968 			this.setMaxBounds(this.options.maxBounds);
       
   969 		}
       
   970 
       
   971 		var center = this.options.center,
       
   972 			zoom = this.options.zoom;
       
   973 
       
   974 		if (center !== null && zoom !== null) {
       
   975 			this.setView(center, zoom, true);
       
   976 		}
       
   977 
       
   978 		var layers = this.options.layers;
       
   979 		layers = (layers instanceof Array ? layers : [layers]);
       
   980 		this._tileLayersNum = 0;
       
   981 		this._initLayers(layers);
       
   982 	},
       
   983 
       
   984 
       
   985 	// public methods that modify map state
       
   986 
       
   987 	// replaced by animation-powered implementation in Map.PanAnimation.js
       
   988 	setView: function (center, zoom) {
       
   989 		// reset the map view
       
   990 		this._resetView(center, this._limitZoom(zoom));
       
   991 		return this;
       
   992 	},
       
   993 
       
   994 	setZoom: function (zoom) { // (Number)
       
   995 		return this.setView(this.getCenter(), zoom);
       
   996 	},
       
   997 
       
   998 	zoomIn: function () {
       
   999 		return this.setZoom(this._zoom + 1);
       
  1000 	},
       
  1001 
       
  1002 	zoomOut: function () {
       
  1003 		return this.setZoom(this._zoom - 1);
       
  1004 	},
       
  1005 
       
  1006 	fitBounds: function (bounds) { // (LatLngBounds)
       
  1007 		var zoom = this.getBoundsZoom(bounds);
       
  1008 		return this.setView(bounds.getCenter(), zoom);
       
  1009 	},
       
  1010 
       
  1011 	fitWorld: function () {
       
  1012 		var sw = new L.LatLng(-60, -170),
       
  1013 			ne = new L.LatLng(85, 179);
       
  1014 		return this.fitBounds(new L.LatLngBounds(sw, ne));
       
  1015 	},
       
  1016 
       
  1017 	panTo: function (center) { // (LatLng)
       
  1018 		return this.setView(center, this._zoom);
       
  1019 	},
       
  1020 
       
  1021 	panBy: function (offset) { // (Point)
       
  1022 		// replaced with animated panBy in Map.Animation.js
       
  1023 		this.fire('movestart');
       
  1024 
       
  1025 		this._rawPanBy(offset);
       
  1026 
       
  1027 		this.fire('move');
       
  1028 		this.fire('moveend');
       
  1029 
       
  1030 		return this;
       
  1031 	},
       
  1032 
       
  1033 	setMaxBounds: function (bounds) {
       
  1034 		this.options.maxBounds = bounds;
       
  1035 
       
  1036 		if (!bounds) {
       
  1037 			this._boundsMinZoom = null;
       
  1038 			return this;
       
  1039 		}
       
  1040 
       
  1041 		var minZoom = this.getBoundsZoom(bounds, true);
       
  1042 
       
  1043 		this._boundsMinZoom = minZoom;
       
  1044 
       
  1045 		if (this._loaded) {
       
  1046 			if (this._zoom < minZoom) {
       
  1047 				this.setView(bounds.getCenter(), minZoom);
       
  1048 			} else {
       
  1049 				this.panInsideBounds(bounds);
       
  1050 			}
       
  1051 		}
       
  1052 		return this;
       
  1053 	},
       
  1054 
       
  1055 	panInsideBounds: function (bounds) {
       
  1056 		var viewBounds = this.getBounds(),
       
  1057 			viewSw = this.project(viewBounds.getSouthWest()),
       
  1058 			viewNe = this.project(viewBounds.getNorthEast()),
       
  1059 			sw = this.project(bounds.getSouthWest()),
       
  1060 			ne = this.project(bounds.getNorthEast()),
       
  1061 			dx = 0,
       
  1062 			dy = 0;
       
  1063 
       
  1064 		if (viewNe.y < ne.y) { // north
       
  1065 			dy = ne.y - viewNe.y;
       
  1066 		}
       
  1067 		if (viewNe.x > ne.x) { // east
       
  1068 			dx = ne.x - viewNe.x;
       
  1069 		}
       
  1070 		if (viewSw.y > sw.y) { // south
       
  1071 			dy = sw.y - viewSw.y;
       
  1072 		}
       
  1073 		if (viewSw.x < sw.x) { // west
       
  1074 			dx = sw.x - viewSw.x;
       
  1075 		}
       
  1076 
       
  1077 		return this.panBy(new L.Point(dx, dy, true));
       
  1078 	},
       
  1079 
       
  1080 	addLayer: function (layer, insertAtTheTop) {
       
  1081 		var id = L.Util.stamp(layer);
       
  1082 
       
  1083 		if (this._layers[id]) {
       
  1084 			return this;
       
  1085 		}
       
  1086 
       
  1087 		this._layers[id] = layer;
       
  1088 
       
  1089 		if (layer.options && !isNaN(layer.options.maxZoom)) {
       
  1090 			this._layersMaxZoom = Math.max(this._layersMaxZoom || 0, layer.options.maxZoom);
       
  1091 		}
       
  1092 		if (layer.options && !isNaN(layer.options.minZoom)) {
       
  1093 			this._layersMinZoom = Math.min(this._layersMinZoom || Infinity, layer.options.minZoom);
       
  1094 		}
       
  1095 		//TODO getMaxZoom, getMinZoom in ILayer (instead of options)
       
  1096 
       
  1097 		if (this.options.zoomAnimation && L.TileLayer && (layer instanceof L.TileLayer)) {
       
  1098 			this._tileLayersNum++;
       
  1099 			layer.on('load', this._onTileLayerLoad, this);
       
  1100 		}
       
  1101 		if (this.attributionControl && layer.getAttribution) {
       
  1102 			this.attributionControl.addAttribution(layer.getAttribution());
       
  1103 		}
       
  1104 
       
  1105 		var onMapLoad = function () {
       
  1106 			layer.onAdd(this, insertAtTheTop);
       
  1107 			this.fire('layeradd', {layer: layer});
       
  1108 		};
       
  1109 
       
  1110 		if (this._loaded) {
       
  1111 			onMapLoad.call(this);
       
  1112 		} else {
       
  1113 			this.on('load', onMapLoad, this);
       
  1114 		}
       
  1115 
       
  1116 		return this;
       
  1117 	},
       
  1118 
       
  1119 	removeLayer: function (layer) {
       
  1120 		var id = L.Util.stamp(layer);
       
  1121 
       
  1122 		if (this._layers[id]) {
       
  1123 			layer.onRemove(this);
       
  1124 			delete this._layers[id];
       
  1125 
       
  1126 			if (this.options.zoomAnimation && L.TileLayer && (layer instanceof L.TileLayer)) {
       
  1127 				this._tileLayersNum--;
       
  1128 				layer.off('load', this._onTileLayerLoad, this);
       
  1129 			}
       
  1130 			if (this.attributionControl && layer.getAttribution) {
       
  1131 				this.attributionControl.removeAttribution(layer.getAttribution());
       
  1132 			}
       
  1133 
       
  1134 			this.fire('layerremove', {layer: layer});
       
  1135 		}
       
  1136 		return this;
       
  1137 	},
       
  1138 
       
  1139 	hasLayer: function (layer) {
       
  1140 		var id = L.Util.stamp(layer);
       
  1141 		return this._layers.hasOwnProperty(id);
       
  1142 	},
       
  1143 
       
  1144 	invalidateSize: function () {
       
  1145 		var oldSize = this.getSize();
       
  1146 
       
  1147 		this._sizeChanged = true;
       
  1148 
       
  1149 		if (this.options.maxBounds) {
       
  1150 			this.setMaxBounds(this.options.maxBounds);
       
  1151 		}
       
  1152 
       
  1153 		if (!this._loaded) {
       
  1154 			return this;
       
  1155 		}
       
  1156 
       
  1157 		this._rawPanBy(oldSize.subtract(this.getSize()).divideBy(2, true));
       
  1158 
       
  1159 		this.fire('move');
       
  1160 
       
  1161 		clearTimeout(this._sizeTimer);
       
  1162 		this._sizeTimer = setTimeout(L.Util.bind(function () {
       
  1163 			this.fire('moveend');
       
  1164 		}, this), 200);
       
  1165 
       
  1166 		return this;
       
  1167 	},
       
  1168 
       
  1169 
       
  1170 	// public methods for getting map state
       
  1171 
       
  1172 	getCenter: function (unbounded) { // (Boolean)
       
  1173 		var viewHalf = this.getSize().divideBy(2),
       
  1174 			centerPoint = this._getTopLeftPoint().add(viewHalf);
       
  1175 		return this.unproject(centerPoint, this._zoom, unbounded);
       
  1176 	},
       
  1177 
       
  1178 	getZoom: function () {
       
  1179 		return this._zoom;
       
  1180 	},
       
  1181 
       
  1182 	getBounds: function () {
       
  1183 		var bounds = this.getPixelBounds(),
       
  1184 			sw = this.unproject(new L.Point(bounds.min.x, bounds.max.y), this._zoom, true),
       
  1185 			ne = this.unproject(new L.Point(bounds.max.x, bounds.min.y), this._zoom, true);
       
  1186 		return new L.LatLngBounds(sw, ne);
       
  1187 	},
       
  1188 
       
  1189 	getMinZoom: function () {
       
  1190 		var z1 = this.options.minZoom || 0,
       
  1191 			z2 = this._layersMinZoom || 0,
       
  1192 			z3 = this._boundsMinZoom || 0;
       
  1193 
       
  1194 		return Math.max(z1, z2, z3);
       
  1195 	},
       
  1196 
       
  1197 	getMaxZoom: function () {
       
  1198 		var z1 = isNaN(this.options.maxZoom) ? Infinity : this.options.maxZoom,
       
  1199 			z2 = this._layersMaxZoom || Infinity;
       
  1200 
       
  1201 		return Math.min(z1, z2);
       
  1202 	},
       
  1203 
       
  1204 	getBoundsZoom: function (bounds, inside) { // (LatLngBounds)
       
  1205 		var size = this.getSize(),
       
  1206 			zoom = this.options.minZoom || 0,
       
  1207 			maxZoom = this.getMaxZoom(),
       
  1208 			ne = bounds.getNorthEast(),
       
  1209 			sw = bounds.getSouthWest(),
       
  1210 			boundsSize,
       
  1211 			nePoint,
       
  1212 			swPoint,
       
  1213 			zoomNotFound = true;
       
  1214 
       
  1215 		if (inside) {
       
  1216 			zoom--;
       
  1217 		}
       
  1218 
       
  1219 		do {
       
  1220 			zoom++;
       
  1221 			nePoint = this.project(ne, zoom);
       
  1222 			swPoint = this.project(sw, zoom);
       
  1223 			boundsSize = new L.Point(nePoint.x - swPoint.x, swPoint.y - nePoint.y);
       
  1224 
       
  1225 			if (!inside) {
       
  1226 				zoomNotFound = (boundsSize.x <= size.x) && (boundsSize.y <= size.y);
       
  1227 			} else {
       
  1228 				zoomNotFound = (boundsSize.x < size.x) || (boundsSize.y < size.y);
       
  1229 			}
       
  1230 		} while (zoomNotFound && (zoom <= maxZoom));
       
  1231 
       
  1232 		if (zoomNotFound && inside) {
       
  1233 			return null;
       
  1234 		}
       
  1235 
       
  1236 		return inside ? zoom : zoom - 1;
       
  1237 	},
       
  1238 
       
  1239 	getSize: function () {
       
  1240 		if (!this._size || this._sizeChanged) {
       
  1241 			this._size = new L.Point(this._container.clientWidth, this._container.clientHeight);
       
  1242 			this._sizeChanged = false;
       
  1243 		}
       
  1244 		return this._size;
       
  1245 	},
       
  1246 
       
  1247 	getPixelBounds: function () {
       
  1248 		var topLeftPoint = this._getTopLeftPoint(),
       
  1249 			size = this.getSize();
       
  1250 		return new L.Bounds(topLeftPoint, topLeftPoint.add(size));
       
  1251 	},
       
  1252 
       
  1253 	getPixelOrigin: function () {
       
  1254 		return this._initialTopLeftPoint;
       
  1255 	},
       
  1256 
       
  1257 	getPanes: function () {
       
  1258 		return this._panes;
       
  1259 	},
       
  1260 
       
  1261 
       
  1262 	// conversion methods
       
  1263 
       
  1264 	mouseEventToContainerPoint: function (e) { // (MouseEvent)
       
  1265 		return L.DomEvent.getMousePosition(e, this._container);
       
  1266 	},
       
  1267 
       
  1268 	mouseEventToLayerPoint: function (e) { // (MouseEvent)
       
  1269 		return this.containerPointToLayerPoint(this.mouseEventToContainerPoint(e));
       
  1270 	},
       
  1271 
       
  1272 	mouseEventToLatLng: function (e) { // (MouseEvent)
       
  1273 		return this.layerPointToLatLng(this.mouseEventToLayerPoint(e));
       
  1274 	},
       
  1275 
       
  1276 	containerPointToLayerPoint: function (point) { // (Point)
       
  1277 		return point.subtract(L.DomUtil.getPosition(this._mapPane));
       
  1278 	},
       
  1279 
       
  1280 	layerPointToContainerPoint: function (point) { // (Point)
       
  1281 		return point.add(L.DomUtil.getPosition(this._mapPane));
       
  1282 	},
       
  1283 
       
  1284 	layerPointToLatLng: function (point) { // (Point)
       
  1285 		return this.unproject(point.add(this._initialTopLeftPoint));
       
  1286 	},
       
  1287 
       
  1288 	latLngToLayerPoint: function (latlng) { // (LatLng)
       
  1289 		return this.project(latlng)._round()._subtract(this._initialTopLeftPoint);
       
  1290 	},
       
  1291 
       
  1292 	project: function (latlng, zoom) { // (LatLng[, Number]) -> Point
       
  1293 		zoom = (typeof zoom === 'undefined' ? this._zoom : zoom);
       
  1294 		return this.options.crs.latLngToPoint(latlng, this.options.scale(zoom));
       
  1295 	},
       
  1296 
       
  1297 	unproject: function (point, zoom, unbounded) { // (Point[, Number, Boolean]) -> LatLng
       
  1298 		zoom = (typeof zoom === 'undefined' ? this._zoom : zoom);
       
  1299 		return this.options.crs.pointToLatLng(point, this.options.scale(zoom), unbounded);
       
  1300 	},
       
  1301 
       
  1302 
       
  1303 	// private methods that modify map state
       
  1304 
       
  1305 	_initLayout: function () {
       
  1306 		var container = this._container;
       
  1307 
       
  1308 		container.innerHTML = '';
       
  1309 
       
  1310 		container.className += ' leaflet-container';
       
  1311 
       
  1312 		if (this.options.fadeAnimation) {
       
  1313 			container.className += ' leaflet-fade-anim';
       
  1314 		}
       
  1315 
       
  1316 		var position = L.DomUtil.getStyle(container, 'position');
       
  1317 		if (position !== 'absolute' && position !== 'relative') {
       
  1318 			container.style.position = 'relative';
       
  1319 		}
       
  1320 
       
  1321 		this._initPanes();
       
  1322 
       
  1323 		if (this._initControlPos) {
       
  1324 			this._initControlPos();
       
  1325 		}
       
  1326 	},
       
  1327 
       
  1328 	_initPanes: function () {
       
  1329 		var panes = this._panes = {};
       
  1330 
       
  1331 		this._mapPane = panes.mapPane = this._createPane('leaflet-map-pane', this._container);
       
  1332 
       
  1333 		this._tilePane = panes.tilePane = this._createPane('leaflet-tile-pane', this._mapPane);
       
  1334 		this._objectsPane = panes.objectsPane = this._createPane('leaflet-objects-pane', this._mapPane);
       
  1335 
       
  1336 		panes.shadowPane = this._createPane('leaflet-shadow-pane');
       
  1337 		panes.overlayPane = this._createPane('leaflet-overlay-pane');
       
  1338 		panes.markerPane = this._createPane('leaflet-marker-pane');
       
  1339 		panes.popupPane = this._createPane('leaflet-popup-pane');
       
  1340 	},
       
  1341 
       
  1342 	_createPane: function (className, container) {
       
  1343 		return L.DomUtil.create('div', className, container || this._objectsPane);
       
  1344 	},
       
  1345 
       
  1346 	_resetView: function (center, zoom, preserveMapOffset, afterZoomAnim) {
       
  1347 		var zoomChanged = (this._zoom !== zoom);
       
  1348 
       
  1349 		if (!afterZoomAnim) {
       
  1350 			this.fire('movestart');
       
  1351 
       
  1352 			if (zoomChanged) {
       
  1353 				this.fire('zoomstart');
       
  1354 			}
       
  1355 		}
       
  1356 
       
  1357 		this._zoom = zoom;
       
  1358 
       
  1359 		this._initialTopLeftPoint = this._getNewTopLeftPoint(center);
       
  1360 
       
  1361 		if (!preserveMapOffset) {
       
  1362 			L.DomUtil.setPosition(this._mapPane, new L.Point(0, 0));
       
  1363 		} else {
       
  1364 			var offset = L.DomUtil.getPosition(this._mapPane);
       
  1365 			this._initialTopLeftPoint._add(offset);
       
  1366 		}
       
  1367 
       
  1368 		this._tileLayersToLoad = this._tileLayersNum;
       
  1369 		this.fire('viewreset', {hard: !preserveMapOffset});
       
  1370 
       
  1371 		this.fire('move');
       
  1372 		if (zoomChanged || afterZoomAnim) {
       
  1373 			this.fire('zoomend');
       
  1374 		}
       
  1375 		this.fire('moveend');
       
  1376 
       
  1377 		if (!this._loaded) {
       
  1378 			this._loaded = true;
       
  1379 			this.fire('load');
       
  1380 		}
       
  1381 	},
       
  1382 
       
  1383 	_initLayers: function (layers) {
       
  1384 		this._layers = {};
       
  1385 
       
  1386 		var i, len;
       
  1387 
       
  1388 		for (i = 0, len = layers.length; i < len; i++) {
       
  1389 			this.addLayer(layers[i]);
       
  1390 		}
       
  1391 	},
       
  1392 
       
  1393 	_initControls: function () {
       
  1394 		if (this.options.zoomControl) {
       
  1395 			this.addControl(new L.Control.Zoom());
       
  1396 		}
       
  1397 		if (this.options.attributionControl) {
       
  1398 			this.attributionControl = new L.Control.Attribution();
       
  1399 			this.addControl(this.attributionControl);
       
  1400 		}
       
  1401 	},
       
  1402 
       
  1403 	_rawPanBy: function (offset) {
       
  1404 		var mapPaneOffset = L.DomUtil.getPosition(this._mapPane);
       
  1405 		L.DomUtil.setPosition(this._mapPane, mapPaneOffset.subtract(offset));
       
  1406 	},
       
  1407 
       
  1408 
       
  1409 	// map events
       
  1410 
       
  1411 	_initEvents: function () {
       
  1412 		L.DomEvent.addListener(this._container, 'click', this._onMouseClick, this);
       
  1413 
       
  1414 		var events = ['dblclick', 'mousedown', 'mouseenter', 'mouseleave', 'mousemove', 'contextmenu'];
       
  1415 
       
  1416 		var i, len;
       
  1417 
       
  1418 		for (i = 0, len = events.length; i < len; i++) {
       
  1419 			L.DomEvent.addListener(this._container, events[i], this._fireMouseEvent, this);
       
  1420 		}
       
  1421 
       
  1422 		if (this.options.trackResize) {
       
  1423 			L.DomEvent.addListener(window, 'resize', this._onResize, this);
       
  1424 		}
       
  1425 	},
       
  1426 
       
  1427 	_onResize: function () {
       
  1428 		L.Util.requestAnimFrame(this.invalidateSize, this, false, this._container);
       
  1429 	},
       
  1430 
       
  1431 	_onMouseClick: function (e) {
       
  1432 		if (!this._loaded || (this.dragging && this.dragging.moved())) {
       
  1433 			return;
       
  1434 		}
       
  1435 
       
  1436 		this.fire('pre' + e.type);
       
  1437 		this._fireMouseEvent(e);
       
  1438 	},
       
  1439 
       
  1440 	_fireMouseEvent: function (e) {
       
  1441 		if (!this._loaded) {
       
  1442 			return;
       
  1443 		}
       
  1444 
       
  1445 		var type = e.type;
       
  1446 		type = (type === 'mouseenter' ? 'mouseover' : (type === 'mouseleave' ? 'mouseout' : type));
       
  1447 
       
  1448 		if (!this.hasEventListeners(type)) {
       
  1449 			return;
       
  1450 		}
       
  1451 
       
  1452 		if (type === 'contextmenu') {
       
  1453 			L.DomEvent.preventDefault(e);
       
  1454 		}
       
  1455 		
       
  1456 		this.fire(type, {
       
  1457 			latlng: this.mouseEventToLatLng(e),
       
  1458 			layerPoint: this.mouseEventToLayerPoint(e)
       
  1459 		});
       
  1460 	},
       
  1461 
       
  1462 	_initInteraction: function () {
       
  1463 		var handlers = {
       
  1464 			dragging: L.Map.Drag,
       
  1465 			touchZoom: L.Map.TouchZoom,
       
  1466 			doubleClickZoom: L.Map.DoubleClickZoom,
       
  1467 			scrollWheelZoom: L.Map.ScrollWheelZoom,
       
  1468 			boxZoom: L.Map.BoxZoom
       
  1469 		};
       
  1470 
       
  1471 		var i;
       
  1472 		for (i in handlers) {
       
  1473 			if (handlers.hasOwnProperty(i) && handlers[i]) {
       
  1474 				this[i] = new handlers[i](this);
       
  1475 				if (this.options[i]) {
       
  1476 					this[i].enable();
       
  1477 				}
       
  1478 				// TODO move enabling to handler contructor
       
  1479 			}
       
  1480 		}
       
  1481 	},
       
  1482 
       
  1483 	_onTileLayerLoad: function () {
       
  1484 		// clear scaled tiles after all new tiles are loaded (for performance)
       
  1485 		this._tileLayersToLoad--;
       
  1486 		if (this._tileLayersNum && !this._tileLayersToLoad && this._tileBg) {
       
  1487 			clearTimeout(this._clearTileBgTimer);
       
  1488 			this._clearTileBgTimer = setTimeout(L.Util.bind(this._clearTileBg, this), 500);
       
  1489 		}
       
  1490 	},
       
  1491 
       
  1492 
       
  1493 	// private methods for getting map state
       
  1494 
       
  1495 	_getTopLeftPoint: function () {
       
  1496 		if (!this._loaded) {
       
  1497 			throw new Error('Set map center and zoom first.');
       
  1498 		}
       
  1499 
       
  1500 		var offset = L.DomUtil.getPosition(this._mapPane);
       
  1501 		return this._initialTopLeftPoint.subtract(offset);
       
  1502 	},
       
  1503 
       
  1504 	_getNewTopLeftPoint: function (center) {
       
  1505 		var viewHalf = this.getSize().divideBy(2);
       
  1506 		return this.project(center).subtract(viewHalf).round();
       
  1507 	},
       
  1508 
       
  1509 	_limitZoom: function (zoom) {
       
  1510 		var min = this.getMinZoom();
       
  1511 		var max = this.getMaxZoom();
       
  1512 		return Math.max(min, Math.min(max, zoom));
       
  1513 	}
       
  1514 });
       
  1515 
       
  1516 
       
  1517 
       
  1518 L.Projection.Mercator = {
       
  1519 	MAX_LATITUDE: 85.0840591556,
       
  1520 
       
  1521 	R_MINOR: 6356752.3142,
       
  1522 	R_MAJOR: 6378137,
       
  1523 
       
  1524 	project: function (/*LatLng*/ latlng) /*-> Point*/ {
       
  1525 		var d = L.LatLng.DEG_TO_RAD,
       
  1526 			max = this.MAX_LATITUDE,
       
  1527 			lat = Math.max(Math.min(max, latlng.lat), -max),
       
  1528 			r = this.R_MAJOR,
       
  1529 			r2 = this.R_MINOR,
       
  1530 			x = latlng.lng * d * r,
       
  1531 			y = lat * d,
       
  1532 			tmp = r2 / r,
       
  1533 			eccent = Math.sqrt(1.0 - tmp * tmp),
       
  1534 			con = eccent * Math.sin(y);
       
  1535 
       
  1536 		con = Math.pow((1 - con) / (1 + con), eccent * 0.5);
       
  1537 
       
  1538 		var ts = Math.tan(0.5 * ((Math.PI * 0.5) - y)) / con;
       
  1539 		y = -r2 * Math.log(ts);
       
  1540 
       
  1541 		return new L.Point(x, y);
       
  1542 	},
       
  1543 
       
  1544 	unproject: function (/*Point*/ point, /*Boolean*/ unbounded) /*-> LatLng*/ {
       
  1545 		var d = L.LatLng.RAD_TO_DEG,
       
  1546 			r = this.R_MAJOR,
       
  1547 			r2 = this.R_MINOR,
       
  1548 			lng = point.x * d / r,
       
  1549 			tmp = r2 / r,
       
  1550 			eccent = Math.sqrt(1 - (tmp * tmp)),
       
  1551 			ts = Math.exp(- point.y / r2),
       
  1552 			phi = (Math.PI / 2) - 2 * Math.atan(ts),
       
  1553 			numIter = 15,
       
  1554 			tol = 1e-7,
       
  1555 			i = numIter,
       
  1556 			dphi = 0.1,
       
  1557 			con;
       
  1558 
       
  1559 		while ((Math.abs(dphi) > tol) && (--i > 0)) {
       
  1560 			con = eccent * Math.sin(phi);
       
  1561 			dphi = (Math.PI / 2) - 2 * Math.atan(ts * Math.pow((1.0 - con) / (1.0 + con), 0.5 * eccent)) - phi;
       
  1562 			phi += dphi;
       
  1563 		}
       
  1564 
       
  1565 		return new L.LatLng(phi * d, lng, unbounded);
       
  1566 	}
       
  1567 };
       
  1568 
       
  1569 
       
  1570 
       
  1571 L.CRS.EPSG3395 = L.Util.extend({}, L.CRS, {
       
  1572 	code: 'EPSG:3395',
       
  1573 
       
  1574 	projection: L.Projection.Mercator,
       
  1575 	transformation: (function () {
       
  1576 		var m = L.Projection.Mercator,
       
  1577 			r = m.R_MAJOR,
       
  1578 			r2 = m.R_MINOR;
       
  1579 
       
  1580 		return new L.Transformation(0.5 / (Math.PI * r), 0.5, -0.5 / (Math.PI * r2), 0.5);
       
  1581 	}())
       
  1582 });
       
  1583 
       
  1584 
       
  1585 /*
       
  1586  * L.TileLayer is used for standard xyz-numbered tile layers.
       
  1587  */
       
  1588 
       
  1589 L.TileLayer = L.Class.extend({
       
  1590 	includes: L.Mixin.Events,
       
  1591 
       
  1592 	options: {
       
  1593 		minZoom: 0,
       
  1594 		maxZoom: 18,
       
  1595 		tileSize: 256,
       
  1596 		subdomains: 'abc',
       
  1597 		errorTileUrl: '',
       
  1598 		attribution: '',
       
  1599 		opacity: 1,
       
  1600 		scheme: 'xyz',
       
  1601 		continuousWorld: false,
       
  1602 		noWrap: false,
       
  1603 		zoomOffset: 0,
       
  1604 		zoomReverse: false,
       
  1605 
       
  1606 		unloadInvisibleTiles: L.Browser.mobile,
       
  1607 		updateWhenIdle: L.Browser.mobile
       
  1608 	},
       
  1609 
       
  1610 	initialize: function (url, options, urlParams) {
       
  1611 		L.Util.setOptions(this, options);
       
  1612 
       
  1613 		this._url = url;
       
  1614 		this._urlParams = urlParams;
       
  1615 
       
  1616 		if (typeof this.options.subdomains === 'string') {
       
  1617 			this.options.subdomains = this.options.subdomains.split('');
       
  1618 		}
       
  1619 	},
       
  1620 
       
  1621 	onAdd: function (map, insertAtTheBottom) {
       
  1622 		this._map = map;
       
  1623 		this._insertAtTheBottom = insertAtTheBottom;
       
  1624 
       
  1625 		// create a container div for tiles
       
  1626 		this._initContainer();
       
  1627 
       
  1628 		// create an image to clone for tiles
       
  1629 		this._createTileProto();
       
  1630 
       
  1631 		// set up events
       
  1632 		map.on('viewreset', this._resetCallback, this);
       
  1633 
       
  1634 		if (this.options.updateWhenIdle) {
       
  1635 			map.on('moveend', this._update, this);
       
  1636 		} else {
       
  1637 			this._limitedUpdate = L.Util.limitExecByInterval(this._update, 150, this);
       
  1638 			map.on('move', this._limitedUpdate, this);
       
  1639 		}
       
  1640 
       
  1641 		this._reset();
       
  1642 		this._update();
       
  1643 	},
       
  1644 
       
  1645 	onRemove: function (map) {
       
  1646 		this._map.getPanes().tilePane.removeChild(this._container);
       
  1647 		this._container = null;
       
  1648 
       
  1649 		this._map.off('viewreset', this._resetCallback, this);
       
  1650 
       
  1651 		if (this.options.updateWhenIdle) {
       
  1652 			this._map.off('moveend', this._update, this);
       
  1653 		} else {
       
  1654 			this._map.off('move', this._limitedUpdate, this);
       
  1655 		}
       
  1656 	},
       
  1657 
       
  1658 	getAttribution: function () {
       
  1659 		return this.options.attribution;
       
  1660 	},
       
  1661 
       
  1662 	setOpacity: function (opacity) {
       
  1663 		this.options.opacity = opacity;
       
  1664 
       
  1665 		this._setOpacity(opacity);
       
  1666 
       
  1667 		// stupid webkit hack to force redrawing of tiles
       
  1668 		if (L.Browser.webkit) {
       
  1669 			for (var i in this._tiles) {
       
  1670 				if (this._tiles.hasOwnProperty(i)) {
       
  1671 					this._tiles[i].style.webkitTransform += ' translate(0,0)';
       
  1672 				}
       
  1673 			}
       
  1674 		}
       
  1675 	},
       
  1676 
       
  1677 	_setOpacity: function (opacity) {
       
  1678 		if (opacity < 1) {
       
  1679 			L.DomUtil.setOpacity(this._container, opacity);
       
  1680 		}
       
  1681 	},
       
  1682 
       
  1683 	_initContainer: function () {
       
  1684 		var tilePane = this._map.getPanes().tilePane,
       
  1685 			first = tilePane.firstChild;
       
  1686 
       
  1687 		if (!this._container || tilePane.empty) {
       
  1688 			this._container = L.DomUtil.create('div', 'leaflet-layer');
       
  1689 
       
  1690 			if (this._insertAtTheBottom && first) {
       
  1691 				tilePane.insertBefore(this._container, first);
       
  1692 			} else {
       
  1693 				tilePane.appendChild(this._container);
       
  1694 			}
       
  1695 
       
  1696 			this._setOpacity(this.options.opacity);
       
  1697 		}
       
  1698 	},
       
  1699 
       
  1700 	_resetCallback: function (e) {
       
  1701 		this._reset(e.hard);
       
  1702 	},
       
  1703 
       
  1704 	_reset: function (clearOldContainer) {
       
  1705 		var key;
       
  1706 		for (key in this._tiles) {
       
  1707 			if (this._tiles.hasOwnProperty(key)) {
       
  1708 				this.fire("tileunload", {tile: this._tiles[key]});
       
  1709 			}
       
  1710 		}
       
  1711 		this._tiles = {};
       
  1712 
       
  1713 		if (this.options.reuseTiles) {
       
  1714 			this._unusedTiles = [];
       
  1715 		}
       
  1716 
       
  1717 		if (clearOldContainer && this._container) {
       
  1718 			this._container.innerHTML = "";
       
  1719 		}
       
  1720 		this._initContainer();
       
  1721 	},
       
  1722 
       
  1723 	_update: function () {
       
  1724 		var bounds = this._map.getPixelBounds(),
       
  1725 			tileSize = this.options.tileSize;
       
  1726 
       
  1727 		var nwTilePoint = new L.Point(
       
  1728 				Math.floor(bounds.min.x / tileSize),
       
  1729 				Math.floor(bounds.min.y / tileSize)),
       
  1730 			seTilePoint = new L.Point(
       
  1731 				Math.floor(bounds.max.x / tileSize),
       
  1732 				Math.floor(bounds.max.y / tileSize)),
       
  1733 			tileBounds = new L.Bounds(nwTilePoint, seTilePoint);
       
  1734 
       
  1735 		this._addTilesFromCenterOut(tileBounds);
       
  1736 
       
  1737 		if (this.options.unloadInvisibleTiles ||
       
  1738 			this.options.reuseTiles) {
       
  1739 			this._removeOtherTiles(tileBounds);
       
  1740 		}
       
  1741 	},
       
  1742 
       
  1743 	_addTilesFromCenterOut: function (bounds) {
       
  1744 		var queue = [],
       
  1745 			center = bounds.getCenter();
       
  1746 
       
  1747 		for (var j = bounds.min.y; j <= bounds.max.y; j++) {
       
  1748 			for (var i = bounds.min.x; i <= bounds.max.x; i++) {
       
  1749 				if ((i + ':' + j) in this._tiles) {
       
  1750 					continue;
       
  1751 				}
       
  1752 				queue.push(new L.Point(i, j));
       
  1753 			}
       
  1754 		}
       
  1755 
       
  1756 		// load tiles in order of their distance to center
       
  1757 		queue.sort(function (a, b) {
       
  1758 			return a.distanceTo(center) - b.distanceTo(center);
       
  1759 		});
       
  1760 
       
  1761 		var fragment = document.createDocumentFragment();
       
  1762 
       
  1763 		this._tilesToLoad = queue.length;
       
  1764 		for (var k = 0, len = this._tilesToLoad; k < len; k++) {
       
  1765 			this._addTile(queue[k], fragment);
       
  1766 		}
       
  1767 
       
  1768 		this._container.appendChild(fragment);
       
  1769 	},
       
  1770 
       
  1771 	_removeOtherTiles: function (bounds) {
       
  1772 		var kArr, x, y, key, tile;
       
  1773 
       
  1774 		for (key in this._tiles) {
       
  1775 			if (this._tiles.hasOwnProperty(key)) {
       
  1776 				kArr = key.split(':');
       
  1777 				x = parseInt(kArr[0], 10);
       
  1778 				y = parseInt(kArr[1], 10);
       
  1779 
       
  1780 				// remove tile if it's out of bounds
       
  1781 				if (x < bounds.min.x || x > bounds.max.x || y < bounds.min.y || y > bounds.max.y) {
       
  1782 
       
  1783 					tile = this._tiles[key];
       
  1784 					this.fire("tileunload", {tile: tile, url: tile.src});
       
  1785 
       
  1786 					// evil, don't do this! crashes Android 3, produces load errors, doesn't solve memory leaks
       
  1787 					// this._tiles[key].src = '';
       
  1788 
       
  1789 					if (tile.parentNode === this._container) {
       
  1790 						this._container.removeChild(tile);
       
  1791 					}
       
  1792 					if (this.options.reuseTiles) {
       
  1793 						this._unusedTiles.push(this._tiles[key]);
       
  1794 					}
       
  1795 					delete this._tiles[key];
       
  1796 				}
       
  1797 			}
       
  1798 		}
       
  1799 	},
       
  1800 
       
  1801 	_addTile: function (tilePoint, container) {
       
  1802 		var tilePos = this._getTilePos(tilePoint),
       
  1803 			zoom = this._map.getZoom(),
       
  1804 			key = tilePoint.x + ':' + tilePoint.y,
       
  1805 			tileLimit = Math.pow(2, this._getOffsetZoom(zoom));
       
  1806 
       
  1807 		// wrap tile coordinates
       
  1808 		if (!this.options.continuousWorld) {
       
  1809 			if (!this.options.noWrap) {
       
  1810 				tilePoint.x = ((tilePoint.x % tileLimit) + tileLimit) % tileLimit;
       
  1811 			} else if (tilePoint.x < 0 || tilePoint.x >= tileLimit) {
       
  1812 				this._tilesToLoad--;
       
  1813 				return;
       
  1814 			}
       
  1815 
       
  1816 			if (tilePoint.y < 0 || tilePoint.y >= tileLimit) {
       
  1817 				this._tilesToLoad--;
       
  1818 				return;
       
  1819 			}
       
  1820 		}
       
  1821 
       
  1822 		// get unused tile - or create a new tile
       
  1823 		var tile = this._getTile();
       
  1824 		L.DomUtil.setPosition(tile, tilePos);
       
  1825 
       
  1826 		this._tiles[key] = tile;
       
  1827 
       
  1828 		if (this.options.scheme === 'tms') {
       
  1829 			tilePoint.y = tileLimit - tilePoint.y - 1;
       
  1830 		}
       
  1831 
       
  1832 		this._loadTile(tile, tilePoint, zoom);
       
  1833 
       
  1834 		container.appendChild(tile);
       
  1835 	},
       
  1836 
       
  1837 	_getOffsetZoom: function (zoom) {
       
  1838 		zoom = this.options.zoomReverse ? this.options.maxZoom - zoom : zoom;
       
  1839 		return zoom + this.options.zoomOffset;
       
  1840 	},
       
  1841 
       
  1842 	_getTilePos: function (tilePoint) {
       
  1843 		var origin = this._map.getPixelOrigin(),
       
  1844 			tileSize = this.options.tileSize;
       
  1845 
       
  1846 		return tilePoint.multiplyBy(tileSize).subtract(origin);
       
  1847 	},
       
  1848 
       
  1849 	// image-specific code (override to implement e.g. Canvas or SVG tile layer)
       
  1850 
       
  1851 	getTileUrl: function (tilePoint, zoom) {
       
  1852 		var subdomains = this.options.subdomains,
       
  1853 			s = this.options.subdomains[(tilePoint.x + tilePoint.y) % subdomains.length];
       
  1854 
       
  1855 		return L.Util.template(this._url, L.Util.extend({
       
  1856 			s: s,
       
  1857 			z: this._getOffsetZoom(zoom),
       
  1858 			x: tilePoint.x,
       
  1859 			y: tilePoint.y
       
  1860 		}, this._urlParams));
       
  1861 	},
       
  1862 
       
  1863 	_createTileProto: function () {
       
  1864 		this._tileImg = L.DomUtil.create('img', 'leaflet-tile');
       
  1865 		this._tileImg.galleryimg = 'no';
       
  1866 
       
  1867 		var tileSize = this.options.tileSize;
       
  1868 		this._tileImg.style.width = tileSize + 'px';
       
  1869 		this._tileImg.style.height = tileSize + 'px';
       
  1870 	},
       
  1871 
       
  1872 	_getTile: function () {
       
  1873 		if (this.options.reuseTiles && this._unusedTiles.length > 0) {
       
  1874 			var tile = this._unusedTiles.pop();
       
  1875 			this._resetTile(tile);
       
  1876 			return tile;
       
  1877 		}
       
  1878 		return this._createTile();
       
  1879 	},
       
  1880 
       
  1881 	_resetTile: function (tile) {
       
  1882 		// Override if data stored on a tile needs to be cleaned up before reuse
       
  1883 	},
       
  1884 
       
  1885 	_createTile: function () {
       
  1886 		var tile = this._tileImg.cloneNode(false);
       
  1887 		tile.onselectstart = tile.onmousemove = L.Util.falseFn;
       
  1888 		return tile;
       
  1889 	},
       
  1890 
       
  1891 	_loadTile: function (tile, tilePoint, zoom) {
       
  1892 		tile._layer = this;
       
  1893 		tile.onload = this._tileOnLoad;
       
  1894 		tile.onerror = this._tileOnError;
       
  1895 		tile.src = this.getTileUrl(tilePoint, zoom);
       
  1896 	},
       
  1897 
       
  1898 	_tileOnLoad: function (e) {
       
  1899 		var layer = this._layer;
       
  1900 
       
  1901 		this.className += ' leaflet-tile-loaded';
       
  1902 
       
  1903 		layer.fire('tileload', {tile: this, url: this.src});
       
  1904 
       
  1905 		layer._tilesToLoad--;
       
  1906 		if (!layer._tilesToLoad) {
       
  1907 			layer.fire('load');
       
  1908 		}
       
  1909 	},
       
  1910 
       
  1911 	_tileOnError: function (e) {
       
  1912 		var layer = this._layer;
       
  1913 
       
  1914 		layer.fire('tileerror', {tile: this, url: this.src});
       
  1915 
       
  1916 		var newUrl = layer.options.errorTileUrl;
       
  1917 		if (newUrl) {
       
  1918 			this.src = newUrl;
       
  1919 		}
       
  1920 	}
       
  1921 });
       
  1922 
       
  1923 
       
  1924 L.TileLayer.WMS = L.TileLayer.extend({
       
  1925 	defaultWmsParams: {
       
  1926 		service: 'WMS',
       
  1927 		request: 'GetMap',
       
  1928 		version: '1.1.1',
       
  1929 		layers: '',
       
  1930 		styles: '',
       
  1931 		format: 'image/jpeg',
       
  1932 		transparent: false
       
  1933 	},
       
  1934 
       
  1935 	initialize: function (/*String*/ url, /*Object*/ options) {
       
  1936 		this._url = url;
       
  1937 
       
  1938 		this.wmsParams = L.Util.extend({}, this.defaultWmsParams);
       
  1939 		this.wmsParams.width = this.wmsParams.height = this.options.tileSize;
       
  1940 
       
  1941 		for (var i in options) {
       
  1942 			// all keys that are not TileLayer options go to WMS params
       
  1943 			if (!this.options.hasOwnProperty(i)) {
       
  1944 				this.wmsParams[i] = options[i];
       
  1945 			}
       
  1946 		}
       
  1947 
       
  1948 		L.Util.setOptions(this, options);
       
  1949 	},
       
  1950 
       
  1951 	onAdd: function (map) {
       
  1952 		var projectionKey = (parseFloat(this.wmsParams.version) >= 1.3 ? 'crs' : 'srs');
       
  1953 		this.wmsParams[projectionKey] = map.options.crs.code;
       
  1954 
       
  1955 		L.TileLayer.prototype.onAdd.call(this, map);
       
  1956 	},
       
  1957 
       
  1958 	getTileUrl: function (/*Point*/ tilePoint, /*Number*/ zoom)/*-> String*/ {
       
  1959 		var tileSize = this.options.tileSize,
       
  1960 			nwPoint = tilePoint.multiplyBy(tileSize),
       
  1961 			sePoint = nwPoint.add(new L.Point(tileSize, tileSize)),
       
  1962 			nwMap = this._map.unproject(nwPoint, this._zoom, true),
       
  1963 			seMap = this._map.unproject(sePoint, this._zoom, true),
       
  1964 			nw = this._map.options.crs.project(nwMap),
       
  1965 			se = this._map.options.crs.project(seMap),
       
  1966 			bbox = [nw.x, se.y, se.x, nw.y].join(',');
       
  1967 
       
  1968 		return this._url + L.Util.getParamString(this.wmsParams) + "&bbox=" + bbox;
       
  1969 	}
       
  1970 });
       
  1971 
       
  1972 
       
  1973 L.TileLayer.Canvas = L.TileLayer.extend({
       
  1974 	options: {
       
  1975 		async: false
       
  1976 	},
       
  1977 
       
  1978 	initialize: function (options) {
       
  1979 		L.Util.setOptions(this, options);
       
  1980 	},
       
  1981 
       
  1982 	redraw: function () {
       
  1983 		for (var i in this._tiles) {
       
  1984 			var tile = this._tiles[i];
       
  1985 			this._redrawTile(tile);
       
  1986 		}
       
  1987 	},
       
  1988 
       
  1989 	_redrawTile: function (tile) {
       
  1990 		this.drawTile(tile, tile._tilePoint, tile._zoom);
       
  1991 	},
       
  1992 
       
  1993 	_createTileProto: function () {
       
  1994 		this._canvasProto = L.DomUtil.create('canvas', 'leaflet-tile');
       
  1995 
       
  1996 		var tileSize = this.options.tileSize;
       
  1997 		this._canvasProto.width = tileSize;
       
  1998 		this._canvasProto.height = tileSize;
       
  1999 	},
       
  2000 
       
  2001 	_createTile: function () {
       
  2002 		var tile = this._canvasProto.cloneNode(false);
       
  2003 		tile.onselectstart = tile.onmousemove = L.Util.falseFn;
       
  2004 		return tile;
       
  2005 	},
       
  2006 
       
  2007 	_loadTile: function (tile, tilePoint, zoom) {
       
  2008 		tile._layer = this;
       
  2009 		tile._tilePoint = tilePoint;
       
  2010 		tile._zoom = zoom;
       
  2011 
       
  2012 		this.drawTile(tile, tilePoint, zoom);
       
  2013 
       
  2014 		if (!this.options.async) {
       
  2015 			this.tileDrawn(tile);
       
  2016 		}
       
  2017 	},
       
  2018 
       
  2019 	drawTile: function (tile, tilePoint, zoom) {
       
  2020 		// override with rendering code
       
  2021 	},
       
  2022 
       
  2023 	tileDrawn: function (tile) {
       
  2024 		this._tileOnLoad.call(tile);
       
  2025 	}
       
  2026 });
       
  2027 
       
  2028 
       
  2029 L.ImageOverlay = L.Class.extend({
       
  2030 	includes: L.Mixin.Events,
       
  2031 
       
  2032 	initialize: function (/*String*/ url, /*LatLngBounds*/ bounds) {
       
  2033 		this._url = url;
       
  2034 		this._bounds = bounds;
       
  2035 	},
       
  2036 
       
  2037 	onAdd: function (map) {
       
  2038 		this._map = map;
       
  2039 
       
  2040 		if (!this._image) {
       
  2041 			this._initImage();
       
  2042 		}
       
  2043 
       
  2044 		map.getPanes().overlayPane.appendChild(this._image);
       
  2045 
       
  2046 		map.on('viewreset', this._reset, this);
       
  2047 		this._reset();
       
  2048 	},
       
  2049 
       
  2050 	onRemove: function (map) {
       
  2051 		map.getPanes().overlayPane.removeChild(this._image);
       
  2052 		map.off('viewreset', this._reset, this);
       
  2053 	},
       
  2054 
       
  2055 	_initImage: function () {
       
  2056 		this._image = L.DomUtil.create('img', 'leaflet-image-layer');
       
  2057 
       
  2058 		this._image.style.visibility = 'hidden';
       
  2059 		//TODO opacity option
       
  2060 
       
  2061 		//TODO createImage util method to remove duplication
       
  2062 		L.Util.extend(this._image, {
       
  2063 			galleryimg: 'no',
       
  2064 			onselectstart: L.Util.falseFn,
       
  2065 			onmousemove: L.Util.falseFn,
       
  2066 			onload: L.Util.bind(this._onImageLoad, this),
       
  2067 			src: this._url
       
  2068 		});
       
  2069 	},
       
  2070 
       
  2071 	_reset: function () {
       
  2072 		var topLeft = this._map.latLngToLayerPoint(this._bounds.getNorthWest()),
       
  2073 			bottomRight = this._map.latLngToLayerPoint(this._bounds.getSouthEast()),
       
  2074 			size = bottomRight.subtract(topLeft);
       
  2075 
       
  2076 		L.DomUtil.setPosition(this._image, topLeft);
       
  2077 
       
  2078 		this._image.style.width = size.x + 'px';
       
  2079 		this._image.style.height = size.y + 'px';
       
  2080 	},
       
  2081 
       
  2082 	_onImageLoad: function () {
       
  2083 		this._image.style.visibility = '';
       
  2084 		this.fire('load');
       
  2085 	}
       
  2086 });
       
  2087 
       
  2088 
       
  2089 L.Icon = L.Class.extend({
       
  2090 	iconUrl: L.ROOT_URL + 'images/marker.png',
       
  2091 	shadowUrl: L.ROOT_URL + 'images/marker-shadow.png',
       
  2092 
       
  2093 	iconSize: new L.Point(25, 41),
       
  2094 	shadowSize: new L.Point(41, 41),
       
  2095 
       
  2096 	iconAnchor: new L.Point(13, 41),
       
  2097 	popupAnchor: new L.Point(0, -33),
       
  2098 
       
  2099 	initialize: function (iconUrl) {
       
  2100 		if (iconUrl) {
       
  2101 			this.iconUrl = iconUrl;
       
  2102 		}
       
  2103 	},
       
  2104 
       
  2105 	createIcon: function () {
       
  2106 		return this._createIcon('icon');
       
  2107 	},
       
  2108 
       
  2109 	createShadow: function () {
       
  2110 		return this._createIcon('shadow');
       
  2111 	},
       
  2112 
       
  2113 	_createIcon: function (name) {
       
  2114 		var size = this[name + 'Size'],
       
  2115 			src = this[name + 'Url'];
       
  2116 		if (!src && name === 'shadow') {
       
  2117 			return null;
       
  2118 		}
       
  2119 
       
  2120 		var img;
       
  2121 		if (!src) {
       
  2122 			img = this._createDiv();
       
  2123 		}
       
  2124 		else {
       
  2125 			img = this._createImg(src);
       
  2126 		}
       
  2127 
       
  2128 		img.className = 'leaflet-marker-' + name;
       
  2129 
       
  2130 		img.style.marginLeft = (-this.iconAnchor.x) + 'px';
       
  2131 		img.style.marginTop = (-this.iconAnchor.y) + 'px';
       
  2132 
       
  2133 		if (size) {
       
  2134 			img.style.width = size.x + 'px';
       
  2135 			img.style.height = size.y + 'px';
       
  2136 		}
       
  2137 
       
  2138 		return img;
       
  2139 	},
       
  2140 
       
  2141 	_createImg: function (src) {
       
  2142 		var el;
       
  2143 		if (!L.Browser.ie6) {
       
  2144 			el = document.createElement('img');
       
  2145 			el.src = src;
       
  2146 		} else {
       
  2147 			el = document.createElement('div');
       
  2148 			el.style.filter = 'progid:DXImageTransform.Microsoft.AlphaImageLoader(src="' + src + '")';
       
  2149 		}
       
  2150 		return el;
       
  2151 	},
       
  2152 
       
  2153 	_createDiv: function () {
       
  2154 		return document.createElement('div');
       
  2155 	}
       
  2156 });
       
  2157 
       
  2158 
       
  2159 /*
       
  2160  * L.Marker is used to display clickable/draggable icons on the map.
       
  2161  */
       
  2162 
       
  2163 L.Marker = L.Class.extend({
       
  2164 
       
  2165 	includes: L.Mixin.Events,
       
  2166 
       
  2167 	options: {
       
  2168 		icon: new L.Icon(),
       
  2169 		title: '',
       
  2170 		clickable: true,
       
  2171 		draggable: false,
       
  2172 		zIndexOffset: 0
       
  2173 	},
       
  2174 
       
  2175 	initialize: function (latlng, options) {
       
  2176 		L.Util.setOptions(this, options);
       
  2177 		this._latlng = latlng;
       
  2178 	},
       
  2179 
       
  2180 	onAdd: function (map) {
       
  2181 		this._map = map;
       
  2182 
       
  2183 		this._initIcon();
       
  2184 
       
  2185 		map.on('viewreset', this._reset, this);
       
  2186 		this._reset();
       
  2187 	},
       
  2188 
       
  2189 	onRemove: function (map) {
       
  2190 		this._removeIcon();
       
  2191 
       
  2192 		// TODO move to Marker.Popup.js
       
  2193 		if (this.closePopup) {
       
  2194 			this.closePopup();
       
  2195 		}
       
  2196 
       
  2197 		this._map = null;
       
  2198 
       
  2199 		map.off('viewreset', this._reset, this);
       
  2200 	},
       
  2201 
       
  2202 	getLatLng: function () {
       
  2203 		return this._latlng;
       
  2204 	},
       
  2205 
       
  2206 	setLatLng: function (latlng) {
       
  2207 		this._latlng = latlng;
       
  2208 		if (this._icon) {
       
  2209 			this._reset();
       
  2210 
       
  2211 			if (this._popup) {
       
  2212 				this._popup.setLatLng(this._latlng);
       
  2213 			}
       
  2214 		}
       
  2215 	},
       
  2216 
       
  2217 	setIcon: function (icon) {
       
  2218 		if (this._map) {
       
  2219 			this._removeIcon();
       
  2220 		}
       
  2221 
       
  2222 		this.options.icon = icon;
       
  2223 
       
  2224 		if (this._map) {
       
  2225 			this._initIcon();
       
  2226 			this._reset();
       
  2227 		}
       
  2228 	},
       
  2229 
       
  2230 	_initIcon: function () {
       
  2231 		if (!this._icon) {
       
  2232 			this._icon = this.options.icon.createIcon();
       
  2233 
       
  2234 			if (this.options.title) {
       
  2235 				this._icon.title = this.options.title;
       
  2236 			}
       
  2237 
       
  2238 			this._initInteraction();
       
  2239 		}
       
  2240 		if (!this._shadow) {
       
  2241 			this._shadow = this.options.icon.createShadow();
       
  2242 		}
       
  2243 
       
  2244 		this._map._panes.markerPane.appendChild(this._icon);
       
  2245 		if (this._shadow) {
       
  2246 			this._map._panes.shadowPane.appendChild(this._shadow);
       
  2247 		}
       
  2248 	},
       
  2249 
       
  2250 	_removeIcon: function () {
       
  2251 		this._map._panes.markerPane.removeChild(this._icon);
       
  2252 		if (this._shadow) {
       
  2253 			this._map._panes.shadowPane.removeChild(this._shadow);
       
  2254 		}
       
  2255 		this._icon = this._shadow = null;
       
  2256 	},
       
  2257 
       
  2258 	_reset: function () {
       
  2259 		var pos = this._map.latLngToLayerPoint(this._latlng).round();
       
  2260 
       
  2261 		L.DomUtil.setPosition(this._icon, pos);
       
  2262 		if (this._shadow) {
       
  2263 			L.DomUtil.setPosition(this._shadow, pos);
       
  2264 		}
       
  2265 
       
  2266 		this._icon.style.zIndex = pos.y + this.options.zIndexOffset;
       
  2267 	},
       
  2268 
       
  2269 	_initInteraction: function () {
       
  2270 		if (this.options.clickable) {
       
  2271 			this._icon.className += ' leaflet-clickable';
       
  2272 
       
  2273 			L.DomEvent.addListener(this._icon, 'click', this._onMouseClick, this);
       
  2274 
       
  2275 			var events = ['dblclick', 'mousedown', 'mouseover', 'mouseout'];
       
  2276 			for (var i = 0; i < events.length; i++) {
       
  2277 				L.DomEvent.addListener(this._icon, events[i], this._fireMouseEvent, this);
       
  2278 			}
       
  2279 		}
       
  2280 
       
  2281 		if (L.Handler.MarkerDrag) {
       
  2282 			this.dragging = new L.Handler.MarkerDrag(this);
       
  2283 
       
  2284 			if (this.options.draggable) {
       
  2285 				this.dragging.enable();
       
  2286 			}
       
  2287 		}
       
  2288 	},
       
  2289 
       
  2290 	_onMouseClick: function (e) {
       
  2291 		L.DomEvent.stopPropagation(e);
       
  2292 		if (this.dragging && this.dragging.moved()) { return; }
       
  2293 		this.fire(e.type);
       
  2294 	},
       
  2295 
       
  2296 	_fireMouseEvent: function (e) {
       
  2297 		this.fire(e.type);
       
  2298 		L.DomEvent.stopPropagation(e);
       
  2299 	}
       
  2300 });
       
  2301 
       
  2302 
       
  2303 
       
  2304 L.Popup = L.Class.extend({
       
  2305 	includes: L.Mixin.Events,
       
  2306 
       
  2307 	options: {
       
  2308 		minWidth: 50,
       
  2309 		maxWidth: 300,
       
  2310 		autoPan: true,
       
  2311 		closeButton: true,
       
  2312 		offset: new L.Point(0, 2),
       
  2313 		autoPanPadding: new L.Point(5, 5),
       
  2314 		className: ''
       
  2315 	},
       
  2316 
       
  2317 	initialize: function (options, source) {
       
  2318 		L.Util.setOptions(this, options);
       
  2319 
       
  2320 		this._source = source;
       
  2321 	},
       
  2322 
       
  2323 	onAdd: function (map) {
       
  2324 		this._map = map;
       
  2325 		if (!this._container) {
       
  2326 			this._initLayout();
       
  2327 		}
       
  2328 		this._updateContent();
       
  2329 
       
  2330 		this._container.style.opacity = '0';
       
  2331 
       
  2332 		this._map._panes.popupPane.appendChild(this._container);
       
  2333 		this._map.on('viewreset', this._updatePosition, this);
       
  2334 
       
  2335 		if (this._map.options.closePopupOnClick) {
       
  2336 			this._map.on('preclick', this._close, this);
       
  2337 		}
       
  2338 
       
  2339 		this._update();
       
  2340 
       
  2341 		this._container.style.opacity = '1'; //TODO fix ugly opacity hack
       
  2342 
       
  2343 		this._opened = true;
       
  2344 	},
       
  2345 
       
  2346 	onRemove: function (map) {
       
  2347 		map._panes.popupPane.removeChild(this._container);
       
  2348 		L.Util.falseFn(this._container.offsetWidth);
       
  2349 
       
  2350 		map.off('viewreset', this._updatePosition, this);
       
  2351 		map.off('click', this._close, this);
       
  2352 
       
  2353 		this._container.style.opacity = '0';
       
  2354 
       
  2355 		this._opened = false;
       
  2356 	},
       
  2357 
       
  2358 	setLatLng: function (latlng) {
       
  2359 		this._latlng = latlng;
       
  2360 		if (this._opened) {
       
  2361 			this._update();
       
  2362 		}
       
  2363 		return this;
       
  2364 	},
       
  2365 
       
  2366 	setContent: function (content) {
       
  2367 		this._content = content;
       
  2368 		if (this._opened) {
       
  2369 			this._update();
       
  2370 		}
       
  2371 		return this;
       
  2372 	},
       
  2373 
       
  2374 	_close: function () {
       
  2375 		if (this._opened) {
       
  2376 			this._map.closePopup();
       
  2377 		}
       
  2378 	},
       
  2379 
       
  2380 	_initLayout: function () {
       
  2381 		this._container = L.DomUtil.create('div', 'leaflet-popup ' + this.options.className);
       
  2382 
       
  2383 		if (this.options.closeButton) {
       
  2384 			this._closeButton = L.DomUtil.create('a', 'leaflet-popup-close-button', this._container);
       
  2385 			this._closeButton.href = '#close';
       
  2386 			L.DomEvent.addListener(this._closeButton, 'click', this._onCloseButtonClick, this);
       
  2387 		}
       
  2388 
       
  2389 		this._wrapper = L.DomUtil.create('div', 'leaflet-popup-content-wrapper', this._container);
       
  2390 		L.DomEvent.disableClickPropagation(this._wrapper);
       
  2391 		this._contentNode = L.DomUtil.create('div', 'leaflet-popup-content', this._wrapper);
       
  2392 
       
  2393 		this._tipContainer = L.DomUtil.create('div', 'leaflet-popup-tip-container', this._container);
       
  2394 		this._tip = L.DomUtil.create('div', 'leaflet-popup-tip', this._tipContainer);
       
  2395 	},
       
  2396 
       
  2397 	_update: function () {
       
  2398 		this._container.style.visibility = 'hidden';
       
  2399 
       
  2400 		this._updateContent();
       
  2401 		this._updateLayout();
       
  2402 		this._updatePosition();
       
  2403 
       
  2404 		this._container.style.visibility = '';
       
  2405 
       
  2406 		this._adjustPan();
       
  2407 	},
       
  2408 
       
  2409 	_updateContent: function () {
       
  2410 		if (!this._content) {
       
  2411 			return;
       
  2412 		}
       
  2413 
       
  2414 		if (typeof this._content === 'string') {
       
  2415 			this._contentNode.innerHTML = this._content;
       
  2416 		} else {
       
  2417 			this._contentNode.innerHTML = '';
       
  2418 			this._contentNode.appendChild(this._content);
       
  2419 		}
       
  2420 	},
       
  2421 
       
  2422 	_updateLayout: function () {
       
  2423 		this._container.style.width = '';
       
  2424 		this._container.style.whiteSpace = 'nowrap';
       
  2425 
       
  2426 		var width = this._container.offsetWidth;
       
  2427 
       
  2428 		this._container.style.width = (width > this.options.maxWidth ?
       
  2429 				this.options.maxWidth : (width < this.options.minWidth ? this.options.minWidth : width)) + 'px';
       
  2430 		this._container.style.whiteSpace = '';
       
  2431 
       
  2432 		this._containerWidth = this._container.offsetWidth;
       
  2433 	},
       
  2434 
       
  2435 	_updatePosition: function () {
       
  2436 		var pos = this._map.latLngToLayerPoint(this._latlng);
       
  2437 
       
  2438 		this._containerBottom = -pos.y - this.options.offset.y;
       
  2439 		this._containerLeft = pos.x - Math.round(this._containerWidth / 2) + this.options.offset.x;
       
  2440 
       
  2441 		this._container.style.bottom = this._containerBottom + 'px';
       
  2442 		this._container.style.left = this._containerLeft + 'px';
       
  2443 	},
       
  2444 
       
  2445 	_adjustPan: function () {
       
  2446 		if (!this.options.autoPan) {
       
  2447 			return;
       
  2448 		}
       
  2449 
       
  2450 		var containerHeight = this._container.offsetHeight,
       
  2451 			layerPos = new L.Point(
       
  2452 				this._containerLeft,
       
  2453 				-containerHeight - this._containerBottom),
       
  2454 			containerPos = this._map.layerPointToContainerPoint(layerPos),
       
  2455 			adjustOffset = new L.Point(0, 0),
       
  2456 			padding = this.options.autoPanPadding,
       
  2457 			size = this._map.getSize();
       
  2458 
       
  2459 		if (containerPos.x < 0) {
       
  2460 			adjustOffset.x = containerPos.x - padding.x;
       
  2461 		}
       
  2462 		if (containerPos.x + this._containerWidth > size.x) {
       
  2463 			adjustOffset.x = containerPos.x + this._containerWidth - size.x + padding.x;
       
  2464 		}
       
  2465 		if (containerPos.y < 0) {
       
  2466 			adjustOffset.y = containerPos.y - padding.y;
       
  2467 		}
       
  2468 		if (containerPos.y + containerHeight > size.y) {
       
  2469 			adjustOffset.y = containerPos.y + containerHeight - size.y + padding.y;
       
  2470 		}
       
  2471 
       
  2472 		if (adjustOffset.x || adjustOffset.y) {
       
  2473 			this._map.panBy(adjustOffset);
       
  2474 		}
       
  2475 	},
       
  2476 
       
  2477 	_onCloseButtonClick: function (e) {
       
  2478 		this._close();
       
  2479 		L.DomEvent.stop(e);
       
  2480 	}
       
  2481 });
       
  2482 
       
  2483 
       
  2484 /*
       
  2485  * Popup extension to L.Marker, adding openPopup & bindPopup methods.
       
  2486  */
       
  2487 
       
  2488 L.Marker.include({
       
  2489 	openPopup: function () {
       
  2490 		this._popup.setLatLng(this._latlng);
       
  2491 		if (this._map) {
       
  2492 			this._map.openPopup(this._popup);
       
  2493 		}
       
  2494 
       
  2495 		return this;
       
  2496 	},
       
  2497 
       
  2498 	closePopup: function () {
       
  2499 		if (this._popup) {
       
  2500 			this._popup._close();
       
  2501 		}
       
  2502 		return this;
       
  2503 	},
       
  2504 
       
  2505 	bindPopup: function (content, options) {
       
  2506 		options = L.Util.extend({offset: this.options.icon.popupAnchor}, options);
       
  2507 
       
  2508 		if (!this._popup) {
       
  2509 			this.on('click', this.openPopup, this);
       
  2510 		}
       
  2511 
       
  2512 		this._popup = new L.Popup(options, this);
       
  2513 		this._popup.setContent(content);
       
  2514 
       
  2515 		return this;
       
  2516 	},
       
  2517 
       
  2518 	unbindPopup: function () {
       
  2519 		if (this._popup) {
       
  2520 			this._popup = null;
       
  2521 			this.off('click', this.openPopup);
       
  2522 		}
       
  2523 		return this;
       
  2524 	}
       
  2525 });
       
  2526 
       
  2527 
       
  2528 
       
  2529 L.Map.include({
       
  2530 	openPopup: function (popup) {
       
  2531 		this.closePopup();
       
  2532 		this._popup = popup;
       
  2533 		this.addLayer(popup);
       
  2534 		this.fire('popupopen', { popup: this._popup });
       
  2535 	
       
  2536 		return this;
       
  2537 	},
       
  2538 
       
  2539 	closePopup: function () {
       
  2540 		if (this._popup) {
       
  2541 			this.removeLayer(this._popup);
       
  2542 			this.fire('popupclose', { popup: this._popup });
       
  2543 			this._popup = null;
       
  2544 		}
       
  2545 		return this;
       
  2546 	}
       
  2547 });
       
  2548 
       
  2549 
       
  2550 /*
       
  2551  * L.LayerGroup is a class to combine several layers so you can manipulate the group (e.g. add/remove it) as one layer.
       
  2552  */
       
  2553 
       
  2554 L.LayerGroup = L.Class.extend({
       
  2555 	initialize: function (layers) {
       
  2556 		this._layers = {};
       
  2557 
       
  2558 		if (layers) {
       
  2559 			for (var i = 0, len = layers.length; i < len; i++) {
       
  2560 				this.addLayer(layers[i]);
       
  2561 			}
       
  2562 		}
       
  2563 	},
       
  2564 
       
  2565 	addLayer: function (layer) {
       
  2566 		var id = L.Util.stamp(layer);
       
  2567 		this._layers[id] = layer;
       
  2568 
       
  2569 		if (this._map) {
       
  2570 			this._map.addLayer(layer);
       
  2571 		}
       
  2572 		return this;
       
  2573 	},
       
  2574 
       
  2575 	removeLayer: function (layer) {
       
  2576 		var id = L.Util.stamp(layer);
       
  2577 		delete this._layers[id];
       
  2578 
       
  2579 		if (this._map) {
       
  2580 			this._map.removeLayer(layer);
       
  2581 		}
       
  2582 		return this;
       
  2583 	},
       
  2584 
       
  2585 	clearLayers: function () {
       
  2586 		this._iterateLayers(this.removeLayer, this);
       
  2587 		return this;
       
  2588 	},
       
  2589 
       
  2590 	invoke: function (methodName) {
       
  2591 		var args = Array.prototype.slice.call(arguments, 1),
       
  2592 			i, layer;
       
  2593 
       
  2594 		for (i in this._layers) {
       
  2595 			if (this._layers.hasOwnProperty(i)) {
       
  2596 				layer = this._layers[i];
       
  2597 
       
  2598 				if (layer[methodName]) {
       
  2599 					layer[methodName].apply(layer, args);
       
  2600 				}
       
  2601 			}
       
  2602 		}
       
  2603 		return this;
       
  2604 	},
       
  2605 
       
  2606 	onAdd: function (map) {
       
  2607 		this._map = map;
       
  2608 		this._iterateLayers(map.addLayer, map);
       
  2609 	},
       
  2610 
       
  2611 	onRemove: function (map) {
       
  2612 		this._iterateLayers(map.removeLayer, map);
       
  2613 		delete this._map;
       
  2614 	},
       
  2615 
       
  2616 	_iterateLayers: function (method, context) {
       
  2617 		for (var i in this._layers) {
       
  2618 			if (this._layers.hasOwnProperty(i)) {
       
  2619 				method.call(context, this._layers[i]);
       
  2620 			}
       
  2621 		}
       
  2622 	}
       
  2623 });
       
  2624 
       
  2625 
       
  2626 /*
       
  2627  * L.FeatureGroup extends L.LayerGroup by introducing mouse events and bindPopup method shared between a group of layers.
       
  2628  */
       
  2629 
       
  2630 L.FeatureGroup = L.LayerGroup.extend({
       
  2631 	includes: L.Mixin.Events,
       
  2632 
       
  2633 	addLayer: function (layer) {
       
  2634 		this._initEvents(layer);
       
  2635 		L.LayerGroup.prototype.addLayer.call(this, layer);
       
  2636 
       
  2637 		if (this._popupContent && layer.bindPopup) {
       
  2638 			layer.bindPopup(this._popupContent);
       
  2639 		}
       
  2640 	},
       
  2641 
       
  2642 	bindPopup: function (content) {
       
  2643 		this._popupContent = content;
       
  2644 
       
  2645 		return this.invoke('bindPopup', content);
       
  2646 	},
       
  2647 
       
  2648 	setStyle: function (style) {
       
  2649 		return this.invoke('setStyle', style);
       
  2650 	},
       
  2651 
       
  2652 	_events: ['click', 'dblclick', 'mouseover', 'mouseout'],
       
  2653 
       
  2654 	_initEvents: function (layer) {
       
  2655 		for (var i = 0, len = this._events.length; i < len; i++) {
       
  2656 			layer.on(this._events[i], this._propagateEvent, this);
       
  2657 		}
       
  2658 	},
       
  2659 
       
  2660 	_propagateEvent: function (e) {
       
  2661 		e.layer = e.target;
       
  2662 		e.target = this;
       
  2663 		this.fire(e.type, e);
       
  2664 	}
       
  2665 });
       
  2666 
       
  2667 
       
  2668 /*
       
  2669  * L.Path is a base class for rendering vector paths on a map. It's inherited by Polyline, Circle, etc.
       
  2670  */
       
  2671 
       
  2672 L.Path = L.Class.extend({
       
  2673 	includes: [L.Mixin.Events],
       
  2674 
       
  2675 	statics: {
       
  2676 		// how much to extend the clip area around the map view
       
  2677 		// (relative to its size, e.g. 0.5 is half the screen in each direction)
       
  2678 		CLIP_PADDING: 0.5
       
  2679 	},
       
  2680 
       
  2681 	options: {
       
  2682 		stroke: true,
       
  2683 		color: '#0033ff',
       
  2684 		weight: 5,
       
  2685 		opacity: 0.5,
       
  2686 
       
  2687 		fill: false,
       
  2688 		fillColor: null, //same as color by default
       
  2689 		fillOpacity: 0.2,
       
  2690 
       
  2691 		clickable: true,
       
  2692 
       
  2693 		// TODO remove this, as all paths now update on moveend
       
  2694 		updateOnMoveEnd: true
       
  2695 	},
       
  2696 
       
  2697 	initialize: function (options) {
       
  2698 		L.Util.setOptions(this, options);
       
  2699 	},
       
  2700 
       
  2701 	onAdd: function (map) {
       
  2702 		this._map = map;
       
  2703 
       
  2704 		this._initElements();
       
  2705 		this._initEvents();
       
  2706 		this.projectLatlngs();
       
  2707 		this._updatePath();
       
  2708 
       
  2709 		map.on('viewreset', this.projectLatlngs, this);
       
  2710 
       
  2711 		this._updateTrigger = this.options.updateOnMoveEnd ? 'moveend' : 'viewreset';
       
  2712 		map.on(this._updateTrigger, this._updatePath, this);
       
  2713 	},
       
  2714 
       
  2715 	onRemove: function (map) {
       
  2716 		this._map = null;
       
  2717 
       
  2718 		map._pathRoot.removeChild(this._container);
       
  2719 
       
  2720 		map.off('viewreset', this.projectLatlngs, this);
       
  2721 		map.off(this._updateTrigger, this._updatePath, this);
       
  2722 	},
       
  2723 
       
  2724 	projectLatlngs: function () {
       
  2725 		// do all projection stuff here
       
  2726 	},
       
  2727 
       
  2728 	setStyle: function (style) {
       
  2729 		L.Util.setOptions(this, style);
       
  2730 		if (this._container) {
       
  2731 			this._updateStyle();
       
  2732 		}
       
  2733 		return this;
       
  2734 	},
       
  2735 
       
  2736 	_redraw: function () {
       
  2737 		if (this._map) {
       
  2738 			this.projectLatlngs();
       
  2739 			this._updatePath();
       
  2740 		}
       
  2741 	}
       
  2742 });
       
  2743 
       
  2744 L.Map.include({
       
  2745 	_updatePathViewport: function () {
       
  2746 		var p = L.Path.CLIP_PADDING,
       
  2747 			size = this.getSize(),
       
  2748 			//TODO this._map._getMapPanePos()
       
  2749 			panePos = L.DomUtil.getPosition(this._mapPane),
       
  2750 			min = panePos.multiplyBy(-1).subtract(size.multiplyBy(p)),
       
  2751 			max = min.add(size.multiplyBy(1 + p * 2));
       
  2752 
       
  2753 		this._pathViewport = new L.Bounds(min, max);
       
  2754 	}
       
  2755 });
       
  2756 
       
  2757 
       
  2758 L.Path.SVG_NS = 'http://www.w3.org/2000/svg';
       
  2759 
       
  2760 L.Browser.svg = !!(document.createElementNS && document.createElementNS(L.Path.SVG_NS, 'svg').createSVGRect);
       
  2761 
       
  2762 L.Path = L.Path.extend({
       
  2763 	statics: {
       
  2764 		SVG: L.Browser.svg,
       
  2765 		_createElement: function (name) {
       
  2766 			return document.createElementNS(L.Path.SVG_NS, name);
       
  2767 		}
       
  2768 	},
       
  2769 
       
  2770 	getPathString: function () {
       
  2771 		// form path string here
       
  2772 	},
       
  2773 
       
  2774 	_initElements: function () {
       
  2775 		this._map._initPathRoot();
       
  2776 		this._initPath();
       
  2777 		this._initStyle();
       
  2778 	},
       
  2779 
       
  2780 	_initPath: function () {
       
  2781 		this._container = L.Path._createElement('g');
       
  2782 
       
  2783 		this._path = L.Path._createElement('path');
       
  2784 		this._container.appendChild(this._path);
       
  2785 
       
  2786 		this._map._pathRoot.appendChild(this._container);
       
  2787 	},
       
  2788 
       
  2789 	_initStyle: function () {
       
  2790 		if (this.options.stroke) {
       
  2791 			this._path.setAttribute('stroke-linejoin', 'round');
       
  2792 			this._path.setAttribute('stroke-linecap', 'round');
       
  2793 		}
       
  2794 		if (this.options.fill) {
       
  2795 			this._path.setAttribute('fill-rule', 'evenodd');
       
  2796 		} else {
       
  2797 			this._path.setAttribute('fill', 'none');
       
  2798 		}
       
  2799 		this._updateStyle();
       
  2800 	},
       
  2801 
       
  2802 	_updateStyle: function () {
       
  2803 		if (this.options.stroke) {
       
  2804 			this._path.setAttribute('stroke', this.options.color);
       
  2805 			this._path.setAttribute('stroke-opacity', this.options.opacity);
       
  2806 			this._path.setAttribute('stroke-width', this.options.weight);
       
  2807 		}
       
  2808 		if (this.options.fill) {
       
  2809 			this._path.setAttribute('fill', this.options.fillColor || this.options.color);
       
  2810 			this._path.setAttribute('fill-opacity', this.options.fillOpacity);
       
  2811 		}
       
  2812 	},
       
  2813 
       
  2814 	_updatePath: function () {
       
  2815 		var str = this.getPathString();
       
  2816 		if (!str) {
       
  2817 			// fix webkit empty string parsing bug
       
  2818 			str = 'M0 0';
       
  2819 		}
       
  2820 		this._path.setAttribute('d', str);
       
  2821 	},
       
  2822 
       
  2823 	// TODO remove duplication with L.Map
       
  2824 	_initEvents: function () {
       
  2825 		if (this.options.clickable) {
       
  2826 			if (!L.Browser.vml) {
       
  2827 				this._path.setAttribute('class', 'leaflet-clickable');
       
  2828 			}
       
  2829 
       
  2830 			L.DomEvent.addListener(this._container, 'click', this._onMouseClick, this);
       
  2831 
       
  2832 			var events = ['dblclick', 'mousedown', 'mouseover', 'mouseout', 'mousemove'];
       
  2833 			for (var i = 0; i < events.length; i++) {
       
  2834 				L.DomEvent.addListener(this._container, events[i], this._fireMouseEvent, this);
       
  2835 			}
       
  2836 		}
       
  2837 	},
       
  2838 
       
  2839 	_onMouseClick: function (e) {
       
  2840 		if (this._map.dragging && this._map.dragging.moved()) {
       
  2841 			return;
       
  2842 		}
       
  2843 		this._fireMouseEvent(e);
       
  2844 	},
       
  2845 
       
  2846 	_fireMouseEvent: function (e) {
       
  2847 		if (!this.hasEventListeners(e.type)) {
       
  2848 			return;
       
  2849 		}
       
  2850 		this.fire(e.type, {
       
  2851 			latlng: this._map.mouseEventToLatLng(e),
       
  2852 			layerPoint: this._map.mouseEventToLayerPoint(e)
       
  2853 		});
       
  2854 		L.DomEvent.stopPropagation(e);
       
  2855 	}
       
  2856 });
       
  2857 
       
  2858 L.Map.include({
       
  2859 	_initPathRoot: function () {
       
  2860 		if (!this._pathRoot) {
       
  2861 			this._pathRoot = L.Path._createElement('svg');
       
  2862 			this._panes.overlayPane.appendChild(this._pathRoot);
       
  2863 
       
  2864 			this.on('moveend', this._updateSvgViewport);
       
  2865 			this._updateSvgViewport();
       
  2866 		}
       
  2867 	},
       
  2868 
       
  2869 	_updateSvgViewport: function () {
       
  2870 		this._updatePathViewport();
       
  2871 
       
  2872 		var vp = this._pathViewport,
       
  2873 			min = vp.min,
       
  2874 			max = vp.max,
       
  2875 			width = max.x - min.x,
       
  2876 			height = max.y - min.y,
       
  2877 			root = this._pathRoot,
       
  2878 			pane = this._panes.overlayPane;
       
  2879 
       
  2880 		// Hack to make flicker on drag end on mobile webkit less irritating
       
  2881 		// Unfortunately I haven't found a good workaround for this yet
       
  2882 		if (L.Browser.webkit) {
       
  2883 			pane.removeChild(root);
       
  2884 		}
       
  2885 
       
  2886 		L.DomUtil.setPosition(root, min);
       
  2887 		root.setAttribute('width', width);
       
  2888 		root.setAttribute('height', height);
       
  2889 		root.setAttribute('viewBox', [min.x, min.y, width, height].join(' '));
       
  2890 
       
  2891 		if (L.Browser.webkit) {
       
  2892 			pane.appendChild(root);
       
  2893 		}
       
  2894 	}
       
  2895 });
       
  2896 
       
  2897 
       
  2898 /*
       
  2899  * Popup extension to L.Path (polylines, polygons, circles), adding bindPopup method.
       
  2900  */
       
  2901 
       
  2902 L.Path.include({
       
  2903 	bindPopup: function (content, options) {
       
  2904 		if (!this._popup || this._popup.options !== options) {
       
  2905 			this._popup = new L.Popup(options, this);
       
  2906 		}
       
  2907 		this._popup.setContent(content);
       
  2908 
       
  2909 		if (!this._openPopupAdded) {
       
  2910 			this.on('click', this._openPopup, this);
       
  2911 			this._openPopupAdded = true;
       
  2912 		}
       
  2913 
       
  2914 		return this;
       
  2915 	},
       
  2916 
       
  2917 	_openPopup: function (e) {
       
  2918 		this._popup.setLatLng(e.latlng);
       
  2919 		this._map.openPopup(this._popup);
       
  2920 	}
       
  2921 });
       
  2922 
       
  2923 
       
  2924 /*
       
  2925  * Vector rendering for IE6-8 through VML.
       
  2926  * Thanks to Dmitry Baranovsky and his Raphael library for inspiration!
       
  2927  */
       
  2928 
       
  2929 L.Browser.vml = (function () {
       
  2930 	var d = document.createElement('div'), s;
       
  2931 	d.innerHTML = '<v:shape adj="1"/>';
       
  2932 	s = d.firstChild;
       
  2933 	s.style.behavior = 'url(#default#VML)';
       
  2934 
       
  2935 	return (s && (typeof s.adj === 'object'));
       
  2936 }());
       
  2937 
       
  2938 L.Path = L.Browser.svg || !L.Browser.vml ? L.Path : L.Path.extend({
       
  2939 	statics: {
       
  2940 		VML: true,
       
  2941 		CLIP_PADDING: 0.02,
       
  2942 		_createElement: (function () {
       
  2943 			try {
       
  2944 				document.namespaces.add('lvml', 'urn:schemas-microsoft-com:vml');
       
  2945 				return function (name) {
       
  2946 					return document.createElement('<lvml:' + name + ' class="lvml">');
       
  2947 				};
       
  2948 			} catch (e) {
       
  2949 				return function (name) {
       
  2950 					return document.createElement('<' + name + ' xmlns="urn:schemas-microsoft.com:vml" class="lvml">');
       
  2951 				};
       
  2952 			}
       
  2953 		}())
       
  2954 	},
       
  2955 
       
  2956 	_initPath: function () {
       
  2957 		this._container = L.Path._createElement('shape');
       
  2958 		this._container.className += ' leaflet-vml-shape' +
       
  2959 				(this.options.clickable ? ' leaflet-clickable' : '');
       
  2960 		this._container.coordsize = '1 1';
       
  2961 
       
  2962 		this._path = L.Path._createElement('path');
       
  2963 		this._container.appendChild(this._path);
       
  2964 
       
  2965 		this._map._pathRoot.appendChild(this._container);
       
  2966 	},
       
  2967 
       
  2968 	_initStyle: function () {
       
  2969 		if (this.options.stroke) {
       
  2970 			this._stroke = L.Path._createElement('stroke');
       
  2971 			this._stroke.endcap = 'round';
       
  2972 			this._container.appendChild(this._stroke);
       
  2973 		} else {
       
  2974 			this._container.stroked = false;
       
  2975 		}
       
  2976 		if (this.options.fill) {
       
  2977 			this._container.filled = true;
       
  2978 			this._fill = L.Path._createElement('fill');
       
  2979 			this._container.appendChild(this._fill);
       
  2980 		} else {
       
  2981 			this._container.filled = false;
       
  2982 		}
       
  2983 		this._updateStyle();
       
  2984 	},
       
  2985 
       
  2986 	_updateStyle: function () {
       
  2987 		if (this.options.stroke) {
       
  2988 			this._stroke.weight = this.options.weight + 'px';
       
  2989 			this._stroke.color = this.options.color;
       
  2990 			this._stroke.opacity = this.options.opacity;
       
  2991 		}
       
  2992 		if (this.options.fill) {
       
  2993 			this._fill.color = this.options.fillColor || this.options.color;
       
  2994 			this._fill.opacity = this.options.fillOpacity;
       
  2995 		}
       
  2996 	},
       
  2997 
       
  2998 	_updatePath: function () {
       
  2999 		this._container.style.display = 'none';
       
  3000 		this._path.v = this.getPathString() + ' '; // the space fixes IE empty path string bug
       
  3001 		this._container.style.display = '';
       
  3002 	}
       
  3003 });
       
  3004 
       
  3005 L.Map.include(L.Browser.svg || !L.Browser.vml ? {} : {
       
  3006 	_initPathRoot: function () {
       
  3007 		if (!this._pathRoot) {
       
  3008 			this._pathRoot = document.createElement('div');
       
  3009 			this._pathRoot.className = 'leaflet-vml-container';
       
  3010 			this._panes.overlayPane.appendChild(this._pathRoot);
       
  3011 
       
  3012 			this.on('moveend', this._updatePathViewport);
       
  3013 			this._updatePathViewport();
       
  3014 		}
       
  3015 	}
       
  3016 });
       
  3017 
       
  3018 
       
  3019 /*
       
  3020  * L.LineUtil contains different utility functions for line segments
       
  3021  * and polylines (clipping, simplification, distances, etc.)
       
  3022  */
       
  3023 
       
  3024 L.LineUtil = {
       
  3025 
       
  3026 	// Simplify polyline with vertex reduction and Douglas-Peucker simplification.
       
  3027 	// Improves rendering performance dramatically by lessening the number of points to draw.
       
  3028 
       
  3029 	simplify: function (/*Point[]*/ points, /*Number*/ tolerance) {
       
  3030 		if (!tolerance || !points.length) {
       
  3031 			return points.slice();
       
  3032 		}
       
  3033 
       
  3034 		var sqTolerance = tolerance * tolerance;
       
  3035 
       
  3036 		// stage 1: vertex reduction
       
  3037 		points = this._reducePoints(points, sqTolerance);
       
  3038 
       
  3039 		// stage 2: Douglas-Peucker simplification
       
  3040 		points = this._simplifyDP(points, sqTolerance);
       
  3041 
       
  3042 		return points;
       
  3043 	},
       
  3044 
       
  3045 	// distance from a point to a segment between two points
       
  3046 	pointToSegmentDistance:  function (/*Point*/ p, /*Point*/ p1, /*Point*/ p2) {
       
  3047 		return Math.sqrt(this._sqClosestPointOnSegment(p, p1, p2, true));
       
  3048 	},
       
  3049 
       
  3050 	closestPointOnSegment: function (/*Point*/ p, /*Point*/ p1, /*Point*/ p2) {
       
  3051 		return this._sqClosestPointOnSegment(p, p1, p2);
       
  3052 	},
       
  3053 
       
  3054 	// Douglas-Peucker simplification, see http://en.wikipedia.org/wiki/Douglas-Peucker_algorithm
       
  3055 	_simplifyDP: function (points, sqTolerance) {
       
  3056 
       
  3057 		var len = points.length,
       
  3058 			ArrayConstructor = typeof Uint8Array !== 'undefined' ? Uint8Array : Array,
       
  3059 			markers = new ArrayConstructor(len);
       
  3060 
       
  3061 		markers[0] = markers[len - 1] = 1;
       
  3062 
       
  3063 		this._simplifyDPStep(points, markers, sqTolerance, 0, len - 1);
       
  3064 
       
  3065 		var i,
       
  3066 			newPoints = [];
       
  3067 
       
  3068 		for (i = 0; i < len; i++) {
       
  3069 			if (markers[i]) {
       
  3070 				newPoints.push(points[i]);
       
  3071 			}
       
  3072 		}
       
  3073 
       
  3074 		return newPoints;
       
  3075 	},
       
  3076 
       
  3077 	_simplifyDPStep: function (points, markers, sqTolerance, first, last) {
       
  3078 
       
  3079 		var maxSqDist = 0,
       
  3080 			index, i, sqDist;
       
  3081 
       
  3082 		for (i = first + 1; i <= last - 1; i++) {
       
  3083 			sqDist = this._sqClosestPointOnSegment(points[i], points[first], points[last], true);
       
  3084 
       
  3085 			if (sqDist > maxSqDist) {
       
  3086 				index = i;
       
  3087 				maxSqDist = sqDist;
       
  3088 			}
       
  3089 		}
       
  3090 
       
  3091 		if (maxSqDist > sqTolerance) {
       
  3092 			markers[index] = 1;
       
  3093 
       
  3094 			this._simplifyDPStep(points, markers, sqTolerance, first, index);
       
  3095 			this._simplifyDPStep(points, markers, sqTolerance, index, last);
       
  3096 		}
       
  3097 	},
       
  3098 
       
  3099 	// reduce points that are too close to each other to a single point
       
  3100 	_reducePoints: function (points, sqTolerance) {
       
  3101 		var reducedPoints = [points[0]];
       
  3102 
       
  3103 		for (var i = 1, prev = 0, len = points.length; i < len; i++) {
       
  3104 			if (this._sqDist(points[i], points[prev]) > sqTolerance) {
       
  3105 				reducedPoints.push(points[i]);
       
  3106 				prev = i;
       
  3107 			}
       
  3108 		}
       
  3109 		if (prev < len - 1) {
       
  3110 			reducedPoints.push(points[len - 1]);
       
  3111 		}
       
  3112 		return reducedPoints;
       
  3113 	},
       
  3114 
       
  3115 	/*jshint bitwise:false */ // temporarily allow bitwise oprations
       
  3116 
       
  3117 	// Cohen-Sutherland line clipping algorithm.
       
  3118 	// Used to avoid rendering parts of a polyline that are not currently visible.
       
  3119 
       
  3120 	clipSegment: function (a, b, bounds, useLastCode) {
       
  3121 		var min = bounds.min,
       
  3122 			max = bounds.max;
       
  3123 
       
  3124 		var codeA = useLastCode ? this._lastCode : this._getBitCode(a, bounds),
       
  3125 			codeB = this._getBitCode(b, bounds);
       
  3126 
       
  3127 		// save 2nd code to avoid calculating it on the next segment
       
  3128 		this._lastCode = codeB;
       
  3129 
       
  3130 		while (true) {
       
  3131 			// if a,b is inside the clip window (trivial accept)
       
  3132 			if (!(codeA | codeB)) {
       
  3133 				return [a, b];
       
  3134 			// if a,b is outside the clip window (trivial reject)
       
  3135 			} else if (codeA & codeB) {
       
  3136 				return false;
       
  3137 			// other cases
       
  3138 			} else {
       
  3139 				var codeOut = codeA || codeB,
       
  3140 					p = this._getEdgeIntersection(a, b, codeOut, bounds),
       
  3141 					newCode = this._getBitCode(p, bounds);
       
  3142 
       
  3143 				if (codeOut === codeA) {
       
  3144 					a = p;
       
  3145 					codeA = newCode;
       
  3146 				} else {
       
  3147 					b = p;
       
  3148 					codeB = newCode;
       
  3149 				}
       
  3150 			}
       
  3151 		}
       
  3152 	},
       
  3153 
       
  3154 	_getEdgeIntersection: function (a, b, code, bounds) {
       
  3155 		var dx = b.x - a.x,
       
  3156 			dy = b.y - a.y,
       
  3157 			min = bounds.min,
       
  3158 			max = bounds.max;
       
  3159 
       
  3160 		if (code & 8) { // top
       
  3161 			return new L.Point(a.x + dx * (max.y - a.y) / dy, max.y);
       
  3162 		} else if (code & 4) { // bottom
       
  3163 			return new L.Point(a.x + dx * (min.y - a.y) / dy, min.y);
       
  3164 		} else if (code & 2) { // right
       
  3165 			return new L.Point(max.x, a.y + dy * (max.x - a.x) / dx);
       
  3166 		} else if (code & 1) { // left
       
  3167 			return new L.Point(min.x, a.y + dy * (min.x - a.x) / dx);
       
  3168 		}
       
  3169 	},
       
  3170 
       
  3171 	_getBitCode: function (/*Point*/ p, bounds) {
       
  3172 		var code = 0;
       
  3173 
       
  3174 		if (p.x < bounds.min.x) { // left
       
  3175 			code |= 1;
       
  3176 		} else if (p.x > bounds.max.x) { // right
       
  3177 			code |= 2;
       
  3178 		}
       
  3179 		if (p.y < bounds.min.y) { // bottom
       
  3180 			code |= 4;
       
  3181 		} else if (p.y > bounds.max.y) { // top
       
  3182 			code |= 8;
       
  3183 		}
       
  3184 
       
  3185 		return code;
       
  3186 	},
       
  3187 
       
  3188 	/*jshint bitwise:true */
       
  3189 
       
  3190 	// square distance (to avoid unnecessary Math.sqrt calls)
       
  3191 	_sqDist: function (p1, p2) {
       
  3192 		var dx = p2.x - p1.x,
       
  3193 			dy = p2.y - p1.y;
       
  3194 		return dx * dx + dy * dy;
       
  3195 	},
       
  3196 
       
  3197 	// return closest point on segment or distance to that point
       
  3198 	_sqClosestPointOnSegment: function (p, p1, p2, sqDist) {
       
  3199 		var x = p1.x,
       
  3200 			y = p1.y,
       
  3201 			dx = p2.x - x,
       
  3202 			dy = p2.y - y,
       
  3203 			dot = dx * dx + dy * dy,
       
  3204 			t;
       
  3205 
       
  3206 		if (dot > 0) {
       
  3207 			t = ((p.x - x) * dx + (p.y - y) * dy) / dot;
       
  3208 
       
  3209 			if (t > 1) {
       
  3210 				x = p2.x;
       
  3211 				y = p2.y;
       
  3212 			} else if (t > 0) {
       
  3213 				x += dx * t;
       
  3214 				y += dy * t;
       
  3215 			}
       
  3216 		}
       
  3217 
       
  3218 		dx = p.x - x;
       
  3219 		dy = p.y - y;
       
  3220 
       
  3221 		return sqDist ? dx * dx + dy * dy : new L.Point(x, y);
       
  3222 	}
       
  3223 };
       
  3224 
       
  3225 
       
  3226 
       
  3227 L.Polyline = L.Path.extend({
       
  3228 	initialize: function (latlngs, options) {
       
  3229 		L.Path.prototype.initialize.call(this, options);
       
  3230 		this._latlngs = latlngs;
       
  3231 	},
       
  3232 
       
  3233 	options: {
       
  3234 		// how much to simplify the polyline on each zoom level
       
  3235 		// more = better performance and smoother look, less = more accurate
       
  3236 		smoothFactor: 1.0,
       
  3237 		noClip: false,
       
  3238 
       
  3239 		updateOnMoveEnd: true
       
  3240 	},
       
  3241 
       
  3242 	projectLatlngs: function () {
       
  3243 		this._originalPoints = [];
       
  3244 
       
  3245 		for (var i = 0, len = this._latlngs.length; i < len; i++) {
       
  3246 			this._originalPoints[i] = this._map.latLngToLayerPoint(this._latlngs[i]);
       
  3247 		}
       
  3248 	},
       
  3249 
       
  3250 	getPathString: function () {
       
  3251 		for (var i = 0, len = this._parts.length, str = ''; i < len; i++) {
       
  3252 			str += this._getPathPartStr(this._parts[i]);
       
  3253 		}
       
  3254 		return str;
       
  3255 	},
       
  3256 
       
  3257 	getLatLngs: function () {
       
  3258 		return this._latlngs;
       
  3259 	},
       
  3260 
       
  3261 	setLatLngs: function (latlngs) {
       
  3262 		this._latlngs = latlngs;
       
  3263 		this._redraw();
       
  3264 		return this;
       
  3265 	},
       
  3266 
       
  3267 	addLatLng: function (latlng) {
       
  3268 		this._latlngs.push(latlng);
       
  3269 		this._redraw();
       
  3270 		return this;
       
  3271 	},
       
  3272 
       
  3273 	spliceLatLngs: function (index, howMany) {
       
  3274 		var removed = [].splice.apply(this._latlngs, arguments);
       
  3275 		this._redraw();
       
  3276 		return removed;
       
  3277 	},
       
  3278 
       
  3279 	closestLayerPoint: function (p) {
       
  3280 		var minDistance = Infinity, parts = this._parts, p1, p2, minPoint = null;
       
  3281 
       
  3282 		for (var j = 0, jLen = parts.length; j < jLen; j++) {
       
  3283 			var points = parts[j];
       
  3284 			for (var i = 1, len = points.length; i < len; i++) {
       
  3285 				p1 = points[i - 1];
       
  3286 				p2 = points[i];
       
  3287 				var point = L.LineUtil._sqClosestPointOnSegment(p, p1, p2);
       
  3288 				if (point._sqDist < minDistance) {
       
  3289 					minDistance = point._sqDist;
       
  3290 					minPoint = point;
       
  3291 				}
       
  3292 			}
       
  3293 		}
       
  3294 		if (minPoint) {
       
  3295 			minPoint.distance = Math.sqrt(minDistance);
       
  3296 		}
       
  3297 		return minPoint;
       
  3298 	},
       
  3299 
       
  3300 	getBounds: function () {
       
  3301 		var b = new L.LatLngBounds();
       
  3302 		var latLngs = this.getLatLngs();
       
  3303 		for (var i = 0, len = latLngs.length; i < len; i++) {
       
  3304 			b.extend(latLngs[i]);
       
  3305 		}
       
  3306 		return b;
       
  3307 	},
       
  3308 
       
  3309 	_getPathPartStr: function (points) {
       
  3310 		var round = L.Path.VML;
       
  3311 
       
  3312 		for (var j = 0, len2 = points.length, str = '', p; j < len2; j++) {
       
  3313 			p = points[j];
       
  3314 			if (round) {
       
  3315 				p._round();
       
  3316 			}
       
  3317 			str += (j ? 'L' : 'M') + p.x + ' ' + p.y;
       
  3318 		}
       
  3319 		return str;
       
  3320 	},
       
  3321 
       
  3322 	_clipPoints: function () {
       
  3323 		var points = this._originalPoints,
       
  3324 			len = points.length,
       
  3325 			i, k, segment;
       
  3326 
       
  3327 		if (this.options.noClip) {
       
  3328 			this._parts = [points];
       
  3329 			return;
       
  3330 		}
       
  3331 
       
  3332 		this._parts = [];
       
  3333 
       
  3334 		var parts = this._parts,
       
  3335 			vp = this._map._pathViewport,
       
  3336 			lu = L.LineUtil;
       
  3337 
       
  3338 		for (i = 0, k = 0; i < len - 1; i++) {
       
  3339 			segment = lu.clipSegment(points[i], points[i + 1], vp, i);
       
  3340 			if (!segment) {
       
  3341 				continue;
       
  3342 			}
       
  3343 
       
  3344 			parts[k] = parts[k] || [];
       
  3345 			parts[k].push(segment[0]);
       
  3346 
       
  3347 			// if segment goes out of screen, or it's the last one, it's the end of the line part
       
  3348 			if ((segment[1] !== points[i + 1]) || (i === len - 2)) {
       
  3349 				parts[k].push(segment[1]);
       
  3350 				k++;
       
  3351 			}
       
  3352 		}
       
  3353 	},
       
  3354 
       
  3355 	// simplify each clipped part of the polyline
       
  3356 	_simplifyPoints: function () {
       
  3357 		var parts = this._parts,
       
  3358 			lu = L.LineUtil;
       
  3359 
       
  3360 		for (var i = 0, len = parts.length; i < len; i++) {
       
  3361 			parts[i] = lu.simplify(parts[i], this.options.smoothFactor);
       
  3362 		}
       
  3363 	},
       
  3364 
       
  3365 	_updatePath: function () {
       
  3366 		this._clipPoints();
       
  3367 		this._simplifyPoints();
       
  3368 
       
  3369 		L.Path.prototype._updatePath.call(this);
       
  3370 	}
       
  3371 });
       
  3372 
       
  3373 
       
  3374 /*
       
  3375  * L.PolyUtil contains utilify functions for polygons (clipping, etc.).
       
  3376  */
       
  3377 
       
  3378 /*jshint bitwise:false */ // allow bitwise oprations here
       
  3379 
       
  3380 L.PolyUtil = {};
       
  3381 
       
  3382 /*
       
  3383  * Sutherland-Hodgeman polygon clipping algorithm.
       
  3384  * Used to avoid rendering parts of a polygon that are not currently visible.
       
  3385  */
       
  3386 L.PolyUtil.clipPolygon = function (points, bounds) {
       
  3387 	var min = bounds.min,
       
  3388 		max = bounds.max,
       
  3389 		clippedPoints,
       
  3390 		edges = [1, 4, 2, 8],
       
  3391 		i, j, k,
       
  3392 		a, b,
       
  3393 		len, edge, p,
       
  3394 		lu = L.LineUtil;
       
  3395 
       
  3396 	for (i = 0, len = points.length; i < len; i++) {
       
  3397 		points[i]._code = lu._getBitCode(points[i], bounds);
       
  3398 	}
       
  3399 
       
  3400 	// for each edge (left, bottom, right, top)
       
  3401 	for (k = 0; k < 4; k++) {
       
  3402 		edge = edges[k];
       
  3403 		clippedPoints = [];
       
  3404 
       
  3405 		for (i = 0, len = points.length, j = len - 1; i < len; j = i++) {
       
  3406 			a = points[i];
       
  3407 			b = points[j];
       
  3408 
       
  3409 			// if a is inside the clip window
       
  3410 			if (!(a._code & edge)) {
       
  3411 				// if b is outside the clip window (a->b goes out of screen)
       
  3412 				if (b._code & edge) {
       
  3413 					p = lu._getEdgeIntersection(b, a, edge, bounds);
       
  3414 					p._code = lu._getBitCode(p, bounds);
       
  3415 					clippedPoints.push(p);
       
  3416 				}
       
  3417 				clippedPoints.push(a);
       
  3418 
       
  3419 			// else if b is inside the clip window (a->b enters the screen)
       
  3420 			} else if (!(b._code & edge)) {
       
  3421 				p = lu._getEdgeIntersection(b, a, edge, bounds);
       
  3422 				p._code = lu._getBitCode(p, bounds);
       
  3423 				clippedPoints.push(p);
       
  3424 			}
       
  3425 		}
       
  3426 		points = clippedPoints;
       
  3427 	}
       
  3428 
       
  3429 	return points;
       
  3430 };
       
  3431 
       
  3432 /*jshint bitwise:true */
       
  3433 
       
  3434 
       
  3435 /*
       
  3436  * L.Polygon is used to display polygons on a map.
       
  3437  */
       
  3438 
       
  3439 L.Polygon = L.Polyline.extend({
       
  3440 	options: {
       
  3441 		fill: true
       
  3442 	},
       
  3443 
       
  3444 	initialize: function (latlngs, options) {
       
  3445 		L.Polyline.prototype.initialize.call(this, latlngs, options);
       
  3446 
       
  3447 		if (latlngs && (latlngs[0] instanceof Array)) {
       
  3448 			this._latlngs = latlngs[0];
       
  3449 			this._holes = latlngs.slice(1);
       
  3450 		}
       
  3451 	},
       
  3452 
       
  3453 	projectLatlngs: function () {
       
  3454 		L.Polyline.prototype.projectLatlngs.call(this);
       
  3455 
       
  3456 		// project polygon holes points
       
  3457 		// TODO move this logic to Polyline to get rid of duplication
       
  3458 		this._holePoints = [];
       
  3459 
       
  3460 		if (!this._holes) {
       
  3461 			return;
       
  3462 		}
       
  3463 
       
  3464 		for (var i = 0, len = this._holes.length, hole; i < len; i++) {
       
  3465 			this._holePoints[i] = [];
       
  3466 
       
  3467 			for (var j = 0, len2 = this._holes[i].length; j < len2; j++) {
       
  3468 				this._holePoints[i][j] = this._map.latLngToLayerPoint(this._holes[i][j]);
       
  3469 			}
       
  3470 		}
       
  3471 	},
       
  3472 
       
  3473 	_clipPoints: function () {
       
  3474 		var points = this._originalPoints,
       
  3475 			newParts = [];
       
  3476 
       
  3477 		this._parts = [points].concat(this._holePoints);
       
  3478 
       
  3479 		if (this.options.noClip) {
       
  3480 			return;
       
  3481 		}
       
  3482 
       
  3483 		for (var i = 0, len = this._parts.length; i < len; i++) {
       
  3484 			var clipped = L.PolyUtil.clipPolygon(this._parts[i], this._map._pathViewport);
       
  3485 			if (!clipped.length) {
       
  3486 				continue;
       
  3487 			}
       
  3488 			newParts.push(clipped);
       
  3489 		}
       
  3490 
       
  3491 		this._parts = newParts;
       
  3492 	},
       
  3493 
       
  3494 	_getPathPartStr: function (points) {
       
  3495 		var str = L.Polyline.prototype._getPathPartStr.call(this, points);
       
  3496 		return str + (L.Browser.svg ? 'z' : 'x');
       
  3497 	}
       
  3498 });
       
  3499 
       
  3500 
       
  3501 /*
       
  3502  * Contains L.MultiPolyline and L.MultiPolygon layers.
       
  3503  */
       
  3504 
       
  3505 (function () {
       
  3506 	function createMulti(Klass) {
       
  3507 		return L.FeatureGroup.extend({
       
  3508 			initialize: function (latlngs, options) {
       
  3509 				this._layers = {};
       
  3510 				this._options = options;
       
  3511 				this.setLatLngs(latlngs);
       
  3512 			},
       
  3513 
       
  3514 			setLatLngs: function (latlngs) {
       
  3515 				var i = 0, len = latlngs.length;
       
  3516 
       
  3517 				this._iterateLayers(function (layer) {
       
  3518 					if (i < len) {
       
  3519 						layer.setLatLngs(latlngs[i++]);
       
  3520 					} else {
       
  3521 						this.removeLayer(layer);
       
  3522 					}
       
  3523 				}, this);
       
  3524 
       
  3525 				while (i < len) {
       
  3526 					this.addLayer(new Klass(latlngs[i++], this._options));
       
  3527 				}
       
  3528 			}
       
  3529 		});
       
  3530 	}
       
  3531 
       
  3532 	L.MultiPolyline = createMulti(L.Polyline);
       
  3533 	L.MultiPolygon = createMulti(L.Polygon);
       
  3534 }());
       
  3535 
       
  3536 
       
  3537 /*
       
  3538  * L.Circle is a circle overlay (with a certain radius in meters).
       
  3539  */
       
  3540 
       
  3541 L.Circle = L.Path.extend({
       
  3542 	initialize: function (latlng, radius, options) {
       
  3543 		L.Path.prototype.initialize.call(this, options);
       
  3544 
       
  3545 		this._latlng = latlng;
       
  3546 		this._mRadius = radius;
       
  3547 	},
       
  3548 
       
  3549 	options: {
       
  3550 		fill: true
       
  3551 	},
       
  3552 
       
  3553 	setLatLng: function (latlng) {
       
  3554 		this._latlng = latlng;
       
  3555 		this._redraw();
       
  3556 		return this;
       
  3557 	},
       
  3558 
       
  3559 	setRadius: function (radius) {
       
  3560 		this._mRadius = radius;
       
  3561 		this._redraw();
       
  3562 		return this;
       
  3563 	},
       
  3564 
       
  3565 	projectLatlngs: function () {
       
  3566 		var equatorLength = 40075017,
       
  3567 			hLength = equatorLength * Math.cos(L.LatLng.DEG_TO_RAD * this._latlng.lat);
       
  3568 
       
  3569 		var lngSpan = (this._mRadius / hLength) * 360,
       
  3570 			latlng2 = new L.LatLng(this._latlng.lat, this._latlng.lng - lngSpan, true),
       
  3571 			point2 = this._map.latLngToLayerPoint(latlng2);
       
  3572 
       
  3573 		this._point = this._map.latLngToLayerPoint(this._latlng);
       
  3574 		this._radius = Math.round(this._point.x - point2.x);
       
  3575 	},
       
  3576 
       
  3577 	getPathString: function () {
       
  3578 		var p = this._point,
       
  3579 			r = this._radius;
       
  3580 
       
  3581 		if (this._checkIfEmpty()) {
       
  3582 			return '';
       
  3583 		}
       
  3584 
       
  3585 		if (L.Browser.svg) {
       
  3586 			return "M" + p.x + "," + (p.y - r) +
       
  3587 					"A" + r + "," + r + ",0,1,1," +
       
  3588 					(p.x - 0.1) + "," + (p.y - r) + " z";
       
  3589 		} else {
       
  3590 			p._round();
       
  3591 			r = Math.round(r);
       
  3592 			return "AL " + p.x + "," + p.y + " " + r + "," + r + " 0," + (65535 * 360);
       
  3593 		}
       
  3594 	},
       
  3595 
       
  3596 	_checkIfEmpty: function () {
       
  3597 		var vp = this._map._pathViewport,
       
  3598 			r = this._radius,
       
  3599 			p = this._point;
       
  3600 
       
  3601 		return p.x - r > vp.max.x || p.y - r > vp.max.y ||
       
  3602 			p.x + r < vp.min.x || p.y + r < vp.min.y;
       
  3603 	}
       
  3604 });
       
  3605 
       
  3606 
       
  3607 /*
       
  3608  * L.CircleMarker is a circle overlay with a permanent pixel radius.
       
  3609  */
       
  3610 
       
  3611 L.CircleMarker = L.Circle.extend({
       
  3612 	options: {
       
  3613 		radius: 10,
       
  3614 		weight: 2
       
  3615 	},
       
  3616 
       
  3617 	initialize: function (latlng, options) {
       
  3618 		L.Circle.prototype.initialize.call(this, latlng, null, options);
       
  3619 		this._radius = this.options.radius;
       
  3620 	},
       
  3621 
       
  3622 	projectLatlngs: function () {
       
  3623 		this._point = this._map.latLngToLayerPoint(this._latlng);
       
  3624 	},
       
  3625 
       
  3626 	setRadius: function (radius) {
       
  3627 		this._radius = radius;
       
  3628 		this._redraw();
       
  3629 		return this;
       
  3630 	}
       
  3631 });
       
  3632 
       
  3633 
       
  3634 /*
       
  3635  * Vector rendering for all browsers that support canvas.
       
  3636  */
       
  3637 
       
  3638 L.Browser.canvas = (function () {
       
  3639 	return !!document.createElement('canvas').getContext;
       
  3640 }());
       
  3641 
       
  3642 L.Path = (L.Path.SVG && !window.L_PREFER_CANVAS) || !L.Browser.canvas ? L.Path : L.Path.extend({
       
  3643 	statics: {
       
  3644 		//CLIP_PADDING: 0.02, // not sure if there's a need to set it to a small value
       
  3645 		CANVAS: true,
       
  3646 		SVG: false
       
  3647 	},
       
  3648 
       
  3649 	options: {
       
  3650 		updateOnMoveEnd: true
       
  3651 	},
       
  3652 
       
  3653 	_initElements: function () {
       
  3654 		this._map._initPathRoot();
       
  3655 		this._ctx = this._map._canvasCtx;
       
  3656 	},
       
  3657 
       
  3658 	_updateStyle: function () {
       
  3659 		if (this.options.stroke) {
       
  3660 			this._ctx.lineWidth = this.options.weight;
       
  3661 			this._ctx.strokeStyle = this.options.color;
       
  3662 		}
       
  3663 		if (this.options.fill) {
       
  3664 			this._ctx.fillStyle = this.options.fillColor || this.options.color;
       
  3665 		}
       
  3666 	},
       
  3667 
       
  3668 	_drawPath: function () {
       
  3669 		var i, j, len, len2, point, drawMethod;
       
  3670 
       
  3671 		this._ctx.beginPath();
       
  3672 
       
  3673 		for (i = 0, len = this._parts.length; i < len; i++) {
       
  3674 			for (j = 0, len2 = this._parts[i].length; j < len2; j++) {
       
  3675 				point = this._parts[i][j];
       
  3676 				drawMethod = (j === 0 ? 'move' : 'line') + 'To';
       
  3677 
       
  3678 				this._ctx[drawMethod](point.x, point.y);
       
  3679 			}
       
  3680 			// TODO refactor ugly hack
       
  3681 			if (this instanceof L.Polygon) {
       
  3682 				this._ctx.closePath();
       
  3683 			}
       
  3684 		}
       
  3685 	},
       
  3686 
       
  3687 	_checkIfEmpty: function () {
       
  3688 		return !this._parts.length;
       
  3689 	},
       
  3690 
       
  3691 	_updatePath: function () {
       
  3692 		if (this._checkIfEmpty()) {
       
  3693 			return;
       
  3694 		}
       
  3695 
       
  3696 		this._drawPath();
       
  3697 
       
  3698 		this._ctx.save();
       
  3699 
       
  3700 		this._updateStyle();
       
  3701 
       
  3702 		var opacity = this.options.opacity,
       
  3703 			fillOpacity = this.options.fillOpacity;
       
  3704 
       
  3705 		if (this.options.fill) {
       
  3706 			if (fillOpacity < 1) {
       
  3707 				this._ctx.globalAlpha = fillOpacity;
       
  3708 			}
       
  3709 			this._ctx.fill();
       
  3710 		}
       
  3711 
       
  3712 		if (this.options.stroke) {
       
  3713 			if (opacity < 1) {
       
  3714 				this._ctx.globalAlpha = opacity;
       
  3715 			}
       
  3716 			this._ctx.stroke();
       
  3717 		}
       
  3718 
       
  3719 		this._ctx.restore();
       
  3720 
       
  3721 		// TODO optimization: 1 fill/stroke for all features with equal style instead of 1 for each feature
       
  3722 	},
       
  3723 
       
  3724 	_initEvents: function () {
       
  3725 		if (this.options.clickable) {
       
  3726 			// TODO hand cursor
       
  3727 			// TODO mouseover, mouseout, dblclick
       
  3728 			this._map.on('click', this._onClick, this);
       
  3729 		}
       
  3730 	},
       
  3731 
       
  3732 	_onClick: function (e) {
       
  3733 		if (this._containsPoint(e.layerPoint)) {
       
  3734 			this.fire('click', e);
       
  3735 		}
       
  3736 	},
       
  3737 
       
  3738     onRemove: function (map) {
       
  3739         map.off('viewreset', this._projectLatlngs, this);
       
  3740         map.off(this._updateTrigger, this._updatePath, this);
       
  3741         map.fire(this._updateTrigger);
       
  3742     }
       
  3743 });
       
  3744 
       
  3745 L.Map.include((L.Path.SVG && !window.L_PREFER_CANVAS) || !L.Browser.canvas ? {} : {
       
  3746 	_initPathRoot: function () {
       
  3747 		var root = this._pathRoot,
       
  3748 			ctx;
       
  3749 
       
  3750 		if (!root) {
       
  3751 			root = this._pathRoot = document.createElement("canvas");
       
  3752 			root.style.position = 'absolute';
       
  3753 			ctx = this._canvasCtx = root.getContext('2d');
       
  3754 
       
  3755 			ctx.lineCap = "round";
       
  3756 			ctx.lineJoin = "round";
       
  3757 
       
  3758 			this._panes.overlayPane.appendChild(root);
       
  3759 
       
  3760 			this.on('moveend', this._updateCanvasViewport);
       
  3761 			this._updateCanvasViewport();
       
  3762 		}
       
  3763 	},
       
  3764 
       
  3765 	_updateCanvasViewport: function () {
       
  3766 		this._updatePathViewport();
       
  3767 
       
  3768 		var vp = this._pathViewport,
       
  3769 			min = vp.min,
       
  3770 			size = vp.max.subtract(min),
       
  3771 			root = this._pathRoot;
       
  3772 
       
  3773 		//TODO check if it's works properly on mobile webkit
       
  3774 		L.DomUtil.setPosition(root, min);
       
  3775 		root.width = size.x;
       
  3776 		root.height = size.y;
       
  3777 		root.getContext('2d').translate(-min.x, -min.y);
       
  3778 	}
       
  3779 });
       
  3780 
       
  3781 
       
  3782 
       
  3783 L.Polyline.include(!L.Path.CANVAS ? {} : {
       
  3784 	_containsPoint: function (p, closed) {
       
  3785 		var i, j, k, len, len2, dist, part,
       
  3786 			w = this.options.weight / 2;
       
  3787 
       
  3788 		if (L.Browser.touch) {
       
  3789 			w += 10; // polyline click tolerance on touch devices
       
  3790 		}
       
  3791 
       
  3792 		for (i = 0, len = this._parts.length; i < len; i++) {
       
  3793 			part = this._parts[i];
       
  3794 			for (j = 0, len2 = part.length, k = len2 - 1; j < len2; k = j++) {
       
  3795 				if (!closed && (j === 0)) {
       
  3796 					continue;
       
  3797 				}
       
  3798 
       
  3799 				dist = L.LineUtil.pointToSegmentDistance(p, part[k], part[j]);
       
  3800 
       
  3801 				if (dist <= w) {
       
  3802 					return true;
       
  3803 				}
       
  3804 			}
       
  3805 		}
       
  3806 		return false;
       
  3807 	}
       
  3808 });
       
  3809 
       
  3810 
       
  3811 
       
  3812 L.Polygon.include(!L.Path.CANVAS ? {} : {
       
  3813 	_containsPoint: function (p) {
       
  3814 		var inside = false,
       
  3815 			part, p1, p2,
       
  3816 			i, j, k,
       
  3817 			len, len2;
       
  3818 
       
  3819 		// TODO optimization: check if within bounds first
       
  3820 
       
  3821 		if (L.Polyline.prototype._containsPoint.call(this, p, true)) {
       
  3822 			// click on polygon border
       
  3823 			return true;
       
  3824 		}
       
  3825 
       
  3826 		// ray casting algorithm for detecting if point is in polygon
       
  3827 
       
  3828 		for (i = 0, len = this._parts.length; i < len; i++) {
       
  3829 			part = this._parts[i];
       
  3830 
       
  3831 			for (j = 0, len2 = part.length, k = len2 - 1; j < len2; k = j++) {
       
  3832 				p1 = part[j];
       
  3833 				p2 = part[k];
       
  3834 
       
  3835 				if (((p1.y > p.y) !== (p2.y > p.y)) &&
       
  3836 						(p.x < (p2.x - p1.x) * (p.y - p1.y) / (p2.y - p1.y) + p1.x)) {
       
  3837 					inside = !inside;
       
  3838 				}
       
  3839 			}
       
  3840 		}
       
  3841 
       
  3842 		return inside;
       
  3843 	}
       
  3844 });
       
  3845 
       
  3846 
       
  3847 /*
       
  3848  * Circle canvas specific drawing parts.
       
  3849  */
       
  3850 
       
  3851 L.Circle.include(!L.Path.CANVAS ? {} : {
       
  3852 	_drawPath: function () {
       
  3853 		var p = this._point;
       
  3854 		this._ctx.beginPath();
       
  3855 		this._ctx.arc(p.x, p.y, this._radius, 0, Math.PI * 2);
       
  3856 	},
       
  3857 
       
  3858 	_containsPoint: function (p) {
       
  3859 		var center = this._point,
       
  3860 			w2 = this.options.stroke ? this.options.weight / 2 : 0;
       
  3861 
       
  3862 		return (p.distanceTo(center) <= this._radius + w2);
       
  3863 	}
       
  3864 });
       
  3865 
       
  3866 
       
  3867 
       
  3868 L.GeoJSON = L.FeatureGroup.extend({
       
  3869 	initialize: function (geojson, options) {
       
  3870 		L.Util.setOptions(this, options);
       
  3871 		this._geojson = geojson;
       
  3872 		this._layers = {};
       
  3873 
       
  3874 		if (geojson) {
       
  3875 			this.addGeoJSON(geojson);
       
  3876 		}
       
  3877 	},
       
  3878 
       
  3879 	addGeoJSON: function (geojson) {
       
  3880 		if (geojson.features) {
       
  3881 			for (var i = 0, len = geojson.features.length; i < len; i++) {
       
  3882 				this.addGeoJSON(geojson.features[i]);
       
  3883 			}
       
  3884 			return;
       
  3885 		}
       
  3886 
       
  3887 		var isFeature = (geojson.type === 'Feature'),
       
  3888 			geometry = (isFeature ? geojson.geometry : geojson),
       
  3889 			layer = L.GeoJSON.geometryToLayer(geometry, this.options.pointToLayer);
       
  3890 
       
  3891 		this.fire('featureparse', {
       
  3892 			layer: layer,
       
  3893 			properties: geojson.properties,
       
  3894 			geometryType: geometry.type,
       
  3895 			bbox: geojson.bbox,
       
  3896 			id: geojson.id
       
  3897 		});
       
  3898 
       
  3899 		this.addLayer(layer);
       
  3900 	}
       
  3901 });
       
  3902 
       
  3903 L.Util.extend(L.GeoJSON, {
       
  3904 	geometryToLayer: function (geometry, pointToLayer) {
       
  3905 		var coords = geometry.coordinates,
       
  3906 			latlng, latlngs,
       
  3907 			i, len,
       
  3908 			layer,
       
  3909 			layers = [];
       
  3910 
       
  3911 		switch (geometry.type) {
       
  3912 		case 'Point':
       
  3913 			latlng = this.coordsToLatLng(coords);
       
  3914 			return pointToLayer ? pointToLayer(latlng) : new L.Marker(latlng);
       
  3915 
       
  3916 		case 'MultiPoint':
       
  3917 			for (i = 0, len = coords.length; i < len; i++) {
       
  3918 				latlng = this.coordsToLatLng(coords[i]);
       
  3919 				layer = pointToLayer ? pointToLayer(latlng) : new L.Marker(latlng);
       
  3920 				layers.push(layer);
       
  3921 			}
       
  3922 			return new L.FeatureGroup(layers);
       
  3923 
       
  3924 		case 'LineString':
       
  3925 			latlngs = this.coordsToLatLngs(coords);
       
  3926 			return new L.Polyline(latlngs);
       
  3927 
       
  3928 		case 'Polygon':
       
  3929 			latlngs = this.coordsToLatLngs(coords, 1);
       
  3930 			return new L.Polygon(latlngs);
       
  3931 
       
  3932 		case 'MultiLineString':
       
  3933 			latlngs = this.coordsToLatLngs(coords, 1);
       
  3934 			return new L.MultiPolyline(latlngs);
       
  3935 
       
  3936 		case "MultiPolygon":
       
  3937 			latlngs = this.coordsToLatLngs(coords, 2);
       
  3938 			return new L.MultiPolygon(latlngs);
       
  3939 
       
  3940 		case "GeometryCollection":
       
  3941 			for (i = 0, len = geometry.geometries.length; i < len; i++) {
       
  3942 				layer = this.geometryToLayer(geometry.geometries[i], pointToLayer);
       
  3943 				layers.push(layer);
       
  3944 			}
       
  3945 			return new L.FeatureGroup(layers);
       
  3946 
       
  3947 		default:
       
  3948 			throw new Error('Invalid GeoJSON object.');
       
  3949 		}
       
  3950 	},
       
  3951 
       
  3952 	coordsToLatLng: function (/*Array*/ coords, /*Boolean*/ reverse)/*: LatLng*/ {
       
  3953 		var lat = parseFloat(coords[reverse ? 0 : 1]),
       
  3954 			lng = parseFloat(coords[reverse ? 1 : 0]);
       
  3955 		return new L.LatLng(lat, lng, true);
       
  3956 	},
       
  3957 
       
  3958 	coordsToLatLngs: function (/*Array*/ coords, /*Number*/ levelsDeep, /*Boolean*/ reverse)/*: Array*/ {
       
  3959 		var latlng, latlngs = [],
       
  3960 			i, len = coords.length;
       
  3961 
       
  3962 		for (i = 0; i < len; i++) {
       
  3963 			latlng = levelsDeep ?
       
  3964 					this.coordsToLatLngs(coords[i], levelsDeep - 1, reverse) :
       
  3965 					this.coordsToLatLng(coords[i], reverse);
       
  3966 			latlngs.push(latlng);
       
  3967 		}
       
  3968 		return latlngs;
       
  3969 	}
       
  3970 });
       
  3971 
       
  3972 
       
  3973 /*
       
  3974  * L.DomEvent contains functions for working with DOM events.
       
  3975  */
       
  3976 
       
  3977 L.DomEvent = {
       
  3978 	/* inpired by John Resig, Dean Edwards and YUI addEvent implementations */
       
  3979 	addListener: function (/*HTMLElement*/ obj, /*String*/ type, /*Function*/ fn, /*Object*/ context) {
       
  3980 		var id = L.Util.stamp(fn),
       
  3981 			key = '_leaflet_' + type + id;
       
  3982 
       
  3983 		if (obj[key]) {
       
  3984 			return;
       
  3985 		}
       
  3986 
       
  3987 		var handler = function (e) {
       
  3988 			return fn.call(context || obj, e || L.DomEvent._getEvent());
       
  3989 		};
       
  3990 
       
  3991 		if (L.Browser.touch && (type === 'dblclick') && this.addDoubleTapListener) {
       
  3992 			this.addDoubleTapListener(obj, handler, id);
       
  3993 		} else if ('addEventListener' in obj) {
       
  3994 			if (type === 'mousewheel') {
       
  3995 				obj.addEventListener('DOMMouseScroll', handler, false);
       
  3996 				obj.addEventListener(type, handler, false);
       
  3997 			} else if ((type === 'mouseenter') || (type === 'mouseleave')) {
       
  3998 				var originalHandler = handler,
       
  3999 					newType = (type === 'mouseenter' ? 'mouseover' : 'mouseout');
       
  4000 				handler = function (e) {
       
  4001 					if (!L.DomEvent._checkMouse(obj, e)) {
       
  4002 						return;
       
  4003 					}
       
  4004 					return originalHandler(e);
       
  4005 				};
       
  4006 				obj.addEventListener(newType, handler, false);
       
  4007 			} else {
       
  4008 				obj.addEventListener(type, handler, false);
       
  4009 			}
       
  4010 		} else if ('attachEvent' in obj) {
       
  4011 			obj.attachEvent("on" + type, handler);
       
  4012 		}
       
  4013 
       
  4014 		obj[key] = handler;
       
  4015 	},
       
  4016 
       
  4017 	removeListener: function (/*HTMLElement*/ obj, /*String*/ type, /*Function*/ fn) {
       
  4018 		var id = L.Util.stamp(fn),
       
  4019 			key = '_leaflet_' + type + id,
       
  4020 			handler = obj[key];
       
  4021 
       
  4022 		if (!handler) {
       
  4023 			return;
       
  4024 		}
       
  4025 
       
  4026 		if (L.Browser.touch && (type === 'dblclick') && this.removeDoubleTapListener) {
       
  4027 			this.removeDoubleTapListener(obj, id);
       
  4028 		} else if ('removeEventListener' in obj) {
       
  4029 			if (type === 'mousewheel') {
       
  4030 				obj.removeEventListener('DOMMouseScroll', handler, false);
       
  4031 				obj.removeEventListener(type, handler, false);
       
  4032 			} else if ((type === 'mouseenter') || (type === 'mouseleave')) {
       
  4033 				obj.removeEventListener((type === 'mouseenter' ? 'mouseover' : 'mouseout'), handler, false);
       
  4034 			} else {
       
  4035 				obj.removeEventListener(type, handler, false);
       
  4036 			}
       
  4037 		} else if ('detachEvent' in obj) {
       
  4038 			obj.detachEvent("on" + type, handler);
       
  4039 		}
       
  4040 		obj[key] = null;
       
  4041 	},
       
  4042 
       
  4043 	_checkMouse: function (el, e) {
       
  4044 		var related = e.relatedTarget;
       
  4045 
       
  4046 		if (!related) {
       
  4047 			return true;
       
  4048 		}
       
  4049 
       
  4050 		try {
       
  4051 			while (related && (related !== el)) {
       
  4052 				related = related.parentNode;
       
  4053 			}
       
  4054 		} catch (err) {
       
  4055 			return false;
       
  4056 		}
       
  4057 
       
  4058 		return (related !== el);
       
  4059 	},
       
  4060 
       
  4061 	/*jshint noarg:false */ // evil magic for IE
       
  4062 	_getEvent: function () {
       
  4063 		var e = window.event;
       
  4064 		if (!e) {
       
  4065 			var caller = arguments.callee.caller;
       
  4066 			while (caller) {
       
  4067 				e = caller['arguments'][0];
       
  4068 				if (e && window.Event === e.constructor) {
       
  4069 					break;
       
  4070 				}
       
  4071 				caller = caller.caller;
       
  4072 			}
       
  4073 		}
       
  4074 		return e;
       
  4075 	},
       
  4076 	/*jshint noarg:false */
       
  4077 
       
  4078 	stopPropagation: function (/*Event*/ e) {
       
  4079 		if (e.stopPropagation) {
       
  4080 			e.stopPropagation();
       
  4081 		} else {
       
  4082 			e.cancelBubble = true;
       
  4083 		}
       
  4084 	},
       
  4085 
       
  4086 	disableClickPropagation: function (/*HTMLElement*/ el) {
       
  4087 		L.DomEvent.addListener(el, L.Draggable.START, L.DomEvent.stopPropagation);
       
  4088 		L.DomEvent.addListener(el, 'click', L.DomEvent.stopPropagation);
       
  4089 		L.DomEvent.addListener(el, 'dblclick', L.DomEvent.stopPropagation);
       
  4090 	},
       
  4091 
       
  4092 	preventDefault: function (/*Event*/ e) {
       
  4093 		if (e.preventDefault) {
       
  4094 			e.preventDefault();
       
  4095 		} else {
       
  4096 			e.returnValue = false;
       
  4097 		}
       
  4098 	},
       
  4099 
       
  4100 	stop: function (e) {
       
  4101 		L.DomEvent.preventDefault(e);
       
  4102 		L.DomEvent.stopPropagation(e);
       
  4103 	},
       
  4104 
       
  4105 	getMousePosition: function (e, container) {
       
  4106 		var x = e.pageX ? e.pageX : e.clientX +
       
  4107 				document.body.scrollLeft + document.documentElement.scrollLeft,
       
  4108 			y = e.pageY ? e.pageY : e.clientY +
       
  4109 					document.body.scrollTop + document.documentElement.scrollTop,
       
  4110 			pos = new L.Point(x, y);
       
  4111 		return (container ?
       
  4112 					pos.subtract(L.DomUtil.getViewportOffset(container)) : pos);
       
  4113 	},
       
  4114 
       
  4115 	getWheelDelta: function (e) {
       
  4116 		var delta = 0;
       
  4117 		if (e.wheelDelta) {
       
  4118 			delta = e.wheelDelta / 120;
       
  4119 		}
       
  4120 		if (e.detail) {
       
  4121 			delta = -e.detail / 3;
       
  4122 		}
       
  4123 		return delta;
       
  4124 	}
       
  4125 };
       
  4126 
       
  4127 
       
  4128 
       
  4129 /*
       
  4130  * L.Draggable allows you to add dragging capabilities to any element. Supports mobile devices too.
       
  4131  */
       
  4132 
       
  4133 L.Draggable = L.Class.extend({
       
  4134 	includes: L.Mixin.Events,
       
  4135 
       
  4136 	statics: {
       
  4137 		START: L.Browser.touch ? 'touchstart' : 'mousedown',
       
  4138 		END: L.Browser.touch ? 'touchend' : 'mouseup',
       
  4139 		MOVE: L.Browser.touch ? 'touchmove' : 'mousemove',
       
  4140 		TAP_TOLERANCE: 15
       
  4141 	},
       
  4142 
       
  4143 	initialize: function (element, dragStartTarget) {
       
  4144 		this._element = element;
       
  4145 		this._dragStartTarget = dragStartTarget || element;
       
  4146 	},
       
  4147 
       
  4148 	enable: function () {
       
  4149 		if (this._enabled) {
       
  4150 			return;
       
  4151 		}
       
  4152 		L.DomEvent.addListener(this._dragStartTarget, L.Draggable.START, this._onDown, this);
       
  4153 		this._enabled = true;
       
  4154 	},
       
  4155 
       
  4156 	disable: function () {
       
  4157 		if (!this._enabled) {
       
  4158 			return;
       
  4159 		}
       
  4160 		L.DomEvent.removeListener(this._dragStartTarget, L.Draggable.START, this._onDown);
       
  4161 		this._enabled = false;
       
  4162 	},
       
  4163 
       
  4164 	_onDown: function (e) {
       
  4165 		if ((!L.Browser.touch && e.shiftKey) || ((e.which !== 1) && (e.button !== 1) && !e.touches)) {
       
  4166 			return;
       
  4167 		}
       
  4168 
       
  4169 		if (e.touches && e.touches.length > 1) {
       
  4170 			return;
       
  4171 		}
       
  4172 
       
  4173 		var first = (e.touches && e.touches.length === 1 ? e.touches[0] : e),
       
  4174 			el = first.target;
       
  4175 
       
  4176 		L.DomEvent.preventDefault(e);
       
  4177 
       
  4178 		if (L.Browser.touch && el.tagName.toLowerCase() === 'a') {
       
  4179 			el.className += ' leaflet-active';
       
  4180 		}
       
  4181 
       
  4182 		this._moved = false;
       
  4183 		if (this._moving) {
       
  4184 			return;
       
  4185 		}
       
  4186 
       
  4187 		if (!L.Browser.touch) {
       
  4188 			L.DomUtil.disableTextSelection();
       
  4189 			this._setMovingCursor();
       
  4190 		}
       
  4191 
       
  4192 		this._startPos = this._newPos = L.DomUtil.getPosition(this._element);
       
  4193 		this._startPoint = new L.Point(first.clientX, first.clientY);
       
  4194 
       
  4195 		L.DomEvent.addListener(document, L.Draggable.MOVE, this._onMove, this);
       
  4196 		L.DomEvent.addListener(document, L.Draggable.END, this._onUp, this);
       
  4197 	},
       
  4198 
       
  4199 	_onMove: function (e) {
       
  4200 		if (e.touches && e.touches.length > 1) {
       
  4201 			return;
       
  4202 		}
       
  4203 
       
  4204 		L.DomEvent.preventDefault(e);
       
  4205 
       
  4206 		var first = (e.touches && e.touches.length === 1 ? e.touches[0] : e);
       
  4207 
       
  4208 		if (!this._moved) {
       
  4209 			this.fire('dragstart');
       
  4210 			this._moved = true;
       
  4211 		}
       
  4212 		this._moving = true;
       
  4213 
       
  4214 		var newPoint = new L.Point(first.clientX, first.clientY);
       
  4215 		this._newPos = this._startPos.add(newPoint).subtract(this._startPoint);
       
  4216 
       
  4217 		L.Util.requestAnimFrame(this._updatePosition, this, true, this._dragStartTarget);
       
  4218 	},
       
  4219 
       
  4220 	_updatePosition: function () {
       
  4221 		this.fire('predrag');
       
  4222 		L.DomUtil.setPosition(this._element, this._newPos);
       
  4223 		this.fire('drag');
       
  4224 	},
       
  4225 
       
  4226 	_onUp: function (e) {
       
  4227 		if (e.changedTouches) {
       
  4228 			var first = e.changedTouches[0],
       
  4229 				el = first.target,
       
  4230 				dist = (this._newPos && this._newPos.distanceTo(this._startPos)) || 0;
       
  4231 
       
  4232 			if (el.tagName.toLowerCase() === 'a') {
       
  4233 				el.className = el.className.replace(' leaflet-active', '');
       
  4234 			}
       
  4235 
       
  4236 			if (dist < L.Draggable.TAP_TOLERANCE) {
       
  4237 				this._simulateEvent('click', first);
       
  4238 			}
       
  4239 		}
       
  4240 
       
  4241 		if (!L.Browser.touch) {
       
  4242 			L.DomUtil.enableTextSelection();
       
  4243 			this._restoreCursor();
       
  4244 		}
       
  4245 
       
  4246 		L.DomEvent.removeListener(document, L.Draggable.MOVE, this._onMove);
       
  4247 		L.DomEvent.removeListener(document, L.Draggable.END, this._onUp);
       
  4248 
       
  4249 		if (this._moved) {
       
  4250 			this.fire('dragend');
       
  4251 		}
       
  4252 		this._moving = false;
       
  4253 	},
       
  4254 
       
  4255 	_setMovingCursor: function () {
       
  4256 		this._bodyCursor = document.body.style.cursor;
       
  4257 		document.body.style.cursor = 'move';
       
  4258 	},
       
  4259 
       
  4260 	_restoreCursor: function () {
       
  4261 		document.body.style.cursor = this._bodyCursor;
       
  4262 	},
       
  4263 
       
  4264 	_simulateEvent: function (type, e) {
       
  4265 		var simulatedEvent = document.createEvent('MouseEvents');
       
  4266 
       
  4267 		simulatedEvent.initMouseEvent(
       
  4268 				type, true, true, window, 1,
       
  4269 				e.screenX, e.screenY,
       
  4270 				e.clientX, e.clientY,
       
  4271 				false, false, false, false, 0, null);
       
  4272 
       
  4273 		e.target.dispatchEvent(simulatedEvent);
       
  4274 	}
       
  4275 });
       
  4276 
       
  4277 
       
  4278 /*
       
  4279  * L.Handler classes are used internally to inject interaction features to classes like Map and Marker.
       
  4280  */
       
  4281 
       
  4282 L.Handler = L.Class.extend({
       
  4283 	initialize: function (map) {
       
  4284 		this._map = map;
       
  4285 	},
       
  4286 
       
  4287 	enable: function () {
       
  4288 		if (this._enabled) {
       
  4289 			return;
       
  4290 		}
       
  4291 		this._enabled = true;
       
  4292 		this.addHooks();
       
  4293 	},
       
  4294 
       
  4295 	disable: function () {
       
  4296 		if (!this._enabled) {
       
  4297 			return;
       
  4298 		}
       
  4299 		this._enabled = false;
       
  4300 		this.removeHooks();
       
  4301 	},
       
  4302 
       
  4303 	enabled: function () {
       
  4304 		return !!this._enabled;
       
  4305 	}
       
  4306 });
       
  4307 
       
  4308 
       
  4309 /*
       
  4310  * L.Handler.MapDrag is used internally by L.Map to make the map draggable.
       
  4311  */
       
  4312 
       
  4313 L.Map.Drag = L.Handler.extend({
       
  4314 	addHooks: function () {
       
  4315 		if (!this._draggable) {
       
  4316 			this._draggable = new L.Draggable(this._map._mapPane, this._map._container);
       
  4317 
       
  4318 			this._draggable
       
  4319 				.on('dragstart', this._onDragStart, this)
       
  4320 				.on('drag', this._onDrag, this)
       
  4321 				.on('dragend', this._onDragEnd, this);
       
  4322 
       
  4323 			var options = this._map.options;
       
  4324 
       
  4325 			if (options.worldCopyJump && !options.continuousWorld) {
       
  4326 				this._draggable.on('predrag', this._onPreDrag, this);
       
  4327 				this._map.on('viewreset', this._onViewReset, this);
       
  4328 			}
       
  4329 		}
       
  4330 		this._draggable.enable();
       
  4331 	},
       
  4332 
       
  4333 	removeHooks: function () {
       
  4334 		this._draggable.disable();
       
  4335 	},
       
  4336 
       
  4337 	moved: function () {
       
  4338 		return this._draggable && this._draggable._moved;
       
  4339 	},
       
  4340 
       
  4341 	_onDragStart: function () {
       
  4342 		this._map
       
  4343 			.fire('movestart')
       
  4344 			.fire('dragstart');
       
  4345 	},
       
  4346 
       
  4347 	_onDrag: function () {
       
  4348 		this._map
       
  4349 			.fire('move')
       
  4350 			.fire('drag');
       
  4351 	},
       
  4352 
       
  4353 	_onViewReset: function () {
       
  4354 		var pxCenter = this._map.getSize().divideBy(2),
       
  4355 			pxWorldCenter = this._map.latLngToLayerPoint(new L.LatLng(0, 0));
       
  4356 
       
  4357 		this._initialWorldOffset = pxWorldCenter.subtract(pxCenter);
       
  4358 	},
       
  4359 
       
  4360 	_onPreDrag: function () {
       
  4361 		var map = this._map,
       
  4362 			worldWidth = map.options.scale(map.getZoom()),
       
  4363 			halfWidth = Math.round(worldWidth / 2),
       
  4364 			dx = this._initialWorldOffset.x,
       
  4365 			x = this._draggable._newPos.x,
       
  4366 			newX1 = (x - halfWidth + dx) % worldWidth + halfWidth - dx,
       
  4367 			newX2 = (x + halfWidth + dx) % worldWidth - halfWidth - dx,
       
  4368 			newX = Math.abs(newX1 + dx) < Math.abs(newX2 + dx) ? newX1 : newX2;
       
  4369 
       
  4370 		this._draggable._newPos.x = newX;
       
  4371 	},
       
  4372 
       
  4373 	_onDragEnd: function () {
       
  4374 		var map = this._map;
       
  4375 
       
  4376 		map
       
  4377 			.fire('moveend')
       
  4378 			.fire('dragend');
       
  4379 
       
  4380 		if (map.options.maxBounds) {
       
  4381 			// TODO predrag validation instead of animation
       
  4382 			L.Util.requestAnimFrame(this._panInsideMaxBounds, map, true, map._container);
       
  4383 		}
       
  4384 	},
       
  4385 
       
  4386 	_panInsideMaxBounds: function () {
       
  4387 		this.panInsideBounds(this.options.maxBounds);
       
  4388 	}
       
  4389 });
       
  4390 
       
  4391 
       
  4392 /*
       
  4393  * L.Handler.DoubleClickZoom is used internally by L.Map to add double-click zooming.
       
  4394  */
       
  4395 
       
  4396 L.Map.DoubleClickZoom = L.Handler.extend({
       
  4397 	addHooks: function () {
       
  4398 		this._map.on('dblclick', this._onDoubleClick);
       
  4399 		// TODO remove 3d argument?
       
  4400 	},
       
  4401 
       
  4402 	removeHooks: function () {
       
  4403 		this._map.off('dblclick', this._onDoubleClick);
       
  4404 	},
       
  4405 
       
  4406 	_onDoubleClick: function (e) {
       
  4407 		this.setView(e.latlng, this._zoom + 1);
       
  4408 	}
       
  4409 });
       
  4410 
       
  4411 
       
  4412 /*
       
  4413  * L.Handler.ScrollWheelZoom is used internally by L.Map to enable mouse scroll wheel zooming on the map.
       
  4414  */
       
  4415 
       
  4416 L.Map.ScrollWheelZoom = L.Handler.extend({
       
  4417 	addHooks: function () {
       
  4418 		L.DomEvent.addListener(this._map._container, 'mousewheel', this._onWheelScroll, this);
       
  4419 		this._delta = 0;
       
  4420 	},
       
  4421 
       
  4422 	removeHooks: function () {
       
  4423 		L.DomEvent.removeListener(this._map._container, 'mousewheel', this._onWheelScroll);
       
  4424 	},
       
  4425 
       
  4426 	_onWheelScroll: function (e) {
       
  4427 		var delta = L.DomEvent.getWheelDelta(e);
       
  4428 		this._delta += delta;
       
  4429 		this._lastMousePos = this._map.mouseEventToContainerPoint(e);
       
  4430 
       
  4431 		clearTimeout(this._timer);
       
  4432 		this._timer = setTimeout(L.Util.bind(this._performZoom, this), 50);
       
  4433 
       
  4434 		L.DomEvent.preventDefault(e);
       
  4435 	},
       
  4436 
       
  4437 	_performZoom: function () {
       
  4438 		var map = this._map,
       
  4439 			delta = Math.round(this._delta),
       
  4440 			zoom = map.getZoom();
       
  4441 
       
  4442 		delta = Math.max(Math.min(delta, 4), -4);
       
  4443 		delta = map._limitZoom(zoom + delta) - zoom;
       
  4444 
       
  4445 		this._delta = 0;
       
  4446 
       
  4447 		if (!delta) {
       
  4448 			return;
       
  4449 		}
       
  4450 
       
  4451 		var newCenter = this._getCenterForScrollWheelZoom(this._lastMousePos, delta),
       
  4452 			newZoom = zoom + delta;
       
  4453 
       
  4454 		map.setView(newCenter, newZoom);
       
  4455 	},
       
  4456 
       
  4457 	_getCenterForScrollWheelZoom: function (mousePos, delta) {
       
  4458 		var map = this._map,
       
  4459 			centerPoint = map.getPixelBounds().getCenter(),
       
  4460 			viewHalf = map.getSize().divideBy(2),
       
  4461 			centerOffset = mousePos.subtract(viewHalf).multiplyBy(1 - Math.pow(2, -delta)),
       
  4462 			newCenterPoint = centerPoint.add(centerOffset);
       
  4463 
       
  4464 		return map.unproject(newCenterPoint, map._zoom, true);
       
  4465 	}
       
  4466 });
       
  4467 
       
  4468 
       
  4469 L.Util.extend(L.DomEvent, {
       
  4470 	// inspired by Zepto touch code by Thomas Fuchs
       
  4471 	addDoubleTapListener: function (obj, handler, id) {
       
  4472 		var last,
       
  4473 			doubleTap = false,
       
  4474 			delay = 250,
       
  4475 			touch,
       
  4476 			pre = '_leaflet_',
       
  4477 			touchstart = 'touchstart',
       
  4478 			touchend = 'touchend';
       
  4479 
       
  4480 		function onTouchStart(e) {
       
  4481 			if (e.touches.length !== 1) {
       
  4482 				return;
       
  4483 			}
       
  4484 
       
  4485 			var now = Date.now(),
       
  4486 				delta = now - (last || now);
       
  4487 
       
  4488 			touch = e.touches[0];
       
  4489 			doubleTap = (delta > 0 && delta <= delay);
       
  4490 			last = now;
       
  4491 		}
       
  4492 		function onTouchEnd(e) {
       
  4493 			if (doubleTap) {
       
  4494 				touch.type = 'dblclick';
       
  4495 				handler(touch);
       
  4496 				last = null;
       
  4497 			}
       
  4498 		}
       
  4499 		obj[pre + touchstart + id] = onTouchStart;
       
  4500 		obj[pre + touchend + id] = onTouchEnd;
       
  4501 
       
  4502 		obj.addEventListener(touchstart, onTouchStart, false);
       
  4503 		obj.addEventListener(touchend, onTouchEnd, false);
       
  4504 	},
       
  4505 
       
  4506 	removeDoubleTapListener: function (obj, id) {
       
  4507 		var pre = '_leaflet_';
       
  4508 		obj.removeEventListener(obj, obj[pre + 'touchstart' + id], false);
       
  4509 		obj.removeEventListener(obj, obj[pre + 'touchend' + id], false);
       
  4510 	}
       
  4511 });
       
  4512 
       
  4513 
       
  4514 /*
       
  4515  * L.Handler.TouchZoom is used internally by L.Map to add touch-zooming on Webkit-powered mobile browsers.
       
  4516  */
       
  4517 
       
  4518 L.Map.TouchZoom = L.Handler.extend({
       
  4519 	addHooks: function () {
       
  4520 		L.DomEvent.addListener(this._map._container, 'touchstart', this._onTouchStart, this);
       
  4521 	},
       
  4522 
       
  4523 	removeHooks: function () {
       
  4524 		L.DomEvent.removeListener(this._map._container, 'touchstart', this._onTouchStart, this);
       
  4525 	},
       
  4526 
       
  4527 	_onTouchStart: function (e) {
       
  4528 		if (!e.touches || e.touches.length !== 2 || this._map._animatingZoom) {
       
  4529 			return;
       
  4530 		}
       
  4531 
       
  4532 		var p1 = this._map.mouseEventToLayerPoint(e.touches[0]),
       
  4533 			p2 = this._map.mouseEventToLayerPoint(e.touches[1]),
       
  4534 			viewCenter = this._map.containerPointToLayerPoint(this._map.getSize().divideBy(2));
       
  4535 
       
  4536 		this._startCenter = p1.add(p2).divideBy(2, true);
       
  4537 		this._startDist = p1.distanceTo(p2);
       
  4538 		//this._startTransform = this._map._mapPane.style.webkitTransform;
       
  4539 
       
  4540 		this._moved = false;
       
  4541 		this._zooming = true;
       
  4542 
       
  4543 		this._centerOffset = viewCenter.subtract(this._startCenter);
       
  4544 
       
  4545 		L.DomEvent.addListener(document, 'touchmove', this._onTouchMove, this);
       
  4546 		L.DomEvent.addListener(document, 'touchend', this._onTouchEnd, this);
       
  4547 
       
  4548 		L.DomEvent.preventDefault(e);
       
  4549 	},
       
  4550 
       
  4551 	_onTouchMove: function (e) {
       
  4552 		if (!e.touches || e.touches.length !== 2) {
       
  4553 			return;
       
  4554 		}
       
  4555 
       
  4556 		if (!this._moved) {
       
  4557 			this._map._mapPane.className += ' leaflet-zoom-anim';
       
  4558 
       
  4559 			this._map
       
  4560 				.fire('zoomstart')
       
  4561 				.fire('movestart')
       
  4562 				._prepareTileBg();
       
  4563 
       
  4564 			this._moved = true;
       
  4565 		}
       
  4566 
       
  4567 		var p1 = this._map.mouseEventToLayerPoint(e.touches[0]),
       
  4568 			p2 = this._map.mouseEventToLayerPoint(e.touches[1]);
       
  4569 
       
  4570 		this._scale = p1.distanceTo(p2) / this._startDist;
       
  4571 		this._delta = p1.add(p2).divideBy(2, true).subtract(this._startCenter);
       
  4572 
       
  4573 		// Used 2 translates instead of transform-origin because of a very strange bug -
       
  4574 		// it didn't count the origin on the first touch-zoom but worked correctly afterwards
       
  4575 
       
  4576 		this._map._tileBg.style.webkitTransform = [
       
  4577             L.DomUtil.getTranslateString(this._delta),
       
  4578             L.DomUtil.getScaleString(this._scale, this._startCenter)
       
  4579         ].join(" ");
       
  4580 
       
  4581 		L.DomEvent.preventDefault(e);
       
  4582 	},
       
  4583 
       
  4584 	_onTouchEnd: function (e) {
       
  4585 		if (!this._moved || !this._zooming) {
       
  4586 			return;
       
  4587 		}
       
  4588 		this._zooming = false;
       
  4589 
       
  4590 		var oldZoom = this._map.getZoom(),
       
  4591 			floatZoomDelta = Math.log(this._scale) / Math.LN2,
       
  4592 			roundZoomDelta = (floatZoomDelta > 0 ? Math.ceil(floatZoomDelta) : Math.floor(floatZoomDelta)),
       
  4593 			zoom = this._map._limitZoom(oldZoom + roundZoomDelta),
       
  4594 			zoomDelta = zoom - oldZoom,
       
  4595 			centerOffset = this._centerOffset.subtract(this._delta).divideBy(this._scale),
       
  4596 			centerPoint = this._map.getPixelOrigin().add(this._startCenter).add(centerOffset),
       
  4597 			center = this._map.unproject(centerPoint);
       
  4598 
       
  4599 		L.DomEvent.removeListener(document, 'touchmove', this._onTouchMove);
       
  4600 		L.DomEvent.removeListener(document, 'touchend', this._onTouchEnd);
       
  4601 
       
  4602 		var finalScale = Math.pow(2, zoomDelta);
       
  4603 
       
  4604 		this._map._runAnimation(center, zoom, finalScale / this._scale, this._startCenter.add(centerOffset));
       
  4605 	}
       
  4606 });
       
  4607 
       
  4608 
       
  4609 /*
       
  4610  * L.Handler.ShiftDragZoom is used internally by L.Map to add shift-drag zoom (zoom to a selected bounding box).
       
  4611  */
       
  4612 
       
  4613 L.Map.BoxZoom = L.Handler.extend({
       
  4614 	initialize: function (map) {
       
  4615 		this._map = map;
       
  4616 		this._container = map._container;
       
  4617 		this._pane = map._panes.overlayPane;
       
  4618 	},
       
  4619 
       
  4620 	addHooks: function () {
       
  4621 		L.DomEvent.addListener(this._container, 'mousedown', this._onMouseDown, this);
       
  4622 	},
       
  4623 
       
  4624 	removeHooks: function () {
       
  4625 		L.DomEvent.removeListener(this._container, 'mousedown', this._onMouseDown);
       
  4626 	},
       
  4627 
       
  4628 	_onMouseDown: function (e) {
       
  4629 		if (!e.shiftKey || ((e.which !== 1) && (e.button !== 1))) {
       
  4630 			return false;
       
  4631 		}
       
  4632 
       
  4633 		L.DomUtil.disableTextSelection();
       
  4634 
       
  4635 		this._startLayerPoint = this._map.mouseEventToLayerPoint(e);
       
  4636 
       
  4637 		this._box = L.DomUtil.create('div', 'leaflet-zoom-box', this._pane);
       
  4638 		L.DomUtil.setPosition(this._box, this._startLayerPoint);
       
  4639 
       
  4640 		//TODO move cursor to styles
       
  4641 		this._container.style.cursor = 'crosshair';
       
  4642 
       
  4643 		L.DomEvent.addListener(document, 'mousemove', this._onMouseMove, this);
       
  4644 		L.DomEvent.addListener(document, 'mouseup', this._onMouseUp, this);
       
  4645 
       
  4646 		L.DomEvent.preventDefault(e);
       
  4647 	},
       
  4648 
       
  4649 	_onMouseMove: function (e) {
       
  4650 		var layerPoint = this._map.mouseEventToLayerPoint(e),
       
  4651 			dx = layerPoint.x - this._startLayerPoint.x,
       
  4652 			dy = layerPoint.y - this._startLayerPoint.y;
       
  4653 
       
  4654 		var newX = Math.min(layerPoint.x, this._startLayerPoint.x),
       
  4655 			newY = Math.min(layerPoint.y, this._startLayerPoint.y),
       
  4656 			newPos = new L.Point(newX, newY);
       
  4657 
       
  4658 		L.DomUtil.setPosition(this._box, newPos);
       
  4659 
       
  4660 		this._box.style.width = (Math.abs(dx) - 4) + 'px';
       
  4661 		this._box.style.height = (Math.abs(dy) - 4) + 'px';
       
  4662 	},
       
  4663 
       
  4664 	_onMouseUp: function (e) {
       
  4665 		this._pane.removeChild(this._box);
       
  4666 		this._container.style.cursor = '';
       
  4667 
       
  4668 		L.DomUtil.enableTextSelection();
       
  4669 
       
  4670 		L.DomEvent.removeListener(document, 'mousemove', this._onMouseMove);
       
  4671 		L.DomEvent.removeListener(document, 'mouseup', this._onMouseUp);
       
  4672 
       
  4673 		var layerPoint = this._map.mouseEventToLayerPoint(e);
       
  4674 
       
  4675 		var bounds = new L.LatLngBounds(
       
  4676 				this._map.layerPointToLatLng(this._startLayerPoint),
       
  4677 				this._map.layerPointToLatLng(layerPoint));
       
  4678 
       
  4679 		this._map.fitBounds(bounds);
       
  4680 	}
       
  4681 });
       
  4682 
       
  4683 
       
  4684 /*
       
  4685  * L.Handler.MarkerDrag is used internally by L.Marker to make the markers draggable.
       
  4686  */
       
  4687 
       
  4688 L.Handler.MarkerDrag = L.Handler.extend({
       
  4689 	initialize: function (marker) {
       
  4690 		this._marker = marker;
       
  4691 	},
       
  4692 
       
  4693 	addHooks: function () {
       
  4694 		var icon = this._marker._icon;
       
  4695 		if (!this._draggable) {
       
  4696 			this._draggable = new L.Draggable(icon, icon);
       
  4697 
       
  4698 			this._draggable
       
  4699 				.on('dragstart', this._onDragStart, this)
       
  4700 				.on('drag', this._onDrag, this)
       
  4701 				.on('dragend', this._onDragEnd, this);
       
  4702 		}
       
  4703 		this._draggable.enable();
       
  4704 	},
       
  4705 
       
  4706 	removeHooks: function () {
       
  4707 		this._draggable.disable();
       
  4708 	},
       
  4709 
       
  4710 	moved: function () {
       
  4711 		return this._draggable && this._draggable._moved;
       
  4712 	},
       
  4713 
       
  4714 	_onDragStart: function (e) {
       
  4715 		this._marker
       
  4716 			.closePopup()
       
  4717 			.fire('movestart')
       
  4718 			.fire('dragstart');
       
  4719 	},
       
  4720 
       
  4721 	_onDrag: function (e) {
       
  4722 		// update shadow position
       
  4723 		var iconPos = L.DomUtil.getPosition(this._marker._icon);
       
  4724 		if (this._marker._shadow) {
       
  4725 			L.DomUtil.setPosition(this._marker._shadow, iconPos);
       
  4726 		}
       
  4727 
       
  4728 		this._marker._latlng = this._marker._map.layerPointToLatLng(iconPos);
       
  4729 
       
  4730 		this._marker
       
  4731 			.fire('move')
       
  4732 			.fire('drag');
       
  4733 	},
       
  4734 
       
  4735 	_onDragEnd: function () {
       
  4736 		this._marker
       
  4737 			.fire('moveend')
       
  4738 			.fire('dragend');
       
  4739 	}
       
  4740 });
       
  4741 
       
  4742 
       
  4743 
       
  4744 L.Control = {};
       
  4745 
       
  4746 L.Control.Position = {
       
  4747 	TOP_LEFT: 'topLeft',
       
  4748 	TOP_RIGHT: 'topRight',
       
  4749 	BOTTOM_LEFT: 'bottomLeft',
       
  4750 	BOTTOM_RIGHT: 'bottomRight'
       
  4751 };
       
  4752 
       
  4753 
       
  4754 L.Map.include({
       
  4755 	addControl: function (control) {
       
  4756 		control.onAdd(this);
       
  4757 
       
  4758 		var pos = control.getPosition(),
       
  4759 			corner = this._controlCorners[pos],
       
  4760 			container = control.getContainer();
       
  4761 
       
  4762 		L.DomUtil.addClass(container, 'leaflet-control');
       
  4763 
       
  4764 		if (pos.indexOf('bottom') !== -1) {
       
  4765 			corner.insertBefore(container, corner.firstChild);
       
  4766 		} else {
       
  4767 			corner.appendChild(container);
       
  4768 		}
       
  4769 		return this;
       
  4770 	},
       
  4771 
       
  4772 	removeControl: function (control) {
       
  4773 		var pos = control.getPosition(),
       
  4774 			corner = this._controlCorners[pos],
       
  4775 			container = control.getContainer();
       
  4776 
       
  4777 		corner.removeChild(container);
       
  4778 
       
  4779 		if (control.onRemove) {
       
  4780 			control.onRemove(this);
       
  4781 		}
       
  4782 		return this;
       
  4783 	},
       
  4784 
       
  4785 	_initControlPos: function () {
       
  4786 		var corners = this._controlCorners = {},
       
  4787 			classPart = 'leaflet-',
       
  4788 			top = classPart + 'top',
       
  4789 			bottom = classPart + 'bottom',
       
  4790 			left = classPart + 'left',
       
  4791 			right = classPart + 'right',
       
  4792 			controlContainer = L.DomUtil.create('div', classPart + 'control-container', this._container);
       
  4793 
       
  4794 		if (L.Browser.touch) {
       
  4795 			controlContainer.className += ' ' + classPart + 'big-buttons';
       
  4796 		}
       
  4797 
       
  4798 		corners.topLeft = L.DomUtil.create('div', top + ' ' + left, controlContainer);
       
  4799 		corners.topRight = L.DomUtil.create('div', top + ' ' + right, controlContainer);
       
  4800 		corners.bottomLeft = L.DomUtil.create('div', bottom + ' ' + left, controlContainer);
       
  4801 		corners.bottomRight = L.DomUtil.create('div', bottom + ' ' + right, controlContainer);
       
  4802 	}
       
  4803 });
       
  4804 
       
  4805 
       
  4806 
       
  4807 L.Control.Zoom = L.Class.extend({
       
  4808 	onAdd: function (map) {
       
  4809 		this._map = map;
       
  4810 		this._container = L.DomUtil.create('div', 'leaflet-control-zoom');
       
  4811 
       
  4812 		this._zoomInButton = this._createButton(
       
  4813 				'Zoom in', 'leaflet-control-zoom-in', this._map.zoomIn, this._map);
       
  4814 		this._zoomOutButton = this._createButton(
       
  4815 				'Zoom out', 'leaflet-control-zoom-out', this._map.zoomOut, this._map);
       
  4816 
       
  4817 		this._container.appendChild(this._zoomInButton);
       
  4818 		this._container.appendChild(this._zoomOutButton);
       
  4819 	},
       
  4820 
       
  4821 	getContainer: function () {
       
  4822 		return this._container;
       
  4823 	},
       
  4824 
       
  4825 	getPosition: function () {
       
  4826 		return L.Control.Position.TOP_LEFT;
       
  4827 	},
       
  4828 
       
  4829 	_createButton: function (title, className, fn, context) {
       
  4830 		var link = document.createElement('a');
       
  4831 		link.href = '#';
       
  4832 		link.title = title;
       
  4833 		link.className = className;
       
  4834 
       
  4835 		L.DomEvent.disableClickPropagation(link);
       
  4836 		L.DomEvent.addListener(link, 'click', L.DomEvent.preventDefault);
       
  4837 		L.DomEvent.addListener(link, 'click', fn, context);
       
  4838 
       
  4839 		return link;
       
  4840 	}
       
  4841 });
       
  4842 
       
  4843 
       
  4844 L.Control.Attribution = L.Class.extend({
       
  4845 	initialize: function (prefix) {
       
  4846 		this._prefix = prefix || 'Powered by <a href="http://leaflet.cloudmade.com">Leaflet</a>';
       
  4847 		this._attributions = {};
       
  4848 	},
       
  4849 	
       
  4850 	onAdd: function (map) {
       
  4851 		this._container = L.DomUtil.create('div', 'leaflet-control-attribution');
       
  4852 		L.DomEvent.disableClickPropagation(this._container);
       
  4853 		this._map = map;
       
  4854 		this._update();
       
  4855 	},
       
  4856 
       
  4857 	getPosition: function () {
       
  4858 		return L.Control.Position.BOTTOM_RIGHT;
       
  4859 	},
       
  4860 
       
  4861 	getContainer: function () {
       
  4862 		return this._container;
       
  4863 	},
       
  4864 
       
  4865 	setPrefix: function (prefix) {
       
  4866 		this._prefix = prefix;
       
  4867 		this._update();
       
  4868 	},
       
  4869 
       
  4870 	addAttribution: function (text) {
       
  4871 		if (!text) {
       
  4872 			return;
       
  4873 		}
       
  4874 		this._attributions[text] = true;
       
  4875 		this._update();
       
  4876 	},
       
  4877 
       
  4878 	removeAttribution: function (text) {
       
  4879 		if (!text) {
       
  4880 			return;
       
  4881 		}
       
  4882 		delete this._attributions[text];
       
  4883 		this._update();
       
  4884 	},
       
  4885 
       
  4886 	_update: function () {
       
  4887 		if (!this._map) {
       
  4888 			return;
       
  4889 		}
       
  4890 
       
  4891 		var attribs = [];
       
  4892 
       
  4893 		for (var i in this._attributions) {
       
  4894 			if (this._attributions.hasOwnProperty(i)) {
       
  4895 				attribs.push(i);
       
  4896 			}
       
  4897 		}
       
  4898 
       
  4899 		var prefixAndAttribs = [];
       
  4900 		if (this._prefix) {
       
  4901 			prefixAndAttribs.push(this._prefix);
       
  4902 		}
       
  4903 		if (attribs.length) {
       
  4904 			prefixAndAttribs.push(attribs.join(', '));
       
  4905 		}
       
  4906 
       
  4907 		this._container.innerHTML = prefixAndAttribs.join(' &mdash; ');
       
  4908 	}
       
  4909 });
       
  4910 
       
  4911 
       
  4912 
       
  4913 L.Control.Layers = L.Class.extend({
       
  4914 	options: {
       
  4915 		collapsed: !L.Browser.touch
       
  4916 	},
       
  4917 
       
  4918 	initialize: function (baseLayers, overlays, options) {
       
  4919 		L.Util.setOptions(this, options);
       
  4920 
       
  4921 		this._layers = {};
       
  4922 
       
  4923 		for (var i in baseLayers) {
       
  4924 			if (baseLayers.hasOwnProperty(i)) {
       
  4925 				this._addLayer(baseLayers[i], i);
       
  4926 			}
       
  4927 		}
       
  4928 
       
  4929 		for (i in overlays) {
       
  4930 			if (overlays.hasOwnProperty(i)) {
       
  4931 				this._addLayer(overlays[i], i, true);
       
  4932 			}
       
  4933 		}
       
  4934 	},
       
  4935 
       
  4936 	onAdd: function (map) {
       
  4937 		this._map = map;
       
  4938 
       
  4939 		this._initLayout();
       
  4940 		this._update();
       
  4941 	},
       
  4942 
       
  4943 	getContainer: function () {
       
  4944 		return this._container;
       
  4945 	},
       
  4946 
       
  4947 	getPosition: function () {
       
  4948 		return L.Control.Position.TOP_RIGHT;
       
  4949 	},
       
  4950 
       
  4951 	addBaseLayer: function (layer, name) {
       
  4952 		this._addLayer(layer, name);
       
  4953 		this._update();
       
  4954 		return this;
       
  4955 	},
       
  4956 
       
  4957 	addOverlay: function (layer, name) {
       
  4958 		this._addLayer(layer, name, true);
       
  4959 		this._update();
       
  4960 		return this;
       
  4961 	},
       
  4962 
       
  4963 	removeLayer: function (layer) {
       
  4964 		var id = L.Util.stamp(layer);
       
  4965 		delete this._layers[id];
       
  4966 		this._update();
       
  4967 		return this;
       
  4968 	},
       
  4969 
       
  4970 	_initLayout: function () {
       
  4971 		this._container = L.DomUtil.create('div', 'leaflet-control-layers');
       
  4972 		L.DomEvent.disableClickPropagation(this._container);
       
  4973 
       
  4974 		this._form = L.DomUtil.create('form', 'leaflet-control-layers-list');
       
  4975 
       
  4976 		if (this.options.collapsed) {
       
  4977 			L.DomEvent.addListener(this._container, 'mouseover', this._expand, this);
       
  4978 			L.DomEvent.addListener(this._container, 'mouseout', this._collapse, this);
       
  4979 
       
  4980 			var link = this._layersLink = L.DomUtil.create('a', 'leaflet-control-layers-toggle');
       
  4981 			link.href = '#';
       
  4982 			link.title = 'Layers';
       
  4983 
       
  4984 			L.DomEvent.addListener(link, 'focus', this._expand, this);
       
  4985 			L.DomEvent.addListener(this._map, L.Draggable.START, this._collapse, this);
       
  4986 			// TODO keyboard accessibility
       
  4987 
       
  4988 			this._container.appendChild(link);
       
  4989 		} else {
       
  4990 			this._expand();
       
  4991 		}
       
  4992 
       
  4993 		this._baseLayersList = L.DomUtil.create('div', 'leaflet-control-layers-base', this._form);
       
  4994 		this._separator = L.DomUtil.create('div', 'leaflet-control-layers-separator', this._form);
       
  4995 		this._overlaysList = L.DomUtil.create('div', 'leaflet-control-layers-overlays', this._form);
       
  4996 
       
  4997 		this._container.appendChild(this._form);
       
  4998 	},
       
  4999 
       
  5000 	_addLayer: function (layer, name, overlay) {
       
  5001 		var id = L.Util.stamp(layer);
       
  5002 		this._layers[id] = {
       
  5003 			layer: layer,
       
  5004 			name: name,
       
  5005 			overlay: overlay
       
  5006 		};
       
  5007 	},
       
  5008 
       
  5009 	_update: function () {
       
  5010 		if (!this._container) {
       
  5011 			return;
       
  5012 		}
       
  5013 
       
  5014 		this._baseLayersList.innerHTML = '';
       
  5015 		this._overlaysList.innerHTML = '';
       
  5016 
       
  5017 		var baseLayersPresent = false,
       
  5018 			overlaysPresent = false;
       
  5019 
       
  5020 		for (var i in this._layers) {
       
  5021 			if (this._layers.hasOwnProperty(i)) {
       
  5022 				var obj = this._layers[i];
       
  5023 				this._addItem(obj);
       
  5024 				overlaysPresent = overlaysPresent || obj.overlay;
       
  5025 				baseLayersPresent = baseLayersPresent || !obj.overlay;
       
  5026 			}
       
  5027 		}
       
  5028 
       
  5029 		this._separator.style.display = (overlaysPresent && baseLayersPresent ? '' : 'none');
       
  5030 	},
       
  5031 
       
  5032 	_addItem: function (obj, onclick) {
       
  5033 		var label = document.createElement('label');
       
  5034 
       
  5035 		var input = document.createElement('input');
       
  5036 		if (!obj.overlay) {
       
  5037 			input.name = 'leaflet-base-layers';
       
  5038 		}
       
  5039 		input.type = obj.overlay ? 'checkbox' : 'radio';
       
  5040 		input.checked = this._map.hasLayer(obj.layer);
       
  5041 		input.layerId = L.Util.stamp(obj.layer);
       
  5042 
       
  5043 		L.DomEvent.addListener(input, 'click', this._onInputClick, this);
       
  5044 
       
  5045 		var name = document.createTextNode(' ' + obj.name);
       
  5046 
       
  5047 		label.appendChild(input);
       
  5048 		label.appendChild(name);
       
  5049 
       
  5050 		var container = obj.overlay ? this._overlaysList : this._baseLayersList;
       
  5051 		container.appendChild(label);
       
  5052 	},
       
  5053 
       
  5054 	_onInputClick: function () {
       
  5055 		var i, input, obj,
       
  5056 			inputs = this._form.getElementsByTagName('input'),
       
  5057 			inputsLen = inputs.length;
       
  5058 
       
  5059 		for (i = 0; i < inputsLen; i++) {
       
  5060 			input = inputs[i];
       
  5061 			obj = this._layers[input.layerId];
       
  5062 
       
  5063 			if (input.checked) {
       
  5064 				this._map.addLayer(obj.layer, !obj.overlay);
       
  5065 			} else {
       
  5066 				this._map.removeLayer(obj.layer);
       
  5067 			}
       
  5068 		}
       
  5069 	},
       
  5070 
       
  5071 	_expand: function () {
       
  5072 		L.DomUtil.addClass(this._container, 'leaflet-control-layers-expanded');
       
  5073 	},
       
  5074 
       
  5075 	_collapse: function () {
       
  5076 		this._container.className = this._container.className.replace(' leaflet-control-layers-expanded', '');
       
  5077 	}
       
  5078 });
       
  5079 
       
  5080 
       
  5081 L.Transition = L.Class.extend({
       
  5082 	includes: L.Mixin.Events,
       
  5083 
       
  5084 	statics: {
       
  5085 		CUSTOM_PROPS_SETTERS: {
       
  5086 			position: L.DomUtil.setPosition
       
  5087 			//TODO transform custom attr
       
  5088 		},
       
  5089 
       
  5090 		implemented: function () {
       
  5091 			return L.Transition.NATIVE || L.Transition.TIMER;
       
  5092 		}
       
  5093 	},
       
  5094 
       
  5095 	options: {
       
  5096 		easing: 'ease',
       
  5097 		duration: 0.5
       
  5098 	},
       
  5099 
       
  5100 	_setProperty: function (prop, value) {
       
  5101 		var setters = L.Transition.CUSTOM_PROPS_SETTERS;
       
  5102 		if (prop in setters) {
       
  5103 			setters[prop](this._el, value);
       
  5104 		} else {
       
  5105 			this._el.style[prop] = value;
       
  5106 		}
       
  5107 	}
       
  5108 });
       
  5109 
       
  5110 
       
  5111 /*
       
  5112  * L.Transition native implementation that powers Leaflet animation
       
  5113  * in browsers that support CSS3 Transitions
       
  5114  */
       
  5115 
       
  5116 L.Transition = L.Transition.extend({
       
  5117 	statics: (function () {
       
  5118 		var transition = L.DomUtil.TRANSITION,
       
  5119 			transitionEnd = (transition === 'webkitTransition' || transition === 'OTransition' ?
       
  5120 				transition + 'End' : 'transitionend');
       
  5121 
       
  5122 		return {
       
  5123 			NATIVE: !!transition,
       
  5124 
       
  5125 			TRANSITION: transition,
       
  5126 			PROPERTY: transition + 'Property',
       
  5127 			DURATION: transition + 'Duration',
       
  5128 			EASING: transition + 'TimingFunction',
       
  5129 			END: transitionEnd,
       
  5130 
       
  5131 			// transition-property value to use with each particular custom property
       
  5132 			CUSTOM_PROPS_PROPERTIES: {
       
  5133 				position: L.Browser.webkit ? L.DomUtil.TRANSFORM : 'top, left'
       
  5134 			}
       
  5135 		};
       
  5136 	}()),
       
  5137 
       
  5138 	options: {
       
  5139 		fakeStepInterval: 100
       
  5140 	},
       
  5141 
       
  5142 	initialize: function (/*HTMLElement*/ el, /*Object*/ options) {
       
  5143 		this._el = el;
       
  5144 		L.Util.setOptions(this, options);
       
  5145 
       
  5146 		L.DomEvent.addListener(el, L.Transition.END, this._onTransitionEnd, this);
       
  5147 		this._onFakeStep = L.Util.bind(this._onFakeStep, this);
       
  5148 	},
       
  5149 
       
  5150 	run: function (/*Object*/ props) {
       
  5151 		var prop,
       
  5152 			propsList = [],
       
  5153 			customProp = L.Transition.CUSTOM_PROPS_PROPERTIES;
       
  5154 
       
  5155 		for (prop in props) {
       
  5156 			if (props.hasOwnProperty(prop)) {
       
  5157 				prop = customProp[prop] ? customProp[prop] : prop;
       
  5158 				prop = this._dasherize(prop);
       
  5159 				propsList.push(prop);
       
  5160 			}
       
  5161 		}
       
  5162 
       
  5163 		this._el.style[L.Transition.DURATION] = this.options.duration + 's';
       
  5164 		this._el.style[L.Transition.EASING] = this.options.easing;
       
  5165 		this._el.style[L.Transition.PROPERTY] = propsList.join(', ');
       
  5166 
       
  5167 		for (prop in props) {
       
  5168 			if (props.hasOwnProperty(prop)) {
       
  5169 				this._setProperty(prop, props[prop]);
       
  5170 			}
       
  5171 		}
       
  5172 
       
  5173 		this._inProgress = true;
       
  5174 
       
  5175 		this.fire('start');
       
  5176 
       
  5177 		if (L.Transition.NATIVE) {
       
  5178 			clearInterval(this._timer);
       
  5179 			this._timer = setInterval(this._onFakeStep, this.options.fakeStepInterval);
       
  5180 		} else {
       
  5181 			this._onTransitionEnd();
       
  5182 		}
       
  5183 	},
       
  5184 
       
  5185 	_dasherize: (function () {
       
  5186 		var re = /([A-Z])/g;
       
  5187 
       
  5188 		function replaceFn(w) {
       
  5189 			return '-' + w.toLowerCase();
       
  5190 		}
       
  5191 
       
  5192 		return function (str) {
       
  5193 			return str.replace(re, replaceFn);
       
  5194 		};
       
  5195 	}()),
       
  5196 
       
  5197 	_onFakeStep: function () {
       
  5198 		this.fire('step');
       
  5199 	},
       
  5200 
       
  5201 	_onTransitionEnd: function () {
       
  5202 		if (this._inProgress) {
       
  5203 			this._inProgress = false;
       
  5204 			clearInterval(this._timer);
       
  5205 
       
  5206 			this._el.style[L.Transition.PROPERTY] = 'none';
       
  5207 
       
  5208 			this.fire('step');
       
  5209 			this.fire('end');
       
  5210 		}
       
  5211 	}
       
  5212 });
       
  5213 
       
  5214 
       
  5215 /*
       
  5216  * L.Transition fallback implementation that powers Leaflet animation
       
  5217  * in browsers that don't support CSS3 Transitions
       
  5218  */
       
  5219 
       
  5220 L.Transition = L.Transition.NATIVE ? L.Transition : L.Transition.extend({
       
  5221 	statics: {
       
  5222 		getTime: Date.now || function () {
       
  5223 			return +new Date();
       
  5224 		},
       
  5225 
       
  5226 		TIMER: true,
       
  5227 
       
  5228 		EASINGS: {
       
  5229 			'ease': [0.25, 0.1, 0.25, 1.0],
       
  5230 			'linear': [0.0, 0.0, 1.0, 1.0],
       
  5231 			'ease-in': [0.42, 0, 1.0, 1.0],
       
  5232 			'ease-out': [0, 0, 0.58, 1.0],
       
  5233 			'ease-in-out': [0.42, 0, 0.58, 1.0]
       
  5234 		},
       
  5235 
       
  5236 		CUSTOM_PROPS_GETTERS: {
       
  5237 			position: L.DomUtil.getPosition
       
  5238 		},
       
  5239 
       
  5240 		//used to get units from strings like "10.5px" (->px)
       
  5241 		UNIT_RE: /^[\d\.]+(\D*)$/
       
  5242 	},
       
  5243 
       
  5244 	options: {
       
  5245 		fps: 50
       
  5246 	},
       
  5247 
       
  5248 	initialize: function (el, options) {
       
  5249 		this._el = el;
       
  5250 		L.Util.extend(this.options, options);
       
  5251 
       
  5252 		var easings = L.Transition.EASINGS[this.options.easing] || L.Transition.EASINGS.ease;
       
  5253 
       
  5254 		this._p1 = new L.Point(0, 0);
       
  5255 		this._p2 = new L.Point(easings[0], easings[1]);
       
  5256 		this._p3 = new L.Point(easings[2], easings[3]);
       
  5257 		this._p4 = new L.Point(1, 1);
       
  5258 
       
  5259 		this._step = L.Util.bind(this._step, this);
       
  5260 		this._interval = Math.round(1000 / this.options.fps);
       
  5261 	},
       
  5262 
       
  5263 	run: function (props) {
       
  5264 		this._props = {};
       
  5265 
       
  5266 		var getters = L.Transition.CUSTOM_PROPS_GETTERS,
       
  5267 			re = L.Transition.UNIT_RE;
       
  5268 
       
  5269 		this.fire('start');
       
  5270 
       
  5271 		for (var prop in props) {
       
  5272 			if (props.hasOwnProperty(prop)) {
       
  5273 				var p = {};
       
  5274 				if (prop in getters) {
       
  5275 					p.from = getters[prop](this._el);
       
  5276 				} else {
       
  5277 					var matches = this._el.style[prop].match(re);
       
  5278 					p.from = parseFloat(matches[0]);
       
  5279 					p.unit = matches[1];
       
  5280 				}
       
  5281 				p.to = props[prop];
       
  5282 				this._props[prop] = p;
       
  5283 			}
       
  5284 		}
       
  5285 
       
  5286 		clearInterval(this._timer);
       
  5287 		this._timer = setInterval(this._step, this._interval);
       
  5288 		this._startTime = L.Transition.getTime();
       
  5289 	},
       
  5290 
       
  5291 	_step: function () {
       
  5292 		var time = L.Transition.getTime(),
       
  5293 			elapsed = time - this._startTime,
       
  5294 			duration = this.options.duration * 1000;
       
  5295 
       
  5296 		if (elapsed < duration) {
       
  5297 			this._runFrame(this._cubicBezier(elapsed / duration));
       
  5298 		} else {
       
  5299 			this._runFrame(1);
       
  5300 			this._complete();
       
  5301 		}
       
  5302 	},
       
  5303 
       
  5304 	_runFrame: function (percentComplete) {
       
  5305 		var setters = L.Transition.CUSTOM_PROPS_SETTERS,
       
  5306 			prop, p, value;
       
  5307 
       
  5308 		for (prop in this._props) {
       
  5309 			if (this._props.hasOwnProperty(prop)) {
       
  5310 				p = this._props[prop];
       
  5311 				if (prop in setters) {
       
  5312 					value = p.to.subtract(p.from).multiplyBy(percentComplete).add(p.from);
       
  5313 					setters[prop](this._el, value);
       
  5314 				} else {
       
  5315 					this._el.style[prop] =
       
  5316 							((p.to - p.from) * percentComplete + p.from) + p.unit;
       
  5317 				}
       
  5318 			}
       
  5319 		}
       
  5320 		this.fire('step');
       
  5321 	},
       
  5322 
       
  5323 	_complete: function () {
       
  5324 		clearInterval(this._timer);
       
  5325 		this.fire('end');
       
  5326 	},
       
  5327 
       
  5328 	_cubicBezier: function (t) {
       
  5329 		var a = Math.pow(1 - t, 3),
       
  5330 			b = 3 * Math.pow(1 - t, 2) * t,
       
  5331 			c = 3 * (1 - t) * Math.pow(t, 2),
       
  5332 			d = Math.pow(t, 3),
       
  5333 			p1 = this._p1.multiplyBy(a),
       
  5334 			p2 = this._p2.multiplyBy(b),
       
  5335 			p3 = this._p3.multiplyBy(c),
       
  5336 			p4 = this._p4.multiplyBy(d);
       
  5337 
       
  5338 		return p1.add(p2).add(p3).add(p4).y;
       
  5339 	}
       
  5340 });
       
  5341 
       
  5342 
       
  5343 L.Map.include(!(L.Transition && L.Transition.implemented()) ? {} : {
       
  5344 	setView: function (center, zoom, forceReset) {
       
  5345 		zoom = this._limitZoom(zoom);
       
  5346 		var zoomChanged = (this._zoom !== zoom);
       
  5347 
       
  5348 		if (this._loaded && !forceReset && this._layers) {
       
  5349 			// difference between the new and current centers in pixels
       
  5350 			var offset = this._getNewTopLeftPoint(center).subtract(this._getTopLeftPoint());
       
  5351 
       
  5352 			center = new L.LatLng(center.lat, center.lng);
       
  5353 
       
  5354 			var done = (zoomChanged ?
       
  5355 						!!this._zoomToIfCenterInView && this._zoomToIfCenterInView(center, zoom, offset) :
       
  5356 						this._panByIfClose(offset));
       
  5357 
       
  5358 			// exit if animated pan or zoom started
       
  5359 			if (done) {
       
  5360 				return this;
       
  5361 			}
       
  5362 		}
       
  5363 
       
  5364 		// reset the map view
       
  5365 		this._resetView(center, zoom);
       
  5366 
       
  5367 		return this;
       
  5368 	},
       
  5369 
       
  5370 	panBy: function (offset) {
       
  5371 		if (!(offset.x || offset.y)) {
       
  5372 			return this;
       
  5373 		}
       
  5374 
       
  5375 		if (!this._panTransition) {
       
  5376 			this._panTransition = new L.Transition(this._mapPane, {duration: 0.3});
       
  5377 
       
  5378 			this._panTransition.on('step', this._onPanTransitionStep, this);
       
  5379 			this._panTransition.on('end', this._onPanTransitionEnd, this);
       
  5380 		}
       
  5381 		this.fire('movestart');
       
  5382 
       
  5383 		this._panTransition.run({
       
  5384 			position: L.DomUtil.getPosition(this._mapPane).subtract(offset)
       
  5385 		});
       
  5386 
       
  5387 		return this;
       
  5388 	},
       
  5389 
       
  5390 	_onPanTransitionStep: function () {
       
  5391 		this.fire('move');
       
  5392 	},
       
  5393 
       
  5394 	_onPanTransitionEnd: function () {
       
  5395 		this.fire('moveend');
       
  5396 	},
       
  5397 
       
  5398 	_panByIfClose: function (offset) {
       
  5399 		if (this._offsetIsWithinView(offset)) {
       
  5400 			this.panBy(offset);
       
  5401 			return true;
       
  5402 		}
       
  5403 		return false;
       
  5404 	},
       
  5405 
       
  5406 	_offsetIsWithinView: function (offset, multiplyFactor) {
       
  5407 		var m = multiplyFactor || 1,
       
  5408 			size = this.getSize();
       
  5409 		return (Math.abs(offset.x) <= size.x * m) &&
       
  5410 				(Math.abs(offset.y) <= size.y * m);
       
  5411 	}
       
  5412 });
       
  5413 
       
  5414 
       
  5415 L.Map.include(!L.DomUtil.TRANSITION ? {} : {
       
  5416 	_zoomToIfCenterInView: function (center, zoom, centerOffset) {
       
  5417 
       
  5418 		if (this._animatingZoom) {
       
  5419 			return true;
       
  5420 		}
       
  5421 		if (!this.options.zoomAnimation) {
       
  5422 			return false;
       
  5423 		}
       
  5424 
       
  5425 		var zoomDelta = zoom - this._zoom,
       
  5426 			scale = Math.pow(2, zoomDelta),
       
  5427 			offset = centerOffset.divideBy(1 - 1 / scale);
       
  5428 
       
  5429 		//if offset does not exceed half of the view
       
  5430 		if (!this._offsetIsWithinView(offset, 1)) {
       
  5431 			return false;
       
  5432 		}
       
  5433 
       
  5434 		this._mapPane.className += ' leaflet-zoom-anim';
       
  5435 
       
  5436         this
       
  5437 			.fire('movestart')
       
  5438 			.fire('zoomstart');
       
  5439 
       
  5440 		var centerPoint = this.containerPointToLayerPoint(this.getSize().divideBy(2)),
       
  5441 			origin = centerPoint.add(offset);
       
  5442 
       
  5443 		this._prepareTileBg();
       
  5444 
       
  5445 		this._runAnimation(center, zoom, scale, origin);
       
  5446 
       
  5447 		return true;
       
  5448 	},
       
  5449 
       
  5450 
       
  5451 	_runAnimation: function (center, zoom, scale, origin) {
       
  5452 		this._animatingZoom = true;
       
  5453 
       
  5454 		this._animateToCenter = center;
       
  5455 		this._animateToZoom = zoom;
       
  5456 
       
  5457 		var transform = L.DomUtil.TRANSFORM;
       
  5458 
       
  5459 		//dumb FireFox hack, I have no idea why this magic zero translate fixes the scale transition problem
       
  5460 		if (L.Browser.gecko || window.opera) {
       
  5461 			this._tileBg.style[transform] += ' translate(0,0)';
       
  5462 		}
       
  5463 
       
  5464 		var scaleStr;
       
  5465 
       
  5466 		// Android doesn't like translate/scale chains, transformOrigin + scale works better but
       
  5467 		// it breaks touch zoom which Anroid doesn't support anyway, so that's a really ugly hack
       
  5468 		// TODO work around this prettier
       
  5469 		if (L.Browser.android) {
       
  5470 			this._tileBg.style[transform + 'Origin'] = origin.x + 'px ' + origin.y + 'px';
       
  5471 			scaleStr = 'scale(' + scale + ')';
       
  5472 		} else {
       
  5473 			scaleStr = L.DomUtil.getScaleString(scale, origin);
       
  5474 		}
       
  5475 
       
  5476 		L.Util.falseFn(this._tileBg.offsetWidth); //hack to make sure transform is updated before running animation
       
  5477 
       
  5478 		var options = {};
       
  5479 		options[transform] = this._tileBg.style[transform] + ' ' + scaleStr;
       
  5480 		this._tileBg.transition.run(options);
       
  5481 	},
       
  5482 
       
  5483 	_prepareTileBg: function () {
       
  5484 		if (!this._tileBg) {
       
  5485 			this._tileBg = this._createPane('leaflet-tile-pane', this._mapPane);
       
  5486 			this._tileBg.style.zIndex = 1;
       
  5487 		}
       
  5488 
       
  5489 		var tilePane = this._tilePane,
       
  5490 			tileBg = this._tileBg;
       
  5491 
       
  5492 		// prepare the background pane to become the main tile pane
       
  5493 		//tileBg.innerHTML = '';
       
  5494 		tileBg.style[L.DomUtil.TRANSFORM] = '';
       
  5495 		tileBg.style.visibility = 'hidden';
       
  5496 
       
  5497 		// tells tile layers to reinitialize their containers
       
  5498 		tileBg.empty = true;
       
  5499 		tilePane.empty = false;
       
  5500 
       
  5501 		this._tilePane = this._panes.tilePane = tileBg;
       
  5502 		this._tileBg = tilePane;
       
  5503 
       
  5504 		if (!this._tileBg.transition) {
       
  5505 			this._tileBg.transition = new L.Transition(this._tileBg, {duration: 0.3, easing: 'cubic-bezier(0.25,0.1,0.25,0.75)'});
       
  5506 			this._tileBg.transition.on('end', this._onZoomTransitionEnd, this);
       
  5507 		}
       
  5508 
       
  5509 		this._stopLoadingBgTiles();
       
  5510 	},
       
  5511 
       
  5512 	// stops loading all tiles in the background layer
       
  5513 	_stopLoadingBgTiles: function () {
       
  5514 		var tiles = [].slice.call(this._tileBg.getElementsByTagName('img'));
       
  5515 
       
  5516 		for (var i = 0, len = tiles.length; i < len; i++) {
       
  5517 			if (!tiles[i].complete) {
       
  5518 				// tiles[i].src = '' - evil, doesn't cancel the request!
       
  5519 				tiles[i].parentNode.removeChild(tiles[i]);
       
  5520 				tiles[i] = null;
       
  5521 			}
       
  5522 		}
       
  5523 	},
       
  5524 
       
  5525 	_onZoomTransitionEnd: function () {
       
  5526 		this._restoreTileFront();
       
  5527 
       
  5528 		L.Util.falseFn(this._tileBg.offsetWidth);
       
  5529 		this._resetView(this._animateToCenter, this._animateToZoom, true, true);
       
  5530 
       
  5531 		this._mapPane.className = this._mapPane.className.replace(' leaflet-zoom-anim', ''); //TODO toggleClass util
       
  5532 		this._animatingZoom = false;
       
  5533 	},
       
  5534 
       
  5535 	_restoreTileFront: function () {
       
  5536 		this._tilePane.innerHTML = '';
       
  5537 		this._tilePane.style.visibility = '';
       
  5538 		this._tilePane.style.zIndex = 2;
       
  5539 		this._tileBg.style.zIndex = 1;
       
  5540 	},
       
  5541 
       
  5542 	_clearTileBg: function () {
       
  5543 		if (!this._animatingZoom && !this.touchZoom._zooming) {
       
  5544 			this._tileBg.innerHTML = '';
       
  5545 		}
       
  5546 	}
       
  5547 });
       
  5548 
       
  5549 
       
  5550 /*
       
  5551  * Provides L.Map with convenient shortcuts for W3C geolocation.
       
  5552  */
       
  5553 
       
  5554 L.Map.include({
       
  5555 	locate: function (/*Object*/ options) {
       
  5556 
       
  5557 		this._locationOptions = options = L.Util.extend({
       
  5558 			watch: false,
       
  5559 			setView: false,
       
  5560 			maxZoom: Infinity,
       
  5561 			timeout: 10000,
       
  5562 			maximumAge: 0,
       
  5563 			enableHighAccuracy: false
       
  5564 		}, options);
       
  5565 
       
  5566 		if (!navigator.geolocation) {
       
  5567 			return this.fire('locationerror', {
       
  5568 				code: 0,
       
  5569 				message: "Geolocation not supported."
       
  5570 			});
       
  5571 		}
       
  5572 
       
  5573 		var onResponse = L.Util.bind(this._handleGeolocationResponse, this),
       
  5574 			onError = L.Util.bind(this._handleGeolocationError, this);
       
  5575 
       
  5576 		if (options.watch) {
       
  5577 			this._locationWatchId = navigator.geolocation.watchPosition(onResponse, onError, options);
       
  5578 		} else {
       
  5579 			navigator.geolocation.getCurrentPosition(onResponse, onError, options);
       
  5580 		}
       
  5581 		return this;
       
  5582 	},
       
  5583 
       
  5584 	stopLocate: function () {
       
  5585 		if (navigator.geolocation) {
       
  5586 			navigator.geolocation.clearWatch(this._locationWatchId);
       
  5587 		}
       
  5588 	},
       
  5589 
       
  5590 	locateAndSetView: function (maxZoom, options) {
       
  5591 		options = L.Util.extend({
       
  5592 			maxZoom: maxZoom || Infinity,
       
  5593 			setView: true
       
  5594 		}, options);
       
  5595 		return this.locate(options);
       
  5596 	},
       
  5597 
       
  5598 	_handleGeolocationError: function (error) {
       
  5599 		var c = error.code,
       
  5600 			message = (c === 1 ? "permission denied" :
       
  5601 				(c === 2 ? "position unavailable" : "timeout"));
       
  5602 
       
  5603 		if (this._locationOptions.setView && !this._loaded) {
       
  5604 			this.fitWorld();
       
  5605 		}
       
  5606 
       
  5607 		this.fire('locationerror', {
       
  5608 			code: c,
       
  5609 			message: "Geolocation error: " + message + "."
       
  5610 		});
       
  5611 	},
       
  5612 
       
  5613 	_handleGeolocationResponse: function (pos) {
       
  5614 		var latAccuracy = 180 * pos.coords.accuracy / 4e7,
       
  5615 			lngAccuracy = latAccuracy * 2,
       
  5616 			lat = pos.coords.latitude,
       
  5617 			lng = pos.coords.longitude,
       
  5618 			latlng = new L.LatLng(lat, lng);
       
  5619 
       
  5620 		var sw = new L.LatLng(lat - latAccuracy, lng - lngAccuracy),
       
  5621 			ne = new L.LatLng(lat + latAccuracy, lng + lngAccuracy),
       
  5622 			bounds = new L.LatLngBounds(sw, ne);
       
  5623 
       
  5624 		if (this._locationOptions.setView) {
       
  5625 			var zoom = Math.min(this.getBoundsZoom(bounds), this._locationOptions.maxZoom);
       
  5626 			this.setView(latlng, zoom);
       
  5627 		}
       
  5628 
       
  5629 		this.fire('locationfound', {
       
  5630 			latlng: latlng,
       
  5631 			bounds: bounds,
       
  5632 			accuracy: pos.coords.accuracy
       
  5633 		});
       
  5634 	}
       
  5635 });
       
  5636 
       
  5637