UNPKG

leaflet-event-forwarder

Version:

Catches unhandled canvas layer events and re-dispatches them to the next layer in the stack

168 lines (139 loc) 5.11 kB
(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('leaflet')) : typeof define === 'function' && define.amd ? define(['leaflet'], factory) : (global['leaflet-event-forwarder'] = factory(global.L)); }(this, (function (L) { 'use strict'; L = L && L.hasOwnProperty('default') ? L['default'] : L; let _options; let _prevTarget = null; const EventForwarder = L.Class.extend({ initialize: function (options) { _options = options; if (!_options.containerName) _options.containerName = 'overlay'; }, enable: function () { if (_options.events.click === true) L.DomEvent.on(_options.map, 'click', this._handleClick, this); if (_options.events.mousemove === true) { L.DomEvent.on(_options.map, 'mousemove', this._throttle(this._handleMouseMove, _options.throttleMs, _options.throttleOptions), this); } }, disable: function () { L.DomEvent.off(_options.map, 'click', this._handleClick, this); }, /** * Handle `mousemove` event from map, i.e. forwards unhandled events * @param event * @private */ _handleMouseMove: function (event) { // we use the maps mousemove event to avoid registering listeners // for each individual layer, however this means we don't receive // the layers mouseover/out events so we need to fudge it a little if (event.originalEvent._stopped) { return; } // get the target pane var currentTarget = event.originalEvent.target; var stopped; var removed; // hide the target node removed = { node: currentTarget, pointerEvents: currentTarget.style.pointerEvents }; currentTarget.style.pointerEvents = 'none'; // attempt to grab the next layer below const nextTarget = document.elementFromPoint(event.originalEvent.clientX, event.originalEvent.clientY); const isCanvas = nextTarget.nodeName.toLowerCase() === 'canvas'; // target has changed so trigger mouseout previous if (_prevTarget && _prevTarget != nextTarget) { _prevTarget.dispatchEvent(new MouseEvent('mouseout', event.originalEvent)); } _prevTarget = nextTarget; // we keep drilling down until we get stopped, // or we reach the map container itself if (nextTarget && nextTarget.nodeName.toLowerCase() !== 'body' && nextTarget.classList.value.indexOf('leaflet-container') === -1) { let eventType = isCanvas ? 'mousemove' : 'mouseover'; var ev = new MouseEvent(eventType, event.originalEvent); stopped = !nextTarget.dispatchEvent(ev); if (stopped || ev._stopped) { L.DomEvent.stop(event); } } // restore pointerEvents removed.node.style.pointerEvents = removed.pointerEvents; }, /** * Handle `click` event from map, i.e. forwards unhandled events * @param event * @private */ _handleClick: function (event) { if (event.originalEvent._stopped) { return; } // get the target pane var currentTarget = event.originalEvent.target; var stopped; var removed; // hide the target node removed = { node: currentTarget, pointerEvents: currentTarget.style.pointerEvents }; currentTarget.style.pointerEvents = 'none'; // attempt to grab the next layer below let nextTarget = document.elementFromPoint(event.originalEvent.clientX, event.originalEvent.clientY); // we keep drilling down until we get stopped, // or we reach the map container itself if (nextTarget && nextTarget.nodeName.toLowerCase() !== 'body' && nextTarget.classList.value.indexOf('leaflet-container') === -1) { var ev = new MouseEvent(event.originalEvent.type, event.originalEvent); stopped = !nextTarget.dispatchEvent(ev); if (stopped || ev._stopped) { L.DomEvent.stop(event); } } // restore pointerEvents removed.node.style.pointerEvents = removed.pointerEvents; }, /** * Pinched from underscore * @param func * @param wait * @param options * @returns {Function} * @private */ _throttle: function (func, wait, options) { var context, args, result; var timeout = null; var previous = 0; if (!options) options = {}; var later = function () { previous = options.leading === false ? 0 : Date.now(); timeout = null; result = func.apply(context, args); if (!timeout) context = args = null; }; return function () { var now = Date.now(); if (!previous && options.leading === false) previous = now; var remaining = wait - (now - previous); context = this; args = arguments; if (remaining <= 0 || remaining > wait) { if (timeout) { clearTimeout(timeout); timeout = null; } previous = now; result = func.apply(context, args); if (!timeout) context = args = null; } else if (!timeout && options.trailing !== false) { timeout = setTimeout(later, remaining); } return result; }; } }); L.eventForwarder = function (options) { return new EventForwarder(options); }; var L_EventForwarder = L.eventForwarder; return L_EventForwarder; }))); //# sourceMappingURL=leaflet-event-forwarder.js.map