@umbraci/jsmind 1.0.0-beta → 1.0.1-beta

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"jsmind.multi-select.js","sources":["../src/jsmind.common.js","../src/jsmind.plugin.js","../src/plugins/jsmind.multi-select.js"],"sourcesContent":["/**\n * @license BSD\n * @copyright 2014-2025 UmbraCi\n *\n * Project Home:\n * https://github.com/UmbraCi/jsmind/\n */\n\n/**\n * Library version string.\n * @type {string}\n */\nexport const __version__ = '0.9.1';\n/**\n * Library author.\n * @type {string}\n */\nexport const __author__ = 'UmbraCi';\n\nif (typeof String.prototype.startsWith != 'function') {\n String.prototype.startsWith = function (p) {\n return this.slice(0, p.length) === p;\n };\n}\n\n/**\n * Direction constants and parser.\n * @typedef {{left:number,center:number,right:number,of:(dir:(string|number))=>number|undefined}} DirectionType\n */\n/** @type {DirectionType} */\nexport const Direction = {\n left: -1,\n center: 0,\n right: 1,\n of: function (dir) {\n if (!dir || dir === -1 || dir === 0 || dir === 1) {\n return dir;\n }\n if (dir === '-1' || dir === '0' || dir === '1') {\n return parseInt(dir);\n }\n if (dir.toLowerCase() === 'left') {\n return this.left;\n }\n if (dir.toLowerCase() === 'right') {\n return this.right;\n }\n if (dir.toLowerCase() === 'center') {\n return this.center;\n }\n },\n};\n/** @enum {number} */\nexport const EventType = { show: 1, resize: 2, edit: 3, select: 4, reset: 5, history_change: 6 };\n/** @enum {number} */\nexport const Key = { meta: 1 << 13, ctrl: 1 << 12, alt: 1 << 11, shift: 1 << 10 };\n/** @enum {number} */\nexport const LogLevel = { debug: 1, info: 2, warn: 3, error: 4, disable: 9 };\n\n// an noop function define\nvar _noop = function () {};\n/**\n * Logger facade with dynamic level.\n * @type {{level:(lvl:number)=>void,log:Function,debug:Function,info:Function,warn:Function,error:Function}}\n */\nexport let logger =\n typeof console === 'undefined'\n ? {\n level: _noop,\n log: _noop,\n debug: _noop,\n info: _noop,\n warn: _noop,\n error: _noop,\n }\n : {\n level: setup_logger_level,\n log: console.log,\n debug: console.debug,\n info: console.info,\n warn: console.warn,\n error: console.error,\n };\n\n/**\n * Set logger level.\n * @param {number} log_level\n */\nfunction setup_logger_level(log_level) {\n if (log_level > LogLevel.debug) {\n logger.debug = _noop;\n } else {\n logger.debug = console.debug;\n }\n if (log_level > LogLevel.info) {\n logger.info = _noop;\n } else {\n logger.info = console.info;\n }\n if (log_level > LogLevel.warn) {\n logger.warn = _noop;\n } else {\n logger.warn = console.warn;\n }\n if (log_level > LogLevel.error) {\n logger.error = _noop;\n } else {\n logger.error = console.error;\n }\n}\n","/**\n * @license BSD\n * @copyright 2014-2025 UmbraCi\n *\n * Project Home:\n * https://github.com/UmbraCi/jsmind/\n */\n\nimport { logger } from './jsmind.common.js';\n\n/**\n * Plugin Manager\n * Manages plugin lifecycle with synchronous initialization,\n * preload support, and lifecycle hooks.\n */\nexport class PluginManager {\n /**\n * @param {import('./jsmind.js').default} jm - jsMind instance\n */\n constructor(jm) {\n this.jm = jm;\n /** @type {Map<string, Plugin>} */\n this.plugins = new Map();\n }\n\n /**\n * Initialize preload plugins (before core modules)\n */\n initPreloadPlugins() {\n const preloadPlugins = this.jm.constructor.pluginList.filter(d => d.preload);\n logger.info('Initializing ' + preloadPlugins.length + ' preload plugins');\n preloadPlugins.forEach(descriptor => {\n this._initPlugin(descriptor);\n });\n }\n\n /**\n * Initialize normal plugins (after core modules)\n */\n initNormalPlugins() {\n const normalPlugins = this.jm.constructor.pluginList.filter(d => !d.preload);\n logger.info('Initializing ' + normalPlugins.length + ' normal plugins');\n normalPlugins.forEach(descriptor => {\n this._initPlugin(descriptor);\n });\n }\n\n /**\n * Internal method: Initialize a single plugin\n * @param {PluginDescriptor} descriptor\n * @private\n */\n _initPlugin(descriptor) {\n try {\n const { PluginClass, pluginOpt } = descriptor;\n\n if (!PluginClass.instanceName) {\n throw new Error('Plugin ' + PluginClass.name + ' must define static instanceName');\n }\n if (this.plugins.has(PluginClass.instanceName)) {\n logger.warn(\n 'Plugin ' + PluginClass.instanceName + ' already exists, will be replaced'\n );\n }\n\n const instance = new PluginClass({\n jm: this.jm,\n pluginOpt: pluginOpt || {},\n });\n\n this.plugins.set(PluginClass.instanceName, instance);\n this.jm[PluginClass.instanceName] = instance;\n descriptor.instance = instance;\n\n logger.info('Plugin ' + PluginClass.instanceName + ' initialized');\n } catch (error) {\n logger.error('Failed to initialize plugin ' + descriptor.PluginClass.name + ':', error);\n }\n }\n\n /**\n * Remove a plugin\n * @param {typeof Plugin} PluginClass\n */\n removePlugin(PluginClass) {\n const instanceName = PluginClass.instanceName;\n if (!instanceName) {\n return;\n }\n\n const instance = this.plugins.get(instanceName);\n if (!instance) {\n return;\n }\n\n try {\n if (typeof instance.beforePluginRemove === 'function') {\n instance.beforePluginRemove();\n }\n\n this.plugins.delete(instanceName);\n delete this.jm[instanceName];\n\n const list = this.jm.constructor.pluginList;\n const index = list.findIndex(d => d.PluginClass === PluginClass);\n if (index !== -1) {\n list.splice(index, 1);\n }\n\n logger.info('Plugin ' + instanceName + ' removed');\n } catch (error) {\n logger.error('Failed to remove plugin ' + instanceName + ':', error);\n }\n }\n\n /**\n * Destroy all plugins\n */\n destroyAllPlugins() {\n this.plugins.forEach((instance, instanceName) => {\n try {\n if (typeof instance.beforePluginDestroy === 'function') {\n instance.beforePluginDestroy();\n }\n } catch (error) {\n logger.error('Failed to destroy plugin ' + instanceName + ':', error);\n }\n });\n\n this.plugins.clear();\n }\n\n /**\n * Get plugin instance by name\n * @param {string} instanceName\n * @returns {Plugin | undefined}\n */\n getPlugin(instanceName) {\n return this.plugins.get(instanceName);\n }\n}\n\n/**\n * Plugin Base Class\n * Provides standard plugin interface.\n */\nexport class Plugin {\n /**\n * Plugin instance name (must be defined by subclass)\n * @type {string}\n */\n static instanceName = '';\n\n /**\n * Whether to initialize before core modules\n * @type {boolean}\n */\n static preload = false;\n\n /**\n * @param {{ jm: import('./jsmind.js').default, pluginOpt: object }} params\n */\n constructor({ jm, pluginOpt }) {\n this.jm = jm;\n this.options = pluginOpt || {};\n }\n\n /**\n * Called before plugin is removed\n */\n beforePluginRemove() {}\n\n /**\n * Called before jsMind instance is destroyed\n */\n beforePluginDestroy() {\n this.beforePluginRemove();\n }\n}\n\n/**\n * Plugin descriptor\n * @typedef {object} PluginDescriptor\n * @property {typeof Plugin} PluginClass - Plugin class\n * @property {string} instanceName - Plugin instance name\n * @property {boolean} preload - Whether to preload\n * @property {object} pluginOpt - Plugin options\n * @property {Plugin | null} instance - Plugin instance (after initialization)\n */\n","/**\n * @license BSD\n * @copyright 2014-2025 UmbraCi\n *\n * Project Home:\n * https://github.com/UmbraCi/jsmind/\n */\n\nimport { Plugin } from '../jsmind.plugin.js';\nimport { EventType, logger } from '../jsmind.common.js';\n\n/**\n * Default options for multi-select plugin.\n * @typedef {Object} MultiSelectOptions\n * @property {boolean} [enable_multi_select=true] - Enable multi-select feature\n * @property {boolean} [include_descendants=true] - Include descendants in subtree selection\n * @property {boolean} [shift_simple_mode=false] - Shift mode: false=simple subtree, true=advanced range\n * @property {((node:import('../jsmind.node.js').Node)=>boolean)|null} [filter=null] - Node filter function\n */\nconst DEFAULT_OPTIONS = {\n enable_multi_select: false, // default off; can be enabled at runtime\n include_descendants: true,\n shift_simple_mode: false,\n allow_ctrl: true,\n allow_shift: true,\n filter: null,\n};\n\n/**\n * Multi-Select Core - Handles all multi-select logic\n */\nclass MultiSelectCore {\n /**\n * @param {import('../jsmind.js').default} jm - jsMind instance\n * @param {MultiSelectOptions} options - Plugin options\n */\n constructor(jm, options) {\n this.jm = jm;\n this.options = options;\n\n // Initialize selection state\n this._ensure_selection_state();\n\n this._selection_mode = null; // 'single' | 'multi' | null\n this._last_selected_node = null;\n }\n\n /**\n * Ensure selection state is initialized\n * @private\n */\n _ensure_selection_state() {\n if (!this.jm.mind) {\n this.jm.mind = {};\n }\n if (!this.jm.mind.selected_nodes) {\n this.jm.mind.selected_nodes = new Set();\n }\n\n if (!this.jm.view) {\n this.jm.view = {};\n }\n if (!this.jm.view.multi_selected_nodes) {\n this.jm.view.multi_selected_nodes = new Map();\n }\n }\n\n /**\n * Get all selected nodes\n * @returns {string[]} Array of selected node IDs\n */\n get_selected_nodes() {\n this._ensure_selection_state();\n if (!this.jm.mind || !this.jm.mind.selected_nodes) {\n return [];\n }\n return Array.from(this.jm.mind.selected_nodes).map(node => node.id);\n }\n\n /**\n * Check if a node is selected\n * @param {string|import('../jsmind.node.js').Node} node - Node ID or Node instance\n * @returns {boolean}\n */\n is_node_selected(node) {\n const nodeObj = this._resolve_node(node);\n if (!nodeObj || !this.jm.mind) {\n return false;\n }\n this._ensure_selection_state();\n return this.jm.mind.selected_nodes.has(nodeObj);\n }\n\n /**\n * Select a single node (clears other selections)\n * @param {string|import('../jsmind.node.js').Node} node - Node ID or Node instance\n */\n select_node(node) {\n const nodeObj = this._resolve_node(node);\n if (!nodeObj) {\n logger.error('[multiSelect] node not found: ' + node);\n return;\n }\n\n this._ensure_selection_state();\n\n // Clear selection state first\n this._clear_selection_state();\n\n // Ensure again after clear, in case mind was reset\n this._ensure_selection_state();\n\n if (!this.jm.mind || !this.jm.mind.selected_nodes) {\n logger.error('[multiSelect] jm.mind or selected_nodes is not available');\n return;\n }\n\n // Set selection state\n this.jm.mind.selected = nodeObj;\n this._last_selected_node = nodeObj;\n\n // Force add node to selection (since we just cleared it)\n if (!this.jm.mind.selected_nodes.has(nodeObj)) {\n this.jm.mind.selected_nodes.add(nodeObj);\n }\n\n // Mark node as selected in view\n this._mark_node_selected(nodeObj);\n\n this._selection_mode = 'single';\n\n // Always use nodeObj.id for event data\n const nodeIds = [nodeObj.id];\n\n // Invoke event with proper data structure\n this._invoke_select_event({\n evt: 'select_node',\n data: nodeIds,\n node: nodeObj.id,\n nodes: nodeIds,\n });\n }\n\n /**\n * Clear all selections\n */\n select_clear() {\n this._ensure_selection_state();\n if (this.jm.mind) {\n this.jm.mind.selected = null;\n this._last_selected_node = null;\n this._clear_selection_state();\n }\n }\n\n /**\n * Toggle node selection (equivalent to Ctrl/Cmd+Click)\n * @param {string|import('../jsmind.node.js').Node} node - Node ID or Node instance\n */\n toggle_node_selection(node) {\n const nodeObj = this._resolve_node(node);\n if (!nodeObj || !this.jm.layout.is_visible(nodeObj)) {\n return;\n }\n\n this._ensure_selection_state();\n if (this.jm.mind.selected_nodes.has(nodeObj)) {\n this._deselect_subtree(nodeObj);\n } else {\n this._selection_mode = 'multi';\n const added = this._append_selection([nodeObj]);\n if (added.length) {\n this.jm.mind.selected = nodeObj;\n this._last_selected_node = nodeObj;\n this._invoke_select_event({\n evt: 'multi_select',\n data: [nodeObj.id],\n node: nodeObj.id,\n nodes: [nodeObj.id],\n });\n }\n }\n }\n\n /**\n * Toggle subtree selection\n * @param {string|import('../jsmind.node.js').Node} node - Node ID or Node instance\n */\n toggle_subtree_selection(node, opts) {\n const nodeObj = this._resolve_node(node);\n if (!nodeObj) {\n logger.error('[multiSelect] node not found: ' + node);\n return;\n }\n\n if (!this.jm.layout.is_visible(nodeObj)) {\n return;\n }\n\n const includeDesc =\n opts && typeof opts.include_descendants !== 'undefined'\n ? !!opts.include_descendants\n : this.options.include_descendants !== false; // default true\n\n this._ensure_selection_state();\n // If node is not selected or not in multi mode, select subtree\n if (!this.jm.mind.selected_nodes.has(nodeObj) || this._selection_mode !== 'multi') {\n this._selection_mode = 'multi';\n let nodes = this._collect_subtree_nodes(nodeObj, {\n includeChildren: includeDesc,\n respectFilter: true,\n skipRootFilter: true,\n });\n if (!nodes.length) {\n nodes = [nodeObj];\n }\n\n const added = this._append_selection(nodes, { focusNode: nodeObj });\n this._ensure_selection_state();\n const ancestors =\n nodeObj.parent && !this.jm.mind.selected_nodes.has(nodeObj.parent)\n ? this._ensure_ancestor_selection([nodeObj], nodeObj, {\n requireAncestorChainSelected: true,\n })\n : [];\n const allAdded = added.concat(ancestors);\n\n if (allAdded.length) {\n this.jm.mind.selected = nodeObj;\n const nodeIds = allAdded.map(n => n.id);\n this._invoke_select_event({\n evt: 'multi_select',\n data: nodeIds,\n node: nodeObj.id,\n nodes: nodeIds,\n });\n }\n } else {\n // Deselect subtree\n let nodes = this._collect_subtree_nodes(nodeObj, {\n includeChildren: true,\n respectFilter: false,\n skipRootFilter: false,\n });\n if (!nodes.length) {\n nodes = [nodeObj];\n }\n\n const removed = this._remove_selection(nodes);\n if (removed.length) {\n if (this.jm.mind.selected && this.jm.mind.selected.id === nodeObj.id) {\n this.jm.mind.selected = null;\n }\n this._selection_mode = this._derive_selection_mode();\n const nodeIds = removed.map(n => n.id);\n this._invoke_select_event({\n evt: 'multi_deselect',\n data: nodeIds,\n node: nodeObj.id,\n nodes: nodeIds,\n });\n }\n }\n }\n\n /**\n * Get current selection mode\n * @returns {'single'|'multi'|null}\n */\n get_selection_mode() {\n return this._selection_mode;\n }\n\n /**\n * Handle node click event\n * @param {Object} payload - Click event payload\n * @param {MouseEvent} payload.e - Mouse event\n * @param {string} payload.node - Node ID\n * @param {HTMLElement} payload.element - Node element\n * @param {string} payload.evt - Event type\n */\n _handle_node_click(payload) {\n const { e, node, element } = payload;\n if (!node || !element) {\n return;\n }\n\n // Ensure selection state is initialized\n this._ensure_selection_state();\n\n const mode = this._get_multi_select_mode(e);\n\n if (mode === 'ctrl') {\n // Ctrl/Cmd + Click: Toggle node selection\n this.toggle_node_selection(node);\n } else if (mode === 'shift') {\n // Shift + Click: Range selection\n const nodeObj = this.jm.get_node(node);\n if (!nodeObj) {\n logger.warn('[multiSelect] Node not found for shift selection: ' + node);\n return;\n }\n\n if (this.options.shift_simple_mode) {\n // Advanced range mode\n if (this.is_node_selected(node)) {\n this._deselect_subtree(nodeObj);\n } else {\n this._range_select_nodes(node);\n }\n } else {\n // Simple subtree mode\n if (this.is_node_selected(node)) {\n this._deselect_subtree(nodeObj);\n } else {\n const includeChildren = this.options.include_descendants !== false;\n let nodes = this._collect_subtree_nodes(nodeObj, {\n includeChildren: includeChildren,\n respectFilter: true,\n skipRootFilter: true,\n });\n if (!nodes.length) {\n nodes = [nodeObj];\n }\n\n const added = this._append_selection(nodes, { focusNode: nodeObj });\n if (added.length) {\n this.jm.mind.selected = nodeObj;\n this._last_selected_node = nodeObj;\n this._selection_mode = this._derive_selection_mode();\n const nodeIds = added.map(n => n.id);\n this._invoke_select_event({\n evt: 'multi_select',\n data: nodeIds,\n node: nodeObj.id,\n nodes: nodeIds,\n });\n } else {\n // Fallback: at least select the clicked node\n this._ensure_selection_state();\n if (!this.jm.mind.selected_nodes.has(nodeObj)) {\n this.jm.mind.selected_nodes.add(nodeObj);\n this._mark_node_selected(nodeObj);\n }\n this.jm.mind.selected = nodeObj;\n this._last_selected_node = nodeObj;\n this._selection_mode = this._derive_selection_mode();\n this._invoke_select_event({\n evt: 'multi_select',\n data: [nodeObj.id],\n node: nodeObj.id,\n nodes: [nodeObj.id],\n });\n }\n }\n }\n } else {\n // Normal click - single select\n this.select_node(node);\n }\n }\n\n /**\n * Handle node removed event\n * @param {import('../jsmind.node.js').Node} node - Removed node\n */\n _handle_node_removed(node) {\n this._ensure_selection_state();\n if (this.jm.mind.selected_nodes.has(node)) {\n this.jm.mind.selected_nodes.delete(node);\n }\n if (this._last_selected_node && this._last_selected_node.id === node.id) {\n this._last_selected_node = null;\n }\n if (this.jm.view.multi_selected_nodes && this.jm.view.multi_selected_nodes.has(node.id)) {\n this._unmark_node_selected(node);\n }\n }\n\n /**\n * Get multi-select mode from event\n * @param {MouseEvent} e - Mouse event\n * @returns {'ctrl'|'shift'|null}\n */\n _get_multi_select_mode(e) {\n if (!e) {\n return null;\n }\n // Check shift key first (higher priority)\n if (this.options.allow_shift !== false && e.shiftKey === true) {\n return 'shift';\n }\n // Check ctrl/cmd key\n if (this.options.allow_ctrl !== false && (e.ctrlKey === true || e.metaKey === true)) {\n return 'ctrl';\n }\n return null;\n }\n\n /**\n * Append nodes to selection\n * @param {import('../jsmind.node.js').Node[]} nodes - Nodes to select\n * @param {Object} [options] - Options\n * @param {import('../jsmind.node.js').Node} [options.focusNode] - Focus node\n * @returns {import('../jsmind.node.js').Node[]} Actually added nodes\n */\n _append_selection(nodes, options) {\n if (!nodes || !nodes.length) {\n return [];\n }\n\n this._ensure_selection_state();\n if (!this.jm.mind || !this.jm.mind.selected_nodes) {\n logger.warn('[multiSelect] Cannot append selection: selected_nodes not available');\n return [];\n }\n\n const added = [];\n for (let i = 0; i < nodes.length; i++) {\n const node = nodes[i];\n if (!this.jm.mind.selected_nodes.has(node)) {\n this.jm.mind.selected_nodes.add(node);\n added.push(node);\n }\n }\n\n if (added.length) {\n const focusNode = options && options.focusNode ? options.focusNode : null;\n this._mark_nodes_selected(added, focusNode || added[added.length - 1]);\n }\n\n return added;\n }\n\n /**\n * Remove nodes from selection\n * @param {import('../jsmind.node.js').Node[]} nodes - Nodes to deselect\n * @returns {import('../jsmind.node.js').Node[]} Actually removed nodes\n */\n _remove_selection(nodes) {\n if (!nodes || !nodes.length) {\n return [];\n }\n\n this._ensure_selection_state();\n const removed = [];\n for (let i = 0; i < nodes.length; i++) {\n const node = nodes[i];\n if (this.jm.mind.selected_nodes.has(node)) {\n this.jm.mind.selected_nodes.delete(node);\n removed.push(node);\n }\n }\n\n if (removed.length) {\n this._unmark_nodes_selected(removed);\n }\n\n return removed;\n }\n\n /**\n * Deselect subtree\n * @param {import('../jsmind.node.js').Node} node - Root node of subtree\n */\n _deselect_subtree(node) {\n let nodes = this._collect_subtree_nodes(node, {\n includeChildren: true,\n respectFilter: false,\n skipRootFilter: false,\n });\n if (!nodes.length) {\n nodes = [node];\n }\n\n const removed = this._remove_selection(nodes);\n if (removed.length) {\n if (this.jm.mind.selected && this.jm.mind.selected.id === node.id) {\n this.jm.mind.selected = null;\n }\n this._selection_mode = this._derive_selection_mode();\n const nodeIds = removed.map(n => n.id);\n this._invoke_select_event({\n evt: 'multi_deselect',\n data: nodeIds,\n node: node.id,\n nodes: nodeIds,\n });\n }\n }\n\n /**\n * Clear selection state\n * @returns {import('../jsmind.node.js').Node[]} Removed nodes\n */\n _clear_selection_state() {\n this._ensure_selection_state();\n this._selection_mode = null;\n\n // Double check after ensure\n if (!this.jm.mind || !this.jm.mind.selected_nodes) {\n this._ensure_selection_state();\n }\n\n if (\n !this.jm.mind ||\n !this.jm.mind.selected_nodes ||\n this.jm.mind.selected_nodes.size === 0\n ) {\n this._clear_all_selected_nodes_view();\n return [];\n }\n\n const nodes = Array.from(this.jm.mind.selected_nodes);\n this.jm.mind.selected_nodes.clear();\n this._unmark_nodes_selected(nodes);\n return nodes;\n }\n\n /**\n * Collect subtree nodes\n * @param {import('../jsmind.node.js').Node} node - Root node\n * @param {Object} options - Options\n * @param {boolean} [options.includeChildren=true] - Include children\n * @param {boolean} [options.respectFilter=false] - Respect filter\n * @param {boolean} [options.skipRootFilter=false] - Skip root filter\n * @returns {import('../jsmind.node.js').Node[]}\n */\n _collect_subtree_nodes(node, options) {\n const opts = options || {};\n const includeChildren = opts.includeChildren !== false;\n const respectFilter = !!opts.respectFilter;\n const skipRootFilter = !!opts.skipRootFilter;\n\n const filter = respectFilter ? this._get_selection_filter() : null;\n const result = [];\n\n const collect = (n, isRoot) => {\n let shouldInclude = true;\n if (filter && (!skipRootFilter || !isRoot)) {\n shouldInclude = filter(n) !== false;\n }\n\n if (shouldInclude) {\n result.push(n);\n }\n\n if (includeChildren && n.children) {\n for (let i = 0; i < n.children.length; i++) {\n collect(n.children[i], false);\n }\n }\n };\n\n collect(node, true);\n return result;\n }\n\n /**\n * Ensure ancestor selection\n * @param {import('../jsmind.node.js').Node[]} nodes - Nodes\n * @param {import('../jsmind.node.js').Node} focusNode - Focus node\n * @param {Object} options - Options\n * @returns {import('../jsmind.node.js').Node[]}\n */\n _ensure_ancestor_selection(nodes, focusNode, options) {\n if (!nodes || !nodes.length) {\n return [];\n }\n\n this._ensure_selection_state();\n const requireAncestorChainSelected = !!(options && options.requireAncestorChainSelected);\n const added = [];\n const addedMap = Object.create(null);\n\n for (let i = 0; i < nodes.length; i++) {\n let parent = nodes[i].parent;\n if (!parent) {\n continue;\n }\n\n const path = [];\n let shouldAdd = !requireAncestorChainSelected;\n\n // Check if any ancestor is already selected\n while (parent) {\n if (this.jm.mind.selected_nodes.has(parent)) {\n shouldAdd = true;\n break;\n }\n path.push(parent);\n parent = parent.parent;\n }\n\n if (shouldAdd) {\n for (let j = 0; j < path.length; j++) {\n const p = path[j];\n if (!this.jm.mind.selected_nodes.has(p) && !addedMap[p.id]) {\n added.push(p);\n addedMap[p.id] = true;\n }\n }\n }\n }\n\n if (!added.length) {\n return [];\n }\n\n const focus = focusNode || nodes[nodes.length - 1];\n return this._append_selection(added, { focusNode: focus });\n }\n\n /**\n * Get selection filter\n * @returns {Function|null}\n */\n _get_selection_filter() {\n if (this.options && typeof this.options.filter === 'function') {\n return this.options.filter;\n }\n return null;\n }\n\n /**\n * Range select nodes (advanced mode)\n * @param {string} nodeId - Target node ID\n */\n _range_select_nodes(nodeId) {\n const nodeObj = this._resolve_node(nodeId);\n if (!nodeObj || !this.jm.layout.is_visible(nodeObj)) {\n return;\n }\n\n this._ensure_selection_state();\n // If no selection, select subtree\n if (this.jm.mind.selected_nodes.size === 0) {\n let nodes = this._collect_subtree_nodes(nodeObj, {\n includeChildren: true,\n respectFilter: true,\n skipRootFilter: true,\n });\n if (!nodes.length) {\n nodes = [nodeObj];\n }\n\n const added = this._append_selection(nodes);\n if (added.length) {\n this.jm.mind.selected = nodeObj;\n this._last_selected_node = nodeObj;\n this._selection_mode = this._derive_selection_mode();\n const nodeIds = added.map(n => n.id);\n this._invoke_select_event({\n evt: 'multi_select',\n data: nodeIds,\n node: nodeObj.id,\n nodes: nodeIds,\n });\n }\n return;\n }\n\n // Find anchor node\n this._ensure_selection_state();\n const selectedArray = Array.from(this.jm.mind.selected_nodes);\n const anchor =\n this._last_selected_node && this.jm.mind.selected_nodes.has(this._last_selected_node)\n ? this._last_selected_node\n : selectedArray[0];\n\n // Find nodes between anchor and target\n const nodesBetween = this._find_nodes_between(anchor, nodeObj);\n if (!nodesBetween.length) {\n const nodes = [nodeObj];\n const added = this._append_selection(nodes);\n if (added.length) {\n this.jm.mind.selected = nodeObj;\n this._last_selected_node = nodeObj;\n this._selection_mode = this._derive_selection_mode();\n const nodeIds = added.map(n => n.id);\n this._invoke_select_event({\n evt: 'multi_select',\n data: nodeIds,\n node: nodeObj.id,\n nodes: nodeIds,\n });\n }\n return;\n }\n\n // Select all nodes between anchor and target\n this._ensure_selection_state();\n const toAdd = nodesBetween.filter(n => !this.jm.mind.selected_nodes.has(n));\n const added = this._append_selection(toAdd);\n if (added.length) {\n this.jm.mind.selected = nodeObj;\n this._last_selected_node = nodeObj;\n this._selection_mode = this._derive_selection_mode();\n const nodeIds = added.map(n => n.id);\n this._invoke_select_event({\n evt: 'multi_select',\n data: nodeIds,\n node: nodeObj.id,\n nodes: nodeIds,\n });\n }\n }\n\n /**\n * Find nodes between two nodes\n * @param {import('../jsmind.node.js').Node} from - Start node\n * @param {import('../jsmind.node.js').Node} to - End node\n * @returns {import('../jsmind.node.js').Node[]}\n */\n _find_nodes_between(from, to) {\n if (!from || !to) {\n return [];\n }\n\n // Collect all visible nodes in order\n const allNodes = [];\n const collect = node => {\n if (this.jm.layout.is_visible(node)) {\n allNodes.push(node);\n }\n if (node.children) {\n for (let i = 0; i < node.children.length; i++) {\n collect(node.children[i]);\n }\n }\n };\n\n if (this.jm.mind && this.jm.mind.root) {\n collect(this.jm.mind.root);\n }\n\n // Find indices\n let fromIndex = -1;\n let toIndex = -1;\n for (let i = 0; i < allNodes.length; i++) {\n if (allNodes[i].id === from.id) {\n fromIndex = i;\n }\n if (allNodes[i].id === to.id) {\n toIndex = i;\n }\n }\n\n if (fromIndex === -1 || toIndex === -1) {\n return [];\n }\n\n const start = Math.min(fromIndex, toIndex);\n const end = Math.max(fromIndex, toIndex);\n return allNodes.slice(start, end + 1);\n }\n\n /**\n * Derive selection mode from current state\n * @returns {'single'|'multi'|null}\n */\n _derive_selection_mode() {\n this._ensure_selection_state();\n const count = this.jm.mind.selected_nodes.size;\n if (count === 0) {\n return null;\n }\n return count > 1 ? 'multi' : 'single';\n }\n\n /**\n * Mark nodes as selected in view\n * @param {import('../jsmind.node.js').Node[]} nodes - Nodes to mark\n * @param {import('../jsmind.node.js').Node} focusNode - Focus node\n */\n _mark_nodes_selected(nodes, focusNode) {\n if (nodes && nodes.length) {\n for (let i = 0; i < nodes.length; i++) {\n this._mark_node_selected(nodes[i]);\n }\n this.jm.view.selected_node = focusNode || nodes[nodes.length - 1];\n }\n }\n\n /**\n * Mark a node as selected in view\n * @param {import('../jsmind.node.js').Node} node - Node to mark\n */\n _mark_node_selected(node) {\n if (!node || this.jm.view.multi_selected_nodes.has(node.id)) {\n return;\n }\n\n const element = node._data && node._data.view && node._data.view.element;\n if (element) {\n if (element.classList) {\n element.classList.add('selected');\n } else if (!/(\\s|^)selected(\\s|$)/.test(element.className)) {\n element.className += ' selected';\n }\n this.jm.view.multi_selected_nodes.set(node.id, node);\n }\n }\n\n /**\n * Unmark nodes as selected in view\n * @param {import('../jsmind.node.js').Node[]} nodes - Nodes to unmark\n */\n _unmark_nodes_selected(nodes) {\n if (nodes && nodes.length) {\n for (let i = 0; i < nodes.length; i++) {\n this._unmark_node_selected(nodes[i]);\n }\n }\n }\n\n /**\n * Unmark a node as selected in view\n * @param {import('../jsmind.node.js').Node} node - Node to unmark\n */\n _unmark_node_selected(node) {\n if (node) {\n if (this.jm.view.multi_selected_nodes.has(node.id)) {\n const element = node._data && node._data.view && node._data.view.element;\n if (element) {\n if (element.classList) {\n element.classList.remove('selected');\n } else {\n element.className = element.className.replace(/\\s*selected\\b/i, '');\n }\n }\n this.jm.view.multi_selected_nodes.delete(node.id);\n }\n if (this.jm.view.selected_node && this.jm.view.selected_node.id === node.id) {\n this.jm.view.selected_node = null;\n }\n }\n }\n\n /**\n * Clear all selected nodes view\n */\n _clear_all_selected_nodes_view() {\n this._ensure_selection_state();\n if (!this.jm.view.multi_selected_nodes || !this.jm.view.multi_selected_nodes.size) {\n if (this.jm.view) {\n this.jm.view.selected_node = null;\n }\n return;\n }\n\n const nodes = Array.from(this.jm.view.multi_selected_nodes.values());\n for (let i = 0; i < nodes.length; i++) {\n this._unmark_node_selected(nodes[i]);\n }\n if (this.jm.view) {\n this.jm.view.selected_node = null;\n }\n }\n\n /**\n * Invoke select event\n * @param {Object} data - Event data\n */\n _invoke_select_event(data) {\n try {\n // Ensure nodes is always an array\n if (!data.nodes) {\n data.nodes = data.data || [];\n }\n // Ensure data is always an array\n if (!data.data) {\n data.data = data.nodes || [];\n }\n this.jm.invoke_event_handle(EventType.select, data);\n } catch (e) {\n logger.warn('[multiSelect] failed to invoke select event', e);\n }\n }\n\n /**\n * Resolve node from ID or Node instance\n * @param {string|import('../jsmind.node.js').Node} node - Node ID or Node instance\n * @returns {import('../jsmind.node.js').Node|null}\n */\n _resolve_node(node) {\n if (!node) {\n return null;\n }\n if (typeof node === 'string') {\n return this.jm.get_node(node);\n }\n return node;\n }\n}\n\n/**\n * Multi-Select Plugin - Plugin for jsMind\n */\nexport class MultiSelectPlugin extends Plugin {\n static instanceName = 'multiSelectPlugin';\n static preload = false;\n\n /**\n * @param {{ jm: import('../jsmind.js').default, pluginOpt: object }} params\n */\n constructor({ jm, pluginOpt }) {\n super({ jm, pluginOpt });\n\n const options = Object.assign({}, DEFAULT_OPTIONS, pluginOpt || {});\n this.options = options;\n this._mounted = false;\n this._core = null;\n this._listener = null;\n this._enabled = !!options.enable_multi_select; // runtime gate\n\n this._initCore();\n }\n\n /**\n * Initialize core and mount API\n */\n _initCore() {\n const jm = this.jm;\n const options = this.options;\n const plugin = this; // Save reference for closure\n\n // Create core instance\n this._core = new MultiSelectCore(jm, options);\n\n // Mount API\n this._mountAPI();\n\n // Setup event listener\n this._listener = (type, data) => {\n try {\n if (type === EventType.show) {\n plugin._core.select_clear();\n }\n } catch (e) {\n logger.warn('[multiSelect] listener error', e);\n }\n };\n\n jm.add_event_listener(this._listener);\n\n // Patch core selection APIs to route through plugin to avoid duplicate events\n this._original_select_node = jm.select_node.bind(jm);\n this._original_select_clear = jm.select_clear.bind(jm);\n jm.select_node = node => {\n // Route based on runtime gate\n if (plugin._enabled) return plugin._core.select_node(node);\n return plugin._original_select_node(node);\n };\n jm.select_clear = () => {\n if (plugin._enabled) return plugin._core.select_clear();\n return plugin._original_select_clear();\n };\n\n // Always bind our click handler once; gate controls interception\n const $ = jm.constructor.$;\n this._domClickHandler = function (e) {\n // Gate: only intercept when enabled\n if (!plugin._enabled) {\n return;\n }\n const element = e.target || e.currentTarget;\n const node_id = jm.view.get_binded_nodeid(element);\n\n // Only intercept jmnode, let expander clicks pass through\n if (node_id && jm.view.is_node(element)) {\n e.preventDefault();\n e.stopPropagation();\n if (e.stopImmediatePropagation) e.stopImmediatePropagation();\n\n plugin._core._handle_node_click({\n e,\n node: node_id,\n element,\n evt: 'click',\n });\n } else if (!node_id) {\n // Blank click without modifiers clears selection\n if (!e.ctrlKey && !e.metaKey && !e.shiftKey) {\n plugin._core.select_clear();\n }\n }\n };\n\n if (jm.view && jm.view.e_nodes) {\n $.on(jm.view.e_nodes, 'click', this._domClickHandler);\n logger.info('[multiSelect] plugin click handler attached');\n } else {\n logger.warn('[multiSelect] nodes container not ready; DOM handler not attached');\n }\n\n // Respect initial enabled state: toggle core default handler\n if (this._enabled && typeof jm.disable_event_handle === 'function') {\n jm.disable_event_handle('mousedown');\n }\n\n logger.info('[multiSelect] API mounted and event listener attached.');\n }\n\n /**\n * Mount API to jsMind instance\n */\n _mountAPI() {\n if (this._mounted) {\n return;\n }\n\n const jm = this.jm;\n const plugin = this; // Save reference for closure\n\n const api = {\n get_selected_nodes: () => {\n if (!plugin._core) {\n logger.warn('[multiSelect] Core not initialized');\n return [];\n }\n return plugin._core.get_selected_nodes();\n },\n is_node_selected: node => {\n if (!plugin._core) {\n return false;\n }\n return plugin._core.is_node_selected(node);\n },\n select_node: node => {\n if (!plugin._core) {\n logger.warn('[multiSelect] Core not initialized');\n return;\n }\n plugin._core.select_node(node);\n },\n select_clear: () => {\n if (!plugin._core) {\n return;\n }\n plugin._core.select_clear();\n },\n toggle_node_selection: node => {\n if (!plugin._core) {\n logger.warn('[multiSelect] Core not initialized');\n return;\n }\n plugin._core.toggle_node_selection(node);\n },\n toggle_subtree_selection: node => {\n if (!plugin._core) {\n logger.warn('[multiSelect] Core not initialized');\n return;\n }\n plugin._core.toggle_subtree_selection(node);\n },\n get_selection_mode: () => {\n if (!plugin._core) {\n return null;\n }\n return plugin._core.get_selection_mode();\n },\n getOptions: () => {\n const base = plugin._core ? plugin._core.options : plugin.options;\n return Object.assign({}, base, { enable_multi_select: plugin._enabled });\n },\n enable: () => plugin.setEnabled(true),\n disable: () => plugin.setEnabled(false),\n setEnabled: flag => plugin.setEnabled(flag),\n setOptions: partial => plugin.setOptions(partial),\n };\n\n Object.defineProperty(jm, 'multiSelect', {\n value: api,\n configurable: true,\n enumerable: false,\n writable: false,\n });\n\n this._mounted = true;\n logger.info('[multiSelect] API mounted.');\n }\n\n /**\n * Cleanup before plugin removal\n */\n beforePluginRemove() {\n try {\n // Remove event listener\n if (this._listener && this.jm && Array.isArray(this.jm.event_handles)) {\n const index = this.jm.event_handles.indexOf(this._listener);\n if (index >= 0) {\n this.jm.event_handles.splice(index, 1);\n }\n }\n\n // Re-enable core mousedown handle\n if (typeof this.jm.enable_event_handle === 'function') {\n this.jm.enable_event_handle('mousedown');\n }\n\n // Remove DOM click handler if attached\n if (this._domClickHandler && this.jm && this.jm.view && this.jm.view.e_nodes) {\n // No off() helper; leave it attached (noop when disabled). If needed, we could rebuild view.\n this._domClickHandler = null;\n }\n\n // Restore API wrappers\n if (this._original_select_node) {\n this.jm.select_node = this._original_select_node;\n }\n if (this._original_select_clear) {\n this.jm.select_clear = this._original_select_clear;\n }\n\n // Remove API namespace\n if (this.jm && Object.prototype.hasOwnProperty.call(this.jm, 'multiSelect')) {\n delete this.jm.multiSelect;\n }\n\n this._mounted = false;\n } catch (e) {\n logger.error('[multiSelect] remove failed:', e);\n }\n }\n\n // Runtime enable/disable and options update\n setEnabled(flag) {\n const next = !!flag;\n if (this._enabled === next) return;\n this._enabled = next;\n // Keep options in sync for getOptions()\n this.options.enable_multi_select = this._enabled;\n if (this._enabled) {\n if (typeof this.jm.disable_event_handle === 'function') {\n this.jm.disable_event_handle('mousedown');\n }\n } else {\n if (typeof this.jm.enable_event_handle === 'function') {\n this.jm.enable_event_handle('mousedown');\n }\n }\n }\n\n setOptions(partial) {\n const next = Object.assign({}, partial || {});\n // Update plugin-level options\n this.options = Object.assign({}, this.options, next);\n // Propagate to core so that _get_multi_select_mode sees changes\n if (this._core && this._core.options) {\n this._core.options = Object.assign({}, this._core.options, next);\n }\n }\n\n /**\n * Cleanup before plugin destroy\n */\n beforePluginDestroy() {\n logger.debug('[multiSelect] beforePluginDestroy');\n this.beforePluginRemove();\n }\n}\n\n// Export for compatibility\nexport { MultiSelectCore };\nexport default MultiSelectPlugin;\n"],"names":["String","prototype","startsWith","p","this","slice","length","EventType","LogLevel","_noop","logger","console","level","log","debug","info","warn","error","log_level","Plugin","static","constructor","jm","pluginOpt","options","beforePluginRemove","beforePluginDestroy","DEFAULT_OPTIONS","enable_multi_select","include_descendants","shift_simple_mode","allow_ctrl","allow_shift","filter","MultiSelectCore","_ensure_selection_state","_selection_mode","_last_selected_node","mind","selected_nodes","Set","view","multi_selected_nodes","Map","get_selected_nodes","Array","from","map","node","id","is_node_selected","nodeObj","_resolve_node","has","select_node","_clear_selection_state","selected","add","_mark_node_selected","nodeIds","_invoke_select_event","evt","data","nodes","select_clear","toggle_node_selection","layout","is_visible","_deselect_subtree","_append_selection","toggle_subtree_selection","opts","includeDesc","_collect_subtree_nodes","includeChildren","respectFilter","skipRootFilter","removed","_remove_selection","_derive_selection_mode","n","added","focusNode","ancestors","parent","_ensure_ancestor_selection","requireAncestorChainSelected","allAdded","concat","get_selection_mode","_handle_node_click","payload","e","element","mode","_get_multi_select_mode","get_node","_range_select_nodes","_handle_node_removed","delete","_unmark_node_selected","shiftKey","ctrlKey","metaKey","i","push","_mark_nodes_selected","_unmark_nodes_selected","size","_clear_all_selected_nodes_view","clear","_get_selection_filter","result","collect","isRoot","shouldInclude","children","addedMap","Object","create","path","shouldAdd","j","focus","nodeId","selectedArray","anchor","nodesBetween","_find_nodes_between","toAdd","to","allNodes","root","fromIndex","toIndex","start","Math","min","end","max","count","selected_node","_data","classList","test","className","set","remove","replace","values","invoke_event_handle","MultiSelectPlugin","super","assign","_mounted","_core","_listener","_enabled","_initCore","plugin","_mountAPI","type","add_event_listener","_original_select_node","bind","_original_select_clear","$","_domClickHandler","target","currentTarget","node_id","get_binded_nodeid","is_node","preventDefault","stopPropagation","stopImmediatePropagation","e_nodes","on","disable_event_handle","api","getOptions","base","enable","setEnabled","disable","flag","setOptions","partial","defineProperty","value","configurable","enumerable","writable","isArray","event_handles","index","indexOf","splice","enable_event_handle","hasOwnProperty","call","multiSelect","next"],"mappings":"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"}
1
+ {"version":3,"file":"jsmind.multi-select.js","sources":["../src/jsmind.common.js","../src/jsmind.plugin.js","../src/plugins/multi-select/jsmind.multi-select.js"],"sourcesContent":["/**\n * @license BSD\n * @copyright 2014-2025 UmbraCi\n *\n * Project Home:\n * https://github.com/UmbraCi/jsmind/\n */\n\n/**\n * Library version string.\n * @type {string}\n */\nexport const __version__ = '0.9.1';\n/**\n * Library author.\n * @type {string}\n */\nexport const __author__ = 'UmbraCi';\n\nif (typeof String.prototype.startsWith != 'function') {\n String.prototype.startsWith = function (p) {\n return this.slice(0, p.length) === p;\n };\n}\n\n/**\n * Direction constants and parser.\n * @typedef {{left:number,center:number,right:number,of:(dir:(string|number))=>number|undefined}} DirectionType\n */\n/** @type {DirectionType} */\nexport const Direction = {\n left: -1,\n center: 0,\n right: 1,\n of: function (dir) {\n if (!dir || dir === -1 || dir === 0 || dir === 1) {\n return dir;\n }\n if (dir === '-1' || dir === '0' || dir === '1') {\n return parseInt(dir);\n }\n if (dir.toLowerCase() === 'left') {\n return this.left;\n }\n if (dir.toLowerCase() === 'right') {\n return this.right;\n }\n if (dir.toLowerCase() === 'center') {\n return this.center;\n }\n },\n};\n/** @enum {number} */\nexport const EventType = { show: 1, resize: 2, edit: 3, select: 4, reset: 5, history_change: 6 };\n/** @enum {number} */\nexport const Key = { meta: 1 << 13, ctrl: 1 << 12, alt: 1 << 11, shift: 1 << 10 };\n/** @enum {number} */\nexport const LogLevel = { debug: 1, info: 2, warn: 3, error: 4, disable: 9 };\n\n// an noop function define\nvar _noop = function () {};\n/**\n * Logger facade with dynamic level.\n * @type {{level:(lvl:number)=>void,log:Function,debug:Function,info:Function,warn:Function,error:Function}}\n */\nexport let logger =\n typeof console === 'undefined'\n ? {\n level: _noop,\n log: _noop,\n debug: _noop,\n info: _noop,\n warn: _noop,\n error: _noop,\n }\n : {\n level: setup_logger_level,\n log: console.log,\n debug: console.debug,\n info: console.info,\n warn: console.warn,\n error: console.error,\n };\n\n/**\n * Set logger level.\n * @param {number} log_level\n */\nfunction setup_logger_level(log_level) {\n if (log_level > LogLevel.debug) {\n logger.debug = _noop;\n } else {\n logger.debug = console.debug;\n }\n if (log_level > LogLevel.info) {\n logger.info = _noop;\n } else {\n logger.info = console.info;\n }\n if (log_level > LogLevel.warn) {\n logger.warn = _noop;\n } else {\n logger.warn = console.warn;\n }\n if (log_level > LogLevel.error) {\n logger.error = _noop;\n } else {\n logger.error = console.error;\n }\n}\n","/**\n * @license BSD\n * @copyright 2014-2025 UmbraCi\n *\n * Project Home:\n * https://github.com/UmbraCi/jsmind/\n */\n\nimport { logger } from './jsmind.common.js';\n\n/**\n * Plugin Manager\n * Manages plugin lifecycle with synchronous initialization,\n * preload support, and lifecycle hooks.\n */\nexport class PluginManager {\n /**\n * @param {import('./jsmind.js').default} jm - jsMind instance\n */\n constructor(jm) {\n this.jm = jm;\n /** @type {Map<string, Plugin>} */\n this.plugins = new Map();\n }\n\n /**\n * Initialize preload plugins (before core modules)\n */\n initPreloadPlugins() {\n const preloadPlugins = this.jm.constructor.pluginList.filter(d => d.preload);\n logger.info('Initializing ' + preloadPlugins.length + ' preload plugins');\n preloadPlugins.forEach(descriptor => {\n this._initPlugin(descriptor);\n });\n }\n\n /**\n * Initialize normal plugins (after core modules)\n */\n initNormalPlugins() {\n const normalPlugins = this.jm.constructor.pluginList.filter(d => !d.preload);\n logger.info('Initializing ' + normalPlugins.length + ' normal plugins');\n normalPlugins.forEach(descriptor => {\n this._initPlugin(descriptor);\n });\n }\n\n /**\n * Internal method: Initialize a single plugin\n * @param {PluginDescriptor} descriptor\n * @private\n */\n _initPlugin(descriptor) {\n try {\n const { PluginClass, pluginOpt } = descriptor;\n\n if (!PluginClass.instanceName) {\n throw new Error('Plugin ' + PluginClass.name + ' must define static instanceName');\n }\n if (this.plugins.has(PluginClass.instanceName)) {\n logger.warn(\n 'Plugin ' + PluginClass.instanceName + ' already exists, will be replaced'\n );\n }\n\n const instance = new PluginClass({\n jm: this.jm,\n pluginOpt: pluginOpt || {},\n });\n\n this.plugins.set(PluginClass.instanceName, instance);\n this.jm[PluginClass.instanceName] = instance;\n descriptor.instance = instance;\n\n logger.info('Plugin ' + PluginClass.instanceName + ' initialized');\n } catch (error) {\n logger.error('Failed to initialize plugin ' + descriptor.PluginClass.name + ':', error);\n }\n }\n\n /**\n * Remove a plugin\n * @param {typeof Plugin} PluginClass\n */\n removePlugin(PluginClass) {\n const instanceName = PluginClass.instanceName;\n if (!instanceName) {\n return;\n }\n\n const instance = this.plugins.get(instanceName);\n if (!instance) {\n return;\n }\n\n try {\n if (typeof instance.beforePluginRemove === 'function') {\n instance.beforePluginRemove();\n }\n\n this.plugins.delete(instanceName);\n delete this.jm[instanceName];\n\n const list = this.jm.constructor.pluginList;\n const index = list.findIndex(d => d.PluginClass === PluginClass);\n if (index !== -1) {\n list.splice(index, 1);\n }\n\n logger.info('Plugin ' + instanceName + ' removed');\n } catch (error) {\n logger.error('Failed to remove plugin ' + instanceName + ':', error);\n }\n }\n\n /**\n * Destroy all plugins\n */\n destroyAllPlugins() {\n this.plugins.forEach((instance, instanceName) => {\n try {\n if (typeof instance.beforePluginDestroy === 'function') {\n instance.beforePluginDestroy();\n }\n } catch (error) {\n logger.error('Failed to destroy plugin ' + instanceName + ':', error);\n }\n });\n\n this.plugins.clear();\n }\n\n /**\n * Get plugin instance by name\n * @param {string} instanceName\n * @returns {Plugin | undefined}\n */\n getPlugin(instanceName) {\n return this.plugins.get(instanceName);\n }\n}\n\n/**\n * Plugin Base Class\n * Provides standard plugin interface.\n */\nexport class Plugin {\n /**\n * Plugin instance name (must be defined by subclass)\n * @type {string}\n */\n static instanceName = '';\n\n /**\n * Whether to initialize before core modules\n * @type {boolean}\n */\n static preload = false;\n\n /**\n * @param {{ jm: import('./jsmind.js').default, pluginOpt: object }} params\n */\n constructor({ jm, pluginOpt }) {\n this.jm = jm;\n this.options = pluginOpt || {};\n }\n\n /**\n * Called before plugin is removed\n */\n beforePluginRemove() {}\n\n /**\n * Called before jsMind instance is destroyed\n */\n beforePluginDestroy() {\n this.beforePluginRemove();\n }\n}\n\n/**\n * Plugin descriptor\n * @typedef {object} PluginDescriptor\n * @property {typeof Plugin} PluginClass - Plugin class\n * @property {string} instanceName - Plugin instance name\n * @property {boolean} preload - Whether to preload\n * @property {object} pluginOpt - Plugin options\n * @property {Plugin | null} instance - Plugin instance (after initialization)\n */\n","/**\n * @license BSD\n * @copyright 2014-2025 UmbraCi\n *\n * Project Home:\n * https://github.com/UmbraCi/jsmind/\n */\n\nimport { Plugin } from '../../jsmind.plugin.js';\nimport { EventType, logger } from '../../jsmind.common.js';\n\n/**\n * @typedef {Object} MultiSelectOptions\n * @property {boolean} [enable_multi_select=false] - Enable multi-select feature\n * @property {boolean} [enable_ctrl_key_node_selection=true] - Enable Ctrl/Cmd + click toggle\n * @property {boolean} [enable_box_selection=true] - Enable box selection\n * @property {boolean} [use_left_key_selection_right_key_drag=false] - true: left mouse drag; false: Ctrl/Cmd + right drag\n * @property {boolean} [shift_simple_mode] - Deprecated; no effect\n * @property {boolean} [include_descendants] - Deprecated; no effect\n */\n\nconst DEFAULT_OPTIONS = {\n enable_multi_select: false,\n enable_ctrl_key_node_selection: true,\n enable_box_selection: true,\n use_left_key_selection_right_key_drag: false,\n};\n\nclass MultiSelectState {\n constructor(jm) {\n this.jm = jm;\n this._mode = null;\n this._last_selected_node = null;\n this._marked_ids = new Set();\n this._ensure_state();\n }\n\n _ensure_state() {\n if (!this.jm.mind) {\n this.jm.mind = {};\n }\n if (!this.jm.mind.selected_nodes) {\n this.jm.mind.selected_nodes = new Set();\n }\n }\n\n _resolve_node(node) {\n if (!node) {\n return null;\n }\n return typeof node === 'string' ? this.jm.get_node(node) : node;\n }\n\n _is_visible(node) {\n return !!(node && this.jm.layout && this.jm.layout.is_visible(node));\n }\n\n _derive_mode() {\n this._ensure_state();\n const size = this.jm.mind.selected_nodes.size;\n if (size === 0) {\n return null;\n }\n return size === 1 ? 'single' : 'multi';\n }\n\n _set_focus(node) {\n this.jm.mind.selected = node || null;\n if (this.jm.view) {\n this.jm.view.selected_node = node || null;\n }\n this._last_selected_node = node || null;\n }\n\n _mark_node(node) {\n if (!node) {\n return;\n }\n const element = node._data && node._data.view && node._data.view.element;\n if (!element) {\n return;\n }\n if (element.classList) {\n element.classList.add('selected');\n } else if (!/(\\s|^)selected(\\s|$)/.test(element.className)) {\n element.className += ' selected';\n }\n this._marked_ids.add(node.id);\n }\n\n _unmark_node(node) {\n if (!node) {\n return;\n }\n const element = node._data && node._data.view && node._data.view.element;\n if (element) {\n if (element.classList) {\n element.classList.remove('selected');\n } else {\n element.className = element.className.replace(/\\s*selected\\b/i, '');\n }\n }\n this._marked_ids.delete(node.id);\n }\n\n _sync_view_from_state() {\n this._ensure_state();\n\n const selected_ids = new Set();\n const selected_nodes = Array.from(this.jm.mind.selected_nodes);\n for (let i = 0; i < selected_nodes.length; i++) {\n selected_ids.add(selected_nodes[i].id);\n }\n\n const stale_ids = [];\n this._marked_ids.forEach(id => {\n if (!selected_ids.has(id)) {\n stale_ids.push(id);\n }\n });\n for (let i = 0; i < stale_ids.length; i++) {\n const stale_node = this.jm.get_node(stale_ids[i]);\n if (stale_node) {\n this._unmark_node(stale_node);\n } else {\n this._marked_ids.delete(stale_ids[i]);\n }\n }\n\n for (let i = 0; i < selected_nodes.length; i++) {\n this._mark_node(selected_nodes[i]);\n }\n }\n\n _emit(evt, changedNodes, focusNode) {\n const nodeIds = (changedNodes || []).map(node => node.id);\n this.jm.invoke_event_handle(EventType.select, {\n evt,\n data: nodeIds,\n node: focusNode ? focusNode.id : null,\n nodes: nodeIds,\n });\n }\n\n get_selected_nodes() {\n this._ensure_state();\n return Array.from(this.jm.mind.selected_nodes).map(node => node.id);\n }\n\n get_selected_node_set() {\n this._ensure_state();\n return new Set(this.jm.mind.selected_nodes);\n }\n\n is_node_selected(node) {\n const nodeObj = this._resolve_node(node);\n if (!nodeObj) {\n return false;\n }\n this._ensure_state();\n return this.jm.mind.selected_nodes.has(nodeObj);\n }\n\n get_selection_mode() {\n return this._mode;\n }\n\n select_node(node) {\n const nodeObj = this._resolve_node(node);\n if (!this._is_visible(nodeObj)) {\n return;\n }\n\n this._ensure_state();\n const prev = Array.from(this.jm.mind.selected_nodes);\n\n this.jm.mind.selected_nodes.clear();\n this.jm.mind.selected_nodes.add(nodeObj);\n this._set_focus(nodeObj);\n this._mode = 'single';\n this._sync_view_from_state();\n\n const removed = prev.filter(n => n.id !== nodeObj.id);\n if (removed.length) {\n this._emit('multi_deselect', removed, nodeObj);\n }\n this._emit('select_node', [nodeObj], nodeObj);\n }\n\n select_clear() {\n this._ensure_state();\n if (this.jm.mind.selected_nodes.size === 0) {\n this._set_focus(null);\n this._mode = null;\n return;\n }\n\n const removed = Array.from(this.jm.mind.selected_nodes);\n this.jm.mind.selected_nodes.clear();\n this._set_focus(null);\n this._mode = null;\n this._sync_view_from_state();\n this._emit('clear_selection', removed, null);\n }\n\n toggle_node_selection(node) {\n const nodeObj = this._resolve_node(node);\n if (!this._is_visible(nodeObj)) {\n return;\n }\n\n this._ensure_state();\n if (this.jm.mind.selected_nodes.has(nodeObj)) {\n this.jm.mind.selected_nodes.delete(nodeObj);\n if (this.jm.mind.selected === nodeObj) {\n const remain = Array.from(this.jm.mind.selected_nodes);\n this._set_focus(remain.length ? remain[remain.length - 1] : null);\n }\n this._mode = this._derive_mode();\n this._sync_view_from_state();\n this._emit('multi_deselect', [nodeObj], this.jm.mind.selected || null);\n return;\n }\n\n this.jm.mind.selected_nodes.add(nodeObj);\n this._set_focus(nodeObj);\n this._mode = this._derive_mode();\n this._sync_view_from_state();\n this._emit('multi_select', [nodeObj], nodeObj);\n }\n\n set_selection(nodes, opts) {\n this._ensure_state();\n const options = opts || {};\n const normalized = [];\n const uniq = new Set();\n for (let i = 0; i < nodes.length; i++) {\n const node = nodes[i];\n if (!node || !this._is_visible(node) || uniq.has(node.id)) {\n continue;\n }\n uniq.add(node.id);\n normalized.push(node);\n }\n\n const prev = Array.from(this.jm.mind.selected_nodes);\n this.jm.mind.selected_nodes.clear();\n for (let i = 0; i < normalized.length; i++) {\n this.jm.mind.selected_nodes.add(normalized[i]);\n }\n\n const focus = options.focusNode || (normalized.length ? normalized[normalized.length - 1] : null);\n this._set_focus(focus);\n this._mode = this._derive_mode();\n this._sync_view_from_state();\n\n if (options.emit === false) {\n return;\n }\n\n const prevIds = new Set(prev.map(n => n.id));\n const nextIds = new Set(normalized.map(n => n.id));\n\n const added = normalized.filter(n => !prevIds.has(n.id));\n const removed = prev.filter(n => !nextIds.has(n.id));\n\n if (removed.length) {\n this._emit('multi_deselect', removed, focus);\n }\n if (added.length) {\n this._emit('multi_select', added, focus);\n }\n if (!added.length && !removed.length && focus && options.forceSelectEvent) {\n this._emit('select_node', [focus], focus);\n }\n }\n\n prune_removed(nodeIds) {\n this._ensure_state();\n if (!nodeIds || !nodeIds.length || this.jm.mind.selected_nodes.size === 0) {\n return;\n }\n\n const ids = new Set(nodeIds);\n const removed = [];\n this.jm.mind.selected_nodes.forEach(node => {\n if (ids.has(node.id)) {\n removed.push(node);\n }\n });\n if (!removed.length) {\n return;\n }\n\n for (let i = 0; i < removed.length; i++) {\n this.jm.mind.selected_nodes.delete(removed[i]);\n }\n\n if (this.jm.mind.selected && ids.has(this.jm.mind.selected.id)) {\n const remain = Array.from(this.jm.mind.selected_nodes);\n this._set_focus(remain.length ? remain[remain.length - 1] : null);\n }\n this._mode = this._derive_mode();\n this._sync_view_from_state();\n this._emit('multi_deselect', removed, this.jm.mind.selected || null);\n }\n}\n\nclass SelectBox {\n constructor(jm, state, getOptions, isEnabled) {\n this.jm = jm;\n this.state = state;\n this.getOptions = getOptions;\n this.isEnabled = isEnabled;\n\n this._dragging = false;\n this._active = false;\n this._append_mode = false;\n this._base_selected = [];\n this._start_client = { x: 0, y: 0 };\n this._current_client = { x: 0, y: 0 };\n this._overlay = null;\n\n this._on_mouse_move = this._on_mouse_move.bind(this);\n this._on_mouse_up = this._on_mouse_up.bind(this);\n }\n\n is_selecting() {\n return this._dragging;\n }\n\n should_start(e) {\n if (!this.isEnabled()) {\n return false;\n }\n const options = this.getOptions();\n if (!options.enable_box_selection) {\n return false;\n }\n\n if (options.use_left_key_selection_right_key_drag) {\n return e.which === 1;\n }\n\n return (e.ctrlKey || e.metaKey) && e.which === 3;\n }\n\n start(e) {\n if (this._dragging) {\n return;\n }\n\n this._dragging = true;\n this._active = false;\n this._append_mode = !!(e.ctrlKey || e.metaKey);\n this._base_selected = Array.from(this.state.get_selected_node_set());\n this._start_client = { x: e.clientX, y: e.clientY };\n this._current_client = { x: e.clientX, y: e.clientY };\n\n if (!this._append_mode) {\n this.state.set_selection([], { emit: false });\n }\n\n this._ensure_overlay();\n this._update_overlay();\n document.addEventListener('mousemove', this._on_mouse_move, true);\n document.addEventListener('mouseup', this._on_mouse_up, true);\n }\n\n _ensure_overlay() {\n if (this._overlay) {\n return;\n }\n const overlay = document.createElement('div');\n overlay.style.position = 'fixed';\n overlay.style.pointerEvents = 'none';\n overlay.style.border = '1px solid #0984e3';\n overlay.style.background = 'rgba(9,132,227,0.2)';\n overlay.style.zIndex = '2147483000';\n overlay.style.display = 'none';\n document.body.appendChild(overlay);\n this._overlay = overlay;\n }\n\n _update_overlay() {\n if (!this._overlay) {\n return;\n }\n const x = Math.min(this._start_client.x, this._current_client.x);\n const y = Math.min(this._start_client.y, this._current_client.y);\n const w = Math.abs(this._start_client.x - this._current_client.x);\n const h = Math.abs(this._start_client.y - this._current_client.y);\n\n this._overlay.style.left = x + 'px';\n this._overlay.style.top = y + 'px';\n this._overlay.style.width = w + 'px';\n this._overlay.style.height = h + 'px';\n this._overlay.style.display = this._active ? 'block' : 'none';\n }\n\n _collect_nodes_in_rect() {\n if (!this.jm.mind || !this.jm.mind.nodes) {\n return [];\n }\n\n const x1 = Math.min(this._start_client.x, this._current_client.x);\n const y1 = Math.min(this._start_client.y, this._current_client.y);\n const x2 = Math.max(this._start_client.x, this._current_client.x);\n const y2 = Math.max(this._start_client.y, this._current_client.y);\n\n const values = Object.values(this.jm.mind.nodes);\n const selected = [];\n for (let i = 0; i < values.length; i++) {\n const node = values[i];\n if (!this.jm.layout.is_visible(node)) {\n continue;\n }\n const element = node._data && node._data.view && node._data.view.element;\n if (!element) {\n continue;\n }\n const rect = element.getBoundingClientRect();\n const overlap = !(rect.right < x1 || rect.left > x2 || rect.bottom < y1 || rect.top > y2);\n if (overlap) {\n selected.push(node);\n }\n }\n return selected;\n }\n\n _on_mouse_move(e) {\n if (!this._dragging) {\n return;\n }\n this._current_client = { x: e.clientX, y: e.clientY };\n const dx = Math.abs(this._start_client.x - this._current_client.x);\n const dy = Math.abs(this._start_client.y - this._current_client.y);\n this._active = dx > 4 || dy > 4;\n\n if (!this._active) {\n this._update_overlay();\n return;\n }\n\n const hitNodes = this._collect_nodes_in_rect();\n const merged = this._append_mode\n ? this._base_selected.concat(hitNodes.filter(node => !this._base_selected.find(n => n.id === node.id)))\n : hitNodes;\n\n this.state.set_selection(merged, {\n focusNode: merged.length ? merged[merged.length - 1] : null,\n emit: false,\n });\n this._update_overlay();\n\n e.preventDefault();\n e.stopPropagation();\n }\n\n _on_mouse_up(e) {\n if (!this._dragging) {\n return;\n }\n\n const wasActive = this._active;\n this._dragging = false;\n this._active = false;\n this._base_selected = [];\n if (this._overlay) {\n this._overlay.style.display = 'none';\n }\n\n document.removeEventListener('mousemove', this._on_mouse_move, true);\n document.removeEventListener('mouseup', this._on_mouse_up, true);\n\n if (wasActive) {\n const current = Array.from(this.state.get_selected_node_set());\n this.state.set_selection(current, {\n focusNode: current.length ? current[current.length - 1] : null,\n emit: true,\n });\n e.preventDefault();\n e.stopPropagation();\n }\n }\n\n destroy() {\n this._dragging = false;\n document.removeEventListener('mousemove', this._on_mouse_move, true);\n document.removeEventListener('mouseup', this._on_mouse_up, true);\n if (this._overlay && this._overlay.parentNode) {\n this._overlay.parentNode.removeChild(this._overlay);\n }\n this._overlay = null;\n }\n}\n\nclass MultiSelectInteraction {\n constructor(jm, state, getOptions, isEnabled) {\n this.jm = jm;\n this.state = state;\n this.getOptions = getOptions;\n this.isEnabled = isEnabled;\n this.selectBox = new SelectBox(jm, state, getOptions, isEnabled);\n\n this._on_nodes_mousedown = this._on_nodes_mousedown.bind(this);\n this._on_panel_contextmenu = this._on_panel_contextmenu.bind(this);\n }\n\n bind() {\n if (!this.jm.view || !this.jm.view.e_nodes || !this.jm.view.e_panel) {\n logger.warn('[multiSelect] view is not ready, interaction skipped');\n return;\n }\n this.jm.view.e_nodes.addEventListener('mousedown', this._on_nodes_mousedown, true);\n this.jm.view.e_panel.addEventListener('contextmenu', this._on_panel_contextmenu, true);\n }\n\n _on_panel_contextmenu(e) {\n if (!this.isEnabled()) {\n return;\n }\n if (this.selectBox.is_selecting()) {\n e.preventDefault();\n e.stopPropagation();\n }\n }\n\n _on_nodes_mousedown(e) {\n if (!this.isEnabled()) {\n return;\n }\n\n const options = this.getOptions();\n const element = e.target || e.currentTarget;\n const nodeId = this.jm.view.get_binded_nodeid(element);\n const isNode = nodeId && this.jm.view.is_node(element);\n\n if (isNode) {\n if (options.enable_ctrl_key_node_selection && (e.ctrlKey || e.metaKey)) {\n this.state.toggle_node_selection(nodeId);\n } else {\n this.state.select_node(nodeId);\n }\n e.preventDefault();\n e.stopPropagation();\n if (e.stopImmediatePropagation) {\n e.stopImmediatePropagation();\n }\n return;\n }\n\n if (!nodeId && this.selectBox.should_start(e)) {\n this.selectBox.start(e);\n e.preventDefault();\n e.stopPropagation();\n if (e.stopImmediatePropagation) {\n e.stopImmediatePropagation();\n }\n return;\n }\n\n if (!nodeId && !(e.ctrlKey || e.metaKey || e.shiftKey)) {\n this.state.select_clear();\n e.preventDefault();\n e.stopPropagation();\n if (e.stopImmediatePropagation) {\n e.stopImmediatePropagation();\n }\n }\n }\n\n destroy() {\n if (this.jm.view && this.jm.view.e_nodes) {\n this.jm.view.e_nodes.removeEventListener('mousedown', this._on_nodes_mousedown, true);\n }\n if (this.jm.view && this.jm.view.e_panel) {\n this.jm.view.e_panel.removeEventListener('contextmenu', this._on_panel_contextmenu, true);\n }\n this.selectBox.destroy();\n }\n}\n\nexport class MultiSelectPlugin extends Plugin {\n static instanceName = 'multiSelectPlugin';\n static preload = false;\n\n constructor({ jm, pluginOpt }) {\n super({ jm, pluginOpt });\n this.options = Object.assign({}, DEFAULT_OPTIONS, pluginOpt || {});\n this._enabled = !!this.options.enable_multi_select;\n\n this._state = new MultiSelectState(jm);\n this._interaction = new MultiSelectInteraction(\n jm,\n this._state,\n () => this.options,\n () => this._enabled\n );\n\n this._listener = (type, data) => {\n if (!this._enabled) {\n return;\n }\n if (type === EventType.show) {\n this._state.select_clear();\n return;\n }\n if (type === EventType.edit && data && data.evt === 'remove_node') {\n this._state.prune_removed(data.data || []);\n }\n };\n\n this._mountAPI();\n this.jm.add_event_listener(this._listener);\n this._interaction.bind();\n\n if (this._enabled && typeof this.jm.disable_event_handle === 'function') {\n this.jm.disable_event_handle('mousedown');\n }\n }\n\n _mountAPI() {\n const plugin = this;\n const api = {\n get_selected_nodes: () => plugin._state.get_selected_nodes(),\n is_node_selected: node => plugin._state.is_node_selected(node),\n select_node: node => plugin._state.select_node(node),\n select_clear: () => plugin._state.select_clear(),\n toggle_node_selection: node => plugin._state.toggle_node_selection(node),\n get_selection_mode: () => plugin._state.get_selection_mode(),\n getOptions: () => Object.assign({}, plugin.options, { enable_multi_select: plugin._enabled }),\n enable: () => plugin.setEnabled(true),\n disable: () => plugin.setEnabled(false),\n setEnabled: flag => plugin.setEnabled(flag),\n setOptions: partial => plugin.setOptions(partial),\n };\n\n Object.defineProperty(this.jm, 'multiSelect', {\n value: api,\n configurable: true,\n enumerable: false,\n writable: false,\n });\n }\n\n setEnabled(flag) {\n this._enabled = !!flag;\n this.options.enable_multi_select = this._enabled;\n if (this._enabled) {\n if (typeof this.jm.disable_event_handle === 'function') {\n this.jm.disable_event_handle('mousedown');\n }\n } else if (typeof this.jm.enable_event_handle === 'function') {\n this.jm.enable_event_handle('mousedown');\n }\n }\n\n setOptions(partial) {\n this.options = Object.assign({}, this.options, partial || {});\n }\n\n beforePluginRemove() {\n if (typeof this.jm.enable_event_handle === 'function') {\n this.jm.enable_event_handle('mousedown');\n }\n this._enabled = false;\n\n if (this._interaction) {\n this._interaction.destroy();\n }\n if (this._listener && this.jm && Array.isArray(this.jm.event_handles)) {\n const index = this.jm.event_handles.indexOf(this._listener);\n if (index >= 0) {\n this.jm.event_handles.splice(index, 1);\n }\n }\n\n if (this.jm && Object.prototype.hasOwnProperty.call(this.jm, 'multiSelect')) {\n delete this.jm.multiSelect;\n }\n }\n\n beforePluginDestroy() {\n this.beforePluginRemove();\n }\n}\n\nexport const MultiSelectCore = MultiSelectState;\nexport { MultiSelectState, MultiSelectInteraction, SelectBox };\nexport default MultiSelectPlugin;\n"],"names":["String","prototype","startsWith","p","this","slice","length","EventType","LogLevel","_noop","logger","console","level","log","debug","info","warn","error","log_level","Plugin","static","constructor","jm","pluginOpt","options","beforePluginRemove","beforePluginDestroy","DEFAULT_OPTIONS","enable_multi_select","enable_ctrl_key_node_selection","enable_box_selection","use_left_key_selection_right_key_drag","MultiSelectState","_mode","_last_selected_node","_marked_ids","Set","_ensure_state","mind","selected_nodes","_resolve_node","node","get_node","_is_visible","layout","is_visible","_derive_mode","size","_set_focus","selected","view","selected_node","_mark_node","element","_data","classList","add","test","className","id","_unmark_node","remove","replace","delete","_sync_view_from_state","selected_ids","Array","from","i","stale_ids","forEach","has","push","stale_node","_emit","evt","changedNodes","focusNode","nodeIds","map","invoke_event_handle","data","nodes","get_selected_nodes","get_selected_node_set","is_node_selected","nodeObj","get_selection_mode","select_node","prev","clear","removed","filter","n","select_clear","toggle_node_selection","remain","set_selection","opts","normalized","uniq","focus","emit","prevIds","nextIds","added","forceSelectEvent","prune_removed","ids","SelectBox","state","getOptions","isEnabled","_dragging","_active","_append_mode","_base_selected","_start_client","x","y","_current_client","_overlay","_on_mouse_move","bind","_on_mouse_up","is_selecting","should_start","e","which","ctrlKey","metaKey","start","clientX","clientY","_ensure_overlay","_update_overlay","document","addEventListener","overlay","createElement","style","position","pointerEvents","border","background","zIndex","display","body","appendChild","Math","min","w","abs","h","left","top","width","height","_collect_nodes_in_rect","x1","y1","x2","max","y2","values","Object","rect","getBoundingClientRect","right","bottom","dx","dy","hitNodes","merged","concat","find","preventDefault","stopPropagation","wasActive","removeEventListener","current","destroy","parentNode","removeChild","MultiSelectInteraction","selectBox","_on_nodes_mousedown","_on_panel_contextmenu","e_nodes","e_panel","target","currentTarget","nodeId","get_binded_nodeid","is_node","stopImmediatePropagation","shiftKey","MultiSelectPlugin","super","assign","_enabled","_state","_interaction","_listener","type","_mountAPI","add_event_listener","disable_event_handle","plugin","api","enable","setEnabled","disable","flag","setOptions","partial","defineProperty","value","configurable","enumerable","writable","enable_event_handle","isArray","event_handles","index","indexOf","splice","hasOwnProperty","call","multiSelect","MultiSelectCore"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@umbraci/jsmind",
3
- "version": "1.0.0-beta",
3
+ "version": "1.0.1-beta",
4
4
  "description": "jsMind is a pure javascript library for mindmap, it base on html5 canvas. jsMind was released under BSD license, you can embed it in any project, if only you observe the license.",
5
5
  "main": "lib/jsmind.js",
6
6
  "module": "es/jsmind.js",
@@ -0,0 +1,109 @@
1
+ export class MultiSelectPlugin extends Plugin {
2
+ constructor({ jm, pluginOpt }: {
3
+ jm: any;
4
+ pluginOpt: any;
5
+ });
6
+ options: any;
7
+ _enabled: boolean;
8
+ _state: MultiSelectState;
9
+ _interaction: MultiSelectInteraction;
10
+ _listener: (type: any, data: any) => void;
11
+ _mountAPI(): void;
12
+ setEnabled(flag: any): void;
13
+ setOptions(partial: any): void;
14
+ }
15
+ export const MultiSelectCore: typeof MultiSelectState;
16
+ export default MultiSelectPlugin;
17
+ export type MultiSelectOptions = {
18
+ /**
19
+ * - Enable multi-select feature
20
+ */
21
+ enable_multi_select?: boolean;
22
+ /**
23
+ * - Enable Ctrl/Cmd + click toggle
24
+ */
25
+ enable_ctrl_key_node_selection?: boolean;
26
+ /**
27
+ * - Enable box selection
28
+ */
29
+ enable_box_selection?: boolean;
30
+ /**
31
+ * - true: left mouse drag; false: Ctrl/Cmd + right drag
32
+ */
33
+ use_left_key_selection_right_key_drag?: boolean;
34
+ /**
35
+ * - Deprecated; no effect
36
+ */
37
+ shift_simple_mode?: boolean;
38
+ /**
39
+ * - Deprecated; no effect
40
+ */
41
+ include_descendants?: boolean;
42
+ };
43
+ import { Plugin } from '../../jsmind.plugin.js';
44
+ export class MultiSelectState {
45
+ constructor(jm: any);
46
+ jm: any;
47
+ _mode: string;
48
+ _last_selected_node: any;
49
+ _marked_ids: Set<any>;
50
+ _ensure_state(): void;
51
+ _resolve_node(node: any): any;
52
+ _is_visible(node: any): boolean;
53
+ _derive_mode(): "single" | "multi";
54
+ _set_focus(node: any): void;
55
+ _mark_node(node: any): void;
56
+ _unmark_node(node: any): void;
57
+ _sync_view_from_state(): void;
58
+ _emit(evt: any, changedNodes: any, focusNode: any): void;
59
+ get_selected_nodes(): any[];
60
+ get_selected_node_set(): Set<any>;
61
+ is_node_selected(node: any): any;
62
+ get_selection_mode(): string;
63
+ select_node(node: any): void;
64
+ select_clear(): void;
65
+ toggle_node_selection(node: any): void;
66
+ set_selection(nodes: any, opts: any): void;
67
+ prune_removed(nodeIds: any): void;
68
+ }
69
+ export class MultiSelectInteraction {
70
+ constructor(jm: any, state: any, getOptions: any, isEnabled: any);
71
+ jm: any;
72
+ state: any;
73
+ getOptions: any;
74
+ isEnabled: any;
75
+ selectBox: SelectBox;
76
+ _on_nodes_mousedown(e: any): void;
77
+ _on_panel_contextmenu(e: any): void;
78
+ bind(): void;
79
+ destroy(): void;
80
+ }
81
+ export class SelectBox {
82
+ constructor(jm: any, state: any, getOptions: any, isEnabled: any);
83
+ jm: any;
84
+ state: any;
85
+ getOptions: any;
86
+ isEnabled: any;
87
+ _dragging: boolean;
88
+ _active: boolean;
89
+ _append_mode: boolean;
90
+ _base_selected: any[];
91
+ _start_client: {
92
+ x: number;
93
+ y: number;
94
+ };
95
+ _current_client: {
96
+ x: number;
97
+ y: number;
98
+ };
99
+ _overlay: HTMLDivElement;
100
+ _on_mouse_move(e: any): void;
101
+ _on_mouse_up(e: any): void;
102
+ is_selecting(): boolean;
103
+ should_start(e: any): boolean;
104
+ start(e: any): void;
105
+ _ensure_overlay(): void;
106
+ _update_overlay(): void;
107
+ _collect_nodes_in_rect(): any[];
108
+ destroy(): void;
109
+ }