import {Point} from '../geometry/Point'; import * as Util from '../core/Util'; import Browser from '../core/Browser'; import {addPointerListener, removePointerListener} from './DomEvent.Pointer'; import {addDoubleTapListener, removeDoubleTapListener} from './DomEvent.DoubleTap'; import {getScale} from './DomUtil'; /* * @namespace DomEvent * Utility functions to work with the [DOM events](https://developer.mozilla.org/docs/Web/API/Event), used by Leaflet internally. */ // Inspired by John Resig, Dean Edwards and YUI addEvent implementations. // @function on(el: HTMLElement, types: String, fn: Function, context?: Object): this // Adds a listener function (`fn`) to a particular DOM event type of the // element `el`. You can optionally specify the context of the listener // (object the `this` keyword will point to). You can also pass several // space-separated types (e.g. `'click dblclick'`). // @alternative // @function on(el: HTMLElement, eventMap: Object, context?: Object): this // Adds a set of type/listener pairs, e.g. `{click: onClick, mousemove: onMouseMove}` export function on(obj, types, fn, context) { if (types && typeof types === 'object') { for (var type in types) { addOne(obj, type, types[type], fn); } } else { types = Util.splitWords(types); for (var i = 0, len = types.length; i < len; i++) { addOne(obj, types[i], fn, context); } } return this; } var eventsKey = '_leaflet_events'; // @function off(el: HTMLElement, types: String, fn: Function, context?: Object): this // Removes a previously added listener function. // Note that if you passed a custom context to on, you must pass the same // context to `off` in order to remove the listener. // @alternative // @function off(el: HTMLElement, eventMap: Object, context?: Object): this // Removes a set of type/listener pairs, e.g. `{click: onClick, mousemove: onMouseMove}` // @alternative // @function off(el: HTMLElement, types: String): this // Removes all previously added listeners of given types. // @alternative // @function off(el: HTMLElement): this // Removes all previously added listeners from given HTMLElement export function off(obj, types, fn, context) { if (arguments.length === 1) { batchRemove(obj); delete obj[eventsKey]; } else if (types && typeof types === 'object') { for (var type in types) { removeOne(obj, type, types[type], fn); } } else { types = Util.splitWords(types); if (arguments.length === 2) { batchRemove(obj, function (type) { return Util.indexOf(types, type) !== -1; }); } else { for (var i = 0, len = types.length; i < len; i++) { removeOne(obj, types[i], fn, context); } } } return this; } function batchRemove(obj, filterFn) { for (var id in obj[eventsKey]) { var type = id.split(/\d/)[0]; if (!filterFn || filterFn(type)) { removeOne(obj, type, null, null, id); } } } var mouseSubst = { mouseenter: 'mouseover', mouseleave: 'mouseout', wheel: !('onwheel' in window) && 'mousewheel' }; function addOne(obj, type, fn, context) { var id = type + Util.stamp(fn) + (context ? '_' + Util.stamp(context) : ''); if (obj[eventsKey] && obj[eventsKey][id]) { return this; } var handler = function (e) { return fn.call(context || obj, e || window.event); }; var originalHandler = handler; if (!Browser.touchNative && Browser.pointer && type.indexOf('touch') === 0) { // Needs DomEvent.Pointer.js handler = addPointerListener(obj, type, handler); } else if (Browser.touch && (type === 'dblclick')) { handler = addDoubleTapListener(obj, handler); } else if ('addEventListener' in obj) { if (type === 'touchstart' || type === 'touchmove' || type === 'wheel' || type === 'mousewheel') { obj.addEventListener(mouseSubst[type] || type, handler, Browser.passiveEvents ? {passive: false} : false); } else if (type === 'mouseenter' || type === 'mouseleave') { handler = function (e) { e = e || window.event; if (isExternalTarget(obj, e)) { originalHandler(e); } }; obj.addEventListener(mouseSubst[type], handler, false); } else { obj.addEventListener(type, originalHandler, false); } } else { obj.attachEvent('on' + type, handler); } obj[eventsKey] = obj[eventsKey] || {}; obj[eventsKey][id] = handler; } function removeOne(obj, type, fn, context, id) { id = id || type + Util.stamp(fn) + (context ? '_' + Util.stamp(context) : ''); var handler = obj[eventsKey] && obj[eventsKey][id]; if (!handler) { return this; } if (!Browser.touchNative && Browser.pointer && type.indexOf('touch') === 0) { removePointerListener(obj, type, handler); } else if (Browser.touch && (type === 'dblclick')) { removeDoubleTapListener(obj, handler); } else if ('removeEventListener' in obj) { obj.removeEventListener(mouseSubst[type] || type, handler, false); } else { obj.detachEvent('on' + type, handler); } obj[eventsKey][id] = null; } // @function stopPropagation(ev: DOMEvent): this // Stop the given event from propagation to parent elements. Used inside the listener functions: // ```js // L.DomEvent.on(div, 'click', function (ev) { // L.DomEvent.stopPropagation(ev); // }); // ``` export function stopPropagation(e) { if (e.stopPropagation) { e.stopPropagation(); } else if (e.originalEvent) { // In case of Leaflet event. e.originalEvent._stopped = true; } else { e.cancelBubble = true; } return this; } // @function disableScrollPropagation(el: HTMLElement): this // Adds `stopPropagation` to the element's `'wheel'` events (plus browser variants). export function disableScrollPropagation(el) { addOne(el, 'wheel', stopPropagation); return this; } // @function disableClickPropagation(el: HTMLElement): this // Adds `stopPropagation` to the element's `'click'`, `'dblclick'`, `'contextmenu'`, // `'mousedown'` and `'touchstart'` events (plus browser variants). export function disableClickPropagation(el) { on(el, 'mousedown touchstart dblclick contextmenu', stopPropagation); el['_leaflet_disable_click'] = true; return this; } // @function preventDefault(ev: DOMEvent): this // Prevents the default action of the DOM Event `ev` from happening (such as // following a link in the href of the a element, or doing a POST request // with page reload when a `