UNPKG

devextreme

Version:

HTML5 JavaScript Component Suite for Responsive Web Development

562 lines (544 loc) • 22.8 kB
/** * DevExtreme (cjs/__internal/events/core/m_events_engine.js) * Version: 24.2.6 * Build date: Mon Mar 17 2025 * * Copyright (c) 2012 - 2025 Developer Express Inc. ALL RIGHTS RESERVED * Read about DevExtreme licensing here: https://js.devexpress.com/Licensing/ */ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; var _event_registrator_callbacks = _interopRequireDefault(require("../../../common/core/events/core/event_registrator_callbacks")); var _hook_touch_props = _interopRequireDefault(require("../../../common/core/events/core/hook_touch_props")); var _event_target = require("../../../common/core/events/utils/event_target"); var _dom_adapter = _interopRequireDefault(require("../../../core/dom_adapter")); var _errors = _interopRequireDefault(require("../../../core/errors")); var _call_once = _interopRequireDefault(require("../../../core/utils/call_once")); var _callbacks = _interopRequireDefault(require("../../../core/utils/callbacks")); var _dependency_injector = _interopRequireDefault(require("../../../core/utils/dependency_injector")); var _extend = require("../../../core/utils/extend"); var _type = require("../../../core/utils/type"); var _window = require("../../../core/utils/window"); function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e } } const window = (0, _window.getWindow)(); const EMPTY_EVENT_NAME = "dxEmptyEventType"; const NATIVE_EVENTS_TO_SUBSCRIBE = { mouseenter: "mouseover", mouseleave: "mouseout", pointerenter: "pointerover", pointerleave: "pointerout" }; const NATIVE_EVENTS_TO_TRIGGER = { focusin: "focus", focusout: "blur" }; const NO_BUBBLE_EVENTS = ["blur", "focus", "load"]; const forcePassiveFalseEventNames = ["touchmove", "wheel", "mousewheel", "touchstart"]; const EVENT_PROPERTIES = ["target", "relatedTarget", "delegateTarget", "altKey", "bubbles", "cancelable", "changedTouches", "ctrlKey", "detail", "eventPhase", "metaKey", "shiftKey", "view", "char", "code", "charCode", "key", "keyCode", "button", "buttons", "offsetX", "offsetY", "pointerId", "pointerType", "targetTouches", "toElement", "touches"]; function matchesSafe(target, selector) { return !(0, _type.isWindow)(target) && "#document" !== target.nodeName && _dom_adapter.default.elementMatches(target, selector) } const elementDataMap = new WeakMap; let guid = 0; let skipEvent; const special = function() { const specialData = {}; _event_registrator_callbacks.default.add(((eventName, eventObject) => { specialData[eventName] = eventObject })); return { getField: (eventName, field) => specialData[eventName] && specialData[eventName][field], callMethod: (eventName, methodName, context, args) => specialData[eventName] && specialData[eventName][methodName] && specialData[eventName][methodName].apply(context, args) } }(); const eventsEngine = (0, _dependency_injector.default)({ on: getHandler(normalizeOnArguments(iterate(((element, eventName, selector, data, handler) => { const handlersController = getHandlersController(element, eventName); handlersController.addHandler(handler, selector, data) })))), one: getHandler(normalizeOnArguments(((element, eventName, selector, data, handler) => { const oneTimeHandler = function() { eventsEngine.off(element, eventName, selector, oneTimeHandler); handler.apply(this, arguments) }; eventsEngine.on(element, eventName, selector, data, oneTimeHandler) }))), off: getHandler(normalizeOffArguments(iterate(((element, eventName, selector, handler) => { const handlersController = getHandlersController(element, eventName); handlersController.removeHandler(handler, selector) })))), trigger: getHandler(normalizeTriggerArguments(((element, event, extraParameters) => { const eventName = event.type; const handlersController = getHandlersController(element, event.type); special.callMethod(eventName, "trigger", element, [event, extraParameters]); handlersController.callHandlers(event, extraParameters); const noBubble = special.getField(eventName, "noBubble") || event.isPropagationStopped() || NO_BUBBLE_EVENTS.includes(eventName); if (!noBubble) { const parents = []; const getParents = function(element) { const parent = element.parentNode ?? ((0, _type.isObject)(element.host) ? element.host : null); if (parent) { parents.push(parent); getParents(parent) } }; getParents(element); parents.push(window); let i = 0; while (parents[i] && !event.isPropagationStopped()) { const parentDataByEvent = getHandlersController(parents[i], event.type); parentDataByEvent.callHandlers((0, _extend.extend)(event, { currentTarget: parents[i] }), extraParameters); i++ } } if (element.nodeType || (0, _type.isWindow)(element)) { special.callMethod(eventName, "_default", element, [event, extraParameters]); callNativeMethod(eventName, element) } }))), triggerHandler: getHandler(normalizeTriggerArguments(((element, event, extraParameters) => { const handlersController = getHandlersController(element, event.type); handlersController.callHandlers(event, extraParameters) }))) }); function applyForEach(args, method) { const element = args[0]; if (!element) { return } if (_dom_adapter.default.isNode(element) || (0, _type.isWindow)(element)) { method.apply(eventsEngine, args) } else if (!(0, _type.isString)(element) && "length" in element) { const itemArgs = Array.prototype.slice.call(args, 0); Array.prototype.forEach.call(element, (itemElement => { itemArgs[0] = itemElement; applyForEach(itemArgs, method) })) } else { throw _errors.default.Error("E0025") } } function getHandler(method) { return function() { applyForEach(arguments, method) } } function detectPassiveEventHandlersSupport() { let isSupported = false; try { const options = Object.defineProperty({}, "passive", { get() { isSupported = true; return true } }); window.addEventListener("test", null, options) } catch (e) {} return isSupported } const passiveEventHandlersSupported = (0, _call_once.default)(detectPassiveEventHandlersSupport); const contains = (container, element) => { if ((0, _type.isWindow)(container)) { return contains(container.document, element) } return container.contains ? container.contains(element) : !!(element.compareDocumentPosition(container) & element.DOCUMENT_POSITION_CONTAINS) }; function getHandlersController(element, eventName) { let elementData = elementDataMap.get(element); eventName = eventName || ""; const eventNameParts = eventName.split("."); const namespaces = eventNameParts.slice(1); const eventNameIsDefined = !!eventNameParts[0]; eventName = eventNameParts[0] || EMPTY_EVENT_NAME; if (!elementData) { elementData = {}; elementDataMap.set(element, elementData) } if (!elementData[eventName]) { elementData[eventName] = { handleObjects: [], nativeHandler: null } } const eventData = elementData[eventName]; return { addHandler(handler, selector, data) { const callHandler = function(e, extraParameters) { const handlerArgs = [e]; const target = e.currentTarget; const { relatedTarget: relatedTarget } = e; let secondaryTargetIsInside; let result; if (eventName in NATIVE_EVENTS_TO_SUBSCRIBE) { secondaryTargetIsInside = relatedTarget && target && (relatedTarget === target || contains(target, relatedTarget)) } if (void 0 !== extraParameters) { handlerArgs.push(extraParameters) } special.callMethod(eventName, "handle", element, [e, data]); if (!secondaryTargetIsInside) { result = handler.apply(target, handlerArgs) } if (false === result) { e.preventDefault(); e.stopPropagation() } }; const handleObject = { handler: handler, wrappedHandler: function(e, extraParameters) { if (skipEvent && e.type === skipEvent) { return } e.data = data; e.delegateTarget = element; if (selector) { let currentTarget = e.target; while (currentTarget && currentTarget !== element) { if (matchesSafe(currentTarget, selector)) { e.currentTarget = currentTarget; callHandler(e, extraParameters) } currentTarget = currentTarget.parentNode } } else { var _e$target; e.currentTarget = e.delegateTarget || e.target; const isTargetInShadowDOM = Boolean(null === (_e$target = e.target) || void 0 === _e$target ? void 0 : _e$target.shadowRoot); if (isTargetInShadowDOM) { const target = (0, _event_target.getEventTarget)(e); e.target = target } callHandler(e, extraParameters) } }, selector: selector, type: eventName, data: data, namespace: namespaces.join("."), namespaces: namespaces, guid: ++guid }; eventData.handleObjects.push(handleObject); const firstHandlerForTheType = 1 === eventData.handleObjects.length; let shouldAddNativeListener = firstHandlerForTheType && eventNameIsDefined; let nativeListenerOptions; if (shouldAddNativeListener) { shouldAddNativeListener = !special.callMethod(eventName, "setup", element, [data, namespaces, handler]) } if (shouldAddNativeListener) { eventData.nativeHandler = getNativeHandler(eventName); if (passiveEventHandlersSupported() && forcePassiveFalseEventNames.includes(eventName)) { nativeListenerOptions = { passive: false } } eventData.removeListener = _dom_adapter.default.listen(element, NATIVE_EVENTS_TO_SUBSCRIBE[eventName] || eventName, eventData.nativeHandler, nativeListenerOptions) } special.callMethod(eventName, "add", element, [handleObject]) }, removeHandler(handler, selector) { const removeByEventName = function(eventName) { const eventData = elementData[eventName]; if (!eventData.handleObjects.length) { delete elementData[eventName]; return } let removedHandler; eventData.handleObjects = eventData.handleObjects.filter((handleObject => { const skip = namespaces.length && !isSubset(handleObject.namespaces, namespaces) || handler && handleObject.handler !== handler || selector && handleObject.selector !== selector; if (!skip) { removedHandler = handleObject.handler; special.callMethod(eventName, "remove", element, [handleObject]) } return skip })); const lastHandlerForTheType = !eventData.handleObjects.length; const shouldRemoveNativeListener = lastHandlerForTheType && eventName !== EMPTY_EVENT_NAME; if (shouldRemoveNativeListener) { special.callMethod(eventName, "teardown", element, [namespaces, removedHandler]); if (eventData.nativeHandler) { eventData.removeListener() } delete elementData[eventName] } }; if (eventNameIsDefined) { removeByEventName(eventName) } else { for (const name in elementData) { removeByEventName(name) } } const elementDataIsEmpty = 0 === Object.keys(elementData).length; if (elementDataIsEmpty) { elementDataMap.delete(element) } }, callHandlers(event, extraParameters) { let forceStop = false; const handleCallback = function(handleObject) { if (forceStop) { return } if (!namespaces.length || isSubset(handleObject.namespaces, namespaces)) { handleObject.wrappedHandler(event, extraParameters); forceStop = event.isImmediatePropagationStopped() } }; eventData.handleObjects.forEach(handleCallback); if (namespaces.length && elementData[EMPTY_EVENT_NAME]) { elementData[EMPTY_EVENT_NAME].handleObjects.forEach(handleCallback) } } } } function getNativeHandler(subscribeName) { return function(event, extraParameters) { const handlersController = getHandlersController(this, subscribeName); event = eventsEngine.Event(event); handlersController.callHandlers(event, extraParameters) } } function isSubset(original, checked) { for (let i = 0; i < checked.length; i++) { if (original.indexOf(checked[i]) < 0) { return false } } return true } function normalizeOnArguments(callback) { return function(element, eventName, selector, data, handler) { if (!handler) { handler = data; data = void 0 } if ("string" !== typeof selector) { data = selector; selector = void 0 } if (!handler && "string" === typeof eventName) { handler = data || selector; selector = void 0; data = void 0 } callback(element, eventName, selector, data, handler) } } function normalizeOffArguments(callback) { return function(element, eventName, selector, handler) { if ("function" === typeof selector) { handler = selector; selector = void 0 } callback(element, eventName, selector, handler) } } function normalizeTriggerArguments(callback) { return function(element, src, extraParameters) { if ("string" === typeof src) { src = { type: src } } if (!src.target) { src.target = element } src.currentTarget = element; if (!src.delegateTarget) { src.delegateTarget = element } if (!src.type && src.originalEvent) { src.type = src.originalEvent.type } callback(element, src instanceof eventsEngine.Event ? src : eventsEngine.Event(src), extraParameters) } } function normalizeEventArguments(callback) { eventsEngine.Event = function(src, config) { if (!(this instanceof eventsEngine.Event)) { return new eventsEngine.Event(src, config) } if (!src) { src = {} } if ("string" === typeof src) { src = { type: src } } if (!config) { config = {} } callback.call(this, src, config) }; Object.assign(eventsEngine.Event.prototype, { _propagationStopped: false, _immediatePropagationStopped: false, _defaultPrevented: false, isPropagationStopped() { return !!(this._propagationStopped || this.originalEvent && this.originalEvent.propagationStopped) }, stopPropagation() { this._propagationStopped = true; this.originalEvent && this.originalEvent.stopPropagation() }, isImmediatePropagationStopped() { return this._immediatePropagationStopped }, stopImmediatePropagation() { this.stopPropagation(); this._immediatePropagationStopped = true; this.originalEvent && this.originalEvent.stopImmediatePropagation() }, isDefaultPrevented() { return !!(this._defaultPrevented || this.originalEvent && this.originalEvent.defaultPrevented) }, preventDefault() { this._defaultPrevented = true; this.originalEvent && this.originalEvent.preventDefault() } }); return eventsEngine.Event } function iterate(callback) { const iterateEventNames = function(element, eventName) { if (eventName && eventName.indexOf(" ") > -1) { const args = Array.prototype.slice.call(arguments, 0); eventName.split(" ").forEach((function(eventName) { args[1] = eventName; callback.apply(this, args) })) } else { callback.apply(this, arguments) } }; return function(element, eventName) { if ("object" === typeof eventName) { const args = Array.prototype.slice.call(arguments, 0); for (const name in eventName) { args[1] = name; args[args.length - 1] = eventName[name]; iterateEventNames.apply(this, args) } } else { iterateEventNames.apply(this, arguments) } } } function callNativeMethod(eventName, element) { const nativeMethodName = NATIVE_EVENTS_TO_TRIGGER[eventName] || eventName; if (function(eventName, element) { return "click" === eventName && "a" === element.localName }(eventName, element)) { return } if ((0, _type.isFunction)(element[nativeMethodName])) { skipEvent = eventName; element[nativeMethodName](); skipEvent = void 0 } } function calculateWhich(event) { if (function(event) { return null == event.which && 0 === event.type.indexOf("key") }(event)) { return null != event.charCode ? event.charCode : event.keyCode } if (function(event) { return !event.which && void 0 !== event.button && /^(?:mouse|pointer|contextmenu|drag|drop)|click/.test(event.type) }(event)) { const whichByButton = { 1: 1, 2: 3, 3: 1, 4: 2 }; return whichByButton[event.button] } return event.which } function initEvent(EventClass) { if (EventClass) { eventsEngine.Event = EventClass; eventsEngine.Event.prototype = EventClass.prototype } } initEvent(normalizeEventArguments((function(src, config) { var _src$view; const srcIsEvent = src instanceof eventsEngine.Event || (0, _window.hasWindow)() && src instanceof window.Event || (null === (_src$view = src.view) || void 0 === _src$view ? void 0 : _src$view.Event) && src instanceof src.view.Event; if (srcIsEvent) { this.originalEvent = src; this.type = src.type; this.currentTarget = void 0; if (Object.prototype.hasOwnProperty.call(src, "isTrusted")) { this.isTrusted = src.isTrusted } this.timeStamp = src.timeStamp || Date.now() } else { Object.assign(this, src) } addProperty("which", calculateWhich, this); if (0 === src.type.indexOf("touch")) { delete config.pageX; delete config.pageY } Object.assign(this, config); this.guid = ++guid }))); function addProperty(propName, hook, eventInstance) { Object.defineProperty(eventInstance || eventsEngine.Event.prototype, propName, { enumerable: true, configurable: true, get() { return this.originalEvent && hook(this.originalEvent) }, set(value) { Object.defineProperty(this, propName, { enumerable: true, configurable: true, writable: true, value: value }) } }) } EVENT_PROPERTIES.forEach((prop => addProperty(prop, (event => event[prop])))); (0, _hook_touch_props.default)(addProperty); const beforeSetStrategy = (0, _callbacks.default)(); const afterSetStrategy = (0, _callbacks.default)(); eventsEngine.set = function(engine) { beforeSetStrategy.fire(); eventsEngine.inject(engine); initEvent(engine.Event); afterSetStrategy.fire() }; eventsEngine.subscribeGlobal = function() { applyForEach(arguments, normalizeOnArguments((function() { const args = arguments; eventsEngine.on.apply(this, args); beforeSetStrategy.add((function() { const offArgs = Array.prototype.slice.call(args, 0); offArgs.splice(3, 1); eventsEngine.off.apply(this, offArgs) })); afterSetStrategy.add((function() { eventsEngine.on.apply(this, args) })) }))) }; eventsEngine.forcePassiveFalseEventNames = forcePassiveFalseEventNames; eventsEngine.passiveEventHandlersSupported = passiveEventHandlersSupported; var _default = exports.default = eventsEngine;