PageRenderTime 26ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 0ms

/files/leaflet/0.6.2/leaflet-src.js

https://gitlab.com/Mirros/jsdelivr
JavaScript | 2053 lines | 1398 code | 513 blank | 142 comment | 291 complexity | 3a5eb55db7f425f866344556f9ee6ada MD5 | raw file
  1. /*
  2. Leaflet, a JavaScript library for mobile-friendly interactive maps. http://leafletjs.com
  3. (c) 2010-2013, Vladimir Agafonkin
  4. (c) 2010-2011, CloudMade
  5. */
  6. (function (window, document, undefined) {
  7. var oldL = window.L,
  8. L = {};
  9. L.version = '0.6.2';
  10. // define Leaflet for Node module pattern loaders, including Browserify
  11. if (typeof module === 'object' && typeof module.exports === 'object') {
  12. module.exports = L;
  13. // define Leaflet as an AMD module
  14. } else if (typeof define === 'function' && define.amd) {
  15. define(L);
  16. }
  17. // define Leaflet as a global L variable, saving the original L to restore later if needed
  18. L.noConflict = function () {
  19. window.L = oldL;
  20. return this;
  21. };
  22. window.L = L;
  23. /*
  24. * L.Util contains various utility functions used throughout Leaflet code.
  25. */
  26. L.Util = {
  27. extend: function (dest) { // (Object[, Object, ...]) ->
  28. var sources = Array.prototype.slice.call(arguments, 1),
  29. i, j, len, src;
  30. for (j = 0, len = sources.length; j < len; j++) {
  31. src = sources[j] || {};
  32. for (i in src) {
  33. if (src.hasOwnProperty(i)) {
  34. dest[i] = src[i];
  35. }
  36. }
  37. }
  38. return dest;
  39. },
  40. bind: function (fn, obj) { // (Function, Object) -> Function
  41. var args = arguments.length > 2 ? Array.prototype.slice.call(arguments, 2) : null;
  42. return function () {
  43. return fn.apply(obj, args || arguments);
  44. };
  45. },
  46. stamp: (function () {
  47. var lastId = 0,
  48. key = '_leaflet_id';
  49. return function (obj) {
  50. obj[key] = obj[key] || ++lastId;
  51. return obj[key];
  52. };
  53. }()),
  54. invokeEach: function (obj, method, context) {
  55. var i, args;
  56. if (typeof obj === 'object') {
  57. args = Array.prototype.slice.call(arguments, 3);
  58. for (i in obj) {
  59. method.apply(context, [i, obj[i]].concat(args));
  60. }
  61. return true;
  62. }
  63. return false;
  64. },
  65. limitExecByInterval: function (fn, time, context) {
  66. var lock, execOnUnlock;
  67. return function wrapperFn() {
  68. var args = arguments;
  69. if (lock) {
  70. execOnUnlock = true;
  71. return;
  72. }
  73. lock = true;
  74. setTimeout(function () {
  75. lock = false;
  76. if (execOnUnlock) {
  77. wrapperFn.apply(context, args);
  78. execOnUnlock = false;
  79. }
  80. }, time);
  81. fn.apply(context, args);
  82. };
  83. },
  84. falseFn: function () {
  85. return false;
  86. },
  87. formatNum: function (num, digits) {
  88. var pow = Math.pow(10, digits || 5);
  89. return Math.round(num * pow) / pow;
  90. },
  91. trim: function (str) {
  92. return str.trim ? str.trim() : str.replace(/^\s+|\s+$/g, '');
  93. },
  94. splitWords: function (str) {
  95. return L.Util.trim(str).split(/\s+/);
  96. },
  97. setOptions: function (obj, options) {
  98. obj.options = L.extend({}, obj.options, options);
  99. return obj.options;
  100. },
  101. getParamString: function (obj, existingUrl, uppercase) {
  102. var params = [];
  103. for (var i in obj) {
  104. params.push(encodeURIComponent(uppercase ? i.toUpperCase() : i) + '=' + encodeURIComponent(obj[i]));
  105. }
  106. return ((!existingUrl || existingUrl.indexOf('?') === -1) ? '?' : '&') + params.join('&');
  107. },
  108. template: function (str, data) {
  109. return str.replace(/\{ *([\w_]+) *\}/g, function (str, key) {
  110. var value = data[key];
  111. if (value === undefined) {
  112. throw new Error('No value provided for variable ' + str);
  113. } else if (typeof value === 'function') {
  114. value = value(data);
  115. }
  116. return value;
  117. });
  118. },
  119. isArray: function (obj) {
  120. return (Object.prototype.toString.call(obj) === '[object Array]');
  121. },
  122. emptyImageUrl: 'data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs='
  123. };
  124. (function () {
  125. // inspired by http://paulirish.com/2011/requestanimationframe-for-smart-animating/
  126. function getPrefixed(name) {
  127. var i, fn,
  128. prefixes = ['webkit', 'moz', 'o', 'ms'];
  129. for (i = 0; i < prefixes.length && !fn; i++) {
  130. fn = window[prefixes[i] + name];
  131. }
  132. return fn;
  133. }
  134. var lastTime = 0;
  135. function timeoutDefer(fn) {
  136. var time = +new Date(),
  137. timeToCall = Math.max(0, 16 - (time - lastTime));
  138. lastTime = time + timeToCall;
  139. return window.setTimeout(fn, timeToCall);
  140. }
  141. var requestFn = window.requestAnimationFrame ||
  142. getPrefixed('RequestAnimationFrame') || timeoutDefer;
  143. var cancelFn = window.cancelAnimationFrame ||
  144. getPrefixed('CancelAnimationFrame') ||
  145. getPrefixed('CancelRequestAnimationFrame') ||
  146. function (id) { window.clearTimeout(id); };
  147. L.Util.requestAnimFrame = function (fn, context, immediate, element) {
  148. fn = L.bind(fn, context);
  149. if (immediate && requestFn === timeoutDefer) {
  150. fn();
  151. } else {
  152. return requestFn.call(window, fn, element);
  153. }
  154. };
  155. L.Util.cancelAnimFrame = function (id) {
  156. if (id) {
  157. cancelFn.call(window, id);
  158. }
  159. };
  160. }());
  161. // shortcuts for most used utility functions
  162. L.extend = L.Util.extend;
  163. L.bind = L.Util.bind;
  164. L.stamp = L.Util.stamp;
  165. L.setOptions = L.Util.setOptions;
  166. /*
  167. * L.Class powers the OOP facilities of the library.
  168. * Thanks to John Resig and Dean Edwards for inspiration!
  169. */
  170. L.Class = function () {};
  171. L.Class.extend = function (props) {
  172. // extended class with the new prototype
  173. var NewClass = function () {
  174. // call the constructor
  175. if (this.initialize) {
  176. this.initialize.apply(this, arguments);
  177. }
  178. // call all constructor hooks
  179. if (this._initHooks) {
  180. this.callInitHooks();
  181. }
  182. };
  183. // instantiate class without calling constructor
  184. var F = function () {};
  185. F.prototype = this.prototype;
  186. var proto = new F();
  187. proto.constructor = NewClass;
  188. NewClass.prototype = proto;
  189. //inherit parent's statics
  190. for (var i in this) {
  191. if (this.hasOwnProperty(i) && i !== 'prototype') {
  192. NewClass[i] = this[i];
  193. }
  194. }
  195. // mix static properties into the class
  196. if (props.statics) {
  197. L.extend(NewClass, props.statics);
  198. delete props.statics;
  199. }
  200. // mix includes into the prototype
  201. if (props.includes) {
  202. L.Util.extend.apply(null, [proto].concat(props.includes));
  203. delete props.includes;
  204. }
  205. // merge options
  206. if (props.options && proto.options) {
  207. props.options = L.extend({}, proto.options, props.options);
  208. }
  209. // mix given properties into the prototype
  210. L.extend(proto, props);
  211. proto._initHooks = [];
  212. var parent = this;
  213. // jshint camelcase: false
  214. NewClass.__super__ = parent.prototype;
  215. // add method for calling all hooks
  216. proto.callInitHooks = function () {
  217. if (this._initHooksCalled) { return; }
  218. if (parent.prototype.callInitHooks) {
  219. parent.prototype.callInitHooks.call(this);
  220. }
  221. this._initHooksCalled = true;
  222. for (var i = 0, len = proto._initHooks.length; i < len; i++) {
  223. proto._initHooks[i].call(this);
  224. }
  225. };
  226. return NewClass;
  227. };
  228. // method for adding properties to prototype
  229. L.Class.include = function (props) {
  230. L.extend(this.prototype, props);
  231. };
  232. // merge new default options to the Class
  233. L.Class.mergeOptions = function (options) {
  234. L.extend(this.prototype.options, options);
  235. };
  236. // add a constructor hook
  237. L.Class.addInitHook = function (fn) { // (Function) || (String, args...)
  238. var args = Array.prototype.slice.call(arguments, 1);
  239. var init = typeof fn === 'function' ? fn : function () {
  240. this[fn].apply(this, args);
  241. };
  242. this.prototype._initHooks = this.prototype._initHooks || [];
  243. this.prototype._initHooks.push(init);
  244. };
  245. /*
  246. * L.Mixin.Events is used to add custom events functionality to Leaflet classes.
  247. */
  248. var eventsKey = '_leaflet_events';
  249. L.Mixin = {};
  250. L.Mixin.Events = {
  251. addEventListener: function (types, fn, context) { // (String, Function[, Object]) or (Object[, Object])
  252. // types can be a map of types/handlers
  253. if (L.Util.invokeEach(types, this.addEventListener, this, fn, context)) { return this; }
  254. var events = this[eventsKey] = this[eventsKey] || {},
  255. contextId = context && L.stamp(context),
  256. i, len, event, type, indexKey, indexLenKey, typeIndex;
  257. // types can be a string of space-separated words
  258. types = L.Util.splitWords(types);
  259. for (i = 0, len = types.length; i < len; i++) {
  260. event = {
  261. action: fn,
  262. context: context || this
  263. };
  264. type = types[i];
  265. if (context) {
  266. // store listeners of a particular context in a separate hash (if it has an id)
  267. // gives a major performance boost when removing thousands of map layers
  268. indexKey = type + '_idx';
  269. indexLenKey = indexKey + '_len';
  270. typeIndex = events[indexKey] = events[indexKey] || {};
  271. if (!typeIndex[contextId]) {
  272. typeIndex[contextId] = [];
  273. // keep track of the number of keys in the index to quickly check if it's empty
  274. events[indexLenKey] = (events[indexLenKey] || 0) + 1;
  275. }
  276. typeIndex[contextId].push(event);
  277. } else {
  278. events[type] = events[type] || [];
  279. events[type].push(event);
  280. }
  281. }
  282. return this;
  283. },
  284. hasEventListeners: function (type) { // (String) -> Boolean
  285. var events = this[eventsKey];
  286. return !!events && ((type in events && events[type].length > 0) ||
  287. (type + '_idx' in events && events[type + '_idx_len'] > 0));
  288. },
  289. removeEventListener: function (types, fn, context) { // ([String, Function, Object]) or (Object[, Object])
  290. if (!this[eventsKey]) {
  291. return this;
  292. }
  293. if (!types) {
  294. return this.clearAllEventListeners();
  295. }
  296. if (L.Util.invokeEach(types, this.removeEventListener, this, fn, context)) { return this; }
  297. var events = this[eventsKey],
  298. contextId = context && L.stamp(context),
  299. i, len, type, listeners, j, indexKey, indexLenKey, typeIndex, removed;
  300. types = L.Util.splitWords(types);
  301. for (i = 0, len = types.length; i < len; i++) {
  302. type = types[i];
  303. indexKey = type + '_idx';
  304. indexLenKey = indexKey + '_len';
  305. typeIndex = events[indexKey];
  306. if (!fn) {
  307. // clear all listeners for a type if function isn't specified
  308. delete events[type];
  309. delete events[indexKey];
  310. } else {
  311. listeners = context && typeIndex ? typeIndex[contextId] : events[type];
  312. if (listeners) {
  313. for (j = listeners.length - 1; j >= 0; j--) {
  314. if ((listeners[j].action === fn) && (!context || (listeners[j].context === context))) {
  315. removed = listeners.splice(j, 1);
  316. // set the old action to a no-op, because it is possible
  317. // that the listener is being iterated over as part of a dispatch
  318. removed[0].action = L.Util.falseFn;
  319. }
  320. }
  321. if (context && typeIndex && (listeners.length === 0)) {
  322. delete typeIndex[contextId];
  323. events[indexLenKey]--;
  324. }
  325. }
  326. }
  327. }
  328. return this;
  329. },
  330. clearAllEventListeners: function () {
  331. delete this[eventsKey];
  332. return this;
  333. },
  334. fireEvent: function (type, data) { // (String[, Object])
  335. if (!this.hasEventListeners(type)) {
  336. return this;
  337. }
  338. var event = L.Util.extend({}, data, { type: type, target: this });
  339. var events = this[eventsKey],
  340. listeners, i, len, typeIndex, contextId;
  341. if (events[type]) {
  342. // make sure adding/removing listeners inside other listeners won't cause infinite loop
  343. listeners = events[type].slice();
  344. for (i = 0, len = listeners.length; i < len; i++) {
  345. listeners[i].action.call(listeners[i].context || this, event);
  346. }
  347. }
  348. // fire event for the context-indexed listeners as well
  349. typeIndex = events[type + '_idx'];
  350. for (contextId in typeIndex) {
  351. listeners = typeIndex[contextId].slice();
  352. if (listeners) {
  353. for (i = 0, len = listeners.length; i < len; i++) {
  354. listeners[i].action.call(listeners[i].context || this, event);
  355. }
  356. }
  357. }
  358. return this;
  359. },
  360. addOneTimeEventListener: function (types, fn, context) {
  361. if (L.Util.invokeEach(types, this.addOneTimeEventListener, this, fn, context)) { return this; }
  362. var handler = L.bind(function () {
  363. this
  364. .removeEventListener(types, fn, context)
  365. .removeEventListener(types, handler, context);
  366. }, this);
  367. return this
  368. .addEventListener(types, fn, context)
  369. .addEventListener(types, handler, context);
  370. }
  371. };
  372. L.Mixin.Events.on = L.Mixin.Events.addEventListener;
  373. L.Mixin.Events.off = L.Mixin.Events.removeEventListener;
  374. L.Mixin.Events.once = L.Mixin.Events.addOneTimeEventListener;
  375. L.Mixin.Events.fire = L.Mixin.Events.fireEvent;
  376. /*
  377. * L.Browser handles different browser and feature detections for internal Leaflet use.
  378. */
  379. (function () {
  380. var ie = !!window.ActiveXObject,
  381. ie6 = ie && !window.XMLHttpRequest,
  382. ie7 = ie && !document.querySelector,
  383. ielt9 = ie && !document.addEventListener,
  384. // terrible browser detection to work around Safari / iOS / Android browser bugs
  385. ua = navigator.userAgent.toLowerCase(),
  386. webkit = ua.indexOf('webkit') !== -1,
  387. chrome = ua.indexOf('chrome') !== -1,
  388. phantomjs = ua.indexOf('phantom') !== -1,
  389. android = ua.indexOf('android') !== -1,
  390. android23 = ua.search('android [23]') !== -1,
  391. mobile = typeof orientation !== undefined + '',
  392. msTouch = window.navigator && window.navigator.msPointerEnabled &&
  393. window.navigator.msMaxTouchPoints,
  394. retina = ('devicePixelRatio' in window && window.devicePixelRatio > 1) ||
  395. ('matchMedia' in window && window.matchMedia('(min-resolution:144dpi)') &&
  396. window.matchMedia('(min-resolution:144dpi)').matches),
  397. doc = document.documentElement,
  398. ie3d = ie && ('transition' in doc.style),
  399. webkit3d = ('WebKitCSSMatrix' in window) && ('m11' in new window.WebKitCSSMatrix()),
  400. gecko3d = 'MozPerspective' in doc.style,
  401. opera3d = 'OTransition' in doc.style,
  402. any3d = !window.L_DISABLE_3D && (ie3d || webkit3d || gecko3d || opera3d) && !phantomjs;
  403. // PhantomJS has 'ontouchstart' in document.documentElement, but doesn't actually support touch.
  404. // https://github.com/Leaflet/Leaflet/pull/1434#issuecomment-13843151
  405. var touch = !window.L_NO_TOUCH && !phantomjs && (function () {
  406. var startName = 'ontouchstart';
  407. // IE10+ (We simulate these into touch* events in L.DomEvent and L.DomEvent.MsTouch) or WebKit, etc.
  408. if (msTouch || (startName in doc)) {
  409. return true;
  410. }
  411. // Firefox/Gecko
  412. var div = document.createElement('div'),
  413. supported = false;
  414. if (!div.setAttribute) {
  415. return false;
  416. }
  417. div.setAttribute(startName, 'return;');
  418. if (typeof div[startName] === 'function') {
  419. supported = true;
  420. }
  421. div.removeAttribute(startName);
  422. div = null;
  423. return supported;
  424. }());
  425. L.Browser = {
  426. ie: ie,
  427. ie6: ie6,
  428. ie7: ie7,
  429. ielt9: ielt9,
  430. webkit: webkit,
  431. android: android,
  432. android23: android23,
  433. chrome: chrome,
  434. ie3d: ie3d,
  435. webkit3d: webkit3d,
  436. gecko3d: gecko3d,
  437. opera3d: opera3d,
  438. any3d: any3d,
  439. mobile: mobile,
  440. mobileWebkit: mobile && webkit,
  441. mobileWebkit3d: mobile && webkit3d,
  442. mobileOpera: mobile && window.opera,
  443. touch: touch,
  444. msTouch: msTouch,
  445. retina: retina
  446. };
  447. }());
  448. /*
  449. * L.Point represents a point with x and y coordinates.
  450. */
  451. L.Point = function (/*Number*/ x, /*Number*/ y, /*Boolean*/ round) {
  452. this.x = (round ? Math.round(x) : x);
  453. this.y = (round ? Math.round(y) : y);
  454. };
  455. L.Point.prototype = {
  456. clone: function () {
  457. return new L.Point(this.x, this.y);
  458. },
  459. // non-destructive, returns a new point
  460. add: function (point) {
  461. return this.clone()._add(L.point(point));
  462. },
  463. // destructive, used directly for performance in situations where it's safe to modify existing point
  464. _add: function (point) {
  465. this.x += point.x;
  466. this.y += point.y;
  467. return this;
  468. },
  469. subtract: function (point) {
  470. return this.clone()._subtract(L.point(point));
  471. },
  472. _subtract: function (point) {
  473. this.x -= point.x;
  474. this.y -= point.y;
  475. return this;
  476. },
  477. divideBy: function (num) {
  478. return this.clone()._divideBy(num);
  479. },
  480. _divideBy: function (num) {
  481. this.x /= num;
  482. this.y /= num;
  483. return this;
  484. },
  485. multiplyBy: function (num) {
  486. return this.clone()._multiplyBy(num);
  487. },
  488. _multiplyBy: function (num) {
  489. this.x *= num;
  490. this.y *= num;
  491. return this;
  492. },
  493. round: function () {
  494. return this.clone()._round();
  495. },
  496. _round: function () {
  497. this.x = Math.round(this.x);
  498. this.y = Math.round(this.y);
  499. return this;
  500. },
  501. floor: function () {
  502. return this.clone()._floor();
  503. },
  504. _floor: function () {
  505. this.x = Math.floor(this.x);
  506. this.y = Math.floor(this.y);
  507. return this;
  508. },
  509. distanceTo: function (point) {
  510. point = L.point(point);
  511. var x = point.x - this.x,
  512. y = point.y - this.y;
  513. return Math.sqrt(x * x + y * y);
  514. },
  515. equals: function (point) {
  516. point = L.point(point);
  517. return point.x === this.x &&
  518. point.y === this.y;
  519. },
  520. contains: function (point) {
  521. point = L.point(point);
  522. return Math.abs(point.x) <= Math.abs(this.x) &&
  523. Math.abs(point.y) <= Math.abs(this.y);
  524. },
  525. toString: function () {
  526. return 'Point(' +
  527. L.Util.formatNum(this.x) + ', ' +
  528. L.Util.formatNum(this.y) + ')';
  529. }
  530. };
  531. L.point = function (x, y, round) {
  532. if (x instanceof L.Point) {
  533. return x;
  534. }
  535. if (L.Util.isArray(x)) {
  536. return new L.Point(x[0], x[1]);
  537. }
  538. if (x === undefined || x === null) {
  539. return x;
  540. }
  541. return new L.Point(x, y, round);
  542. };
  543. /*
  544. * L.Bounds represents a rectangular area on the screen in pixel coordinates.
  545. */
  546. L.Bounds = function (a, b) { //(Point, Point) or Point[]
  547. if (!a) { return; }
  548. var points = b ? [a, b] : a;
  549. for (var i = 0, len = points.length; i < len; i++) {
  550. this.extend(points[i]);
  551. }
  552. };
  553. L.Bounds.prototype = {
  554. // extend the bounds to contain the given point
  555. extend: function (point) { // (Point)
  556. point = L.point(point);
  557. if (!this.min && !this.max) {
  558. this.min = point.clone();
  559. this.max = point.clone();
  560. } else {
  561. this.min.x = Math.min(point.x, this.min.x);
  562. this.max.x = Math.max(point.x, this.max.x);
  563. this.min.y = Math.min(point.y, this.min.y);
  564. this.max.y = Math.max(point.y, this.max.y);
  565. }
  566. return this;
  567. },
  568. getCenter: function (round) { // (Boolean) -> Point
  569. return new L.Point(
  570. (this.min.x + this.max.x) / 2,
  571. (this.min.y + this.max.y) / 2, round);
  572. },
  573. getBottomLeft: function () { // -> Point
  574. return new L.Point(this.min.x, this.max.y);
  575. },
  576. getTopRight: function () { // -> Point
  577. return new L.Point(this.max.x, this.min.y);
  578. },
  579. getSize: function () {
  580. return this.max.subtract(this.min);
  581. },
  582. contains: function (obj) { // (Bounds) or (Point) -> Boolean
  583. var min, max;
  584. if (typeof obj[0] === 'number' || obj instanceof L.Point) {
  585. obj = L.point(obj);
  586. } else {
  587. obj = L.bounds(obj);
  588. }
  589. if (obj instanceof L.Bounds) {
  590. min = obj.min;
  591. max = obj.max;
  592. } else {
  593. min = max = obj;
  594. }
  595. return (min.x >= this.min.x) &&
  596. (max.x <= this.max.x) &&
  597. (min.y >= this.min.y) &&
  598. (max.y <= this.max.y);
  599. },
  600. intersects: function (bounds) { // (Bounds) -> Boolean
  601. bounds = L.bounds(bounds);
  602. var min = this.min,
  603. max = this.max,
  604. min2 = bounds.min,
  605. max2 = bounds.max,
  606. xIntersects = (max2.x >= min.x) && (min2.x <= max.x),
  607. yIntersects = (max2.y >= min.y) && (min2.y <= max.y);
  608. return xIntersects && yIntersects;
  609. },
  610. isValid: function () {
  611. return !!(this.min && this.max);
  612. }
  613. };
  614. L.bounds = function (a, b) { // (Bounds) or (Point, Point) or (Point[])
  615. if (!a || a instanceof L.Bounds) {
  616. return a;
  617. }
  618. return new L.Bounds(a, b);
  619. };
  620. /*
  621. * L.Transformation is an utility class to perform simple point transformations through a 2d-matrix.
  622. */
  623. L.Transformation = function (a, b, c, d) {
  624. this._a = a;
  625. this._b = b;
  626. this._c = c;
  627. this._d = d;
  628. };
  629. L.Transformation.prototype = {
  630. transform: function (point, scale) { // (Point, Number) -> Point
  631. return this._transform(point.clone(), scale);
  632. },
  633. // destructive transform (faster)
  634. _transform: function (point, scale) {
  635. scale = scale || 1;
  636. point.x = scale * (this._a * point.x + this._b);
  637. point.y = scale * (this._c * point.y + this._d);
  638. return point;
  639. },
  640. untransform: function (point, scale) {
  641. scale = scale || 1;
  642. return new L.Point(
  643. (point.x / scale - this._b) / this._a,
  644. (point.y / scale - this._d) / this._c);
  645. }
  646. };
  647. /*
  648. * L.DomUtil contains various utility functions for working with DOM.
  649. */
  650. L.DomUtil = {
  651. get: function (id) {
  652. return (typeof id === 'string' ? document.getElementById(id) : id);
  653. },
  654. getStyle: function (el, style) {
  655. var value = el.style[style];
  656. if (!value && el.currentStyle) {
  657. value = el.currentStyle[style];
  658. }
  659. if ((!value || value === 'auto') && document.defaultView) {
  660. var css = document.defaultView.getComputedStyle(el, null);
  661. value = css ? css[style] : null;
  662. }
  663. return value === 'auto' ? null : value;
  664. },
  665. getViewportOffset: function (element) {
  666. var top = 0,
  667. left = 0,
  668. el = element,
  669. docBody = document.body,
  670. docEl = document.documentElement,
  671. pos,
  672. ie7 = L.Browser.ie7;
  673. do {
  674. top += el.offsetTop || 0;
  675. left += el.offsetLeft || 0;
  676. //add borders
  677. top += parseInt(L.DomUtil.getStyle(el, 'borderTopWidth'), 10) || 0;
  678. left += parseInt(L.DomUtil.getStyle(el, 'borderLeftWidth'), 10) || 0;
  679. pos = L.DomUtil.getStyle(el, 'position');
  680. if (el.offsetParent === docBody && pos === 'absolute') { break; }
  681. if (pos === 'fixed') {
  682. top += docBody.scrollTop || docEl.scrollTop || 0;
  683. left += docBody.scrollLeft || docEl.scrollLeft || 0;
  684. break;
  685. }
  686. if (pos === 'relative' && !el.offsetLeft) {
  687. var width = L.DomUtil.getStyle(el, 'width'),
  688. maxWidth = L.DomUtil.getStyle(el, 'max-width'),
  689. r = el.getBoundingClientRect();
  690. if (width !== 'none' || maxWidth !== 'none') {
  691. left += r.left + el.clientLeft;
  692. }
  693. //calculate full y offset since we're breaking out of the loop
  694. top += r.top + (docBody.scrollTop || docEl.scrollTop || 0);
  695. break;
  696. }
  697. el = el.offsetParent;
  698. } while (el);
  699. el = element;
  700. do {
  701. if (el === docBody) { break; }
  702. top -= el.scrollTop || 0;
  703. left -= el.scrollLeft || 0;
  704. // webkit (and ie <= 7) handles RTL scrollLeft different to everyone else
  705. // https://code.google.com/p/closure-library/source/browse/trunk/closure/goog/style/bidi.js
  706. if (!L.DomUtil.documentIsLtr() && (L.Browser.webkit || ie7)) {
  707. left += el.scrollWidth - el.clientWidth;
  708. // ie7 shows the scrollbar by default and provides clientWidth counting it, so we
  709. // need to add it back in if it is visible; scrollbar is on the left as we are RTL
  710. if (ie7 && L.DomUtil.getStyle(el, 'overflow-y') !== 'hidden' &&
  711. L.DomUtil.getStyle(el, 'overflow') !== 'hidden') {
  712. left += 17;
  713. }
  714. }
  715. el = el.parentNode;
  716. } while (el);
  717. return new L.Point(left, top);
  718. },
  719. documentIsLtr: function () {
  720. if (!L.DomUtil._docIsLtrCached) {
  721. L.DomUtil._docIsLtrCached = true;
  722. L.DomUtil._docIsLtr = L.DomUtil.getStyle(document.body, 'direction') === 'ltr';
  723. }
  724. return L.DomUtil._docIsLtr;
  725. },
  726. create: function (tagName, className, container) {
  727. var el = document.createElement(tagName);
  728. el.className = className;
  729. if (container) {
  730. container.appendChild(el);
  731. }
  732. return el;
  733. },
  734. hasClass: function (el, name) {
  735. return (el.className.length > 0) &&
  736. new RegExp('(^|\\s)' + name + '(\\s|$)').test(el.className);
  737. },
  738. addClass: function (el, name) {
  739. if (!L.DomUtil.hasClass(el, name)) {
  740. el.className += (el.className ? ' ' : '') + name;
  741. }
  742. },
  743. removeClass: function (el, name) {
  744. el.className = L.Util.trim((' ' + el.className + ' ').replace(' ' + name + ' ', ' '));
  745. },
  746. setOpacity: function (el, value) {
  747. if ('opacity' in el.style) {
  748. el.style.opacity = value;
  749. } else if ('filter' in el.style) {
  750. var filter = false,
  751. filterName = 'DXImageTransform.Microsoft.Alpha';
  752. // filters collection throws an error if we try to retrieve a filter that doesn't exist
  753. try {
  754. filter = el.filters.item(filterName);
  755. } catch (e) {
  756. // don't set opacity to 1 if we haven't already set an opacity,
  757. // it isn't needed and breaks transparent pngs.
  758. if (value === 1) { return; }
  759. }
  760. value = Math.round(value * 100);
  761. if (filter) {
  762. filter.Enabled = (value !== 100);
  763. filter.Opacity = value;
  764. } else {
  765. el.style.filter += ' progid:' + filterName + '(opacity=' + value + ')';
  766. }
  767. }
  768. },
  769. testProp: function (props) {
  770. var style = document.documentElement.style;
  771. for (var i = 0; i < props.length; i++) {
  772. if (props[i] in style) {
  773. return props[i];
  774. }
  775. }
  776. return false;
  777. },
  778. getTranslateString: function (point) {
  779. // on WebKit browsers (Chrome/Safari/iOS Safari/Android) using translate3d instead of translate
  780. // makes animation smoother as it ensures HW accel is used. Firefox 13 doesn't care
  781. // (same speed either way), Opera 12 doesn't support translate3d
  782. var is3d = L.Browser.webkit3d,
  783. open = 'translate' + (is3d ? '3d' : '') + '(',
  784. close = (is3d ? ',0' : '') + ')';
  785. return open + point.x + 'px,' + point.y + 'px' + close;
  786. },
  787. getScaleString: function (scale, origin) {
  788. var preTranslateStr = L.DomUtil.getTranslateString(origin.add(origin.multiplyBy(-1 * scale))),
  789. scaleStr = ' scale(' + scale + ') ';
  790. return preTranslateStr + scaleStr;
  791. },
  792. setPosition: function (el, point, disable3D) { // (HTMLElement, Point[, Boolean])
  793. // jshint camelcase: false
  794. el._leaflet_pos = point;
  795. if (!disable3D && L.Browser.any3d) {
  796. el.style[L.DomUtil.TRANSFORM] = L.DomUtil.getTranslateString(point);
  797. // workaround for Android 2/3 stability (https://github.com/CloudMade/Leaflet/issues/69)
  798. if (L.Browser.mobileWebkit3d) {
  799. el.style.WebkitBackfaceVisibility = 'hidden';
  800. }
  801. } else {
  802. el.style.left = point.x + 'px';
  803. el.style.top = point.y + 'px';
  804. }
  805. },
  806. getPosition: function (el) {
  807. // this method is only used for elements previously positioned using setPosition,
  808. // so it's safe to cache the position for performance
  809. // jshint camelcase: false
  810. return el._leaflet_pos;
  811. }
  812. };
  813. // prefix style property names
  814. L.DomUtil.TRANSFORM = L.DomUtil.testProp(
  815. ['transform', 'WebkitTransform', 'OTransform', 'MozTransform', 'msTransform']);
  816. // webkitTransition comes first because some browser versions that drop vendor prefix don't do
  817. // the same for the transitionend event, in particular the Android 4.1 stock browser
  818. L.DomUtil.TRANSITION = L.DomUtil.testProp(
  819. ['webkitTransition', 'transition', 'OTransition', 'MozTransition', 'msTransition']);
  820. L.DomUtil.TRANSITION_END =
  821. L.DomUtil.TRANSITION === 'webkitTransition' || L.DomUtil.TRANSITION === 'OTransition' ?
  822. L.DomUtil.TRANSITION + 'End' : 'transitionend';
  823. (function () {
  824. var userSelectProperty = L.DomUtil.testProp(
  825. ['userSelect', 'WebkitUserSelect', 'OUserSelect', 'MozUserSelect', 'msUserSelect']);
  826. var userDragProperty = L.DomUtil.testProp(
  827. ['userDrag', 'WebkitUserDrag', 'OUserDrag', 'MozUserDrag', 'msUserDrag']);
  828. L.extend(L.DomUtil, {
  829. disableTextSelection: function () {
  830. if (userSelectProperty) {
  831. var style = document.documentElement.style;
  832. this._userSelect = style[userSelectProperty];
  833. style[userSelectProperty] = 'none';
  834. } else {
  835. L.DomEvent.on(window, 'selectstart', L.DomEvent.stop);
  836. }
  837. },
  838. enableTextSelection: function () {
  839. if (userSelectProperty) {
  840. document.documentElement.style[userSelectProperty] = this._userSelect;
  841. delete this._userSelect;
  842. } else {
  843. L.DomEvent.off(window, 'selectstart', L.DomEvent.stop);
  844. }
  845. },
  846. disableImageDrag: function () {
  847. if (userDragProperty) {
  848. var style = document.documentElement.style;
  849. this._userDrag = style[userDragProperty];
  850. style[userDragProperty] = 'none';
  851. } else {
  852. L.DomEvent.on(window, 'dragstart', L.DomEvent.stop);
  853. }
  854. },
  855. enableImageDrag: function () {
  856. if (userDragProperty) {
  857. document.documentElement.style[userDragProperty] = this._userDrag;
  858. delete this._userDrag;
  859. } else {
  860. L.DomEvent.off(window, 'dragstart', L.DomEvent.stop);
  861. }
  862. }
  863. });
  864. })();
  865. /*
  866. * L.LatLng represents a geographical point with latitude and longitude coordinates.
  867. */
  868. L.LatLng = function (rawLat, rawLng) { // (Number, Number)
  869. var lat = parseFloat(rawLat),
  870. lng = parseFloat(rawLng);
  871. if (isNaN(lat) || isNaN(lng)) {
  872. throw new Error('Invalid LatLng object: (' + rawLat + ', ' + rawLng + ')');
  873. }
  874. this.lat = lat;
  875. this.lng = lng;
  876. };
  877. L.extend(L.LatLng, {
  878. DEG_TO_RAD: Math.PI / 180,
  879. RAD_TO_DEG: 180 / Math.PI,
  880. MAX_MARGIN: 1.0E-9 // max margin of error for the "equals" check
  881. });
  882. L.LatLng.prototype = {
  883. equals: function (obj) { // (LatLng) -> Boolean
  884. if (!obj) { return false; }
  885. obj = L.latLng(obj);
  886. var margin = Math.max(
  887. Math.abs(this.lat - obj.lat),
  888. Math.abs(this.lng - obj.lng));
  889. return margin <= L.LatLng.MAX_MARGIN;
  890. },
  891. toString: function (precision) { // (Number) -> String
  892. return 'LatLng(' +
  893. L.Util.formatNum(this.lat, precision) + ', ' +
  894. L.Util.formatNum(this.lng, precision) + ')';
  895. },
  896. // Haversine distance formula, see http://en.wikipedia.org/wiki/Haversine_formula
  897. // TODO move to projection code, LatLng shouldn't know about Earth
  898. distanceTo: function (other) { // (LatLng) -> Number
  899. other = L.latLng(other);
  900. var R = 6378137, // earth radius in meters
  901. d2r = L.LatLng.DEG_TO_RAD,
  902. dLat = (other.lat - this.lat) * d2r,
  903. dLon = (other.lng - this.lng) * d2r,
  904. lat1 = this.lat * d2r,
  905. lat2 = other.lat * d2r,
  906. sin1 = Math.sin(dLat / 2),
  907. sin2 = Math.sin(dLon / 2);
  908. var a = sin1 * sin1 + sin2 * sin2 * Math.cos(lat1) * Math.cos(lat2);
  909. return R * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
  910. },
  911. wrap: function (a, b) { // (Number, Number) -> LatLng
  912. var lng = this.lng;
  913. a = a || -180;
  914. b = b || 180;
  915. lng = (lng + b) % (b - a) + (lng < a || lng === b ? b : a);
  916. return new L.LatLng(this.lat, lng);
  917. }
  918. };
  919. L.latLng = function (a, b) { // (LatLng) or ([Number, Number]) or (Number, Number)
  920. if (a instanceof L.LatLng) {
  921. return a;
  922. }
  923. if (L.Util.isArray(a)) {
  924. return new L.LatLng(a[0], a[1]);
  925. }
  926. if (a === undefined || a === null) {
  927. return a;
  928. }
  929. if (typeof a === 'object' && 'lat' in a) {
  930. return new L.LatLng(a.lat, 'lng' in a ? a.lng : a.lon);
  931. }
  932. return new L.LatLng(a, b);
  933. };
  934. /*
  935. * L.LatLngBounds represents a rectangular area on the map in geographical coordinates.
  936. */
  937. L.LatLngBounds = function (southWest, northEast) { // (LatLng, LatLng) or (LatLng[])
  938. if (!southWest) { return; }
  939. var latlngs = northEast ? [southWest, northEast] : southWest;
  940. for (var i = 0, len = latlngs.length; i < len; i++) {
  941. this.extend(latlngs[i]);
  942. }
  943. };
  944. L.LatLngBounds.prototype = {
  945. // extend the bounds to contain the given point or bounds
  946. extend: function (obj) { // (LatLng) or (LatLngBounds)
  947. if (!obj) { return this; }
  948. if (typeof obj[0] === 'number' || typeof obj[0] === 'string' || obj instanceof L.LatLng) {
  949. obj = L.latLng(obj);
  950. } else {
  951. obj = L.latLngBounds(obj);
  952. }
  953. if (obj instanceof L.LatLng) {
  954. if (!this._southWest && !this._northEast) {
  955. this._southWest = new L.LatLng(obj.lat, obj.lng);
  956. this._northEast = new L.LatLng(obj.lat, obj.lng);
  957. } else {
  958. this._southWest.lat = Math.min(obj.lat, this._southWest.lat);
  959. this._southWest.lng = Math.min(obj.lng, this._southWest.lng);
  960. this._northEast.lat = Math.max(obj.lat, this._northEast.lat);
  961. this._northEast.lng = Math.max(obj.lng, this._northEast.lng);
  962. }
  963. } else if (obj instanceof L.LatLngBounds) {
  964. this.extend(obj._southWest);
  965. this.extend(obj._northEast);
  966. }
  967. return this;
  968. },
  969. // extend the bounds by a percentage
  970. pad: function (bufferRatio) { // (Number) -> LatLngBounds
  971. var sw = this._southWest,
  972. ne = this._northEast,
  973. heightBuffer = Math.abs(sw.lat - ne.lat) * bufferRatio,
  974. widthBuffer = Math.abs(sw.lng - ne.lng) * bufferRatio;
  975. return new L.LatLngBounds(
  976. new L.LatLng(sw.lat - heightBuffer, sw.lng - widthBuffer),
  977. new L.LatLng(ne.lat + heightBuffer, ne.lng + widthBuffer));
  978. },
  979. getCenter: function () { // -> LatLng
  980. return new L.LatLng(
  981. (this._southWest.lat + this._northEast.lat) / 2,
  982. (this._southWest.lng + this._northEast.lng) / 2);
  983. },
  984. getSouthWest: function () {
  985. return this._southWest;
  986. },
  987. getNorthEast: function () {
  988. return this._northEast;
  989. },
  990. getNorthWest: function () {
  991. return new L.LatLng(this.getNorth(), this.getWest());
  992. },
  993. getSouthEast: function () {
  994. return new L.LatLng(this.getSouth(), this.getEast());
  995. },
  996. getWest: function () {
  997. return this._southWest.lng;
  998. },
  999. getSouth: function () {
  1000. return this._southWest.lat;
  1001. },
  1002. getEast: function () {
  1003. return this._northEast.lng;
  1004. },
  1005. getNorth: function () {
  1006. return this._northEast.lat;
  1007. },
  1008. contains: function (obj) { // (LatLngBounds) or (LatLng) -> Boolean
  1009. if (typeof obj[0] === 'number' || obj instanceof L.LatLng) {
  1010. obj = L.latLng(obj);
  1011. } else {
  1012. obj = L.latLngBounds(obj);
  1013. }
  1014. var sw = this._southWest,
  1015. ne = this._northEast,
  1016. sw2, ne2;
  1017. if (obj instanceof L.LatLngBounds) {
  1018. sw2 = obj.getSouthWest();
  1019. ne2 = obj.getNorthEast();
  1020. } else {
  1021. sw2 = ne2 = obj;
  1022. }
  1023. return (sw2.lat >= sw.lat) && (ne2.lat <= ne.lat) &&
  1024. (sw2.lng >= sw.lng) && (ne2.lng <= ne.lng);
  1025. },
  1026. intersects: function (bounds) { // (LatLngBounds)
  1027. bounds = L.latLngBounds(bounds);
  1028. var sw = this._southWest,
  1029. ne = this._northEast,
  1030. sw2 = bounds.getSouthWest(),
  1031. ne2 = bounds.getNorthEast(),
  1032. latIntersects = (ne2.lat >= sw.lat) && (sw2.lat <= ne.lat),
  1033. lngIntersects = (ne2.lng >= sw.lng) && (sw2.lng <= ne.lng);
  1034. return latIntersects && lngIntersects;
  1035. },
  1036. toBBoxString: function () {
  1037. return [this.getWest(), this.getSouth(), this.getEast(), this.getNorth()].join(',');
  1038. },
  1039. equals: function (bounds) { // (LatLngBounds)
  1040. if (!bounds) { return false; }
  1041. bounds = L.latLngBounds(bounds);
  1042. return this._southWest.equals(bounds.getSouthWest()) &&
  1043. this._northEast.equals(bounds.getNorthEast());
  1044. },
  1045. isValid: function () {
  1046. return !!(this._southWest && this._northEast);
  1047. }
  1048. };
  1049. //TODO International date line?
  1050. L.latLngBounds = function (a, b) { // (LatLngBounds) or (LatLng, LatLng)
  1051. if (!a || a instanceof L.LatLngBounds) {
  1052. return a;
  1053. }
  1054. return new L.LatLngBounds(a, b);
  1055. };
  1056. /*
  1057. * L.Projection contains various geographical projections used by CRS classes.
  1058. */
  1059. L.Projection = {};
  1060. /*
  1061. * Spherical Mercator is the most popular map projection, used by EPSG:3857 CRS used by default.
  1062. */
  1063. L.Projection.SphericalMercator = {
  1064. MAX_LATITUDE: 85.0511287798,
  1065. project: function (latlng) { // (LatLng) -> Point
  1066. var d = L.LatLng.DEG_TO_RAD,
  1067. max = this.MAX_LATITUDE,
  1068. lat = Math.max(Math.min(max, latlng.lat), -max),
  1069. x = latlng.lng * d,
  1070. y = lat * d;
  1071. y = Math.log(Math.tan((Math.PI / 4) + (y / 2)));
  1072. return new L.Point(x, y);
  1073. },
  1074. unproject: function (point) { // (Point, Boolean) -> LatLng
  1075. var d = L.LatLng.RAD_TO_DEG,
  1076. lng = point.x * d,
  1077. lat = (2 * Math.atan(Math.exp(point.y)) - (Math.PI / 2)) * d;
  1078. return new L.LatLng(lat, lng);
  1079. }
  1080. };
  1081. /*
  1082. * Simple equirectangular (Plate Carree) projection, used by CRS like EPSG:4326 and Simple.
  1083. */
  1084. L.Projection.LonLat = {
  1085. project: function (latlng) {
  1086. return new L.Point(latlng.lng, latlng.lat);
  1087. },
  1088. unproject: function (point) {
  1089. return new L.LatLng(point.y, point.x);
  1090. }
  1091. };
  1092. /*
  1093. * L.CRS is a base object for all defined CRS (Coordinate Reference Systems) in Leaflet.
  1094. */
  1095. L.CRS = {
  1096. latLngToPoint: function (latlng, zoom) { // (LatLng, Number) -> Point
  1097. var projectedPoint = this.projection.project(latlng),
  1098. scale = this.scale(zoom);
  1099. return this.transformation._transform(projectedPoint, scale);
  1100. },
  1101. pointToLatLng: function (point, zoom) { // (Point, Number[, Boolean]) -> LatLng
  1102. var scale = this.scale(zoom),
  1103. untransformedPoint = this.transformation.untransform(point, scale);
  1104. return this.projection.unproject(untransformedPoint);
  1105. },
  1106. project: function (latlng) {
  1107. return this.projection.project(latlng);
  1108. },
  1109. scale: function (zoom) {
  1110. return 256 * Math.pow(2, zoom);
  1111. }
  1112. };
  1113. /*
  1114. * A simple CRS that can be used for flat non-Earth maps like panoramas or game maps.
  1115. */
  1116. L.CRS.Simple = L.extend({}, L.CRS, {
  1117. projection: L.Projection.LonLat,
  1118. transformation: new L.Transformation(1, 0, -1, 0),
  1119. scale: function (zoom) {
  1120. return Math.pow(2, zoom);
  1121. }
  1122. });
  1123. /*
  1124. * L.CRS.EPSG3857 (Spherical Mercator) is the most common CRS for web mapping
  1125. * and is used by Leaflet by default.
  1126. */
  1127. L.CRS.EPSG3857 = L.extend({}, L.CRS, {
  1128. code: 'EPSG:3857',
  1129. projection: L.Projection.SphericalMercator,
  1130. transformation: new L.Transformation(0.5 / Math.PI, 0.5, -0.5 / Math.PI, 0.5),
  1131. project: function (latlng) { // (LatLng) -> Point
  1132. var projectedPoint = this.projection.project(latlng),
  1133. earthRadius = 6378137;
  1134. return projectedPoint.multiplyBy(earthRadius);
  1135. }
  1136. });
  1137. L.CRS.EPSG900913 = L.extend({}, L.CRS.EPSG3857, {
  1138. code: 'EPSG:900913'
  1139. });
  1140. /*
  1141. * L.CRS.EPSG4326 is a CRS popular among advanced GIS specialists.
  1142. */
  1143. L.CRS.EPSG4326 = L.extend({}, L.CRS, {
  1144. code: 'EPSG:4326',
  1145. projection: L.Projection.LonLat,
  1146. transformation: new L.Transformation(1 / 360, 0.5, -1 / 360, 0.5)
  1147. });
  1148. /*
  1149. * L.Map is the central class of the API - it is used to create a map.
  1150. */
  1151. L.Map = L.Class.extend({
  1152. includes: L.Mixin.Events,
  1153. options: {
  1154. crs: L.CRS.EPSG3857,
  1155. /*
  1156. center: LatLng,
  1157. zoom: Number,
  1158. layers: Array,
  1159. */
  1160. fadeAnimation: L.DomUtil.TRANSITION && !L.Browser.android23,
  1161. trackResize: true,
  1162. markerZoomAnimation: L.DomUtil.TRANSITION && L.Browser.any3d
  1163. },
  1164. initialize: function (id, options) { // (HTMLElement or String, Object)
  1165. options = L.setOptions(this, options);
  1166. this._initContainer(id);
  1167. this._initLayout();
  1168. this._initEvents();
  1169. if (options.maxBounds) {
  1170. this.setMaxBounds(options.maxBounds);
  1171. }
  1172. if (options.center && options.zoom !== undefined) {
  1173. this.setView(L.latLng(options.center), options.zoom, {reset: true});
  1174. }
  1175. this._handlers = [];
  1176. this._layers = {};
  1177. this._zoomBoundLayers = {};
  1178. this._tileLayersNum = 0;
  1179. this.callInitHooks();
  1180. this._addLayers(options.layers);
  1181. },
  1182. // public methods that modify map state
  1183. // replaced by animation-powered implementation in Map.PanAnimation.js
  1184. setView: function (center, zoom) {
  1185. this._resetView(L.latLng(center), this._limitZoom(zoom));
  1186. return this;
  1187. },
  1188. setZoom: function (zoom, options) {
  1189. return this.setView(this.getCenter(), zoom, {zoom: options});
  1190. },
  1191. zoomIn: function (delta, options) {
  1192. return this.setZoom(this._zoom + (delta || 1), options);
  1193. },
  1194. zoomOut: function (delta, options) {
  1195. return this.setZoom(this._zoom - (delta || 1), options);
  1196. },
  1197. setZoomAround: function (latlng, zoom, options) {
  1198. var scale = this.getZoomScale(zoom),
  1199. viewHalf = this.getSize().divideBy(2),
  1200. containerPoint = latlng instanceof L.Point ? latlng : this.latLngToContainerPoint(latlng),
  1201. centerOffset = containerPoint.subtract(viewHalf).multiplyBy(1 - 1 / scale),
  1202. newCenter = this.containerPointToLatLng(viewHalf.add(centerOffset));
  1203. return this.setView(newCenter, zoom, {zoom: options});
  1204. },
  1205. fitBounds: function (bounds, options) {
  1206. options = options || {};
  1207. bounds = bounds.getBounds ? bounds.getBounds() : L.latLngBounds(bounds);
  1208. var paddingTL = L.point(options.paddingTopLeft || options.padding || [0, 0]),
  1209. paddingBR = L.point(options.paddingBottomRight || options.padding || [0, 0]),
  1210. zoom = this.getBoundsZoom(bounds, false, paddingTL.add(paddingBR)),
  1211. paddingOffset = paddingBR.subtract(paddingTL).divideBy(2),
  1212. swPoint = this.project(bounds.getSouthWest(), zoom),
  1213. nePoint = this.project(bounds.getNorthEast(), zoom),
  1214. center = this.unproject(swPoint.add(nePoint).divideBy(2).add(paddingOffset), zoom);
  1215. return this.setView(center, zoom, options);
  1216. },
  1217. fitWorld: function (options) {
  1218. return this.fitBounds([[-90, -180], [90, 180]], options);
  1219. },
  1220. panTo: function (center, options) { // (LatLng)
  1221. return this.setView(center, this._zoom, {pan: options});
  1222. },
  1223. panBy: function (offset) { // (Point)
  1224. // replaced with animated panBy in Map.Animation.js
  1225. this.fire('movestart');
  1226. this._rawPanBy(L.point(offset));
  1227. this.fire('move');
  1228. return this.fire('moveend');
  1229. },
  1230. setMaxBounds: function (bounds) {
  1231. bounds = L.latLngBounds(bounds);
  1232. this.options.maxBounds = bounds;
  1233. if (!bounds) {
  1234. this._boundsMinZoom = null;
  1235. this.off('moveend', this._panInsideMaxBounds, this);
  1236. return this;
  1237. }
  1238. var minZoom = this.getBoundsZoom(bounds, true);
  1239. this._boundsMinZoom = minZoom;
  1240. if (this._loaded) {
  1241. if (this._zoom < minZoom) {
  1242. this.setView(bounds.getCenter(), minZoom);
  1243. } else {
  1244. this.panInsideBounds(bounds);
  1245. }
  1246. }
  1247. this.on('moveend', this._panInsideMaxBounds, this);
  1248. return this;
  1249. },
  1250. panInsideBounds: function (bounds) {
  1251. bounds = L.latLngBounds(bounds);
  1252. var viewBounds = this.getPixelBounds(),
  1253. viewSw = viewBounds.getBottomLeft(),
  1254. viewNe = viewBounds.getTopRight(),
  1255. sw = this.project(bounds.getSouthWest()),
  1256. ne = this.project(bounds.getNorthEast()),
  1257. dx = 0,
  1258. dy = 0;
  1259. if (viewNe.y < ne.y) { // north
  1260. dy = Math.ceil(ne.y - viewNe.y);
  1261. }
  1262. if (viewNe.x > ne.x) { // east
  1263. dx = Math.floor(ne.x - viewNe.x);
  1264. }
  1265. if (viewSw.y > sw.y) { // south
  1266. dy = Math.floor(sw.y - viewSw.y);
  1267. }
  1268. if (viewSw.x < sw.x) { // west
  1269. dx = Math.ceil(sw.x - viewSw.x);
  1270. }
  1271. if (dx || dy) {
  1272. return this.panBy([dx, dy]);
  1273. }
  1274. return this;
  1275. },
  1276. addLayer: function (layer) {
  1277. // TODO method is too big, refactor
  1278. var id = L.stamp(layer);
  1279. if (this._layers[id]) { return this; }
  1280. this._layers[id] = layer;
  1281. // TODO getMaxZoom, getMinZoom in ILayer (instead of options)
  1282. if (layer.options && (!isNaN(layer.options.maxZoom) || !isNaN(layer.options.minZoom))) {
  1283. this._zoomBoundLayers[id] = layer;
  1284. this._updateZoomLevels();
  1285. }
  1286. // TODO looks ugly, refactor!!!
  1287. if (this.options.zoomAnimation && L.TileLayer && (layer instanceof L.TileLayer)) {
  1288. this._tileLayersNum++;
  1289. this._tileLayersToLoad++;
  1290. layer.on('load', this._onTileLayerLoad, this);
  1291. }
  1292. if (this._loaded) {
  1293. this._layerAdd(layer);
  1294. }
  1295. return this;
  1296. },
  1297. removeLayer: function (layer) {
  1298. var id = L.stamp(layer);
  1299. if (!this._layers[id]) { return; }
  1300. if (this._loaded) {
  1301. layer.onRemove(this);
  1302. this.fire('layerremove', {layer: layer});
  1303. }
  1304. delete this._layers[id];
  1305. if (this._zoomBoundLayers[id]) {
  1306. delete this._zoomBoundLayers[id];
  1307. this._updateZoomLevels();
  1308. }
  1309. // TODO looks ugly, refactor
  1310. if (this.options.zoomAnimation && L.TileLayer && (layer instanceof L.TileLayer)) {
  1311. this._tileLayersNum--;
  1312. this._tileLayersToLoad--;
  1313. layer.off('load', this._onTileLayerLoad, this);
  1314. }
  1315. return this;
  1316. },
  1317. hasLayer: function (layer) {
  1318. if (!layer) { return false; }
  1319. return (L.stamp(layer) in this._layers);
  1320. },
  1321. eachLayer: function (method, context) {
  1322. for (var i in this._layers) {
  1323. method.call(context, this._layers[i]);
  1324. }
  1325. return this;
  1326. },
  1327. invalidateSize: function (options) {
  1328. options = L.extend({
  1329. animate: false,
  1330. pan: true
  1331. }, options === true ? {animate: true} : options);
  1332. var oldSize = this.getSize();
  1333. this._sizeChanged = true;
  1334. if (this.options.maxBounds) {
  1335. this.setMaxBounds(this.options.maxBounds);
  1336. }
  1337. if (!this._loaded) { return this; }
  1338. var newSize = this.getSize(),
  1339. offset = oldSize.subtract(newSize).divideBy(2).round();
  1340. if (!offset.x && !offset.y) { return this; }
  1341. if (options.animate && options.pan) {
  1342. this.panBy(offset);
  1343. } else {
  1344. if (options.pan) {
  1345. this._rawPanBy(offset);
  1346. }
  1347. this.fire('move');
  1348. // make sure moveend is not fired too often on resize
  1349. clearTimeout(this._sizeTimer);
  1350. this._sizeTimer = setTimeout(L.bind(this.fire, this, 'moveend'), 200);
  1351. }
  1352. return this.fire('resize', {
  1353. oldSize: oldSize,
  1354. newSize: newSize
  1355. });
  1356. },
  1357. // TODO handler.addTo
  1358. addHandler: function (name, HandlerClass) {
  1359. if (!HandlerClass) { return; }
  1360. var handler = this[name] = new HandlerClass(this);
  1361. this._handlers.push(handler);
  1362. if (this.options[name]) {
  1363. handler.enable();
  1364. }
  1365. return this;
  1366. },
  1367. remove: function () {
  1368. if (this._loaded) {
  1369. this.fire('unload');
  1370. }
  1371. this._initEvents('off');
  1372. delete this._container._leaflet;
  1373. this._clearPanes();
  1374. if (this._clearControlPos) {
  1375. this._clearControlPos();
  1376. }
  1377. this._clearHandlers();
  1378. return this;
  1379. },
  1380. // public methods for getting map state
  1381. getCenter: function () { // (Boolean) -> LatLng
  1382. this._checkIfLoaded();
  1383. if (!this._moved()) {
  1384. return this._initialCenter;
  1385. }
  1386. return this.layerPointToLatLng(this._getCenterLayerPoint());
  1387. },
  1388. getZoom: function () {
  1389. return this._zoom;
  1390. },
  1391. getBounds: function () {
  1392. var bounds = this.getPixelBounds(),
  1393. sw = this.unproject(bounds.getBottomLeft()),
  1394. ne = this.unproject(bounds.getTopRight());
  1395. return new L.LatLngBounds(sw, ne);
  1396. },
  1397. getMinZoom: function () {
  1398. var z1 = this.options.minZoom || 0,
  1399. z2 = this._layersMinZoom || 0,
  1400. z3 = this._boundsMinZoom || 0;
  1401. return Math.max(z1, z2, z3);
  1402. },
  1403. getMaxZoom: function () {
  1404. var z1 = this.options.maxZoom === undefined ? Infinity : this.options.maxZoom,
  1405. z2 = this._layersMaxZoom === undefined ? Infinity : this._layersMaxZoom;
  1406. return Math.min(z1, z2);
  1407. },
  1408. getBoundsZoom: function (bounds, inside, padding) { // (LatLngBounds[, Boolean, Point]) -> Number
  1409. bounds = L.latLngBounds(bounds);
  1410. var zoom = this.getMinZoom() - (inside ? 1 : 0),
  1411. maxZoom = this.getMaxZoom(),
  1412. size = this.getSize(),
  1413. nw = bounds.getNorthWest(),
  1414. se = bounds.getSouthEast(),
  1415. zoomNotFound = true,
  1416. boundsSize;
  1417. padding = L.point(padding || [0, 0]);
  1418. do {
  1419. zoom++;
  1420. boundsSize = this.project(se, zoom).subtract(this.project(nw, zoom)).add(padding);
  1421. zoomNotFound = !inside ? size.contains(boundsSize) : boundsSize.x < size.x || boundsSize.y < size.y;
  1422. } while (zoomNotFound && zoom <= maxZoom);
  1423. if (zoomNotFound && inside) {
  1424. return null;
  1425. }
  1426. return inside ? zoom : zoom - 1;
  1427. },
  1428. getSize: function () {
  1429. if (!this._size || this._sizeChanged) {
  1430. this._size = new L.Point(
  1431. this._container.clientWidth,
  1432. this._container.clientHeight);
  1433. this._sizeChanged = false;
  1434. }
  1435. return this._size.clone();
  1436. },
  1437. getPixelBounds: function () {
  1438. var topLeftPoint = this._getTopLeftPoint();
  1439. return new L.Bounds(topLeftPoint, topLeftPoint.add(this.getSize()));
  1440. },
  1441. getPixelOrigin: function () {
  1442. this._checkIfLoaded();
  1443. return this._initialTopLeftPoint;
  1444. },
  1445. getPanes: function () {
  1446. return this._panes;
  1447. },
  1448. getContainer: function () {
  1449. return this._container;
  1450. },
  1451. // TODO replace with universal implementation after refactoring projections
  1452. getZoomScale: function (toZoom) {
  1453. var crs = this.options.crs;
  1454. return crs.scale(toZoom) / crs.scale(this._zoom);
  1455. },
  1456. getScaleZoom: function (scale) {
  1457. return this._zoom + (Math.log(scale) / Math.LN2);
  1458. },
  1459. // conversion methods
  1460. project: function (latlng, zoom) { // (LatLng[, Number]) -> Point
  1461. zoom = zoom === undefined ? this._zoom : zoom;
  1462. return this.options.crs.latLngToPoint(L.latLng(latlng), zoom);
  1463. },
  1464. unproject: function (point, zoom) { // (Point[, Number]) -> LatLng
  1465. zoom = zoom === undefined ? this._zoom : zoom;
  1466. return this.options.crs.pointToLatLng(L.point(point), zoom);
  1467. },
  1468. layerPointToLatLng: function (point) { // (Point)
  1469. var projectedPoint = L.point(point).add(this.getPixelOrigin());
  1470. return this.unproject(projectedPoint);
  1471. },
  1472. latLngToLayerPoint: function (latlng) { // (LatLng)
  1473. var projectedPoint = this.project(L.latLng(latlng))._round();
  1474. return projectedPoint._subtract(this.getPixelOrigin());
  1475. },
  1476. containerPointToLayerPoint: function (point) { // (Point)
  1477. return L.point(point).subtract(this._getMapPanePos());
  1478. },
  1479. layerPointToContainerPoint: function (point) { // (Point)
  1480. return L.point(point).add(this._getMapPanePos());
  1481. },
  1482. containerPointToLatLng: function (point) {
  1483. var layerPoint = this.containerPointToLayerPoint(L.point(point));
  1484. return this.layerPointToLatLng(layerPoint);
  1485. },
  1486. latLngToContainerPoint: function (latlng) {
  1487. return this.layerPointToContainerPoint(this.latLngToLayerPoint(L.latLng(latlng)));
  1488. },
  1489. mouseEventToContainerPoint: function (e) { // (MouseEvent)
  1490. return L.DomEvent.getMousePosition(e, this._container);
  1491. },
  1492. mouseEventToLayerPoint: function (e) { // (MouseEvent)
  1493. return this.containerPointToLayerPoint(this.mouseEventToContainerPoint(e));
  1494. },
  1495. mouseEventToLatLng: function (e) { // (MouseEvent)
  1496. return this.layerPointToLatLng(this.mouseEventToLayerPoint(e));
  1497. },
  1498. // map initialization methods
  1499. _initContainer: function (id) {
  1500. var container = this._container = L.DomUtil.get(id);
  1501. if (!container) {
  1502. throw new Error('Map container not found.');
  1503. } else if (container._leaflet) {
  1504. throw new Error('Map container is already initialized.');
  1505. }
  1506. container._leaflet = true;
  1507. },
  1508. _initLayout: function () {
  1509. var container = this._container;
  1510. L.DomUtil.addClass(container, 'leaflet-container' +
  1511. (L.Browser.touch ? ' leaflet-touch' : '') +
  1512. (L.Browser.retina ? ' leaflet-retina' : '') +
  1513. (this.options.fadeAnimation ? ' leaflet-fade-anim' : ''));
  1514. var position = L.DomUtil.getStyle(container, 'position');
  1515. if (position !== 'absolute' && position !== 'relative' && position !== 'fixed') {
  1516. container.style.position = 'relative';
  1517. }
  1518. this._initPanes();
  1519. if (this._initControlPos) {
  1520. this._initControlPos();
  1521. }
  1522. },
  1523. _initPanes: function () {
  1524. var panes = this._panes = {};
  1525. this._mapPane = panes.mapPane = this._createPane('leaflet-map-pane', this._container);
  1526. this._tilePane = panes.tilePane = this._createPane('leaflet-tile-pane', this._mapPane);
  1527. panes.objectsPane = this._createPane('leaflet-objects-pane', this._mapPane);
  1528. panes.shadowPane = this._createPane('leaflet-shadow-pane');
  1529. panes.overlayPane = this._createPane('leaflet-overlay-pane');
  1530. panes.markerPane = this._createPane('leaflet-marker-pane');
  1531. panes.popupPane = this._createPane('leaflet-popup-pane');
  1532. var zoomHide = ' leaflet-zoom-hide';
  1533. if (!this.options.markerZoomAnimation) {
  1534. L.DomUtil.addClass(panes.markerPane, zoomHide);
  1535. L.DomUtil.addClass(panes.shadowPane, zoomHide);
  1536. L.DomUtil.addClass(panes.popupPane, zoomHide);
  1537. }
  1538. },
  1539. _createPane: function (className, container) {
  1540. return L.DomUtil.create('div', className, container || this._pan