@antv/x6
Version:
JavaScript diagramming library that uses SVG and HTML for rendering
310 lines • 12.4 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.on = on;
exports.off = off;
exports.dispatch = dispatch;
exports.trigger = trigger;
const tslib_1 = require("tslib");
const util_1 = require("./util");
const store_1 = require("./store");
const hook_1 = require("./hook");
const store_2 = require("./store");
const object_1 = require("./object");
require("./special");
let triggered;
function on(elem, types, handler, data, selector) {
if (!(0, util_1.isValidTarget)(elem)) {
return;
}
// Caller can pass in an object of custom data in lieu of the handler
let handlerData;
if (typeof handler !== 'function') {
const { handler: h, selector: s } = handler, others = tslib_1.__rest(handler, ["handler", "selector"]);
handler = h; // eslint-disable-line
selector = s; // eslint-disable-line
handlerData = others;
}
// Ensure that invalid selectors throw exceptions at attach time
// if (!isValidSelector(elem, selector)) {
// throw new Error('Delegate event with invalid selector.')
// }
const store = (0, store_1.ensure)(elem);
// Ensure the main handle
let mainHandler = store.handler;
if (mainHandler == null) {
mainHandler = store.handler = function (e, ...args) {
return triggered !== e.type ? dispatch(elem, e, ...args) : undefined;
};
}
// Make sure that the handler has a unique ID, used to find/remove it later
const guid = (0, util_1.ensureHandlerId)(handler);
// Handle multiple events separated by a space
(0, util_1.splitType)(types).forEach((item) => {
const { originType, namespaces } = (0, util_1.normalizeType)(item);
// There *must* be a type, no attaching namespace-only handlers
if (!originType) {
return;
}
let type = originType;
let hook = (0, hook_1.get)(type);
// If selector defined, determine special event type, otherwise given type
type = (selector ? hook.delegateType : hook.bindType) || type;
// Update hook based on newly reset type
hook = (0, hook_1.get)(type);
// handleObj is passed to all event handlers
const handleObj = Object.assign({ type,
originType,
data,
selector,
guid, handler: handler, namespace: namespaces.join('.') }, handlerData);
// Init the event handler queue if we're the first
const events = store.events;
let bag = events[type];
if (!bag) {
bag = events[type] = { handlers: [], delegateCount: 0 };
// Only use addEventListener if the `hook.steup` returns false
if (!hook.setup ||
hook.setup(elem, data, namespaces, mainHandler) === false) {
(0, util_1.addEventListener)(elem, type, mainHandler);
}
}
if (hook.add) {
(0, util_1.removeHandlerId)(handleObj.handler);
hook.add(elem, handleObj);
(0, util_1.setHandlerId)(handleObj.handler, guid);
}
// Add to the element's handler list, delegates in front
if (selector) {
bag.handlers.splice(bag.delegateCount, 0, handleObj);
bag.delegateCount += 1;
}
else {
bag.handlers.push(handleObj);
}
});
}
function off(elem, types, handler, selector, mappedTypes) {
const store = (0, store_2.get)(elem);
if (!store) {
return;
}
const events = store.events;
if (!events) {
return;
}
// Once for each type.namespace in types; type may be omitted
(0, util_1.splitType)(types).forEach((item) => {
const { originType, namespaces } = (0, util_1.normalizeType)(item);
// Unbind all events (on this namespace, if provided) for the element
if (!originType) {
Object.keys(events).forEach((key) => {
off(elem, key + item, handler, selector, true);
});
return;
}
let type = originType;
const hook = (0, hook_1.get)(type);
type = (selector ? hook.delegateType : hook.bindType) || type;
const bag = events[type];
if (!bag) {
return;
}
const rns = namespaces.length > 0
? new RegExp(`(^|\\.)${namespaces.join('\\.(?:.*\\.|)')}(\\.|$)`)
: null;
// Remove matching events
const originHandlerCount = bag.handlers.length;
for (let i = bag.handlers.length - 1; i >= 0; i -= 1) {
const handleObj = bag.handlers[i];
if ((mappedTypes || originType === handleObj.originType) &&
(!handler || (0, util_1.getHandlerId)(handler) === handleObj.guid) &&
(rns == null ||
(handleObj.namespace && rns.test(handleObj.namespace))) &&
(selector == null ||
selector === handleObj.selector ||
(selector === '**' && handleObj.selector))) {
bag.handlers.splice(i, 1);
if (handleObj.selector) {
bag.delegateCount -= 1;
}
if (hook.remove) {
hook.remove(elem, handleObj);
}
}
}
if (originHandlerCount && bag.handlers.length === 0) {
if (!hook.teardown ||
hook.teardown(elem, namespaces, store.handler) === false) {
(0, util_1.removeEventListener)(elem, type, store.handler);
}
delete events[type];
}
});
// Remove data and the expando if it's no longer used
if (Object.keys(events).length === 0) {
(0, store_1.remove)(elem);
}
}
function dispatch(elem, evt, ...args) {
const event = object_1.EventObject.create(evt);
event.delegateTarget = elem;
const hook = (0, hook_1.get)(event.type);
if (hook.preDispatch && hook.preDispatch(elem, event) === false) {
return;
}
const handlerQueue = (0, util_1.getHandlerQueue)(elem, event);
// Run delegates first; they may want to stop propagation beneath us
for (let i = 0, l = handlerQueue.length; i < l && !event.isPropagationStopped(); i += 1) {
const matched = handlerQueue[i];
event.currentTarget = matched.elem;
for (let j = 0, k = matched.handlers.length; j < k && !event.isImmediatePropagationStopped(); j += 1) {
const handleObj = matched.handlers[j];
// If event is namespaced, then each handler is only invoked if it is
// specially universal or its namespaces are a superset of the event's.
if (event.rnamespace == null ||
(handleObj.namespace && event.rnamespace.test(handleObj.namespace))) {
event.handleObj = handleObj;
event.data = handleObj.data;
const hookHandle = (0, hook_1.get)(handleObj.originType).handle;
const result = hookHandle
? hookHandle(matched.elem, event, ...args)
: handleObj.handler.call(matched.elem, event, ...args);
if (result !== undefined) {
event.result = result;
if (result === false) {
event.preventDefault();
event.stopPropagation();
}
}
}
}
}
// Call the postDispatch hook for the mapped type
if (hook.postDispatch) {
hook.postDispatch(elem, event);
}
return event.result;
}
function trigger(event, eventArgs, elem, onlyHandlers) {
let eventObj = event;
let type = typeof event === 'string' ? event : event.type;
let namespaces = typeof event === 'string' || eventObj.namespace == null
? []
: eventObj.namespace.split('.');
const node = elem;
// Don't do events on text and comment nodes
if (node.nodeType === 3 || node.nodeType === 8) {
return;
}
if (type.indexOf('.') > -1) {
// Namespaced trigger; create a regexp to match event type in handle()
namespaces = type.split('.');
type = namespaces.shift();
namespaces.sort();
}
const ontype = type.indexOf(':') < 0 && `on${type}`;
// Caller can pass in a EventObject, Object, or just an event type string
eventObj =
event instanceof object_1.EventObject
? event
: new object_1.EventObject(type, typeof event === 'object' ? event : null);
eventObj.namespace = namespaces.join('.');
eventObj.rnamespace = eventObj.namespace
? new RegExp(`(^|\\.)${namespaces.join('\\.(?:.*\\.|)')}(\\.|$)`)
: null;
// Clean up the event in case it is being reused
eventObj.result = undefined;
if (!eventObj.target) {
eventObj.target = node;
}
const args = [eventObj];
if (Array.isArray(eventArgs)) {
args.push(...eventArgs);
}
else {
args.push(eventArgs);
}
const hook = (0, hook_1.get)(type);
if (!onlyHandlers &&
hook.trigger &&
hook.trigger(node, eventObj, eventArgs) === false) {
return;
}
let bubbleType;
// Determine event propagation path in advance, per W3C events spec.
// Bubble up to document, then to window; watch for a global ownerDocument
const eventPath = [node];
if (!onlyHandlers && !hook.noBubble && !(0, util_1.isWindow)(node)) {
bubbleType = hook.delegateType || type;
let last = node;
let curr = node.parentNode;
while (curr != null) {
eventPath.push(curr);
last = curr;
curr = curr.parentNode;
}
// Only add window if we got to document
const doc = node.ownerDocument || document;
if (last === doc) {
const win = last.defaultView || last.parentWindow || window;
eventPath.push(win);
}
}
let lastElement = node;
// Fire handlers on the event path
for (let i = 0, l = eventPath.length; i < l && !eventObj.isPropagationStopped(); i += 1) {
const currElement = eventPath[i];
lastElement = currElement;
eventObj.type = i > 1 ? bubbleType : hook.bindType || type;
// Custom handler
const store = (0, store_2.get)(currElement);
if (store) {
if (store.events[eventObj.type] && store.handler) {
store.handler.call(currElement, ...args);
}
}
// Native handler
const handle = (ontype && currElement[ontype]) || null;
if (handle && (0, util_1.isValidTarget)(currElement)) {
eventObj.result = handle.call(currElement, ...args);
if (eventObj.result === false) {
eventObj.preventDefault();
}
}
}
eventObj.type = type;
// If nobody prevented the default action, do it now
if (!onlyHandlers && !eventObj.isDefaultPrevented()) {
const preventDefault = hook.preventDefault;
if ((preventDefault == null ||
preventDefault(eventPath.pop(), eventObj, eventArgs) === false) &&
(0, util_1.isValidTarget)(node)) {
// Call a native DOM method on the target with the same name as the
// event. Don't do default actions on window.
if (ontype &&
typeof node[type] === 'function' &&
!(0, util_1.isWindow)(node)) {
// Don't re-trigger an onFOO event when we call its FOO() method
const tmp = node[ontype];
if (tmp) {
node[ontype] = null;
}
// Prevent re-triggering of the same event, since we already bubbled it above
triggered = type;
if (eventObj.isPropagationStopped()) {
lastElement.addEventListener(type, util_1.stopPropagationCallback);
}
node[type]();
if (eventObj.isPropagationStopped()) {
lastElement.removeEventListener(type, util_1.stopPropagationCallback);
}
triggered = undefined;
if (tmp) {
node[ontype] = tmp;
}
}
}
}
return eventObj.result;
}
//# sourceMappingURL=core.js.map