(function () {
- var ua = navigator.userAgent.toLowerCase(),
- ie = !!window.ActiveXObject,
+
+ var ie = !!window.ActiveXObject,
+ // http://tanalin.com/en/articles/ie-version-js/
ie6 = ie && !window.XMLHttpRequest,
+ ie7 = ie && !document.querySelector,
+
+ // terrible browser detection to work around Safari / iOS / Android browser bugs
+ // see TileLayer._addTile and debug/hacks/jitter.html
+
+ ua = navigator.userAgent.toLowerCase(),
webkit = ua.indexOf("webkit") !== -1,
- gecko = ua.indexOf("gecko") !== -1,
- //Terrible browser detection to work around a safari / iOS / android browser bug. See TileLayer._addTile and debug/hacks/jitter.html
chrome = ua.indexOf("chrome") !== -1,
- opera = window.opera,
android = ua.indexOf("android") !== -1,
android23 = ua.search("android [23]") !== -1,
- mobile = typeof orientation !== undefined + '' ? true : false,
+
+ mobile = typeof orientation !== undefined + '',
+ msTouch = (window.navigator && window.navigator.msPointerEnabled && window.navigator.msMaxTouchPoints),
+ retina = (('devicePixelRatio' in window && window.devicePixelRatio > 1) ||
+ ('matchMedia' in window && window.matchMedia("(min-resolution:144dpi)").matches)),
+
doc = document.documentElement,
ie3d = ie && ('transition' in doc.style),
- webkit3d = webkit && ('WebKitCSSMatrix' in window) && ('m11' in new window.WebKitCSSMatrix()),
- gecko3d = gecko && ('MozPerspective' in doc.style),
- opera3d = opera && ('OTransition' in doc.style);
+ webkit3d = ('WebKitCSSMatrix' in window) && ('m11' in new window.WebKitCSSMatrix()),
+ gecko3d = 'MozPerspective' in doc.style,
+ opera3d = 'OTransition' in doc.style,
+ any3d = !window.L_DISABLE_3D && (ie3d || webkit3d || gecko3d || opera3d);
+
var touch = !window.L_NO_TOUCH && (function () {
+
var startName = 'ontouchstart';
- // WebKit, etc
- if (startName in doc) {
+ // IE10+ (We simulate these into touch* events in L.DomEvent and L.DomEvent.MsTouch) or WebKit, etc.
+ if (msTouch || (startName in doc)) {
return true;
}
return supported;
}());
- var retina = (('devicePixelRatio' in window && window.devicePixelRatio > 1) || ('matchMedia' in window && window.matchMedia("(min-resolution:144dpi)").matches));
L.Browser = {
- ua: ua,
- ie: ie,
ie6: ie6,
+ ie7: ie7,
webkit: webkit,
- gecko: gecko,
- opera: opera,
+
android: android,
android23: android23,
webkit3d: webkit3d,
gecko3d: gecko3d,
opera3d: opera3d,
- any3d: !window.L_DISABLE_3D && (ie3d || webkit3d || gecko3d || opera3d),
+ any3d: any3d,
mobile: mobile,
mobileWebkit: mobile && webkit,
mobileWebkit3d: mobile && webkit3d,
- mobileOpera: mobile && opera,
+ mobileOpera: mobile && window.opera,
touch: touch,
+ msTouch: msTouch,
retina: retina
};
+
}());
value = el.currentStyle[style];
}
- if (!value || value === 'auto') {
+ if ((!value || value === 'auto') && document.defaultView) {
var css = document.defaultView.getComputedStyle(el, null);
value = css ? css[style] : null;
}
left = 0,
el = element,
docBody = document.body,
- pos;
+ pos,
+ ie7 = L.Browser.ie7;
do {
- top += el.offsetTop || 0;
+ top += el.offsetTop || 0;
left += el.offsetLeft || 0;
pos = L.DomUtil.getStyle(el, 'position');
top -= el.scrollTop || 0;
left -= el.scrollLeft || 0;
+ //Webkit (and ie <= 7) handles RTL scrollLeft different to everyone else
+ // https://code.google.com/p/closure-library/source/browse/trunk/closure/goog/style/bidi.js
+ if (!L.DomUtil.documentIsLtr() && (L.Browser.webkit || ie7)) {
+ left += el.scrollWidth - el.clientWidth;
+
+ //ie7 shows the scrollbar by default and provides clientWidth counting it, so we need to add it back in if it is visible
+ // Scrollbar is on the left as we are RTL
+ if (ie7 && L.DomUtil.getStyle(el, 'overflow-y') !== 'hidden' && L.DomUtil.getStyle(el, 'overflow') !== 'hidden') {
+ left += 17;
+ }
+ }
+
el = el.parentNode;
} while (el);
return new L.Point(left, top);
},
+ documentIsLtr: function () {
+ if (!L.DomUtil._docIsLtrCached) {
+ L.DomUtil._docIsLtrCached = true;
+ L.DomUtil._docIsLtr = L.DomUtil.getStyle(document.body, 'direction') === "ltr";
+ }
+ return L.DomUtil._docIsLtr;
+ },
+
create: function (tagName, className, container) {
var el = document.createElement(tagName);
if ('opacity' in el.style) {
el.style.opacity = value;
- } else if (L.Browser.ie) {
+ } else if ('filter' in el.style) {
var filter = false,
filterName = 'DXImageTransform.Microsoft.Alpha';
+L.CRS.Simple = L.Util.extend({}, L.CRS, {
+ projection: L.Projection.LonLat,
+ transformation: new L.Transformation(1, 0, 1, 0)
+});
+
+
+
L.CRS.EPSG3857 = L.Util.extend({}, L.CRS, {
code: 'EPSG:3857',
},
_redrawTile: function (tile) {
- this.drawTile(tile, tile._tilePoint, tile._zoom);
+ this.drawTile(tile, tile._tilePoint, this._map._zoom);
},
_createTileProto: function () {
return tile;
},
- _loadTile: function (tile, tilePoint, zoom) {
+ _loadTile: function (tile, tilePoint) {
tile._layer = this;
tile._tilePoint = tilePoint;
- tile._zoom = zoom;
-
- this.drawTile(tile, tilePoint, zoom);
+
+ this._redrawTile(tile);
if (!this.options.async) {
this.tileDrawn(tile);
}
},
- drawTile: function (tile, tilePoint, zoom) {
+ drawTile: function (tile, tilePoint) {
// override with rendering code
},
clickable: true,
draggable: false,
zIndexOffset: 0,
- opacity: 1
+ opacity: 1,
+ riseOnHover: false,
+ riseOffset: 250
},
initialize: function (latlng, options) {
needOpacityUpdate = (this.options.opacity < 1);
L.DomUtil.addClass(this._icon, classToAdd);
+
+ if (options.riseOnHover) {
+ L.DomEvent
+ .on(this._icon, 'mouseover', this._bringToFront, this)
+ .on(this._icon, 'mouseout', this._resetZIndex, this);
+ }
}
+
if (!this._shadow) {
this._shadow = options.icon.createShadow();
_removeIcon: function () {
var panes = this._map._panes;
+ if (this.options.riseOnHover) {
+ L.DomEvent
+ .off(this._icon, 'mouseover', this._bringToFront)
+ .off(this._icon, 'mouseout', this._resetZIndex);
+ }
+
panes.markerPane.removeChild(this._icon);
if (this._shadow) {
L.DomUtil.setPosition(this._shadow, pos);
}
- this._icon.style.zIndex = pos.y + this.options.zIndexOffset;
+ this._zIndex = pos.y + this.options.zIndexOffset;
+
+ this._resetZIndex();
+ },
+
+ _updateZIndex: function (offset) {
+ this._icon.style.zIndex = this._zIndex + offset;
},
_animateZoom: function (opt) {
if (this._shadow) {
L.DomUtil.setOpacity(this._shadow, this.options.opacity);
}
+ },
+
+ _bringToFront: function () {
+ this._updateZIndex(this.options.riseOffset);
+ },
+
+ _resetZIndex: function () {
+ this._updateZIndex(0);
}
});
},
_updatePosition: function () {
+ if (!this._map) { return; }
+
var pos = this._map.latLngToLayerPoint(this._latlng),
is3d = L.Browser.any3d,
offset = this.options.offset;
_animatePathZoom: function (opt) {
var scale = this.getZoomScale(opt.zoom),
- offset = this._getCenterOffset(opt.center).divideBy(1 - 1 / scale),
- viewportPos = this.containerPointToLayerPoint(this.getSize().multiplyBy(-L.Path.CLIP_PADDING)),
- origin = viewportPos.add(offset).round();
+ offset = this._getCenterOffset(opt.center),
+ translate = offset.multiplyBy(-scale)._add(this._pathViewport.min);
- this._pathRoot.style[L.DomUtil.TRANSFORM] = L.DomUtil.getTranslateString((origin.multiplyBy(-1).add(L.DomUtil.getPosition(this._pathRoot)).multiplyBy(scale).add(origin))) + ' scale(' + scale + ') ';
+ this._pathRoot.style[L.DomUtil.TRANSFORM] =
+ L.DomUtil.getTranslateString(translate) + ' scale(' + scale + ') ';
this._pathZooming = true;
},
return fn.call(context || obj, e || L.DomEvent._getEvent());
};
- if (L.Browser.touch && (type === 'dblclick') && this.addDoubleTapListener) {
+ if (L.Browser.msTouch && type.indexOf('touch') === 0) {
+ return this.addMsTouchListener(obj, type, handler, id);
+ } else if (L.Browser.touch && (type === 'dblclick') && this.addDoubleTapListener) {
return this.addDoubleTapListener(obj, handler, id);
} else if ('addEventListener' in obj) {
if (!handler) { return; }
- if (L.Browser.touch && (type === 'dblclick') && this.removeDoubleTapListener) {
+ if (L.Browser.msTouch && type.indexOf('touch') === 0) {
+ this.removeMsTouchListener(obj, type, id);
+ } else if (L.Browser.touch && (type === 'dblclick') && this.removeDoubleTapListener) {
this.removeDoubleTapListener(obj, id);
} else if ('removeEventListener' in obj) {
},
_onUp: function (e) {
+ var simulateClickTouch;
if (this._simulateClick && e.changedTouches) {
var first = e.changedTouches[0],
el = first.target,
}
if (dist < L.Draggable.TAP_TOLERANCE) {
- this._simulateEvent('click', first);
+ simulateClickTouch = first;
}
}
this.fire('dragend');
}
this._moving = false;
+
+ if (simulateClickTouch) {
+ this._moved = false;
+ this._simulateEvent('click', simulateClickTouch);
+ }
},
_setMovingCursor: function () {
*/
L.Map.mergeOptions({
- scrollWheelZoom: !L.Browser.touch
+ scrollWheelZoom: !L.Browser.touch || L.Browser.msTouch
});
L.Map.ScrollWheelZoom = L.Handler.extend({
L.Util.extend(L.DomEvent, {
+
+ _touchstart: L.Browser.msTouch ? 'MSPointerDown' : 'touchstart',
+ _touchend: L.Browser.msTouch ? 'MSPointerUp' : 'touchend',
+
// inspired by Zepto touch code by Thomas Fuchs
addDoubleTapListener: function (obj, handler, id) {
var last,
delay = 250,
touch,
pre = '_leaflet_',
- touchstart = 'touchstart',
- touchend = 'touchend';
+ touchstart = this._touchstart,
+ touchend = this._touchend,
+ trackedTouches = [];
function onTouchStart(e) {
- if (e.touches.length !== 1) {
+ var count;
+ if (L.Browser.msTouch) {
+ trackedTouches.push(e.pointerId);
+ count = trackedTouches.length;
+ } else {
+ count = e.touches.length;
+ }
+ if (count > 1) {
return;
}
var now = Date.now(),
delta = now - (last || now);
- touch = e.touches[0];
+ touch = e.touches ? e.touches[0] : e;
doubleTap = (delta > 0 && delta <= delay);
last = now;
}
function onTouchEnd(e) {
+ if (L.Browser.msTouch) {
+ var idx = trackedTouches.indexOf(e.pointerId);
+ if (idx === -1) {
+ return;
+ }
+ trackedTouches.splice(idx, 1);
+ }
+
if (doubleTap) {
+ if (L.Browser.msTouch) {
+ //Work around .type being readonly with MSPointer* events
+ var newTouch = { },
+ prop;
+ for (var i in touch) {
+ if (true) { //Make JSHint happy, we want to copy all properties
+ prop = touch[i];
+ if (typeof prop === 'function') {
+ newTouch[i] = prop.bind(touch);
+ } else {
+ newTouch[i] = prop;
+ }
+ }
+ }
+ touch = newTouch;
+ }
touch.type = 'dblclick';
handler(touch);
last = null;
obj[pre + touchstart + id] = onTouchStart;
obj[pre + touchend + id] = onTouchEnd;
+ //On msTouch we need to listen on the document otherwise a drag starting on the map and moving off screen will not come through to us
+ // so we will lose track of how many touches are ongoing
+ var endElement = L.Browser.msTouch ? document.documentElement : obj;
+
obj.addEventListener(touchstart, onTouchStart, false);
- obj.addEventListener(touchend, onTouchEnd, false);
+ endElement.addEventListener(touchend, onTouchEnd, false);
+ if (L.Browser.msTouch) {
+ endElement.addEventListener('MSPointerCancel', onTouchEnd, false);
+ }
return this;
},
removeDoubleTapListener: function (obj, id) {
var pre = '_leaflet_';
- obj.removeEventListener(obj, obj[pre + 'touchstart' + id], false);
- obj.removeEventListener(obj, obj[pre + 'touchend' + id], false);
+ obj.removeEventListener(this._touchstart, obj[pre + this._touchstart + id], false);
+ (L.Browser.msTouch ? document.documentElement : obj).removeEventListener(this._touchend, obj[pre + this._touchend + id], false);
+ if (L.Browser.msTouch) {
+ document.documentElement.removeEventListener('MSPointerCancel', obj[pre + this._touchend + id], false);
+ }
+ return this;
+ }
+});
+
+
+L.Util.extend(L.DomEvent, {
+
+ _msTouches: [],
+ _msDocumentListener: false,
+
+ // Provides a touch events wrapper for msPointer events.
+ // Based on changes by veproza https://github.com/CloudMade/Leaflet/pull/1019
+
+ addMsTouchListener: function (obj, type, handler, id) {
+
+ switch (type) {
+ case 'touchstart':
+ return this.addMsTouchListenerStart(obj, type, handler, id);
+ case 'touchend':
+ return this.addMsTouchListenerEnd(obj, type, handler, id);
+ case 'touchmove':
+ return this.addMsTouchListenerMove(obj, type, handler, id);
+ default:
+ throw 'Unknown touch event type';
+ }
+ },
+
+ addMsTouchListenerStart: function (obj, type, handler, id) {
+ var pre = '_leaflet_',
+ touches = this._msTouches;
+
+ var cb = function (e) {
+
+ var alreadyInArray = false;
+ for (var i = 0; i < touches.length; i++) {
+ if (touches[i].pointerId === e.pointerId) {
+ alreadyInArray = true;
+ break;
+ }
+ }
+ if (!alreadyInArray) {
+ touches.push(e);
+ }
+
+ e.touches = touches.slice();
+ e.changedTouches = [e];
+
+ handler(e);
+ };
+
+ obj[pre + 'touchstart' + id] = cb;
+ obj.addEventListener('MSPointerDown', cb, false);
+
+ //Need to also listen for end events to keep the _msTouches list accurate
+ //this needs to be on the body and never go away
+ if (!this._msDocumentListener) {
+ var internalCb = function (e) {
+ for (var i = 0; i < touches.length; i++) {
+ if (touches[i].pointerId === e.pointerId) {
+ touches.splice(i, 1);
+ break;
+ }
+ }
+ };
+ //We listen on the documentElement as any drags that end by moving the touch off the screen get fired there
+ document.documentElement.addEventListener('MSPointerUp', internalCb, false);
+ document.documentElement.addEventListener('MSPointerCancel', internalCb, false);
+
+ this._msDocumentListener = true;
+ }
+
+ return this;
+ },
+
+ addMsTouchListenerMove: function (obj, type, handler, id) {
+ var pre = '_leaflet_';
+
+ var touches = this._msTouches;
+ var cb = function (e) {
+
+ //Don't fire touch moves when mouse isn't down
+ if (e.pointerType === e.MSPOINTER_TYPE_MOUSE && e.buttons === 0) {
+ return;
+ }
+
+ for (var i = 0; i < touches.length; i++) {
+ if (touches[i].pointerId === e.pointerId) {
+ touches[i] = e;
+ break;
+ }
+ }
+
+ e.touches = touches.slice();
+ e.changedTouches = [e];
+
+ handler(e);
+ };
+
+ obj[pre + 'touchmove' + id] = cb;
+ obj.addEventListener('MSPointerMove', cb, false);
+
+ return this;
+ },
+
+ addMsTouchListenerEnd: function (obj, type, handler, id) {
+ var pre = '_leaflet_',
+ touches = this._msTouches;
+
+ var cb = function (e) {
+ for (var i = 0; i < touches.length; i++) {
+ if (touches[i].pointerId === e.pointerId) {
+ touches.splice(i, 1);
+ break;
+ }
+ }
+
+ e.touches = touches.slice();
+ e.changedTouches = [e];
+
+ handler(e);
+ };
+
+ obj[pre + 'touchend' + id] = cb;
+ obj.addEventListener('MSPointerUp', cb, false);
+ obj.addEventListener('MSPointerCancel', cb, false);
+
+ return this;
+ },
+
+ removeMsTouchListener: function (obj, type, id) {
+ var pre = '_leaflet_',
+ cb = obj[pre + type + id];
+
+ switch (type) {
+ case 'touchstart':
+ obj.removeEventListener('MSPointerDown', cb, false);
+ break;
+ case 'touchmove':
+ obj.removeEventListener('MSPointerMove', cb, false);
+ break;
+ case 'touchend':
+ obj.removeEventListener('MSPointerUp', cb, false);
+ obj.removeEventListener('MSPointerCancel', cb, false);
+ break;
+ }
+
return this;
}
});
L.DomUtil.setPosition(box, newPos);
// TODO refactor: remove hardcoded 4 pixels
- box.style.width = (Math.abs(offset.x) - 4) + 'px';
- box.style.height = (Math.abs(offset.y) - 4) + 'px';
+ box.style.width = (Math.max(0, Math.abs(offset.x) - 4)) + 'px';
+ box.style.height = (Math.max(0, Math.abs(offset.y) - 4)) + 'px';
},
_onMouseUp: function (e) {