devextreme
Version:
HTML5 JavaScript Component Suite for Responsive Web Development
598 lines (486 loc) • 20.2 kB
JavaScript
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
var registerEventCallbacks = require("./event_registrator_callbacks");
var extend = require("../../core/utils/extend").extend;
var domAdapter = require("../../core/dom_adapter");
var windowUtils = require("../../core/utils/window");
var window = windowUtils.getWindow();
var injector = require("../../core/utils/dependency_injector");
var typeUtils = require("../../core/utils/type");
var Callbacks = require("../../core/utils/callbacks");
var isWindow = typeUtils.isWindow;
var isFunction = typeUtils.isFunction;
var isString = typeUtils.isString;
var errors = require("../../core/errors");
var WeakMap = require("../../core/polyfills/weak_map");
var hookTouchProps = require("../../events/core/hook_touch_props");
var EMPTY_EVENT_NAME = "dxEmptyEventType";
var NATIVE_EVENTS_TO_SUBSCRIBE = {
"mouseenter": "mouseover",
"mouseleave": "mouseout",
"pointerenter": "pointerover",
"pointerleave": "pointerout"
};
var NATIVE_EVENTS_TO_TRIGGER = {
"focusin": "focus",
"focusout": "blur"
};
var NO_BUBBLE_EVENTS = ["blur", "focusout", "focus", "focusin", "load"];
var matchesSafe = function matchesSafe(target, selector) {
return !isWindow(target) && target.nodeName !== "#document" && domAdapter.elementMatches(target, selector);
};
var elementDataMap = new WeakMap();
var guid = 0;
var skipEvent;
var special = function () {
var specialData = {};
registerEventCallbacks.add(function (eventName, eventObject) {
specialData[eventName] = eventObject;
});
return {
getField: function getField(eventName, field) {
return specialData[eventName] && specialData[eventName][field];
},
callMethod: function callMethod(eventName, methodName, context, args) {
return specialData[eventName] && specialData[eventName][methodName] && specialData[eventName][methodName].apply(context, args);
}
};
}();
var applyForEach = function applyForEach(args, method) {
var element = args[0];
if (!element) {
return;
}
if (domAdapter.isNode(element) || isWindow(element)) {
method.apply(eventsEngine, args);
} else if (!isString(element) && "length" in element) {
var itemArgs = Array.prototype.slice.call(args, 0);
Array.prototype.forEach.call(element, function (itemElement) {
itemArgs[0] = itemElement;
applyForEach(itemArgs, method);
});
} else {
throw errors.Error("E0025");
}
};
var getHandler = function getHandler(method) {
return function () {
applyForEach(arguments, method);
};
};
var getHandlersController = function getHandlersController(element, eventName) {
var elementData = elementDataMap.get(element);
eventName = eventName || "";
var eventNameParts = eventName.split(".");
var namespaces = eventNameParts.slice(1);
var eventNameIsDefined = !!eventNameParts[0];
eventName = eventNameParts[0] || EMPTY_EVENT_NAME;
if (!elementData) {
elementData = {};
elementDataMap.set(element, elementData);
}
if (!elementData[eventName]) {
elementData[eventName] = {
handleObjects: [],
nativeHandler: null
};
}
var eventData = elementData[eventName];
return {
addHandler: function addHandler(handler, selector, data) {
var callHandler = function callHandler(e, extraParameters) {
var handlerArgs = [e],
target = e.currentTarget,
relatedTarget = e.relatedTarget,
secondaryTargetIsInside,
result;
if (eventName in NATIVE_EVENTS_TO_SUBSCRIBE) {
secondaryTargetIsInside = relatedTarget && target && (relatedTarget === target || target.contains(relatedTarget));
}
if (extraParameters !== undefined) {
handlerArgs.push(extraParameters);
}
special.callMethod(eventName, "handle", element, [e, data]);
if (!secondaryTargetIsInside) {
result = handler.apply(target, handlerArgs);
}
if (result === false) {
e.preventDefault();
e.stopPropagation();
}
};
var wrappedHandler = function wrappedHandler(e, extraParameters) {
if (skipEvent && e.type === skipEvent) {
return;
}
e.data = data;
e.delegateTarget = element;
if (selector) {
var currentTarget = e.target;
while (currentTarget && currentTarget !== element) {
if (matchesSafe(currentTarget, selector)) {
e.currentTarget = currentTarget;
callHandler(e, extraParameters);
}
currentTarget = currentTarget.parentNode;
}
} else {
callHandler(e, extraParameters);
}
};
var handleObject = {
handler: handler,
wrappedHandler: wrappedHandler,
selector: selector,
type: eventName,
data: data,
namespace: namespaces.join("."),
namespaces: namespaces,
guid: ++guid
};
eventData.handleObjects.push(handleObject);
var firstHandlerForTheType = eventData.handleObjects.length === 1;
var shouldAddNativeListener = firstHandlerForTheType && eventNameIsDefined;
var nativeHandler = getNativeHandler(eventName);
if (shouldAddNativeListener) {
shouldAddNativeListener = !special.callMethod(eventName, "setup", element, [data, namespaces, nativeHandler]);
}
if (shouldAddNativeListener) {
eventData.nativeHandler = nativeHandler;
eventData.removeListener = domAdapter.listen(element, NATIVE_EVENTS_TO_SUBSCRIBE[eventName] || eventName, eventData.nativeHandler);
}
special.callMethod(eventName, "add", element, [handleObject]);
},
removeHandler: function removeHandler(handler, selector) {
var removeByEventName = function removeByEventName(eventName) {
var eventData = elementData[eventName];
if (!eventData.handleObjects.length) {
delete elementData[eventName];
return;
}
var removedHandler;
eventData.handleObjects = eventData.handleObjects.filter(function (handleObject) {
var 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;
});
var lastHandlerForTheType = !eventData.handleObjects.length;
var 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 (var name in elementData) {
removeByEventName(name);
}
}
var elementDataIsEmpty = Object.keys(elementData).length === 0;
if (elementDataIsEmpty) {
elementDataMap.delete(element);
}
},
callHandlers: function callHandlers(event, extraParameters) {
var forceStop = false;
var handleCallback = function handleCallback(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);
}
}
};
};
var getNativeHandler = function getNativeHandler(subscribeName) {
return function (event, extraParameters) {
var handlersController = getHandlersController(this, subscribeName);
event = eventsEngine.Event(event);
handlersController.callHandlers(event, extraParameters);
};
};
var isSubset = function isSubset(original, checked) {
for (var i = 0; i < checked.length; i++) {
if (original.indexOf(checked[i]) < 0) return false;
}
return true;
};
var normalizeOnArguments = function normalizeOnArguments(callback) {
return function (element, eventName, selector, data, handler) {
if (!handler) {
handler = data;
data = undefined;
}
if (typeof selector !== "string") {
data = selector;
selector = undefined;
}
if (!handler && typeof eventName === "string") {
handler = data || selector;
selector = undefined;
data = undefined;
}
callback(element, eventName, selector, data, handler);
};
};
var normalizeOffArguments = function normalizeOffArguments(callback) {
return function (element, eventName, selector, handler) {
if (typeof selector === "function") {
handler = selector;
selector = undefined;
}
callback(element, eventName, selector, handler);
};
};
var normalizeTriggerArguments = function normalizeTriggerArguments(callback) {
return function (element, src, extraParameters) {
if (typeof src === "string") {
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);
};
};
var normalizeEventArguments = function normalizeEventArguments(callback) {
return function (src, config) {
if (!(this instanceof eventsEngine.Event)) {
return new eventsEngine.Event(src, config);
}
if (!src) {
src = {};
}
if (typeof src === "string") {
src = {
type: src
};
}
if (!config) {
config = {};
}
callback.call(this, src, config);
};
};
var iterate = function iterate(callback) {
var iterateEventNames = function iterateEventNames(element, eventName) {
if (eventName && eventName.indexOf(" ") > -1) {
var 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 ((typeof eventName === "undefined" ? "undefined" : _typeof(eventName)) === "object") {
var args = Array.prototype.slice.call(arguments, 0);
for (var name in eventName) {
args[1] = name;
args[args.length - 1] = eventName[name];
iterateEventNames.apply(this, args);
}
} else {
iterateEventNames.apply(this, arguments);
}
};
};
var callNativeMethod = function callNativeMethod(eventName, element) {
var nativeMethodName = NATIVE_EVENTS_TO_TRIGGER[eventName] || eventName;
var isLinkClickEvent = function isLinkClickEvent(eventName, element) {
return eventName === "click" && element.localName === "a";
};
if (isLinkClickEvent(eventName, element)) return;
if (isFunction(element[nativeMethodName])) {
skipEvent = eventName;
element[nativeMethodName]();
skipEvent = undefined;
}
};
var calculateWhich = function calculateWhich(event) {
var setForMouseEvent = function setForMouseEvent(event) {
var mouseEventRegex = /^(?:mouse|pointer|contextmenu|drag|drop)|click/;
return !event.which && event.button !== undefined && mouseEventRegex.test(event.type);
};
var setForKeyEvent = function setForKeyEvent(event) {
return event.which == null && event.type.indexOf("key") === 0;
};
if (setForKeyEvent(event)) {
return event.charCode != null ? event.charCode : event.keyCode;
}
if (setForMouseEvent(event)) {
var whichByButton = { 1: 1, 2: 3, 3: 1, 4: 2 };
return whichByButton[event.button];
}
return event.which;
};
var eventsEngine = injector({
on: getHandler(normalizeOnArguments(iterate(function (element, eventName, selector, data, handler) {
var handlersController = getHandlersController(element, eventName);
handlersController.addHandler(handler, selector, data);
}))),
one: getHandler(normalizeOnArguments(function (element, eventName, selector, data, handler) {
var oneTimeHandler = function oneTimeHandler() {
eventsEngine.off(element, eventName, selector, oneTimeHandler);
handler.apply(this, arguments);
};
eventsEngine.on(element, eventName, selector, data, oneTimeHandler);
})),
off: getHandler(normalizeOffArguments(iterate(function (element, eventName, selector, handler) {
var handlersController = getHandlersController(element, eventName);
handlersController.removeHandler(handler, selector);
}))),
trigger: getHandler(normalizeTriggerArguments(function (element, event, extraParameters) {
var eventName = event.type;
var handlersController = getHandlersController(element, event.type);
special.callMethod(eventName, "trigger", element, [event, extraParameters]);
handlersController.callHandlers(event, extraParameters);
var noBubble = special.getField(eventName, "noBubble") || event.isPropagationStopped() || NO_BUBBLE_EVENTS.indexOf(eventName) !== -1;
if (!noBubble) {
var parents = [];
var getParents = function getParents(element) {
var parent = element.parentNode;
if (parent) {
parents.push(parent);
getParents(parent);
}
};
getParents(element);
parents.push(window);
var i = 0;
while (parents[i] && !event.isPropagationStopped()) {
var parentDataByEvent = getHandlersController(parents[i], event.type);
parentDataByEvent.callHandlers(extend(event, { currentTarget: parents[i] }), extraParameters);
i++;
}
}
if (element.nodeType || isWindow(element)) {
special.callMethod(eventName, "_default", element, [event, extraParameters]);
callNativeMethod(eventName, element);
}
})),
triggerHandler: getHandler(normalizeTriggerArguments(function (element, event, extraParameters) {
var handlersController = getHandlersController(element, event.type);
handlersController.callHandlers(event, extraParameters);
}))
});
var initEvent = function initEvent(EventClass) {
if (EventClass) {
eventsEngine.Event = EventClass;
eventsEngine.Event.prototype = EventClass.prototype;
}
};
initEvent(normalizeEventArguments(function (src, config) {
var that = this;
var propagationStopped = false;
var immediatePropagationStopped = false;
var defaultPrevented = false;
extend(that, src);
if (src instanceof eventsEngine.Event || windowUtils.hasWindow() && src instanceof window.Event) {
that.originalEvent = src;
that.currentTarget = undefined;
}
if (!(src instanceof eventsEngine.Event)) {
extend(that, {
isPropagationStopped: function isPropagationStopped() {
return !!(propagationStopped || that.originalEvent && that.originalEvent.propagationStopped);
},
stopPropagation: function stopPropagation() {
propagationStopped = true;
that.originalEvent && that.originalEvent.stopPropagation();
},
isImmediatePropagationStopped: function isImmediatePropagationStopped() {
return immediatePropagationStopped;
},
stopImmediatePropagation: function stopImmediatePropagation() {
this.stopPropagation();
immediatePropagationStopped = true;
that.originalEvent && that.originalEvent.stopImmediatePropagation();
},
isDefaultPrevented: function isDefaultPrevented() {
return !!(defaultPrevented || that.originalEvent && that.originalEvent.defaultPrevented);
},
preventDefault: function preventDefault() {
defaultPrevented = true;
that.originalEvent && that.originalEvent.preventDefault();
}
});
}
addProperty("which", calculateWhich, that);
if (src.type.indexOf("touch") === 0) {
delete config.pageX;
delete config.pageY;
}
extend(that, config);
that.guid = ++guid;
}));
var addProperty = function addProperty(propName, hook, eventInstance) {
Object.defineProperty(eventInstance || eventsEngine.Event.prototype, propName, {
enumerable: true,
configurable: true,
get: function get() {
return this.originalEvent && hook(this.originalEvent);
},
set: function set(value) {
Object.defineProperty(this, propName, {
enumerable: true,
configurable: true,
writable: true,
value: value
});
}
});
};
hookTouchProps(addProperty);
var beforeSetStrategy = Callbacks();
var afterSetStrategy = Callbacks();
eventsEngine.set = function (engine) {
beforeSetStrategy.fire();
eventsEngine.inject(engine);
initEvent(engine.Event);
afterSetStrategy.fire();
};
eventsEngine.subscribeGlobal = function () {
applyForEach(arguments, normalizeOnArguments(function () {
var args = arguments;
eventsEngine.on.apply(this, args);
beforeSetStrategy.add(function () {
var offArgs = Array.prototype.slice.call(args, 0);
offArgs.splice(3, 1);
eventsEngine.off.apply(this, offArgs);
});
afterSetStrategy.add(function () {
eventsEngine.on.apply(this, args);
});
}));
};
///#DEBUG
eventsEngine.elementDataMap = elementDataMap;
///#ENDDEBUG
module.exports = eventsEngine;
;