UNPKG

mind.svg.js

Version:

Display and operate MindMap using SVG in browser

240 lines (211 loc) 7.55 kB
import { isENode, ENode } from "./mind.svg.node"; // 对象实例里的事件自动分发处理程序实例名称 const SYMBOL_EVENT_DISPATCHER = Symbol("MindSVG.Event.Dispatcher"); // 阻止事件内部分发的变量符号名称 const SYMBOL_EVENT_BLOCK_SIBLING = Symbol("MindSVG.Event.BlockSibling.Private"); // 对象里的事件处理程序的名称前缀 const NAME_EVENTHANDLER_PREFIX = "_$!"; // 思维导图的标记戳 const ATTR_EVENT_STAMP = "mind-event-stamp"; /////////////////////////////////////////////////////////////////////////////// // 以下定义思维导图事件名称常数 // 请求重新布局的事件 const EVENT_REQUIRE_LAYOUT = "mindevent.require.layout"; // 焦点发生变更 const EVENT_FOCUS_CHANGE = "mindevent.focus.change"; // 查询附件 const EVENT_QUERY_ATTACHMENT = "mindevent.query.attachment"; // 唤起链接 const EVENT_INVOKE_LINK = "mindevent.invoke.link"; // 唤起备注 const EVENT_INVOKE_NOTES = "mindevent.invoke.notes"; // 唤起标签 const EVENT_INVOKE_LABELS = "mindevent.invoke.labels"; // 唤起图像 const EVENT_INVOKE_IMAGE = "mindevent.invoke.image"; // 唤起扩展标记 const EVENT_INVOKE_MARKERS = "mindevent.invoke.markers"; // 拖拽等待确认 const EVENT_CONFIRM_DRAG = "mindevent.confirm.drag"; // 拖拽完成 const EVENT_END_DRAG = "mindevent.end.drag"; /////////////////////////////////////////////////////////////////////////////// /** * 生成事件句柄的名称 * @param {*} _event * @param {*} _stamp */ export function namedHandler(_event, _stamp) { return _stamp ? `${NAME_EVENTHANDLER_PREFIX}${_event}|${_stamp}` : `${NAME_EVENTHANDLER_PREFIX}${_event}|`; } /** * 检查一个对象的是不是具备某个事件的处理程序 * @param {*} _constructor 对象的构造类 * @param {*} _event 事件 * @param {*} _stamp 事件的元素标记戳,如果不传入该参数,则只要是符合事件名的就算有对应的处理程序 */ export function hasEventHandler(_constructor, _event, _stamp) { const map = Object.getOwnPropertyNames(_constructor.prototype); if (_stamp) { return map.indexOf(namedHandler(_event, _stamp)); } else { for (let item of map) { if (item.startsWith(`${NAME_EVENTHANDLER_PREFIX}${_event}|`)) { return true; } } } return false; } /** * 从对象的构造类中抽取对象的事件处理程序表 * @param {*} _constructor 对象的构造类 */ function getEventHandlerMap(_constructor) { const srcMap = Object.getOwnPropertyNames(_constructor.prototype); const map = []; for (let item of srcMap) { if (item.startsWith(NAME_EVENTHANDLER_PREFIX)) { const event = item.substr(NAME_EVENTHANDLER_PREFIX.length).split("|")[0]; (event.length > 0) && (map.indexOf(event) < 0) && map.push(event); } } return map; } /** * 对阻止事件冒泡和默认处理的封装 * @param {*} _event 要处理的事件 * @param {*} _prevent 是否要阻止默认处理,默认为true */ export function handledEvent(_event, _prevent = true) { _prevent && _event.preventDefault(); _event.stopPropagation(); } /** * 检查事件是否来源于MindSVG组件 * @param {*} _event * @param {*} _mindSVG */ export function fromMindSVG(_event, _mindSVG) { const target = ENode().attach(_event.target); return target && (target.isSVG || target.isSame(_mindSVG.container)); } /** * 允许同级继续分发事件 * @param {*} _event */ export function continueSiblingHandler(_event) { Object.isExtensible(_event) && (_event[SYMBOL_EVENT_BLOCK_SIBLING] = undefined); } /** * 循环调用处理程序 * @param {*} _list * @param {*} _thisArg * @param {*} _args */ function loopCallHandler(_list, _thisArg, _args) { const _event = _args[0]; if (_list.length) { continueSiblingHandler(_event); } else { let ret = undefined; for (let idx in _list) { if (_list[idx]) { _args[3] = ret; ret = _list[idx].apply(this, _args); if (_event[SYMBOL_EVENT_BLOCK_SIBLING]) { break; } } } return ret; } } /** * 分发主题事件的处理程序 * @param {*} _event */ function mindEventDispatcher(_event) { const element = _event.target; if (element) { let ret = undefined; // 获取事件元素的思维导图标记戳 const stamp = element.getAttribute(ATTR_EVENT_STAMP); if (stamp) { // 如果事件元素标记为思维导图的元素,且对象有对应的处理程序,则进行处理 const fn = this[namedHandler(_event.type, stamp)]; if (fn) { Object.isExtensible(_event) && (_event[SYMBOL_EVENT_BLOCK_SIBLING] = true); ret = (fn instanceof Array) ? loopCallHandler(fn, this, [_event, stamp, element, ret]) : fn.call(this, _event, stamp, element); } } // 如果没有标记戳,或者处理程序允许同级继续分发,就要由未标记的默认处理程序继续处理 if (!_event[SYMBOL_EVENT_BLOCK_SIBLING]) { const fn = this[namedHandler(_event.type)]; if (fn) { Object.isExtensible(_event) && (_event[SYMBOL_EVENT_BLOCK_SIBLING] = true); (fn instanceof Array) ? loopCallHandler(fn, this, [_event, stamp, element, ret]) : fn.call(this, _event, stamp, element, ret); } } } } /** * 批量关联事件处理程序 * @param {*} _element 被关联事件的元素 * @param {*} _map 处理程序集 * @param {*} _binder 处理程序的this对象 */ export function mapEventHandler(_element, _binder, _map) { if (_element && _binder) { const map = _map || getEventHandlerMap(_binder.constructor); const dispatcher = _binder[SYMBOL_EVENT_DISPATCHER] || (_binder[SYMBOL_EVENT_DISPATCHER] = mindEventDispatcher.bind(_binder)); const eventBind = (_element.on || _element.addEventListener).bind(_element); for (let event of map) { eventBind(event, dispatcher); } } } /** * 触发思维导图事件 * @param {*} _event * @param {*} _data */ export function fireEvent(_event, _data, _defRet) { if (isENode(this)) { const event = new CustomEvent(_event, { bubbles: true, cancelable: true, composed: true, detail: {value: _data, result: _defRet} }); this.node.dispatchEvent(event); return event.detail.result; } } /** * 获取事件关联元素的标记戳 * @param {*} _event */ export function eventStamp(_event) { return (_event && _event.target) ? _event.target.getAttribute(ATTR_EVENT_STAMP) : undefined; } /** * 导出事件类接口和常数 */ export const Constants = { ATTR_EVENT_STAMP, EVENT_FOCUS_CHANGE, EVENT_REQUIRE_LAYOUT, EVENT_QUERY_ATTACHMENT, EVENT_CONFIRM_DRAG, EVENT_END_DRAG, EVENT_INVOKE_IMAGE, EVENT_INVOKE_LABELS, EVENT_INVOKE_LINK, EVENT_INVOKE_NOTES, EVENT_INVOKE_MARKERS };