@umbraci/jsmind 0.10.13 → 0.10.15
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.
- package/LICENSE +24 -24
- package/README.md +116 -116
- package/dist/jsmind.copy-paste.js +9 -0
- package/dist/jsmind.copy-paste.js.map +1 -0
- package/dist/jsmind.draggable-node.js +1 -1
- package/dist/jsmind.draggable-node.js.map +1 -1
- package/dist/jsmind.history.js +1 -1
- package/dist/jsmind.history.js.map +1 -1
- package/dist/jsmind.js +1 -1
- package/dist/jsmind.js.map +1 -1
- package/dist/jsmind.multi-select.js +9 -0
- package/dist/jsmind.multi-select.js.map +1 -0
- package/dist/jsmind.multiline-text.js +1 -1
- package/dist/jsmind.multiline-text.js.map +1 -1
- package/dist/jsmind.screenshot.js +1 -1
- package/dist/jsmind.screenshot.js.map +1 -1
- package/es/jsmind.copy-paste.js +9 -0
- package/es/jsmind.copy-paste.js.map +1 -0
- package/es/jsmind.draggable-node.js +1 -1
- package/es/jsmind.draggable-node.js.map +1 -1
- package/es/jsmind.history.js +1 -1
- package/es/jsmind.history.js.map +1 -1
- package/es/jsmind.js +1 -1
- package/es/jsmind.js.map +1 -1
- package/es/jsmind.multi-select.js +9 -0
- package/es/jsmind.multi-select.js.map +1 -0
- package/es/jsmind.multiline-text.js +1 -1
- package/es/jsmind.multiline-text.js.map +1 -1
- package/es/jsmind.screenshot.js +1 -1
- package/es/jsmind.screenshot.js.map +1 -1
- package/lib/jsmind.copy-paste.js +9 -0
- package/lib/jsmind.copy-paste.js.map +1 -0
- package/lib/jsmind.draggable-node.js +1 -1
- package/lib/jsmind.draggable-node.js.map +1 -1
- package/lib/jsmind.history.js +1 -1
- package/lib/jsmind.history.js.map +1 -1
- package/lib/jsmind.js +1 -1
- package/lib/jsmind.js.map +1 -1
- package/lib/jsmind.multi-select.js +9 -0
- package/lib/jsmind.multi-select.js.map +1 -0
- package/lib/jsmind.multiline-text.js +1 -1
- package/lib/jsmind.multiline-text.js.map +1 -1
- package/lib/jsmind.screenshot.js +1 -1
- package/lib/jsmind.screenshot.js.map +1 -1
- package/package.json +115 -111
- package/style/jsmind.css +408 -408
- package/types/generated/jsmind.option.d.ts +0 -1
- package/types/tsconfig.declaration.json +19 -19
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license BSD-3-Clause
|
|
3
|
+
* @copyright 2014-2025 hizzgdev@163.com
|
|
4
|
+
*
|
|
5
|
+
* Project Home:
|
|
6
|
+
* https://github.com/hizzgdev/jsmind/
|
|
7
|
+
*/
|
|
8
|
+
"function"!=typeof String.prototype.startsWith&&(String.prototype.startsWith=function(e){return this.slice(0,e.length)===e});const e=1,t=4,s=1,i=2,n=3,l=4;var o=function(){};let _="undefined"==typeof console?{level:o,log:o,debug:o,info:o,warn:o,error:o}:{level:function(e){_.debug=e>s?o:console.debug;_.info=e>i?o:console.info;_.warn=e>n?o:console.warn;_.error=e>l?o:console.error},log:console.log,debug:console.debug,info:console.info,warn:console.warn,error:console.error};class d{static instanceName="";static preload=!1;constructor({jm:e,pluginOpt:t}){this.jm=e,this.options=t||{}}beforePluginRemove(){}beforePluginDestroy(){this.beforePluginRemove()}}const c={enable_multi_select:!1,include_descendants:!0,shift_simple_mode:!1,allow_ctrl:!0,allow_shift:!0,filter:null};class r{constructor(e,t){this.jm=e,this.options=t,this._ensure_selection_state(),this._selection_mode=null,this._last_selected_node=null}_ensure_selection_state(){this.jm.mind||(this.jm.mind={}),this.jm.mind.selected_nodes||(this.jm.mind.selected_nodes=new Set),this.jm.view||(this.jm.view={}),this.jm.view.multi_selected_nodes||(this.jm.view.multi_selected_nodes=new Map)}get_selected_nodes(){return this._ensure_selection_state(),this.jm.mind&&this.jm.mind.selected_nodes?Array.from(this.jm.mind.selected_nodes).map(e=>e.id):[]}is_node_selected(e){const t=this._resolve_node(e);return!(!t||!this.jm.mind)&&(this._ensure_selection_state(),this.jm.mind.selected_nodes.has(t))}select_node(e){const t=this._resolve_node(e);if(!t)return void _.error("[multiSelect] node not found: "+e);if(this._ensure_selection_state(),this._clear_selection_state(),this._ensure_selection_state(),!this.jm.mind||!this.jm.mind.selected_nodes)return void _.error("[multiSelect] jm.mind or selected_nodes is not available");this.jm.mind.selected=t,this._last_selected_node=t,this.jm.mind.selected_nodes.has(t)||this.jm.mind.selected_nodes.add(t),this._mark_node_selected(t),this._selection_mode="single";const s=[t.id];this._invoke_select_event({evt:"select_node",data:s,node:t.id,nodes:s})}select_clear(){this._ensure_selection_state(),this.jm.mind&&(this.jm.mind.selected=null,this._last_selected_node=null,this._clear_selection_state())}toggle_node_selection(e){const t=this._resolve_node(e);if(t&&this.jm.layout.is_visible(t))if(this._ensure_selection_state(),this.jm.mind.selected_nodes.has(t))this._deselect_subtree(t);else{this._selection_mode="multi";this._append_selection([t]).length&&(this.jm.mind.selected=t,this._last_selected_node=t,this._invoke_select_event({evt:"multi_select",data:[t.id],node:t.id,nodes:[t.id]}))}}toggle_subtree_selection(e,t){const s=this._resolve_node(e);if(!s)return void _.error("[multiSelect] node not found: "+e);if(!this.jm.layout.is_visible(s))return;const i=t&&void 0!==t.include_descendants?!!t.include_descendants:!1!==this.options.include_descendants;if(this._ensure_selection_state(),this.jm.mind.selected_nodes.has(s)&&"multi"===this._selection_mode){let e=this._collect_subtree_nodes(s,{includeChildren:!0,respectFilter:!1,skipRootFilter:!1});e.length||(e=[s]);const t=this._remove_selection(e);if(t.length){this.jm.mind.selected&&this.jm.mind.selected.id===s.id&&(this.jm.mind.selected=null),this._selection_mode=this._derive_selection_mode();const e=t.map(e=>e.id);this._invoke_select_event({evt:"multi_deselect",data:e,node:s.id,nodes:e})}}else{this._selection_mode="multi";let e=this._collect_subtree_nodes(s,{includeChildren:i,respectFilter:!0,skipRootFilter:!0});e.length||(e=[s]);const t=this._append_selection(e,{focusNode:s});this._ensure_selection_state();const n=s.parent&&!this.jm.mind.selected_nodes.has(s.parent)?this._ensure_ancestor_selection([s],s,{requireAncestorChainSelected:!0}):[],l=t.concat(n);if(l.length){this.jm.mind.selected=s;const e=l.map(e=>e.id);this._invoke_select_event({evt:"multi_select",data:e,node:s.id,nodes:e})}}}get_selection_mode(){return this._selection_mode}_handle_node_click(e){const{e:t,node:s,element:i}=e;if(!s||!i)return;this._ensure_selection_state();const n=this._get_multi_select_mode(t);if("ctrl"===n)this.toggle_node_selection(s);else if("shift"===n){const e=this.jm.get_node(s);if(!e)return void _.warn("[multiSelect] Node not found for shift selection: "+s);if(this.options.shift_simple_mode)this.is_node_selected(s)?this._deselect_subtree(e):this._range_select_nodes(s);else if(this.is_node_selected(s))this._deselect_subtree(e);else{const t=!1!==this.options.include_descendants;let s=this._collect_subtree_nodes(e,{includeChildren:t,respectFilter:!0,skipRootFilter:!0});s.length||(s=[e]);const i=this._append_selection(s,{focusNode:e});if(i.length){this.jm.mind.selected=e,this._last_selected_node=e,this._selection_mode=this._derive_selection_mode();const t=i.map(e=>e.id);this._invoke_select_event({evt:"multi_select",data:t,node:e.id,nodes:t})}else this._ensure_selection_state(),this.jm.mind.selected_nodes.has(e)||(this.jm.mind.selected_nodes.add(e),this._mark_node_selected(e)),this.jm.mind.selected=e,this._last_selected_node=e,this._selection_mode=this._derive_selection_mode(),this._invoke_select_event({evt:"multi_select",data:[e.id],node:e.id,nodes:[e.id]})}}else this.select_node(s)}_handle_node_removed(e){this._ensure_selection_state(),this.jm.mind.selected_nodes.has(e)&&this.jm.mind.selected_nodes.delete(e),this._last_selected_node&&this._last_selected_node.id===e.id&&(this._last_selected_node=null),this.jm.view.multi_selected_nodes&&this.jm.view.multi_selected_nodes.has(e.id)&&this._unmark_node_selected(e)}_get_multi_select_mode(e){return e?!1!==this.options.allow_shift&&!0===e.shiftKey?"shift":!1===this.options.allow_ctrl||!0!==e.ctrlKey&&!0!==e.metaKey?null:"ctrl":null}_append_selection(e,t){if(!e||!e.length)return[];if(this._ensure_selection_state(),!this.jm.mind||!this.jm.mind.selected_nodes)return _.warn("[multiSelect] Cannot append selection: selected_nodes not available"),[];const s=[];for(let t=0;t<e.length;t++){const i=e[t];this.jm.mind.selected_nodes.has(i)||(this.jm.mind.selected_nodes.add(i),s.push(i))}if(s.length){const e=t&&t.focusNode?t.focusNode:null;this._mark_nodes_selected(s,e||s[s.length-1])}return s}_remove_selection(e){if(!e||!e.length)return[];this._ensure_selection_state();const t=[];for(let s=0;s<e.length;s++){const i=e[s];this.jm.mind.selected_nodes.has(i)&&(this.jm.mind.selected_nodes.delete(i),t.push(i))}return t.length&&this._unmark_nodes_selected(t),t}_deselect_subtree(e){let t=this._collect_subtree_nodes(e,{includeChildren:!0,respectFilter:!1,skipRootFilter:!1});t.length||(t=[e]);const s=this._remove_selection(t);if(s.length){this.jm.mind.selected&&this.jm.mind.selected.id===e.id&&(this.jm.mind.selected=null),this._selection_mode=this._derive_selection_mode();const t=s.map(e=>e.id);this._invoke_select_event({evt:"multi_deselect",data:t,node:e.id,nodes:t})}}_clear_selection_state(){if(this._ensure_selection_state(),this._selection_mode=null,this.jm.mind&&this.jm.mind.selected_nodes||this._ensure_selection_state(),!this.jm.mind||!this.jm.mind.selected_nodes||0===this.jm.mind.selected_nodes.size)return this._clear_all_selected_nodes_view(),[];const e=Array.from(this.jm.mind.selected_nodes);return this.jm.mind.selected_nodes.clear(),this._unmark_nodes_selected(e),e}_collect_subtree_nodes(e,t){const s=t||{},i=!1!==s.includeChildren,n=!!s.respectFilter,l=!!s.skipRootFilter,o=n?this._get_selection_filter():null,_=[],d=(e,t)=>{let s=!0;if(!o||l&&t||(s=!1!==o(e)),s&&_.push(e),i&&e.children)for(let t=0;t<e.children.length;t++)d(e.children[t],!1)};return d(e,!0),_}_ensure_ancestor_selection(e,t,s){if(!e||!e.length)return[];this._ensure_selection_state();const i=!(!s||!s.requireAncestorChainSelected),n=[],l=Object.create(null);for(let t=0;t<e.length;t++){let s=e[t].parent;if(!s)continue;const o=[];let _=!i;for(;s;){if(this.jm.mind.selected_nodes.has(s)){_=!0;break}o.push(s),s=s.parent}if(_)for(let e=0;e<o.length;e++){const t=o[e];this.jm.mind.selected_nodes.has(t)||l[t.id]||(n.push(t),l[t.id]=!0)}}if(!n.length)return[];const o=t||e[e.length-1];return this._append_selection(n,{focusNode:o})}_get_selection_filter(){return this.options&&"function"==typeof this.options.filter?this.options.filter:null}_range_select_nodes(e){const t=this._resolve_node(e);if(!t||!this.jm.layout.is_visible(t))return;if(this._ensure_selection_state(),0===this.jm.mind.selected_nodes.size){let e=this._collect_subtree_nodes(t,{includeChildren:!0,respectFilter:!0,skipRootFilter:!0});e.length||(e=[t]);const s=this._append_selection(e);if(s.length){this.jm.mind.selected=t,this._last_selected_node=t,this._selection_mode=this._derive_selection_mode();const e=s.map(e=>e.id);this._invoke_select_event({evt:"multi_select",data:e,node:t.id,nodes:e})}return}this._ensure_selection_state();const s=Array.from(this.jm.mind.selected_nodes),i=this._last_selected_node&&this.jm.mind.selected_nodes.has(this._last_selected_node)?this._last_selected_node:s[0],n=this._find_nodes_between(i,t);if(!n.length){const e=[t],s=this._append_selection(e);if(s.length){this.jm.mind.selected=t,this._last_selected_node=t,this._selection_mode=this._derive_selection_mode();const e=s.map(e=>e.id);this._invoke_select_event({evt:"multi_select",data:e,node:t.id,nodes:e})}return}this._ensure_selection_state();const l=n.filter(e=>!this.jm.mind.selected_nodes.has(e)),o=this._append_selection(l);if(o.length){this.jm.mind.selected=t,this._last_selected_node=t,this._selection_mode=this._derive_selection_mode();const e=o.map(e=>e.id);this._invoke_select_event({evt:"multi_select",data:e,node:t.id,nodes:e})}}_find_nodes_between(e,t){if(!e||!t)return[];const s=[],i=e=>{if(this.jm.layout.is_visible(e)&&s.push(e),e.children)for(let t=0;t<e.children.length;t++)i(e.children[t])};this.jm.mind&&this.jm.mind.root&&i(this.jm.mind.root);let n=-1,l=-1;for(let i=0;i<s.length;i++)s[i].id===e.id&&(n=i),s[i].id===t.id&&(l=i);if(-1===n||-1===l)return[];const o=Math.min(n,l),_=Math.max(n,l);return s.slice(o,_+1)}_derive_selection_mode(){this._ensure_selection_state();const e=this.jm.mind.selected_nodes.size;return 0===e?null:e>1?"multi":"single"}_mark_nodes_selected(e,t){if(e&&e.length){for(let t=0;t<e.length;t++)this._mark_node_selected(e[t]);this.jm.view.selected_node=t||e[e.length-1]}}_mark_node_selected(e){if(!e||this.jm.view.multi_selected_nodes.has(e.id))return;const t=e._data&&e._data.view&&e._data.view.element;t&&(t.classList?t.classList.add("selected"):/(\s|^)selected(\s|$)/.test(t.className)||(t.className+=" selected"),this.jm.view.multi_selected_nodes.set(e.id,e))}_unmark_nodes_selected(e){if(e&&e.length)for(let t=0;t<e.length;t++)this._unmark_node_selected(e[t])}_unmark_node_selected(e){if(e){if(this.jm.view.multi_selected_nodes.has(e.id)){const t=e._data&&e._data.view&&e._data.view.element;t&&(t.classList?t.classList.remove("selected"):t.className=t.className.replace(/\s*selected\b/i,"")),this.jm.view.multi_selected_nodes.delete(e.id)}this.jm.view.selected_node&&this.jm.view.selected_node.id===e.id&&(this.jm.view.selected_node=null)}}_clear_all_selected_nodes_view(){if(this._ensure_selection_state(),!this.jm.view.multi_selected_nodes||!this.jm.view.multi_selected_nodes.size)return void(this.jm.view&&(this.jm.view.selected_node=null));const e=Array.from(this.jm.view.multi_selected_nodes.values());for(let t=0;t<e.length;t++)this._unmark_node_selected(e[t]);this.jm.view&&(this.jm.view.selected_node=null)}_invoke_select_event(e){try{e.nodes||(e.nodes=e.data||[]),e.data||(e.data=e.nodes||[]),this.jm.invoke_event_handle(t,e)}catch(e){_.warn("[multiSelect] failed to invoke select event",e)}}_resolve_node(e){return e?"string"==typeof e?this.jm.get_node(e):e:null}}class h extends d{static instanceName="multiSelectPlugin";static preload=!1;constructor({jm:e,pluginOpt:t}){super({jm:e,pluginOpt:t});const s=Object.assign({},c,t||{});this.options=s,this._mounted=!1,this._core=null,this._listener=null,this._enabled=!!s.enable_multi_select,this._initCore()}_initCore(){const t=this.jm,s=this.options,i=this;this._core=new r(t,s),this._mountAPI(),this._listener=(t,s)=>{try{t===e&&i._core.select_clear()}catch(e){_.warn("[multiSelect] listener error",e)}},t.add_event_listener(this._listener),this._original_select_node=t.select_node.bind(t),this._original_select_clear=t.select_clear.bind(t),t.select_node=e=>i._enabled?i._core.select_node(e):i._original_select_node(e),t.select_clear=()=>i._enabled?i._core.select_clear():i._original_select_clear();const n=t.constructor.$;this._domClickHandler=function(e){if(!i._enabled)return;const s=e.target||e.currentTarget,n=t.view.get_binded_nodeid(s);n&&t.view.is_node(s)?(e.preventDefault(),e.stopPropagation(),e.stopImmediatePropagation&&e.stopImmediatePropagation(),i._core._handle_node_click({e:e,node:n,element:s,evt:"click"})):n||e.ctrlKey||e.metaKey||e.shiftKey||i._core.select_clear()},t.view&&t.view.e_nodes?(n.on(t.view.e_nodes,"click",this._domClickHandler),_.info("[multiSelect] plugin click handler attached")):_.warn("[multiSelect] nodes container not ready; DOM handler not attached"),this._enabled&&"function"==typeof t.disable_event_handle&&t.disable_event_handle("mousedown"),_.info("[multiSelect] API mounted and event listener attached.")}_mountAPI(){if(this._mounted)return;const e=this.jm,t=this,s={get_selected_nodes:()=>t._core?t._core.get_selected_nodes():(_.warn("[multiSelect] Core not initialized"),[]),is_node_selected:e=>!!t._core&&t._core.is_node_selected(e),select_node:e=>{t._core?t._core.select_node(e):_.warn("[multiSelect] Core not initialized")},select_clear:()=>{t._core&&t._core.select_clear()},toggle_node_selection:e=>{t._core?t._core.toggle_node_selection(e):_.warn("[multiSelect] Core not initialized")},toggle_subtree_selection:e=>{t._core?t._core.toggle_subtree_selection(e):_.warn("[multiSelect] Core not initialized")},get_selection_mode:()=>t._core?t._core.get_selection_mode():null,getOptions:()=>{const e=t._core?t._core.options:t.options;return Object.assign({},e,{enable_multi_select:t._enabled})},enable:()=>t.setEnabled(!0),disable:()=>t.setEnabled(!1),setEnabled:e=>t.setEnabled(e),setOptions:e=>t.setOptions(e)};Object.defineProperty(e,"multiSelect",{value:s,configurable:!0,enumerable:!1,writable:!1}),this._mounted=!0,_.info("[multiSelect] API mounted.")}beforePluginRemove(){try{if(this._listener&&this.jm&&Array.isArray(this.jm.event_handles)){const e=this.jm.event_handles.indexOf(this._listener);e>=0&&this.jm.event_handles.splice(e,1)}"function"==typeof this.jm.enable_event_handle&&this.jm.enable_event_handle("mousedown"),this._domClickHandler&&this.jm&&this.jm.view&&this.jm.view.e_nodes&&(this._domClickHandler=null),this._original_select_node&&(this.jm.select_node=this._original_select_node),this._original_select_clear&&(this.jm.select_clear=this._original_select_clear),this.jm&&Object.prototype.hasOwnProperty.call(this.jm,"multiSelect")&&delete this.jm.multiSelect,this._mounted=!1}catch(e){_.error("[multiSelect] remove failed:",e)}}setEnabled(e){const t=!!e;this._enabled!==t&&(this._enabled=t,this.options.enable_multi_select=this._enabled,this._enabled?"function"==typeof this.jm.disable_event_handle&&this.jm.disable_event_handle("mousedown"):"function"==typeof this.jm.enable_event_handle&&this.jm.enable_event_handle("mousedown"))}setOptions(e){const t=Object.assign({},e||{});this.options=Object.assign({},this.options,t),this._core&&this._core.options&&(this._core.options=Object.assign({},this._core.options,t))}beforePluginDestroy(){_.debug("[multiSelect] beforePluginDestroy"),this.beforePluginRemove()}}export{r as MultiSelectCore,h as MultiSelectPlugin,h as default};
|
|
9
|
+
//# sourceMappingURL=jsmind.multi-select.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jsmind.multi-select.js","sources":["../src/jsmind.common.js","../src/jsmind.enhanced-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.0';\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 * Enhanced Plugin Manager\n * Manages the lifecycle of enhanced plugins with synchronous initialization,\n * preload support, and lifecycle hooks.\n */\nexport class EnhancedPluginManager {\n /**\n * @param {import('./jsmind.js').default} jm - jsMind instance\n */\n constructor(jm) {\n this.jm = jm;\n /** @type {Map<string, EnhancedPlugin>} */\n this.plugins = new Map();\n }\n\n /**\n * Initialize preload plugins (before core modules)\n */\n initPreloadPlugins() {\n const preloadPlugins = this.jm.constructor.enhancedPluginList.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.enhancedPluginList.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 // Check instanceName\n if (!PluginClass.instanceName) {\n throw new Error('Plugin ' + PluginClass.name + ' must define static instanceName');\n }\n\n // Check naming conflict\n if (this.plugins.has(PluginClass.instanceName)) {\n logger.warn(\n 'Plugin ' + PluginClass.instanceName + ' already exists, will be replaced'\n );\n }\n\n // Instantiate plugin\n const instance = new PluginClass({\n jm: this.jm,\n pluginOpt: pluginOpt || {},\n });\n\n // Save instance\n this.plugins.set(PluginClass.instanceName, instance);\n this.jm[PluginClass.instanceName] = instance;\n descriptor.instance = instance;\n\n logger.info('Enhanced 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 EnhancedPlugin} 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 // Call lifecycle hook\n if (typeof instance.beforePluginRemove === 'function') {\n instance.beforePluginRemove();\n }\n\n // Remove from Map\n this.plugins.delete(instanceName);\n\n // Remove from jsMind instance\n delete this.jm[instanceName];\n\n // Remove from plugin list\n const list = this.jm.constructor.enhancedPluginList;\n const index = list.findIndex(d => d.PluginClass === PluginClass);\n if (index !== -1) {\n list.splice(index, 1);\n }\n\n logger.info('Enhanced 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 // Call lifecycle hook\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 {EnhancedPlugin | undefined}\n */\n getPlugin(instanceName) {\n return this.plugins.get(instanceName);\n }\n}\n\n/**\n * Enhanced Plugin Base Class\n * Provides standard interface for enhanced plugins\n */\nexport class EnhancedPlugin {\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 * Override this method to clean up resources\n */\n beforePluginRemove() {\n // Default implementation: do nothing\n }\n\n /**\n * Called before jsMind instance is destroyed\n * Override this method to clean up resources\n */\n beforePluginDestroy() {\n // Default implementation: call beforePluginRemove\n this.beforePluginRemove();\n }\n}\n\n/**\n * Plugin descriptor\n * @typedef {object} PluginDescriptor\n * @property {typeof EnhancedPlugin} 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 {EnhancedPlugin | 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 {EnhancedPlugin} from '../jsmind.enhanced-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 = (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 = 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 (!this.jm.mind || !this.jm.mind.selected_nodes || this.jm.mind.selected_nodes.size === 0) {\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 - Enhanced plugin for jsMind\n */\nexport class MultiSelectPlugin extends EnhancedPlugin {\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\n"],"names":["String","prototype","startsWith","p","this","slice","length","EventType","LogLevel","_noop","logger","console","level","log","debug","info","warn","error","log_level","EnhancedPlugin","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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"}
|
|
@@ -5,5 +5,5 @@
|
|
|
5
5
|
* Project Home:
|
|
6
6
|
* https://github.com/hizzgdev/jsmind/
|
|
7
7
|
*/
|
|
8
|
-
import e from"@umbraci/jsmind";if(!e)throw new Error("jsMind is not defined");const n=e.$,t={text_width:200,line_height:"1.5"};function i(e={}){const n=Object.assign({},t,e);return function(e,t,i){return!(!i.topic||!i.topic.includes("\n"))&&(t.style.whiteSpace="pre-wrap",t.style.wordBreak="break-word",t.style.maxWidth=n.text_width+"px",t.textContent=i.topic,!0)}}function o(i,o){console.log("[Multiline Plugin] Initializing...",o);const d=Object.assign({},t,o),l=i.view;let r=null,a=null;function s(){if(!r||!a)return;const e=r._data.view.element;a.parentNode&&a.parentNode.removeChild(a),e.style.zIndex="auto",r=null,l.editing_node=null,a=null}l.opts.custom_node_render&&(l.render_node=l._custom_node_render.bind(l),console.log("[Multiline Plugin] Re-bound view.render_node")),i.mind&&i.mind.root&&(!function(e){const n=e.view,t=e.mind;if(!t||!t.root)return;const i=[];for(const e in t.nodes){const n=t.nodes[e];n._data&&n._data.view&&n._data.view.element&&i.push(n)}for(const e of i){const t=e._data.view.element;n.render_node(t,e)}for(const n of i)if(e.layout.is_visible(n)){const e=n._data.view.element;n._data.view.width=e.clientWidth,n._data.view.height=e.clientHeight}e.layout.layout(),e.view.show(!1)}(i),console.log("[Multiline Plugin] Re-rendered all nodes")),l.edit_node_begin=function(e){if(console.log("[Multiline Plugin] edit_node_begin called",e),!e.topic)return;r&&l.edit_node_end(),r=e,l.editing_node=e;const t=n.c("div");t.contentEditable="plaintext-only",t.className="jsmind-multiline-editor",t.textContent=e.topic,a=t;const i=e._data.view.element;Object.assign(t.style,{width:"auto",minHeight:i.clientHeight+"px",lineHeight:d.line_height,border:"none",outline:"none",whiteSpace:"pre-wrap",wordBreak:"break-word",boxSizing:"border-box",overflow:"hidden"});const o=()=>{t.style.height="auto",t.style.height=t.scrollHeight+"px"};n.on(t,"input",o),setTimeout(o,0),n.on(t,"keydown",
|
|
8
|
+
import e from"@umbraci/jsmind";if(!e)throw new Error("jsMind is not defined");const n=e.$,t={text_width:200,line_height:"1.5"};function i(e={}){const n=Object.assign({},t,e);return function(e,t,i){return!(!i.topic||!i.topic.includes("\n"))&&(t.style.whiteSpace="pre-wrap",t.style.wordBreak="break-word",t.style.maxWidth=n.text_width+"px",t.textContent=i.topic,!0)}}function o(i,o){console.log("[Multiline Plugin] Initializing...",o);const d=Object.assign({},t,o),l=i.view;let r=null,a=null;function s(){if(!r||!a)return;const e=r._data.view.element;a.parentNode&&a.parentNode.removeChild(a),e.style.zIndex="auto",r=null,l.editing_node=null,a=null}l.opts.custom_node_render&&(l.render_node=l._custom_node_render.bind(l),console.log("[Multiline Plugin] Re-bound view.render_node")),i.mind&&i.mind.root&&(!function(e){const n=e.view,t=e.mind;if(!t||!t.root)return;const i=[];for(const e in t.nodes){const n=t.nodes[e];n._data&&n._data.view&&n._data.view.element&&i.push(n)}for(const e of i){const t=e._data.view.element;n.render_node(t,e)}for(const n of i)if(e.layout.is_visible(n)){const e=n._data.view.element;n._data.view.width=e.clientWidth,n._data.view.height=e.clientHeight}e.layout.layout(),e.view.show(!1)}(i),console.log("[Multiline Plugin] Re-rendered all nodes")),l.edit_node_begin=function(e){if(console.log("[Multiline Plugin] edit_node_begin called",e),!e.topic)return;r&&l.edit_node_end(),r=e,l.editing_node=e;const t=n.c("div");t.contentEditable="plaintext-only",t.className="jsmind-multiline-editor",t.textContent=e.topic,a=t;const i=e._data.view.element;Object.assign(t.style,{width:"auto",minHeight:i.clientHeight+"px",lineHeight:d.line_height,border:"none",outline:"none",whiteSpace:"pre-wrap",wordBreak:"break-word",boxSizing:"border-box",overflow:"hidden"});const o=()=>{t.style.height="auto",t.style.height=t.scrollHeight+"px"};n.on(t,"input",o),setTimeout(o,0),n.on(t,"keydown",e=>{"Enter"!==e.key||e.shiftKey?"Escape"===e.key?(e.preventDefault(),e.stopPropagation(),function(){if(!r||!a)return;const e=r;s(),l.render_node(e._data.view.element,e),l.e_panel.focus()}()):"Tab"===e.key&&(e.preventDefault(),e.stopPropagation(),l.edit_node_end()):(e.preventDefault(),e.stopPropagation(),l.edit_node_end())}),n.on(t,"blur",()=>{setTimeout(()=>{r&&l.edit_node_end()},100)}),i.innerHTML="",i.appendChild(t),i.style.zIndex=5,t.focus();const c=n.d.createRange();c.selectNodeContents(t);const u=n.w.getSelection();u.removeAllRanges(),u.addRange(c)},l.edit_node_end=function(){if(!r||!a)return;const n=r,t=(a.textContent||"").trim().replace(/\r\n/g,"\n").replace(/\r/g,"\n").replace(/\n{3,}/g,"\n\n");s(),e.util.text.is_empty(t)||n.topic===t?l.render_node(n._data.view.element,n):i.update_node(n.id,t),l.e_panel.focus()}}e.register_plugin(new e.plugin("multiline_text",o));var d={name:"multiline_text",init:o,createMultilineRender:i};export{i as createMultilineRender,d as default};
|
|
9
9
|
//# sourceMappingURL=jsmind.multiline-text.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"jsmind.multiline-text.js","sources":["../src/plugins/jsmind.multiline-text.js"],"sourcesContent":["/**\r\n * @license BSD\r\n * @copyright 2014-2025 UmbraCi\r\n *\r\n * Project Home:\r\n * https://github.com/hizzgdev/jsmind/\r\n */\r\n\r\nimport jsMind from '@umbraci/jsmind';\r\n\r\nif (!jsMind) {\r\n throw new Error('jsMind is not defined');\r\n}\r\n\r\nconst $ = jsMind.$;\r\n\r\n/**\r\n * Multiline Text Plugin\r\n * Provides multiline text support for jsMind nodes\r\n */\r\n\r\n/**\r\n * Default plugin options\r\n * @typedef {Object} MultilineTextOptions\r\n * @property {number} text_width - Maximum width for multiline text nodes (default: 200)\r\n * @property {string} line_height - Line height for text (default: '1.5')\r\n */\r\nconst DEFAULT_OPTIONS = {\r\n text_width: 200,\r\n line_height: '1.5',\r\n};\r\n\r\n/**\r\n * Create a custom node render function for multiline text\r\n * @param {MultilineTextOptions} [options={}] - Plugin options\r\n * @param {number} [options.text_width=200] - Maximum width for multiline text nodes\r\n * @param {string} [options.line_height='1.5'] - Line height for text\r\n * @returns {function(jsMind, HTMLElement, Node): boolean} Custom render function\r\n * @example\r\n * const options = {\r\n * view: {\r\n * custom_node_render: createMultilineRender({\r\n * text_width: 250,\r\n * line_height: '1.6',\r\n * })\r\n * }\r\n * };\r\n */\r\nexport function createMultilineRender(options = {}) {\r\n const opts = Object.assign({}, DEFAULT_OPTIONS, options);\r\n\r\n return function (jm, element, node) {\r\n if (node.topic && node.topic.includes('\\n')) {\r\n // Multiline text - apply styles BEFORE setting content\r\n element.style.whiteSpace = 'pre-wrap';\r\n element.style.wordBreak = 'break-word';\r\n element.style.maxWidth = opts.text_width + 'px';\r\n element.textContent = node.topic;\r\n return true;\r\n }\r\n // Single line text - use default render\r\n return false;\r\n };\r\n}\r\n\r\n/**\r\n * Re-render all nodes to apply multiline styles and recalculate sizes\r\n * @param {import('../jsmind.js').default} jm - jsMind instance\r\n * @private\r\n */\r\nfunction _rerender_all_nodes(jm) {\r\n const view = jm.view;\r\n const mind = jm.mind;\r\n\r\n if (!mind || !mind.root) {\r\n return;\r\n }\r\n\r\n // Collect all nodes to update\r\n const nodesToUpdate = [];\r\n for (const nodeId in mind.nodes) {\r\n const node = mind.nodes[nodeId];\r\n if (node._data && node._data.view && node._data.view.element) {\r\n nodesToUpdate.push(node);\r\n }\r\n }\r\n\r\n // Batch render nodes (only update DOM, no layout trigger)\r\n for (const node of nodesToUpdate) {\r\n const element = node._data.view.element;\r\n view.render_node(element, node);\r\n }\r\n\r\n // Batch update node sizes (read all sizes at once to avoid layout thrashing)\r\n for (const node of nodesToUpdate) {\r\n if (jm.layout.is_visible(node)) {\r\n const element = node._data.view.element;\r\n node._data.view.width = element.clientWidth;\r\n node._data.view.height = element.clientHeight;\r\n }\r\n }\r\n\r\n // Finally recalculate layout and show (only trigger reflow/repaint once)\r\n jm.layout.layout();\r\n jm.view.show(false);\r\n}\r\n\r\n/**\r\n * Plugin initialization function\r\n * @param {import('../jsmind.js').default} jm - jsMind instance\r\n * @param {MultilineTextOptions} options - Plugin options\r\n * @private\r\n */\r\nfunction init(jm, options) {\r\n console.log('[Multiline Plugin] Initializing...', options);\r\n\r\n const opts = Object.assign({}, DEFAULT_OPTIONS, options);\r\n const view = jm.view;\r\n\r\n // Plugin state\r\n let editing_node = null;\r\n let multiline_editor = null;\r\n\r\n // IMPORTANT: Re-set view.render_node to use custom render\r\n // Because ViewProvider constructor already set it based on options.custom_node_render\r\n // We need to ensure it uses the custom render function\r\n if (view.opts.custom_node_render) {\r\n view.render_node = view._custom_node_render.bind(view);\r\n console.log('[Multiline Plugin] Re-bound view.render_node');\r\n }\r\n\r\n // Re-render all nodes to apply multiline styles\r\n if (jm.mind && jm.mind.root) {\r\n _rerender_all_nodes(jm);\r\n console.log('[Multiline Plugin] Re-rendered all nodes');\r\n }\r\n\r\n /**\r\n * Begin editing a node with multiline support\r\n * @param {import('../jsmind.node.js').Node} node\r\n */\r\n view.edit_node_begin = function (node) {\r\n console.log('[Multiline Plugin] edit_node_begin called', node);\r\n if (!node.topic) {\r\n return;\r\n }\r\n\r\n // End editing if another node is being edited\r\n if (editing_node) {\r\n view.edit_node_end();\r\n }\r\n\r\n editing_node = node;\r\n view.editing_node = node;\r\n\r\n // Create editor (div with contentEditable)\r\n const editor = $.c('div');\r\n editor.contentEditable = 'plaintext-only';\r\n editor.className = 'jsmind-multiline-editor';\r\n editor.textContent = node.topic;\r\n multiline_editor = editor;\r\n\r\n // Get element and set editor styles to match\r\n const element = node._data.view.element;\r\n\r\n // Set editor styles to match element width, auto-expand height\r\n Object.assign(editor.style, {\r\n width: 'auto',\r\n minHeight: element.clientHeight + 'px',\r\n lineHeight: opts.line_height,\r\n border: 'none',\r\n outline: 'none',\r\n whiteSpace: 'pre-wrap',\r\n wordBreak: 'break-word',\r\n boxSizing: 'border-box',\r\n overflow: 'hidden',\r\n });\r\n\r\n // Auto-expand height on input\r\n const autoExpand = () => {\r\n editor.style.height = 'auto';\r\n editor.style.height = editor.scrollHeight + 'px';\r\n };\r\n $.on(editor, 'input', autoExpand);\r\n // Initial expand\r\n setTimeout(autoExpand, 0);\r\n\r\n // Keyboard events\r\n $.on(editor, 'keydown', e => {\r\n if (e.key === 'Enter' && !e.shiftKey) {\r\n e.preventDefault();\r\n e.stopPropagation(); // Prevent jsMind shortcut from triggering\r\n view.edit_node_end();\r\n } else if (e.key === 'Escape') {\r\n e.preventDefault();\r\n e.stopPropagation();\r\n cancel_editing();\r\n } else if (e.key === 'Tab') {\r\n e.preventDefault();\r\n e.stopPropagation();\r\n view.edit_node_end();\r\n }\r\n });\r\n\r\n // Blur event - save on blur\r\n $.on(editor, 'blur', () => {\r\n setTimeout(() => {\r\n if (editing_node) {\r\n view.edit_node_end();\r\n }\r\n }, 100);\r\n });\r\n\r\n // Replace node content and focus\r\n element.innerHTML = '';\r\n element.appendChild(editor);\r\n element.style.zIndex = 5;\r\n editor.focus();\r\n\r\n // Select all text\r\n const range = $.d.createRange();\r\n range.selectNodeContents(editor);\r\n const selection = $.w.getSelection();\r\n selection.removeAllRanges();\r\n selection.addRange(range);\r\n };\r\n\r\n /**\r\n * End editing and save changes\r\n */\r\n view.edit_node_end = function () {\r\n if (!editing_node || !multiline_editor) {\r\n return;\r\n }\r\n\r\n const node = editing_node;\r\n const topic = (multiline_editor.textContent || '')\r\n .trim()\r\n .replace(/\\r\\n/g, '\\n')\r\n .replace(/\\r/g, '\\n')\r\n .replace(/\\n{3,}/g, '\\n\\n');\r\n\r\n // Clean up editor\r\n cleanup_editor();\r\n\r\n // Update node if content changed\r\n if (!jsMind.util.text.is_empty(topic) && node.topic !== topic) {\r\n jm.update_node(node.id, topic);\r\n } else {\r\n view.render_node(node._data.view.element, node);\r\n }\r\n\r\n // Focus panel\r\n view.e_panel.focus();\r\n };\r\n\r\n /**\r\n * Cancel editing without saving changes\r\n */\r\n function cancel_editing() {\r\n if (!editing_node || !multiline_editor) {\r\n return;\r\n }\r\n\r\n const node = editing_node;\r\n\r\n // Clean up editor\r\n cleanup_editor();\r\n\r\n // Re-render node\r\n view.render_node(node._data.view.element, node);\r\n\r\n // Focus panel\r\n view.e_panel.focus();\r\n }\r\n\r\n /**\r\n * Clean up editor and reset state\r\n */\r\n function cleanup_editor() {\r\n if (!editing_node || !multiline_editor) {\r\n return;\r\n }\r\n\r\n const element = editing_node._data.view.element;\r\n\r\n // Remove editor\r\n if (multiline_editor.parentNode) {\r\n multiline_editor.parentNode.removeChild(multiline_editor);\r\n }\r\n\r\n // Reset styles and state\r\n element.style.zIndex = 'auto';\r\n editing_node = null;\r\n view.editing_node = null;\r\n multiline_editor = null;\r\n }\r\n}\r\n\r\n// Register plugin\r\njsMind.register_plugin(new jsMind.plugin('multiline_text', init));\r\n\r\n// Export for ES6 modules\r\nexport default {\r\n name: 'multiline_text',\r\n init,\r\n createMultilineRender,\r\n};\r\n"],"names":["jsMind","Error","$","DEFAULT_OPTIONS","text_width","line_height","createMultilineRender","options","opts","Object","assign","jm","element","node","topic","includes","style","whiteSpace","wordBreak","maxWidth","textContent","init","console","log","view","editing_node","multiline_editor","cleanup_editor","_data","parentNode","removeChild","zIndex","custom_node_render","render_node","_custom_node_render","bind","mind","root","nodesToUpdate","nodeId","nodes","push","layout","is_visible","width","clientWidth","height","clientHeight","show","_rerender_all_nodes","edit_node_begin","edit_node_end","editor","c","contentEditable","className","minHeight","lineHeight","border","outline","boxSizing","overflow","autoExpand","scrollHeight","on","setTimeout","e","key","shiftKey","preventDefault","stopPropagation","e_panel","focus","cancel_editing","innerHTML","appendChild","range","d","createRange","selectNodeContents","selection","w","getSelection","removeAllRanges","addRange","trim","replace","util","text","is_empty","update_node","id","register_plugin","plugin","jsmind_multilineText","name"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"jsmind.multiline-text.js","sources":["../src/plugins/jsmind.multiline-text.js"],"sourcesContent":["/**\n * @license BSD\n * @copyright 2014-2025 UmbraCi\n *\n * Project Home:\n * https://github.com/hizzgdev/jsmind/\n */\n\nimport jsMind from '@umbraci/jsmind';\n\nif (!jsMind) {\n throw new Error('jsMind is not defined');\n}\n\nconst $ = jsMind.$;\n\n/**\n * Multiline Text Plugin\n * Provides multiline text support for jsMind nodes\n */\n\n/**\n * Default plugin options\n * @typedef {Object} MultilineTextOptions\n * @property {number} text_width - Maximum width for multiline text nodes (default: 200)\n * @property {string} line_height - Line height for text (default: '1.5')\n */\nconst DEFAULT_OPTIONS = {\n text_width: 200,\n line_height: '1.5',\n};\n\n/**\n * Create a custom node render function for multiline text\n * @param {MultilineTextOptions} [options={}] - Plugin options\n * @param {number} [options.text_width=200] - Maximum width for multiline text nodes\n * @param {string} [options.line_height='1.5'] - Line height for text\n * @returns {function(jsMind, HTMLElement, Node): boolean} Custom render function\n * @example\n * const options = {\n * view: {\n * custom_node_render: createMultilineRender({\n * text_width: 250,\n * line_height: '1.6',\n * })\n * }\n * };\n */\nexport function createMultilineRender(options = {}) {\n const opts = Object.assign({}, DEFAULT_OPTIONS, options);\n\n return function (jm, element, node) {\n if (node.topic && node.topic.includes('\\n')) {\n // Multiline text - apply styles BEFORE setting content\n element.style.whiteSpace = 'pre-wrap';\n element.style.wordBreak = 'break-word';\n element.style.maxWidth = opts.text_width + 'px';\n element.textContent = node.topic;\n return true;\n }\n // Single line text - use default render\n return false;\n };\n}\n\n/**\n * Re-render all nodes to apply multiline styles and recalculate sizes\n * @param {import('../jsmind.js').default} jm - jsMind instance\n * @private\n */\nfunction _rerender_all_nodes(jm) {\n const view = jm.view;\n const mind = jm.mind;\n\n if (!mind || !mind.root) {\n return;\n }\n\n // Collect all nodes to update\n const nodesToUpdate = [];\n for (const nodeId in mind.nodes) {\n const node = mind.nodes[nodeId];\n if (node._data && node._data.view && node._data.view.element) {\n nodesToUpdate.push(node);\n }\n }\n\n // Batch render nodes (only update DOM, no layout trigger)\n for (const node of nodesToUpdate) {\n const element = node._data.view.element;\n view.render_node(element, node);\n }\n\n // Batch update node sizes (read all sizes at once to avoid layout thrashing)\n for (const node of nodesToUpdate) {\n if (jm.layout.is_visible(node)) {\n const element = node._data.view.element;\n node._data.view.width = element.clientWidth;\n node._data.view.height = element.clientHeight;\n }\n }\n\n // Finally recalculate layout and show (only trigger reflow/repaint once)\n jm.layout.layout();\n jm.view.show(false);\n}\n\n/**\n * Plugin initialization function\n * @param {import('../jsmind.js').default} jm - jsMind instance\n * @param {MultilineTextOptions} options - Plugin options\n * @private\n */\nfunction init(jm, options) {\n console.log('[Multiline Plugin] Initializing...', options);\n\n const opts = Object.assign({}, DEFAULT_OPTIONS, options);\n const view = jm.view;\n\n // Plugin state\n let editing_node = null;\n let multiline_editor = null;\n\n // IMPORTANT: Re-set view.render_node to use custom render\n // Because ViewProvider constructor already set it based on options.custom_node_render\n // We need to ensure it uses the custom render function\n if (view.opts.custom_node_render) {\n view.render_node = view._custom_node_render.bind(view);\n console.log('[Multiline Plugin] Re-bound view.render_node');\n }\n\n // Re-render all nodes to apply multiline styles\n if (jm.mind && jm.mind.root) {\n _rerender_all_nodes(jm);\n console.log('[Multiline Plugin] Re-rendered all nodes');\n }\n\n /**\n * Begin editing a node with multiline support\n * @param {import('../jsmind.node.js').Node} node\n */\n view.edit_node_begin = function (node) {\n console.log('[Multiline Plugin] edit_node_begin called', node);\n if (!node.topic) {\n return;\n }\n\n // End editing if another node is being edited\n if (editing_node) {\n view.edit_node_end();\n }\n\n editing_node = node;\n view.editing_node = node;\n\n // Create editor (div with contentEditable)\n const editor = $.c('div');\n editor.contentEditable = 'plaintext-only';\n editor.className = 'jsmind-multiline-editor';\n editor.textContent = node.topic;\n multiline_editor = editor;\n\n // Get element and set editor styles to match\n const element = node._data.view.element;\n\n // Set editor styles to match element width, auto-expand height\n Object.assign(editor.style, {\n width: 'auto',\n minHeight: element.clientHeight + 'px',\n lineHeight: opts.line_height,\n border: 'none',\n outline: 'none',\n whiteSpace: 'pre-wrap',\n wordBreak: 'break-word',\n boxSizing: 'border-box',\n overflow: 'hidden',\n });\n\n // Auto-expand height on input\n const autoExpand = () => {\n editor.style.height = 'auto';\n editor.style.height = editor.scrollHeight + 'px';\n };\n $.on(editor, 'input', autoExpand);\n // Initial expand\n setTimeout(autoExpand, 0);\n\n // Keyboard events\n $.on(editor, 'keydown', e => {\n if (e.key === 'Enter' && !e.shiftKey) {\n e.preventDefault();\n e.stopPropagation(); // Prevent jsMind shortcut from triggering\n view.edit_node_end();\n } else if (e.key === 'Escape') {\n e.preventDefault();\n e.stopPropagation();\n cancel_editing();\n } else if (e.key === 'Tab') {\n e.preventDefault();\n e.stopPropagation();\n view.edit_node_end();\n }\n });\n\n // Blur event - save on blur\n $.on(editor, 'blur', () => {\n setTimeout(() => {\n if (editing_node) {\n view.edit_node_end();\n }\n }, 100);\n });\n\n // Replace node content and focus\n element.innerHTML = '';\n element.appendChild(editor);\n element.style.zIndex = 5;\n editor.focus();\n\n // Select all text\n const range = $.d.createRange();\n range.selectNodeContents(editor);\n const selection = $.w.getSelection();\n selection.removeAllRanges();\n selection.addRange(range);\n };\n\n /**\n * End editing and save changes\n */\n view.edit_node_end = function () {\n if (!editing_node || !multiline_editor) {\n return;\n }\n\n const node = editing_node;\n const topic = (multiline_editor.textContent || '')\n .trim()\n .replace(/\\r\\n/g, '\\n')\n .replace(/\\r/g, '\\n')\n .replace(/\\n{3,}/g, '\\n\\n');\n\n // Clean up editor\n cleanup_editor();\n\n // Update node if content changed\n if (!jsMind.util.text.is_empty(topic) && node.topic !== topic) {\n jm.update_node(node.id, topic);\n } else {\n view.render_node(node._data.view.element, node);\n }\n\n // Focus panel\n view.e_panel.focus();\n };\n\n /**\n * Cancel editing without saving changes\n */\n function cancel_editing() {\n if (!editing_node || !multiline_editor) {\n return;\n }\n\n const node = editing_node;\n\n // Clean up editor\n cleanup_editor();\n\n // Re-render node\n view.render_node(node._data.view.element, node);\n\n // Focus panel\n view.e_panel.focus();\n }\n\n /**\n * Clean up editor and reset state\n */\n function cleanup_editor() {\n if (!editing_node || !multiline_editor) {\n return;\n }\n\n const element = editing_node._data.view.element;\n\n // Remove editor\n if (multiline_editor.parentNode) {\n multiline_editor.parentNode.removeChild(multiline_editor);\n }\n\n // Reset styles and state\n element.style.zIndex = 'auto';\n editing_node = null;\n view.editing_node = null;\n multiline_editor = null;\n }\n}\n\n// Register plugin\njsMind.register_plugin(new jsMind.plugin('multiline_text', init));\n\n// Export for ES6 modules\nexport default {\n name: 'multiline_text',\n init,\n createMultilineRender,\n};\n"],"names":["jsMind","Error","$","DEFAULT_OPTIONS","text_width","line_height","createMultilineRender","options","opts","Object","assign","jm","element","node","topic","includes","style","whiteSpace","wordBreak","maxWidth","textContent","init","console","log","view","editing_node","multiline_editor","cleanup_editor","_data","parentNode","removeChild","zIndex","custom_node_render","render_node","_custom_node_render","bind","mind","root","nodesToUpdate","nodeId","nodes","push","layout","is_visible","width","clientWidth","height","clientHeight","show","_rerender_all_nodes","edit_node_begin","edit_node_end","editor","c","contentEditable","className","minHeight","lineHeight","border","outline","boxSizing","overflow","autoExpand","scrollHeight","on","setTimeout","e","key","shiftKey","preventDefault","stopPropagation","e_panel","focus","cancel_editing","innerHTML","appendChild","range","d","createRange","selectNodeContents","selection","w","getSelection","removeAllRanges","addRange","trim","replace","util","text","is_empty","update_node","id","register_plugin","plugin","jsmind_multilineText","name"],"mappings":"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"}
|
package/es/jsmind.screenshot.js
CHANGED
|
@@ -5,5 +5,5 @@
|
|
|
5
5
|
* Project Home:
|
|
6
6
|
* https://github.com/hizzgdev/jsmind/
|
|
7
7
|
*/
|
|
8
|
-
import t from"@umbraci/jsmind";import e from"dom-to-image";if(!t)throw new Error("jsMind is not defined");if(!e)throw new Error("dom-to-image is required");const i=t.$,n={filename:null,watermark:{left:i.w.location,right:"https://github.com/UmbraCi/jsmind"},background:"transparent"};class r{constructor(e,i){var r={};t.util.json.merge(r,n),t.util.json.merge(r,i),this.version="0.2.0",this.jm=e,this.options=r,this.dpr=e.view.device_pixel_ratio}shoot(){let t=this.create_canvas(),e=t.getContext("2d");e.scale(this.dpr,this.dpr),Promise.resolve(e).then((
|
|
8
|
+
import t from"@umbraci/jsmind";import e from"dom-to-image";if(!t)throw new Error("jsMind is not defined");if(!e)throw new Error("dom-to-image is required");const i=t.$,n={filename:null,watermark:{left:i.w.location,right:"https://github.com/UmbraCi/jsmind"},background:"transparent"};class r{constructor(e,i){var r={};t.util.json.merge(r,n),t.util.json.merge(r,i),this.version="0.2.0",this.jm=e,this.options=r,this.dpr=e.view.device_pixel_ratio}shoot(){let t=this.create_canvas(),e=t.getContext("2d");e.scale(this.dpr,this.dpr),Promise.resolve(e).then(()=>this.draw_background(e)).then(()=>this.draw_lines(e)).then(()=>this.draw_nodes(e)).then(()=>this.draw_watermark(t,e)).then(()=>this.download(t)).then(()=>this.clear(t))}create_canvas(){let t=i.c("canvas");const e=this.jm.view.size.w,n=this.jm.view.size.h;return t.width=e*this.dpr,t.height=n*this.dpr,t.style.width=e+"px",t.style.height=n+"px",t.style.visibility="hidden",this.jm.view.e_panel.appendChild(t),t}clear(t){t.parentNode.removeChild(t)}draw_background(t){return new Promise(function(e,i){const n=this.options.background;n&&"transparent"!==n&&(t.fillStyle=this.options.background,t.fillRect(0,0,this.jm.view.size.w,this.jm.view.size.h)),e(t)}.bind(this))}draw_lines(t){return new Promise(function(e,i){this.jm.view.graph.copy_to(t,function(){e(t)})}.bind(this))}draw_nodes(t){return e.toSvg(this.jm.view.e_nodes,{style:{zoom:1}}).then(this.load_image).then(function(e){return t.drawImage(e,0,0),t})}draw_watermark(t,e){return e.textBaseline="bottom",e.fillStyle="#000",e.font="11px Verdana,Arial,Helvetica,sans-serif",this.options.watermark.left&&(e.textAlign="left",e.fillText(this.options.watermark.left,5.5,t.height-2.5)),this.options.watermark.right&&(e.textAlign="right",e.fillText(this.options.watermark.right,t.width-5.5,t.height-2.5)),e}load_image(t){return new Promise(function(e,i){let n=new Image;n.onload=function(){e(n)},n.onerror=i,n.src=t})}download(t){var e=(this.options.filename||this.jm.mind.name)+".png";if(navigator.msSaveBlob&&t.msToBlob){var n=t.msToBlob();navigator.msSaveBlob(n,e)}else{var r=t.toDataURL(),o=i.c("a");if("download"in o){o.style.visibility="hidden",o.href=r,o.download=e,i.d.body.appendChild(o);var s=i.d.createEvent("MouseEvents");s.initEvent("click",!0,!0),o.dispatchEvent(s),i.d.body.removeChild(o)}else location.href=r}}}const o=new t.plugin("screenshot",function(t,e){var i=new r(t,e);t.screenshot=i,t.shoot=function(){i.shoot()}});t.register_plugin(o);export{r as JmScreenshot,r as default,o as screenshot_plugin};
|
|
9
9
|
//# sourceMappingURL=jsmind.screenshot.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"jsmind.screenshot.js","sources":["../src/plugins/jsmind.screenshot.js"],"sourcesContent":["/**\r\n * @license BSD\r\n * @copyright 2014-2025 UmbraCi\r\n *\r\n * Project Home:\r\n * https://github.com/UmbraCi/jsmind/\r\n */\r\n\r\nimport jsMind from '@umbraci/jsmind';\r\nimport domtoimage from 'dom-to-image';\r\n\r\nif (!jsMind) {\r\n throw new Error('jsMind is not defined');\r\n}\r\n\r\nif (!domtoimage) {\r\n throw new Error('dom-to-image is required');\r\n}\r\n\r\nconst $ = jsMind.$;\r\n\r\n/**\r\n * Default options for screenshot plugin.\r\n * @typedef {Object} ScreenshotOptions\r\n * @property {string|null} [filename]\r\n * @property {{left?:string|Location,right?:string}} [watermark]\r\n * @property {string} [background]\r\n */\r\nconst DEFAULT_OPTIONS = {\r\n filename: null,\r\n watermark: {\r\n left: $.w.location,\r\n right: 'https://github.com/UmbraCi/jsmind',\r\n },\r\n background: 'transparent',\r\n};\r\n\r\n/**\r\n * Screenshot plugin for jsMind.\r\n */\r\nexport class JmScreenshot {\r\n /**\r\n * Create screenshot plugin instance.\r\n * @param {import('../jsmind.js').default} jm - jsMind instance\r\n * @param {Partial<ScreenshotOptions>} options - Plugin options\r\n */\r\n constructor(jm, options) {\r\n var opts = {};\r\n jsMind.util.json.merge(opts, DEFAULT_OPTIONS);\r\n jsMind.util.json.merge(opts, options);\r\n\r\n this.version = '0.2.0';\r\n /** @type {import('../jsmind.js').default} */\r\n this.jm = jm;\r\n /** @type {ScreenshotOptions} */\r\n this.options = opts;\r\n /** @type {number} */\r\n this.dpr = jm.view.device_pixel_ratio;\r\n }\r\n\r\n /** Take a screenshot of the mind map. */\r\n shoot() {\r\n let c = this.create_canvas();\r\n let ctx = c.getContext('2d');\r\n ctx.scale(this.dpr, this.dpr);\r\n Promise.resolve(ctx)\r\n .then(() => this.draw_background(ctx))\r\n .then(() => this.draw_lines(ctx))\r\n .then(() => this.draw_nodes(ctx))\r\n .then(() => this.draw_watermark(c, ctx))\r\n .then(() => this.download(c))\r\n .then(() => this.clear(c));\r\n }\r\n\r\n /**\r\n * Create canvas for screenshot.\r\n * @returns {HTMLCanvasElement} Canvas element\r\n */\r\n create_canvas() {\r\n let c = $.c('canvas');\r\n const w = this.jm.view.size.w;\r\n const h = this.jm.view.size.h;\r\n c.width = w * this.dpr;\r\n c.height = h * this.dpr;\r\n c.style.width = w + 'px';\r\n c.style.height = h + 'px';\r\n\r\n c.style.visibility = 'hidden';\r\n this.jm.view.e_panel.appendChild(c);\r\n return c;\r\n }\r\n\r\n /**\r\n * Clean up canvas element.\r\n * @param {HTMLCanvasElement} c - Canvas to remove\r\n */\r\n clear(c) {\r\n c.parentNode.removeChild(c);\r\n }\r\n\r\n /**\r\n * Draw background on canvas.\r\n * @param {CanvasRenderingContext2D} ctx - Canvas context\r\n * @returns {Promise<CanvasRenderingContext2D>} Promise resolving to context\r\n */\r\n draw_background(ctx) {\r\n return new Promise(\r\n function (resolve, _) {\r\n const bg = this.options.background;\r\n if (!!bg && bg !== 'transparent') {\r\n ctx.fillStyle = this.options.background;\r\n ctx.fillRect(0, 0, this.jm.view.size.w, this.jm.view.size.h);\r\n }\r\n resolve(ctx);\r\n }.bind(this)\r\n );\r\n }\r\n\r\n /**\r\n * Draw connection lines on canvas by copying from view graph.\r\n * @param {CanvasRenderingContext2D} ctx\r\n * @returns {Promise<CanvasRenderingContext2D>}\r\n */\r\n draw_lines(ctx) {\r\n return new Promise(\r\n function (resolve, _) {\r\n this.jm.view.graph.copy_to(ctx, function () {\r\n resolve(ctx);\r\n });\r\n }.bind(this)\r\n );\r\n }\r\n\r\n /**\r\n * Draw node DOM into canvas via SVG snapshot.\r\n * @param {CanvasRenderingContext2D} ctx\r\n * @returns {Promise<CanvasRenderingContext2D>}\r\n */\r\n draw_nodes(ctx) {\r\n return domtoimage\r\n .toSvg(this.jm.view.e_nodes, { style: { zoom: 1 } })\r\n .then(this.load_image)\r\n .then(function (img) {\r\n ctx.drawImage(img, 0, 0);\r\n return ctx;\r\n });\r\n }\r\n\r\n /**\r\n * Draw watermark text on canvas.\r\n * @param {HTMLCanvasElement} c\r\n * @param {CanvasRenderingContext2D} ctx\r\n * @returns {CanvasRenderingContext2D}\r\n */\r\n draw_watermark(c, ctx) {\r\n ctx.textBaseline = 'bottom';\r\n ctx.fillStyle = '#000';\r\n ctx.font = '11px Verdana,Arial,Helvetica,sans-serif';\r\n if (!!this.options.watermark.left) {\r\n ctx.textAlign = 'left';\r\n ctx.fillText(this.options.watermark.left, 5.5, c.height - 2.5);\r\n }\r\n if (!!this.options.watermark.right) {\r\n ctx.textAlign = 'right';\r\n ctx.fillText(this.options.watermark.right, c.width - 5.5, c.height - 2.5);\r\n }\r\n return ctx;\r\n }\r\n\r\n /**\r\n * Load image from URL and resolve img element.\r\n * @param {string} url\r\n * @returns {Promise<HTMLImageElement>}\r\n */\r\n load_image(url) {\r\n return new Promise(function (resolve, reject) {\r\n let img = new Image();\r\n img.onload = function () {\r\n resolve(img);\r\n };\r\n img.onerror = reject;\r\n img.src = url;\r\n });\r\n }\r\n\r\n /**\r\n * Trigger download of canvas content as PNG.\r\n * @param {HTMLCanvasElement} c\r\n */\r\n download(c) {\r\n var name = (this.options.filename || this.jm.mind.name) + '.png';\r\n\r\n if (navigator.msSaveBlob && !!c.msToBlob) {\r\n var blob = c.msToBlob();\r\n navigator.msSaveBlob(blob, name);\r\n } else {\r\n var blob_url = c.toDataURL();\r\n var anchor = $.c('a');\r\n if ('download' in anchor) {\r\n anchor.style.visibility = 'hidden';\r\n anchor.href = blob_url;\r\n anchor.download = name;\r\n $.d.body.appendChild(anchor);\r\n var evt = $.d.createEvent('MouseEvents');\r\n evt.initEvent('click', true, true);\r\n anchor.dispatchEvent(evt);\r\n $.d.body.removeChild(anchor);\r\n } else {\r\n location.href = blob_url;\r\n }\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Screenshot plugin registration.\r\n * @type {import('../jsmind.plugin.js').Plugin<Partial<ScreenshotOptions>>}\r\n */\r\nexport const screenshot_plugin = new jsMind.plugin('screenshot', function (jm, options) {\r\n var jmss = new JmScreenshot(jm, options);\r\n jm.screenshot = jmss;\r\n jm.shoot = function () {\r\n jmss.shoot();\r\n };\r\n});\r\n\r\njsMind.register_plugin(screenshot_plugin);\r\n\r\nexport default JmScreenshot;\r\n"],"names":["jsMind","Error","domtoimage","$","DEFAULT_OPTIONS","filename","watermark","left","w","location","right","background","JmScreenshot","constructor","jm","options","opts","util","json","merge","this","version","dpr","view","device_pixel_ratio","shoot","c","create_canvas","ctx","getContext","scale","Promise","resolve","then","draw_background","draw_lines","draw_nodes","draw_watermark","download","clear","size","h","width","height","style","visibility","e_panel","appendChild","parentNode","removeChild","_","bg","fillStyle","fillRect","bind","graph","copy_to","toSvg","e_nodes","zoom","load_image","img","drawImage","textBaseline","font","textAlign","fillText","url","reject","Image","onload","onerror","src","name","mind","navigator","msSaveBlob","msToBlob","blob","blob_url","toDataURL","anchor","href","d","body","evt","createEvent","initEvent","dispatchEvent","screenshot_plugin","plugin","jmss","screenshot","register_plugin"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"jsmind.screenshot.js","sources":["../src/plugins/jsmind.screenshot.js"],"sourcesContent":["/**\n * @license BSD\n * @copyright 2014-2025 UmbraCi\n *\n * Project Home:\n * https://github.com/UmbraCi/jsmind/\n */\n\nimport jsMind from '@umbraci/jsmind';\nimport domtoimage from 'dom-to-image';\n\nif (!jsMind) {\n throw new Error('jsMind is not defined');\n}\n\nif (!domtoimage) {\n throw new Error('dom-to-image is required');\n}\n\nconst $ = jsMind.$;\n\n/**\n * Default options for screenshot plugin.\n * @typedef {Object} ScreenshotOptions\n * @property {string|null} [filename]\n * @property {{left?:string|Location,right?:string}} [watermark]\n * @property {string} [background]\n */\nconst DEFAULT_OPTIONS = {\n filename: null,\n watermark: {\n left: $.w.location,\n right: 'https://github.com/UmbraCi/jsmind',\n },\n background: 'transparent',\n};\n\n/**\n * Screenshot plugin for jsMind.\n */\nexport class JmScreenshot {\n /**\n * Create screenshot plugin instance.\n * @param {import('../jsmind.js').default} jm - jsMind instance\n * @param {Partial<ScreenshotOptions>} options - Plugin options\n */\n constructor(jm, options) {\n var opts = {};\n jsMind.util.json.merge(opts, DEFAULT_OPTIONS);\n jsMind.util.json.merge(opts, options);\n\n this.version = '0.2.0';\n /** @type {import('../jsmind.js').default} */\n this.jm = jm;\n /** @type {ScreenshotOptions} */\n this.options = opts;\n /** @type {number} */\n this.dpr = jm.view.device_pixel_ratio;\n }\n\n /** Take a screenshot of the mind map. */\n shoot() {\n let c = this.create_canvas();\n let ctx = c.getContext('2d');\n ctx.scale(this.dpr, this.dpr);\n Promise.resolve(ctx)\n .then(() => this.draw_background(ctx))\n .then(() => this.draw_lines(ctx))\n .then(() => this.draw_nodes(ctx))\n .then(() => this.draw_watermark(c, ctx))\n .then(() => this.download(c))\n .then(() => this.clear(c));\n }\n\n /**\n * Create canvas for screenshot.\n * @returns {HTMLCanvasElement} Canvas element\n */\n create_canvas() {\n let c = $.c('canvas');\n const w = this.jm.view.size.w;\n const h = this.jm.view.size.h;\n c.width = w * this.dpr;\n c.height = h * this.dpr;\n c.style.width = w + 'px';\n c.style.height = h + 'px';\n\n c.style.visibility = 'hidden';\n this.jm.view.e_panel.appendChild(c);\n return c;\n }\n\n /**\n * Clean up canvas element.\n * @param {HTMLCanvasElement} c - Canvas to remove\n */\n clear(c) {\n c.parentNode.removeChild(c);\n }\n\n /**\n * Draw background on canvas.\n * @param {CanvasRenderingContext2D} ctx - Canvas context\n * @returns {Promise<CanvasRenderingContext2D>} Promise resolving to context\n */\n draw_background(ctx) {\n return new Promise(\n function (resolve, _) {\n const bg = this.options.background;\n if (!!bg && bg !== 'transparent') {\n ctx.fillStyle = this.options.background;\n ctx.fillRect(0, 0, this.jm.view.size.w, this.jm.view.size.h);\n }\n resolve(ctx);\n }.bind(this)\n );\n }\n\n /**\n * Draw connection lines on canvas by copying from view graph.\n * @param {CanvasRenderingContext2D} ctx\n * @returns {Promise<CanvasRenderingContext2D>}\n */\n draw_lines(ctx) {\n return new Promise(\n function (resolve, _) {\n this.jm.view.graph.copy_to(ctx, function () {\n resolve(ctx);\n });\n }.bind(this)\n );\n }\n\n /**\n * Draw node DOM into canvas via SVG snapshot.\n * @param {CanvasRenderingContext2D} ctx\n * @returns {Promise<CanvasRenderingContext2D>}\n */\n draw_nodes(ctx) {\n return domtoimage\n .toSvg(this.jm.view.e_nodes, { style: { zoom: 1 } })\n .then(this.load_image)\n .then(function (img) {\n ctx.drawImage(img, 0, 0);\n return ctx;\n });\n }\n\n /**\n * Draw watermark text on canvas.\n * @param {HTMLCanvasElement} c\n * @param {CanvasRenderingContext2D} ctx\n * @returns {CanvasRenderingContext2D}\n */\n draw_watermark(c, ctx) {\n ctx.textBaseline = 'bottom';\n ctx.fillStyle = '#000';\n ctx.font = '11px Verdana,Arial,Helvetica,sans-serif';\n if (!!this.options.watermark.left) {\n ctx.textAlign = 'left';\n ctx.fillText(this.options.watermark.left, 5.5, c.height - 2.5);\n }\n if (!!this.options.watermark.right) {\n ctx.textAlign = 'right';\n ctx.fillText(this.options.watermark.right, c.width - 5.5, c.height - 2.5);\n }\n return ctx;\n }\n\n /**\n * Load image from URL and resolve img element.\n * @param {string} url\n * @returns {Promise<HTMLImageElement>}\n */\n load_image(url) {\n return new Promise(function (resolve, reject) {\n let img = new Image();\n img.onload = function () {\n resolve(img);\n };\n img.onerror = reject;\n img.src = url;\n });\n }\n\n /**\n * Trigger download of canvas content as PNG.\n * @param {HTMLCanvasElement} c\n */\n download(c) {\n var name = (this.options.filename || this.jm.mind.name) + '.png';\n\n if (navigator.msSaveBlob && !!c.msToBlob) {\n var blob = c.msToBlob();\n navigator.msSaveBlob(blob, name);\n } else {\n var blob_url = c.toDataURL();\n var anchor = $.c('a');\n if ('download' in anchor) {\n anchor.style.visibility = 'hidden';\n anchor.href = blob_url;\n anchor.download = name;\n $.d.body.appendChild(anchor);\n var evt = $.d.createEvent('MouseEvents');\n evt.initEvent('click', true, true);\n anchor.dispatchEvent(evt);\n $.d.body.removeChild(anchor);\n } else {\n location.href = blob_url;\n }\n }\n }\n}\n\n/**\n * Screenshot plugin registration.\n * @type {import('../jsmind.plugin.js').Plugin<Partial<ScreenshotOptions>>}\n */\nexport const screenshot_plugin = new jsMind.plugin('screenshot', function (jm, options) {\n var jmss = new JmScreenshot(jm, options);\n jm.screenshot = jmss;\n jm.shoot = function () {\n jmss.shoot();\n };\n});\n\njsMind.register_plugin(screenshot_plugin);\n\nexport default JmScreenshot;\n"],"names":["jsMind","Error","domtoimage","$","DEFAULT_OPTIONS","filename","watermark","left","w","location","right","background","JmScreenshot","constructor","jm","options","opts","util","json","merge","this","version","dpr","view","device_pixel_ratio","shoot","c","create_canvas","ctx","getContext","scale","Promise","resolve","then","draw_background","draw_lines","draw_nodes","draw_watermark","download","clear","size","h","width","height","style","visibility","e_panel","appendChild","parentNode","removeChild","_","bg","fillStyle","fillRect","bind","graph","copy_to","toSvg","e_nodes","zoom","load_image","img","drawImage","textBaseline","font","textAlign","fillText","url","reject","Image","onload","onerror","src","name","mind","navigator","msSaveBlob","msToBlob","blob","blob_url","toDataURL","anchor","href","d","body","evt","createEvent","initEvent","dispatchEvent","screenshot_plugin","plugin","jmss","screenshot","register_plugin"],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license BSD-3-Clause
|
|
3
|
+
* @copyright 2014-2025 hizzgdev@163.com
|
|
4
|
+
*
|
|
5
|
+
* Project Home:
|
|
6
|
+
* https://github.com/hizzgdev/jsmind/
|
|
7
|
+
*/
|
|
8
|
+
"use strict";function e(e){return e&&"object"==typeof e&&"default"in e?e:{default:e}}Object.defineProperty(exports,"__esModule",{value:!0});var t=e(require("@umbraci/jsmind"));const i=new class{constructor(e){this.w=e,this.d=e.document,this.g=function(e){return this.d.getElementById(e)},this.c=function(e){return this.d.createElement(e)},this.t=function(e,t){e.hasChildNodes()?e.firstChild.nodeValue=t:e.appendChild(this.d.createTextNode(t))},this.h=function(e,t){t instanceof HTMLElement?(e.innerHTML="",e.appendChild(t)):e.innerHTML=t},this.i=function(e){return!!e&&"object"==typeof e&&1===e.nodeType&&"object"==typeof e.style&&"object"==typeof e.ownerDocument},this.on=function(e,t,i){e.addEventListener?e.addEventListener(t,i,!1):e.attachEvent("on"+t,i)}}}(window),n={file:{read:function(e,t){var i=new FileReader;i.onload=function(){"function"==typeof t&&t(this.result,e.name)},i.readAsText(e)},save:function(e,t,n){var o;if("function"==typeof i.w.Blob)o=new Blob([e],{type:t});else{var r=new(i.w.BlobBuilder||i.w.MozBlobBuilder||i.w.WebKitBlobBuilder||i.w.MSBlobBuilder);r.append(e),o=r.getBlob(t)}if(navigator.msSaveBlob)navigator.msSaveBlob(o,n);else{var a=(i.w.URL||i.w.webkitURL).createObjectURL(o),s=i.c("a");if("download"in s){s.style.visibility="hidden",s.href=a,s.download=n,i.d.body.appendChild(s);var d=i.d.createEvent("MouseEvents");d.initEvent("click",!0,!0),s.dispatchEvent(d),i.d.body.removeChild(s)}else location.href=a}}},json:{json2string:function(e){return JSON.stringify(e)},string2json:function(e){return JSON.parse(e)},merge:function(e,t){for(var i in t)i in e?"object"!=typeof e[i]||"[object object]"!=Object.prototype.toString.call(e[i]).toLowerCase()||e[i].length?e[i]=t[i]:n.json.merge(e[i],t[i]):e[i]=t[i];return e}},uuid:{newid:function(){return((new Date).getTime().toString(16)+Math.random().toString(16).substring(2)).substring(2,18)}},text:{is_empty:function(e){return!e||0==e.replace(/\s*/,"").length}}};class o{constructor(e,t={}){this.jsMind=e,this.options={enabled:!0,shortcuts:{copy:"meta+c",paste:"meta+v",cut:"meta+x"},...t},this.clipboardData=null,this.logger={info:e=>console.info("[CopyPaste]",e),warn:e=>console.warn("[CopyPaste]",e),error:e=>console.error("[CopyPaste]",e),debug:e=>console.debug("[CopyPaste]",e)},this.init()}init(){return this.logger.info("CopyPaste plugin initialized"),!0}handleCopy(e){if(!this.options.enabled)return;const t=this.jsMind.get_selected_node();if(t)try{e.preventDefault(),this.clipboardData={id:t.id,topic:t.topic,data:t.data||{},direction:t.direction,expanded:t.expanded,children:this.copyChildren(t)},this.logger.info("Node copied:",t.topic),this.showMessage("节点已复制")}catch(e){this.logger.error("Copy failed:",e),this.showMessage("复制失败","error")}else this.logger.warn("No node selected for copying")}handlePaste(e){if(!this.options.enabled)return;if(!this.clipboardData)return this.logger.warn("No data in clipboard"),void this.showMessage("剪贴板为空,请先复制节点","warning");const t=this.jsMind.get_selected_node();if(!t)return this.logger.warn("No target node selected for pasting"),void this.showMessage("请选择目标节点","warning");try{e.preventDefault();const i=this.prepareBatchData(this.clipboardData),n=this.jsMind.add_nodes(t,[i]);n&&n.length>0&&(this.logger.info("Nodes pasted successfully:",this.clipboardData.topic),this.showMessage("粘贴成功"))}catch(e){this.logger.error("Paste failed:",e),this.showMessage("粘贴失败","error")}}handleCut(e){if(!this.options.enabled)return;const t=this.jsMind.get_selected_node();if(t){if(t.isroot)return this.logger.warn("Cannot cut root node"),void this.showMessage("不能剪切根节点","warning");try{e.preventDefault(),this.clipboardData={id:t.id,topic:t.topic,data:t.data||{},direction:t.direction,expanded:t.expanded,children:this.copyChildren(t)};this.jsMind.remove_node(t)&&(this.logger.info("Node cut:",t.topic),this.showMessage("节点已剪切"))}catch(e){this.logger.error("Cut failed:",e),this.showMessage("剪切失败","error")}}else this.logger.warn("No node selected for cutting")}copyChildren(e){return e.children&&0!==e.children.length?e.children.map(e=>({id:e.id,topic:e.topic,data:e.data||{},direction:e.direction,expanded:e.expanded,children:this.copyChildren(e)})):[]}prepareBatchData(e){return{id:n.uuid.newid(),topic:e.topic,data:e.data||{},children:e.children&&e.children.length>0?e.children.map(e=>this.prepareBatchData(e)):void 0}}pasteChildren(e,t){if(!t||0===t.length)return[];const i=t.map(e=>this.prepareBatchData(e));return this.jsMind.add_nodes(e,i)}showMessage(e,t="info"){console.log(`[CopyPaste ${t.toUpperCase()}]: ${e}`)}clear(){this.clipboardData=null,this.logger.info("Clipboard cleared")}getStatus(){return{hasData:!!this.clipboardData,data:this.clipboardData}}}const r=new t.default.plugin("copy-paste",function(e,t){var i=t||{};i.shortcuts=i.shortcuts||{copy:"meta+c",paste:"meta+v",cut:"meta+x"};var n=new o(e,i);e.view&&e.view.e_panel&&e.view.e_panel.addEventListener("keydown",function(e){!e.metaKey&&!e.ctrlKey||"c"!==e.key||e.shiftKey||e.altKey?!e.metaKey&&!e.ctrlKey||"v"!==e.key||e.shiftKey||e.altKey?!e.metaKey&&!e.ctrlKey||"x"!==e.key||e.shiftKey||e.altKey||n.handleCut(e):n.handlePaste(e):n.handleCopy(e)}),e.copy_paste_handler=n});t.default.register_plugin(r),exports.copy_paste_plugin=r,exports.default=o;
|
|
9
|
+
//# sourceMappingURL=jsmind.copy-paste.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jsmind.copy-paste.js","sources":["../src/jsmind.dom.js","../src/jsmind.util.js","../src/plugins/copy-paste/copy-paste-plugin-simple.js"],"sourcesContent":["/**\n * @license BSD\n * @copyright 2014-2025 UmbraCi\n *\n * Project Home:\n * https://github.com/UmbraCi/jsmind/\n */\n\n/**\n * Lightweight DOM helpers bound to a window.\n */\nclass Dom {\n /**\n * @param {Window} w\n */\n constructor(w) {\n /** @type {Window} */\n this.w = w;\n /** @type {Document} */\n this.d = w.document;\n /**\n * Get element by id.\n * @param {string} id\n * @returns {HTMLElement|null}\n */\n this.g = function (id) {\n return this.d.getElementById(id);\n };\n /**\n * Create element with given tag.\n * @param {string} tag\n * @returns {HTMLElement}\n */\n this.c = function (tag) {\n return this.d.createElement(tag);\n };\n /**\n * Set text content for element.\n * @param {HTMLElement} n\n * @param {string} t\n */\n this.t = function (n, t) {\n if (n.hasChildNodes()) {\n n.firstChild.nodeValue = t;\n } else {\n n.appendChild(this.d.createTextNode(t));\n }\n };\n\n /**\n * Set inner HTML or append element.\n * @param {HTMLElement} n\n * @param {string|HTMLElement} t\n */\n this.h = function (n, t) {\n if (t instanceof HTMLElement) {\n n.innerHTML = '';\n n.appendChild(t);\n } else {\n n.innerHTML = t;\n }\n };\n // detect isElement\n /**\n * Runtime check for HTMLElement.\n * @param {unknown} el\n * @returns {el is HTMLElement}\n */\n this.i = function (el) {\n return (\n !!el &&\n typeof el === 'object' &&\n el.nodeType === 1 &&\n typeof el.style === 'object' &&\n typeof el.ownerDocument === 'object'\n );\n };\n\n //target,eventType,handler\n /**\n * Add event listener with legacy fallback.\n * @param {HTMLElement} t\n * @param {string} e\n * @param {(ev:Event)=>void} h\n */\n this.on = function (t, e, h) {\n if (!!t.addEventListener) {\n t.addEventListener(e, h, false);\n } else {\n t.attachEvent('on' + e, h);\n }\n };\n }\n}\n\nexport const $ = new Dom(window);\n","/**\n * @license BSD\n * @copyright 2014-2025 UmbraCi\n *\n * Project Home:\n * https://github.com/UmbraCi/jsmind/\n */\n\nimport { $ } from './jsmind.dom.js';\n\n/**\n * Misc utility collection.\n * @type {{\n * file: { read: (file: File, cb:(result:string,name:string)=>void)=>void, save:(data:string,type:string,name:string)=>void},\n * json: { json2string:(v:unknown)=>string, string2json:(s:string)=>unknown, merge:(b:object,a:object)=>object },\n * uuid: { newid:()=>string },\n * text: { is_empty:(s?:string)=>boolean }\n * }}\n */\nexport const util = {\n file: {\n read: function (file_data, fn_callback) {\n var reader = new FileReader();\n reader.onload = function () {\n if (typeof fn_callback === 'function') {\n fn_callback(this.result, file_data.name);\n }\n };\n reader.readAsText(file_data);\n },\n\n save: function (file_data, type, name) {\n var blob;\n if (typeof $.w.Blob === 'function') {\n blob = new Blob([file_data], { type: type });\n } else {\n var BlobBuilder =\n $.w.BlobBuilder ||\n $.w.MozBlobBuilder ||\n $.w.WebKitBlobBuilder ||\n $.w.MSBlobBuilder;\n var bb = new BlobBuilder();\n bb.append(file_data);\n blob = bb.getBlob(type);\n }\n if (navigator.msSaveBlob) {\n navigator.msSaveBlob(blob, name);\n } else {\n var URL = $.w.URL || $.w.webkitURL;\n var blob_url = URL.createObjectURL(blob);\n var anchor = $.c('a');\n if ('download' in anchor) {\n anchor.style.visibility = 'hidden';\n anchor.href = blob_url;\n anchor.download = name;\n $.d.body.appendChild(anchor);\n var evt = $.d.createEvent('MouseEvents');\n evt.initEvent('click', true, true);\n anchor.dispatchEvent(evt);\n $.d.body.removeChild(anchor);\n } else {\n location.href = blob_url;\n }\n }\n },\n },\n\n json: {\n json2string: function (json) {\n return JSON.stringify(json);\n },\n string2json: function (json_str) {\n return JSON.parse(json_str);\n },\n merge: function (b, a) {\n for (var o in a) {\n if (o in b) {\n if (\n typeof b[o] === 'object' &&\n Object.prototype.toString.call(b[o]).toLowerCase() == '[object object]' &&\n !b[o].length\n ) {\n util.json.merge(b[o], a[o]);\n } else {\n b[o] = a[o];\n }\n } else {\n b[o] = a[o];\n }\n }\n return b;\n },\n },\n\n uuid: {\n newid: function () {\n return (\n new Date().getTime().toString(16) + Math.random().toString(16).substring(2)\n ).substring(2, 18);\n },\n },\n\n text: {\n is_empty: function (s) {\n if (!s) {\n return true;\n }\n return s.replace(/\\s*/, '').length == 0;\n },\n },\n};\n","/**\n * @license BSD-3-Clause\n * @copyright 2014-2025 hizzgdev@163.com\n *\n * Project Home:\n * https://github.com/hizzgdev/jsmind/\n */\n\nimport { Plugin } from '../../jsmind.plugin.js';\nimport { util } from '../../jsmind.util.js';\nimport jsMind from '@umbraci/jsmind';\n\n/**\n * 简化版复制粘贴插件\n */\n\nclass CopyPasteHandler {\n constructor(jm, options = {}) {\n this.jsMind = jm;\n this.options = {\n enabled: true,\n shortcuts: {\n copy: 'meta+c',\n paste: 'meta+v',\n cut: 'meta+x'\n },\n ...options\n };\n this.clipboardData = null;\n\n this.logger = {\n info: (msg) => console.info('[CopyPaste]', msg),\n warn: (msg) => console.warn('[CopyPaste]', msg),\n error: (msg) => console.error('[CopyPaste]', msg),\n debug: (msg) => console.debug('[CopyPaste]', msg)\n };\n\n this.init();\n }\n\n init() {\n this.logger.info('CopyPaste plugin initialized');\n return true;\n }\n\n handleCopy(event) {\n if (!this.options.enabled) return;\n\n const selectedNode = this.jsMind.get_selected_node();\n if (!selectedNode) {\n this.logger.warn('No node selected for copying');\n return;\n }\n\n try {\n event.preventDefault();\n\n // 复制节点数据\n this.clipboardData = {\n id: selectedNode.id,\n topic: selectedNode.topic,\n data: selectedNode.data || {},\n direction: selectedNode.direction,\n expanded: selectedNode.expanded,\n children: this.copyChildren(selectedNode)\n };\n\n this.logger.info('Node copied:', selectedNode.topic);\n\n // 显示成功消息\n this.showMessage('节点已复制');\n\n } catch (error) {\n this.logger.error('Copy failed:', error);\n this.showMessage('复制失败', 'error');\n }\n }\n\n handlePaste(event) {\n if (!this.options.enabled) return;\n if (!this.clipboardData) {\n this.logger.warn('No data in clipboard');\n this.showMessage('剪贴板为空,请先复制节点', 'warning');\n return;\n }\n\n const targetNode = this.jsMind.get_selected_node();\n if (!targetNode) {\n this.logger.warn('No target node selected for pasting');\n this.showMessage('请选择目标节点', 'warning');\n return;\n }\n\n try {\n event.preventDefault();\n\n // 准备批量数据(包含所有子节点)\n const batchData = this.prepareBatchData(this.clipboardData);\n\n // 使用 add_nodes 批量创建节点\n const createdNodes = this.jsMind.add_nodes(targetNode, [batchData]);\n\n if (createdNodes && createdNodes.length > 0) {\n this.logger.info('Nodes pasted successfully:', this.clipboardData.topic);\n this.showMessage('粘贴成功');\n }\n\n } catch (error) {\n this.logger.error('Paste failed:', error);\n this.showMessage('粘贴失败', 'error');\n }\n }\n\n handleCut(event) {\n if (!this.options.enabled) return;\n\n const selectedNode = this.jsMind.get_selected_node();\n if (!selectedNode) {\n this.logger.warn('No node selected for cutting');\n return;\n }\n\n if (selectedNode.isroot) {\n this.logger.warn('Cannot cut root node');\n this.showMessage('不能剪切根节点', 'warning');\n return;\n }\n\n try {\n event.preventDefault();\n\n // 先复制到剪贴板\n this.clipboardData = {\n id: selectedNode.id,\n topic: selectedNode.topic,\n data: selectedNode.data || {},\n direction: selectedNode.direction,\n expanded: selectedNode.expanded,\n children: this.copyChildren(selectedNode)\n };\n\n // 删除原节点\n const removed = this.jsMind.remove_node(selectedNode);\n\n if (removed) {\n this.logger.info('Node cut:', selectedNode.topic);\n this.showMessage('节点已剪切');\n }\n\n } catch (error) {\n this.logger.error('Cut failed:', error);\n this.showMessage('剪切失败', 'error');\n }\n }\n\n copyChildren(node) {\n if (!node.children || node.children.length === 0) {\n return [];\n }\n\n return node.children.map(child => ({\n id: child.id,\n topic: child.topic,\n data: child.data || {},\n direction: child.direction,\n expanded: child.expanded,\n children: this.copyChildren(child)\n }));\n }\n\n /**\n * 准备批量数据,将节点数据转换为 add_nodes 所需的格式\n * @param {Object} nodeData - 节点数据\n * @returns {Object} - 格式化后的批量数据\n */\n prepareBatchData(nodeData) {\n return {\n id: util.uuid.newid(),\n topic: nodeData.topic,\n data: nodeData.data || {},\n children: nodeData.children && nodeData.children.length > 0\n ? nodeData.children.map(child => this.prepareBatchData(child))\n : undefined\n };\n }\n\n pasteChildren(parentNode, children) {\n // 保留此方法以备兼容性,但新的实现不再使用递归方式\n if (!children || children.length === 0) return [];\n\n const batchData = children.map(child => this.prepareBatchData(child));\n return this.jsMind.add_nodes(parentNode, batchData);\n }\n\n showMessage(message, type = 'info') {\n // 简单的消息显示\n console.log(`[CopyPaste ${type.toUpperCase()}]: ${message}`);\n }\n\n clear() {\n this.clipboardData = null;\n this.logger.info('Clipboard cleared');\n }\n\n getStatus() {\n return {\n hasData: !!this.clipboardData,\n data: this.clipboardData\n };\n }\n}\n\n/**\n * 复制粘贴插件注册\n */\nexport const copy_paste_plugin = new jsMind.plugin('copy-paste', function (jm, options) {\n // 确保options存在且包含shortcuts\n var pluginOptions = options || {};\n pluginOptions.shortcuts = pluginOptions.shortcuts || {\n copy: 'meta+c',\n paste: 'meta+v',\n cut: 'meta+x'\n };\n\n var handler = new CopyPasteHandler(jm, pluginOptions);\n\n // 直接注册键盘事件监听器,而不是使用不存在的add_shortcut方法\n if (jm.view && jm.view.e_panel) {\n jm.view.e_panel.addEventListener('keydown', function(event) {\n // 检查是否按下了复制快捷键\n if ((event.metaKey || event.ctrlKey) && event.key === 'c' && !event.shiftKey && !event.altKey) {\n handler.handleCopy(event);\n }\n // 检查是否按下了粘贴快捷键\n else if ((event.metaKey || event.ctrlKey) && event.key === 'v' && !event.shiftKey && !event.altKey) {\n handler.handlePaste(event);\n }\n // 检查是否按下了剪切快捷键\n else if ((event.metaKey || event.ctrlKey) && event.key === 'x' && !event.shiftKey && !event.altKey) {\n handler.handleCut(event);\n }\n });\n }\n\n // 将处理器实例附加到jm对象,方便外部调用\n jm.copy_paste_handler = handler;\n});\n\n// 注册插件\njsMind.register_plugin(copy_paste_plugin);\n\nexport default CopyPasteHandler;"],"names":["$","constructor","w","this","d","document","g","id","getElementById","c","tag","createElement","t","n","hasChildNodes","firstChild","nodeValue","appendChild","createTextNode","h","HTMLElement","innerHTML","i","el","nodeType","style","ownerDocument","on","e","addEventListener","attachEvent","window","util","file","read","file_data","fn_callback","reader","FileReader","onload","result","name","readAsText","save","type","blob","Blob","bb","BlobBuilder","MozBlobBuilder","WebKitBlobBuilder","MSBlobBuilder","append","getBlob","navigator","msSaveBlob","blob_url","URL","webkitURL","createObjectURL","anchor","visibility","href","download","body","evt","createEvent","initEvent","dispatchEvent","removeChild","location","json","json2string","JSON","stringify","string2json","json_str","parse","merge","b","a","o","Object","prototype","toString","call","toLowerCase","length","uuid","newid","Date","getTime","Math","random","substring","text","is_empty","s","replace","CopyPasteHandler","jm","options","jsMind","enabled","shortcuts","copy","paste","cut","clipboardData","logger","info","msg","console","warn","error","debug","init","handleCopy","event","selectedNode","get_selected_node","preventDefault","topic","data","direction","expanded","children","copyChildren","showMessage","handlePaste","targetNode","batchData","prepareBatchData","createdNodes","add_nodes","handleCut","isroot","remove_node","node","map","child","nodeData","undefined","pasteChildren","parentNode","message","log","toUpperCase","clear","getStatus","hasData","copy_paste_plugin","plugin","pluginOptions","handler","view","e_panel","metaKey","ctrlKey","key","shiftKey","altKey","copy_paste_handler","register_plugin"],"mappings":"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"}
|
|
@@ -5,5 +5,5 @@
|
|
|
5
5
|
* Project Home:
|
|
6
6
|
* https://github.com/hizzgdev/jsmind/
|
|
7
7
|
*/
|
|
8
|
-
"use strict";function t(t){return t&&"object"==typeof t&&"default"in t?t:{default:t}}Object.defineProperty(exports,"__esModule",{value:!0});var e=t(require("@umbraci/jsmind"));if(!e.default)throw new Error("jsMind is not defined");const i=e.default.$,s="getSelection"in i.w?function(){i.w.getSelection().removeAllRanges()}:function(){i.d.selection.empty()},o={line_width:5,line_color:"rgba(0,0,0,0.3)",line_color_invalid:"rgba(255,51,51,0.6)",lookup_delay:200,lookup_interval:100,scrolling_trigger_width:20,scrolling_step_length:10,shadow_node_class_name:"jsmind-draggable-shadow-node"};class n{constructor(t,i){var s={};e.default.util.json.merge(s,o),e.default.util.json.merge(s,i),this.version="0.4.0",this.jm=t,this.options=s,this.is_svg_engine="svg"===t.view.opts.engine,this.e_canvas=null,this.canvas_ctx=null,this.helper_line=null,this.shadow=null,this.shadow_p_x=0,this.shadow_p_y=0,this.shadow_w=0,this.shadow_h=0,this.active_node=null,this.target_node=null,this.target_direct=null,this.client_w=0,this.client_h=0,this.offset_x=0,this.offset_y=0,this.hlookup_delay=0,this.hlookup_timer=0,this.capture=!1,this.moved=!1,this.canvas_draggable=t.get_view_draggable(),this.view_panel=t.view.e_panel,this.view_panel_rect=null}init(){this.create_canvas(),this.create_shadow(),this.event_bind()}resize(){this.jm.view.e_nodes.appendChild(this.shadow),this.is_svg_engine?(this.e_canvas.setAttribute("width",this.jm.view.size.w),this.e_canvas.setAttribute("height",this.jm.view.size.h)):(this.e_canvas.width=this.jm.view.size.w,this.e_canvas.height=this.jm.view.size.h)}create_canvas(){if(this.is_svg_engine){var t=this._create_svg_element("svg");t.setAttribute("class","jsmind-draggable-helper"),t.setAttribute("style","position: absolute; top: 0; left: 0; pointer-events: none;"),this.jm.view.e_panel.appendChild(t),this.e_canvas=t}else{var e=i.c("canvas");this.jm.view.e_panel.appendChild(e);var s=e.getContext("2d");this.e_canvas=e,this.canvas_ctx=s}}_create_svg_element(t){return i.d.createElementNS("http://www.w3.org/2000/svg",t)}create_shadow(){var t=i.c("jmnode");t.style.visibility="hidden",t.style.zIndex="3",t.style.cursor="move",t.style.opacity="0.7",t.className=this.options.shadow_node_class_name,this.shadow=t}reset_shadow(t){var e=this.shadow.style;this.shadow.innerHTML=t.innerHTML,e.left=t.style.left,e.top=t.style.top,e.width=t.style.width,e.height=t.style.height,e.backgroundImage=t.style.backgroundImage,e.backgroundSize=t.style.backgroundSize,e.transform=t.style.transform,this.shadow_w=this.shadow.clientWidth,this.shadow_h=this.shadow.clientHeight}show_shadow(){this.moved||(this.shadow.style.visibility="visible")}hide_shadow(){this.shadow.style.visibility="hidden"}magnet_shadow(t,e,i){this.clear_lines();var s=i?this.options.line_color_invalid:this.options.line_color;this.is_svg_engine?this.svg_draw_line(t.x,t.y,e.x,e.y,s):(this.canvas_ctx.lineWidth=this.options.line_width,this.canvas_ctx.strokeStyle=s,this.canvas_ctx.lineCap="round",this.canvas_lineto(t.x,t.y,e.x,e.y))}clear_lines(){this.is_svg_engine?this.helper_line&&this.helper_line.parentNode&&(this.e_canvas.removeChild(this.helper_line),this.helper_line=null):this.canvas_ctx.clearRect(0,0,this.jm.view.size.w,this.jm.view.size.h)}canvas_lineto(t,e,i,s){this.canvas_ctx.beginPath(),this.canvas_ctx.moveTo(t,e),this.canvas_ctx.lineTo(i,s),this.canvas_ctx.stroke()}svg_draw_line(t,e,i,s,o){this.helper_line=this._create_svg_element("path"),this.helper_line.setAttribute("stroke",o),this.helper_line.setAttribute("stroke-width",this.options.line_width),this.helper_line.setAttribute("fill","transparent"),this.helper_line.setAttribute("stroke-linecap","round"),this._svg_bezier_to(this.helper_line,t,e,i,s),this.e_canvas.appendChild(this.helper_line)}_svg_bezier_to(t,e,i,s,o){t.setAttribute("d","M "+e+" "+i+" C "+(e+2*(s-e)/3)+" "+i+", "+e+" "+o+", "+s+" "+o)}event_bind(){var t=this,e=this.jm.view.container;i.on(e,"mousedown",(function(e){0===e.button&&t.dragstart.call(t,e)})),i.on(e,"mousemove",(function(e){0===e.movementX&&0===e.movementY||t.drag.call(t,e)})),i.on(e,"mouseup",(function(e){t.dragend.call(t,e)})),i.on(e,"touchstart",(function(e){t.dragstart.call(t,e)})),i.on(e,"touchmove",(function(e){t.drag.call(t,e)})),i.on(e,"touchend",(function(e){t.dragend.call(t,e)}))}dragstart(t){if(this.jm.get_editable()&&!this.capture){var e=this.jm.view;if(!e.is_editing()){this.active_node=null,this.view_draggable=this.jm.get_view_draggable();var s=this.find_node_element(t.target);if(s){this.view_draggable&&this.jm.disable_view_draggable();var o=e.get_binded_nodeid(s);if(o){var n=this.jm.get_node(o);if(!n.isroot){if(n.data&&!1===n.data.draggable)return;this.reset_shadow(s),this.view_panel_rect=this.view_panel.getBoundingClientRect(),this.active_node=n,this.offset_x=(t.clientX||t.touches[0].clientX)/e.zoom_current-s.offsetLeft,this.offset_y=(t.clientY||t.touches[0].clientY)/e.zoom_current-s.offsetTop,this.client_hw=Math.floor(s.clientWidth/2),this.client_hh=Math.floor(s.clientHeight/2),0!=this.hlookup_delay&&i.w.clearTimeout(this.hlookup_delay),0!=this.hlookup_timer&&i.w.clearInterval(this.hlookup_timer);var h=this;this.hlookup_delay=i.w.setTimeout((function(){h.hlookup_delay=0,h.hlookup_timer=i.w.setInterval((function(){h.lookup_target_node.call(h)}),h.options.lookup_interval)}),this.options.lookup_delay),h.capture=!0}}}}}}drag(t){if(this.jm.get_editable()&&this.capture){t.preventDefault(),this.show_shadow(),this.moved=!0,s();var e=this.jm.view,i=(t.clientX||t.touches[0].clientX)/e.zoom_current-this.offset_x,o=(t.clientY||t.touches[0].clientY)/e.zoom_current-this.offset_y;t.clientY-this.view_panel_rect.top<this.options.scrolling_trigger_width&&this.view_panel.scrollTop>this.options.scrolling_step_length?(this.view_panel.scrollBy(0,-this.options.scrolling_step_length),this.offset_y+=this.options.scrolling_step_length/e.zoom_current):this.view_panel_rect.bottom-t.clientY<this.options.scrolling_trigger_width&&this.view_panel.scrollTop<this.view_panel.scrollHeight-this.view_panel_rect.height-this.options.scrolling_step_length&&(this.view_panel.scrollBy(0,this.options.scrolling_step_length),this.offset_y-=this.options.scrolling_step_length/e.zoom_current),t.clientX-this.view_panel_rect.left<this.options.scrolling_trigger_width&&this.view_panel.scrollLeft>this.options.scrolling_step_length?(this.view_panel.scrollBy(-this.options.scrolling_step_length,0),this.offset_x+=this.options.scrolling_step_length/e.zoom_current):this.view_panel_rect.right-t.clientX<this.options.scrolling_trigger_width&&this.view_panel.scrollLeft<this.view_panel.scrollWidth-this.view_panel_rect.width-this.options.scrolling_step_length&&(this.view_panel.scrollBy(this.options.scrolling_step_length,0),this.offset_x-=this.options.scrolling_step_length/e.zoom_current),this.shadow.style.left=i+"px",this.shadow.style.top=o+"px",s()}}dragend(t){if(this.jm.get_editable()){if(this.view_draggable&&this.jm.enable_view_draggable(),this.capture){if(0!=this.hlookup_delay&&(i.w.clearTimeout(this.hlookup_delay),this.hlookup_delay=0,this.clear_lines()),0!=this.hlookup_timer&&(i.w.clearInterval(this.hlookup_timer),this.hlookup_timer=0,this.clear_lines()),this.moved){var e=this.active_node,s=this.target_node,o=this.target_direct;this.move_node(e,s,o)}this.hide_shadow()}this.view_panel_rect=null,this.moved=!1,this.capture=!1}}find_node_element(t){return t===this.jm.view.e_nodes||t===this.jm.view.e_panel||t===this.jm.view.container?null:"jmnode"===t.tagName.toLowerCase()?t:this.find_node_element(t.parentNode)}lookup_target_node(){let t=this.shadow.offsetLeft,i=this.shadow.offsetTop;if(t===this.shadow_p_x&&i===this.shadow_p_y)return;this.shadow_p_x=t,this.shadow_p_y=i;let s=this.shadow_p_x+this.shadow_w/2>=this.get_root_x()?e.default.direction.right:e.default.direction.left,o=this.lookup_overlapping_node_parent(s)||this.lookup_close_node(s);if(o){let t=this.calc_point_of_node(o,s),i=e.default.node.inherited(this.active_node,o);this.magnet_shadow(t.sp,t.np,i),this.target_node=o,this.target_direct=s}}get_root_x(){let t=this.jm.get_root(),e=t.get_location(),i=t.get_size();return e.x+i.w/2}lookup_overlapping_node_parent(t){let e=this.shadow.getBoundingClientRect(),i=e.x+e.width*(1-t)/2,s=(this.jm.options.layout.hspace+this.jm.options.layout.pspace)*t,o=e.height,n=[[i,e.y],[i,e.y+o/2],[i,e.y+o],[i+s/2,e.y],[i+s/2,e.y+o/2],[i+s/2,e.y+o],[i+s,e.y],[i+s,e.y+o/2],[i+s,e.y+o]];for(const t of n){let e=this.lookup_node_parent_by_location(t[0],t[1]);if(e)return e}}lookup_node_parent_by_location(t,e){return i.d.elementsFromPoint(t,e).filter((t=>"JMNODE"===t.tagName&&t.className!==this.options.shadow_node_class_name)).map((t=>this.jm.view.get_binded_nodeid(t))).map((t=>t&&this.jm.mind.nodes[t])).map((t=>t&&t.parent)).find((t=>t))}lookup_close_node(t){return Object.values(this.jm.mind.nodes).filter((e=>e.direction==t||e.isroot)).filter((t=>this.jm.layout.is_visible(t))).filter((e=>this.shadow_on_target_side(e,t))).map((e=>({node:e,distance:this.shadow_to_node(e,t)}))).reduce(((t,e)=>t.distance<e.distance?t:e),{node:this.jm.get_root(),distance:Number.MAX_VALUE}).node}shadow_on_target_side(t,i){return i==e.default.direction.right&&this.shadow_to_right_of_node(t)>0||i==e.default.direction.left&&this.shadow_to_left_of_node(t)>0}shadow_to_right_of_node(t){return this.shadow_p_x-t.get_location().x-t.get_size().w}shadow_to_left_of_node(t){return t.get_location().x-this.shadow_p_x-this.shadow_w}shadow_to_base_line_of_node(t){return this.shadow_p_y+this.shadow_h/2-t.get_location().y-t.get_size().h/2}shadow_to_node(t,i){return(i===e.default.direction.right?Math.abs(this.shadow_to_right_of_node(t)):Math.abs(this.shadow_to_left_of_node(t)))+Math.abs(this.shadow_to_base_line_of_node(t))}calc_point_of_node(t,e){let i=t.get_size(),s=t.get_location(),o=t.isroot?s.x+i.w/2:s.x+i.w*(1+e)/2+this.options.line_width*e,n=s.y+i.h/2;return{sp:{x:this.shadow_p_x+this.shadow_w*(1-e)/2-this.options.line_width*e,y:this.shadow_p_y+this.shadow_h/2},np:{x:o,y:n}}}move_node(t,i,s){var o=this.shadow.offsetTop;if(i&&t&&!e.default.node.inherited(t,i)){if(this.options.validate_drag&&"function"==typeof this.options.validate_drag){if(!this.options.validate_drag(t,i))return this.active_node=null,this.target_node=null,void(this.target_direct=null)}for(var n=i.children,h=n.length,l=null,_=Number.MAX_VALUE,a=null,r="_last_";h--;)if((l=n[h]).direction==s&&l.id!=t.id){var d=l.get_location().y-o;d>0&&d<_&&(_=d,a=l,r="_first_")}a&&(r=a.id),this.jm.move_node(t.id,r,i.id,s)}this.active_node=null,this.target_node=null,this.target_direct=null}jm_event_handle(t,i){t===e.default.event_type.resize&&this.resize()}}const h=new e.default.plugin("draggable_node",(function(t,e){var i=new n(t,e);i.init(),t.add_event_listener((function(t,e){i.jm_event_handle.call(i,t,e)}))}));e.default.register_plugin(h),exports.DraggableNode=n,exports.default=n,exports.draggable_plugin=h;
|
|
8
|
+
"use strict";function t(t){return t&&"object"==typeof t&&"default"in t?t:{default:t}}Object.defineProperty(exports,"__esModule",{value:!0});var e=t(require("@umbraci/jsmind"));if(!e.default)throw new Error("jsMind is not defined");const i=e.default.$,s="getSelection"in i.w?function(){i.w.getSelection().removeAllRanges()}:function(){i.d.selection.empty()},o={line_width:5,line_color:"rgba(0,0,0,0.3)",line_color_invalid:"rgba(255,51,51,0.6)",lookup_delay:200,lookup_interval:100,scrolling_trigger_width:20,scrolling_step_length:10,shadow_node_class_name:"jsmind-draggable-shadow-node"};class n{constructor(t,i){var s={};e.default.util.json.merge(s,o),e.default.util.json.merge(s,i),this.version="0.4.0",this.jm=t,this.options=s,this.is_svg_engine="svg"===t.view.opts.engine,this.e_canvas=null,this.canvas_ctx=null,this.helper_line=null,this.shadow=null,this.shadow_p_x=0,this.shadow_p_y=0,this.shadow_w=0,this.shadow_h=0,this.active_node=null,this.target_node=null,this.target_direct=null,this.client_w=0,this.client_h=0,this.offset_x=0,this.offset_y=0,this.hlookup_delay=0,this.hlookup_timer=0,this.capture=!1,this.moved=!1,this.canvas_draggable=t.get_view_draggable(),this.view_panel=t.view.e_panel,this.view_panel_rect=null}init(){this.create_canvas(),this.create_shadow(),this.event_bind()}resize(){this.jm.view.e_nodes.appendChild(this.shadow),this.is_svg_engine?(this.e_canvas.setAttribute("width",this.jm.view.size.w),this.e_canvas.setAttribute("height",this.jm.view.size.h)):(this.e_canvas.width=this.jm.view.size.w,this.e_canvas.height=this.jm.view.size.h)}create_canvas(){if(this.is_svg_engine){var t=this._create_svg_element("svg");t.setAttribute("class","jsmind-draggable-helper"),t.setAttribute("style","position: absolute; top: 0; left: 0; pointer-events: none;"),this.jm.view.e_panel.appendChild(t),this.e_canvas=t}else{var e=i.c("canvas");this.jm.view.e_panel.appendChild(e);var s=e.getContext("2d");this.e_canvas=e,this.canvas_ctx=s}}_create_svg_element(t){return i.d.createElementNS("http://www.w3.org/2000/svg",t)}create_shadow(){var t=i.c("jmnode");t.style.visibility="hidden",t.style.zIndex="3",t.style.cursor="move",t.style.opacity="0.7",t.className=this.options.shadow_node_class_name,this.shadow=t}reset_shadow(t){var e=this.shadow.style;this.shadow.innerHTML=t.innerHTML,e.left=t.style.left,e.top=t.style.top,e.width=t.style.width,e.height=t.style.height,e.backgroundImage=t.style.backgroundImage,e.backgroundSize=t.style.backgroundSize,e.transform=t.style.transform,this.shadow_w=this.shadow.clientWidth,this.shadow_h=this.shadow.clientHeight}show_shadow(){this.moved||(this.shadow.style.visibility="visible")}hide_shadow(){this.shadow.style.visibility="hidden"}magnet_shadow(t,e,i){this.clear_lines();var s=i?this.options.line_color_invalid:this.options.line_color;this.is_svg_engine?this.svg_draw_line(t.x,t.y,e.x,e.y,s):(this.canvas_ctx.lineWidth=this.options.line_width,this.canvas_ctx.strokeStyle=s,this.canvas_ctx.lineCap="round",this.canvas_lineto(t.x,t.y,e.x,e.y))}clear_lines(){this.is_svg_engine?this.helper_line&&this.helper_line.parentNode&&(this.e_canvas.removeChild(this.helper_line),this.helper_line=null):this.canvas_ctx.clearRect(0,0,this.jm.view.size.w,this.jm.view.size.h)}canvas_lineto(t,e,i,s){this.canvas_ctx.beginPath(),this.canvas_ctx.moveTo(t,e),this.canvas_ctx.lineTo(i,s),this.canvas_ctx.stroke()}svg_draw_line(t,e,i,s,o){this.helper_line=this._create_svg_element("path"),this.helper_line.setAttribute("stroke",o),this.helper_line.setAttribute("stroke-width",this.options.line_width),this.helper_line.setAttribute("fill","transparent"),this.helper_line.setAttribute("stroke-linecap","round"),this._svg_bezier_to(this.helper_line,t,e,i,s),this.e_canvas.appendChild(this.helper_line)}_svg_bezier_to(t,e,i,s,o){t.setAttribute("d","M "+e+" "+i+" C "+(e+2*(s-e)/3)+" "+i+", "+e+" "+o+", "+s+" "+o)}event_bind(){var t=this,e=this.jm.view.container;i.on(e,"mousedown",function(e){0===e.button&&t.dragstart.call(t,e)}),i.on(e,"mousemove",function(e){0===e.movementX&&0===e.movementY||t.drag.call(t,e)}),i.on(e,"mouseup",function(e){t.dragend.call(t,e)}),i.on(e,"touchstart",function(e){t.dragstart.call(t,e)}),i.on(e,"touchmove",function(e){t.drag.call(t,e)}),i.on(e,"touchend",function(e){t.dragend.call(t,e)})}dragstart(t){if(this.jm.get_editable()&&!this.capture){var e=this.jm.view;if(!e.is_editing()){this.active_node=null,this.view_draggable=this.jm.get_view_draggable();var s=this.find_node_element(t.target);if(s){this.view_draggable&&this.jm.disable_view_draggable();var o=e.get_binded_nodeid(s);if(o){var n=this.jm.get_node(o);if(!n.isroot){if(n.data&&!1===n.data.draggable)return;this.reset_shadow(s),this.view_panel_rect=this.view_panel.getBoundingClientRect(),this.active_node=n,this.offset_x=(t.clientX||t.touches[0].clientX)/e.zoom_current-s.offsetLeft,this.offset_y=(t.clientY||t.touches[0].clientY)/e.zoom_current-s.offsetTop,this.client_hw=Math.floor(s.clientWidth/2),this.client_hh=Math.floor(s.clientHeight/2),0!=this.hlookup_delay&&i.w.clearTimeout(this.hlookup_delay),0!=this.hlookup_timer&&i.w.clearInterval(this.hlookup_timer);var h=this;this.hlookup_delay=i.w.setTimeout(function(){h.hlookup_delay=0,h.hlookup_timer=i.w.setInterval(function(){h.lookup_target_node.call(h)},h.options.lookup_interval)},this.options.lookup_delay),h.capture=!0}}}}}}drag(t){if(this.jm.get_editable()&&this.capture){t.preventDefault(),this.show_shadow(),this.moved=!0,s();var e=this.jm.view,i=(t.clientX||t.touches[0].clientX)/e.zoom_current-this.offset_x,o=(t.clientY||t.touches[0].clientY)/e.zoom_current-this.offset_y;t.clientY-this.view_panel_rect.top<this.options.scrolling_trigger_width&&this.view_panel.scrollTop>this.options.scrolling_step_length?(this.view_panel.scrollBy(0,-this.options.scrolling_step_length),this.offset_y+=this.options.scrolling_step_length/e.zoom_current):this.view_panel_rect.bottom-t.clientY<this.options.scrolling_trigger_width&&this.view_panel.scrollTop<this.view_panel.scrollHeight-this.view_panel_rect.height-this.options.scrolling_step_length&&(this.view_panel.scrollBy(0,this.options.scrolling_step_length),this.offset_y-=this.options.scrolling_step_length/e.zoom_current),t.clientX-this.view_panel_rect.left<this.options.scrolling_trigger_width&&this.view_panel.scrollLeft>this.options.scrolling_step_length?(this.view_panel.scrollBy(-this.options.scrolling_step_length,0),this.offset_x+=this.options.scrolling_step_length/e.zoom_current):this.view_panel_rect.right-t.clientX<this.options.scrolling_trigger_width&&this.view_panel.scrollLeft<this.view_panel.scrollWidth-this.view_panel_rect.width-this.options.scrolling_step_length&&(this.view_panel.scrollBy(this.options.scrolling_step_length,0),this.offset_x-=this.options.scrolling_step_length/e.zoom_current),this.shadow.style.left=i+"px",this.shadow.style.top=o+"px",s()}}dragend(t){if(this.jm.get_editable()){if(this.view_draggable&&this.jm.enable_view_draggable(),this.capture){if(0!=this.hlookup_delay&&(i.w.clearTimeout(this.hlookup_delay),this.hlookup_delay=0,this.clear_lines()),0!=this.hlookup_timer&&(i.w.clearInterval(this.hlookup_timer),this.hlookup_timer=0,this.clear_lines()),this.moved){var e=this.active_node,s=this.target_node,o=this.target_direct;this.move_node(e,s,o)}this.hide_shadow()}this.view_panel_rect=null,this.moved=!1,this.capture=!1}}find_node_element(t){return t===this.jm.view.e_nodes||t===this.jm.view.e_panel||t===this.jm.view.container?null:"jmnode"===t.tagName.toLowerCase()?t:this.find_node_element(t.parentNode)}lookup_target_node(){let t=this.shadow.offsetLeft,i=this.shadow.offsetTop;if(t===this.shadow_p_x&&i===this.shadow_p_y)return;this.shadow_p_x=t,this.shadow_p_y=i;let s=this.shadow_p_x+this.shadow_w/2>=this.get_root_x()?e.default.direction.right:e.default.direction.left,o=this.lookup_overlapping_node_parent(s)||this.lookup_close_node(s);if(o){let t=this.calc_point_of_node(o,s),i=e.default.node.inherited(this.active_node,o);this.magnet_shadow(t.sp,t.np,i),this.target_node=o,this.target_direct=s}}get_root_x(){let t=this.jm.get_root(),e=t.get_location(),i=t.get_size();return e.x+i.w/2}lookup_overlapping_node_parent(t){let e=this.shadow.getBoundingClientRect(),i=e.x+e.width*(1-t)/2,s=(this.jm.options.layout.hspace+this.jm.options.layout.pspace)*t,o=e.height,n=[[i,e.y],[i,e.y+o/2],[i,e.y+o],[i+s/2,e.y],[i+s/2,e.y+o/2],[i+s/2,e.y+o],[i+s,e.y],[i+s,e.y+o/2],[i+s,e.y+o]];for(const t of n){let e=this.lookup_node_parent_by_location(t[0],t[1]);if(e)return e}}lookup_node_parent_by_location(t,e){return i.d.elementsFromPoint(t,e).filter(t=>"JMNODE"===t.tagName&&t.className!==this.options.shadow_node_class_name).map(t=>this.jm.view.get_binded_nodeid(t)).map(t=>t&&this.jm.mind.nodes[t]).map(t=>t&&t.parent).find(t=>t)}lookup_close_node(t){return Object.values(this.jm.mind.nodes).filter(e=>e.direction==t||e.isroot).filter(t=>this.jm.layout.is_visible(t)).filter(e=>this.shadow_on_target_side(e,t)).map(e=>({node:e,distance:this.shadow_to_node(e,t)})).reduce((t,e)=>t.distance<e.distance?t:e,{node:this.jm.get_root(),distance:Number.MAX_VALUE}).node}shadow_on_target_side(t,i){return i==e.default.direction.right&&this.shadow_to_right_of_node(t)>0||i==e.default.direction.left&&this.shadow_to_left_of_node(t)>0}shadow_to_right_of_node(t){return this.shadow_p_x-t.get_location().x-t.get_size().w}shadow_to_left_of_node(t){return t.get_location().x-this.shadow_p_x-this.shadow_w}shadow_to_base_line_of_node(t){return this.shadow_p_y+this.shadow_h/2-t.get_location().y-t.get_size().h/2}shadow_to_node(t,i){return(i===e.default.direction.right?Math.abs(this.shadow_to_right_of_node(t)):Math.abs(this.shadow_to_left_of_node(t)))+Math.abs(this.shadow_to_base_line_of_node(t))}calc_point_of_node(t,e){let i=t.get_size(),s=t.get_location(),o=t.isroot?s.x+i.w/2:s.x+i.w*(1+e)/2+this.options.line_width*e,n=s.y+i.h/2;return{sp:{x:this.shadow_p_x+this.shadow_w*(1-e)/2-this.options.line_width*e,y:this.shadow_p_y+this.shadow_h/2},np:{x:o,y:n}}}move_node(t,i,s){var o=this.shadow.offsetTop;if(i&&t&&!e.default.node.inherited(t,i)){if(this.options.validate_drag&&"function"==typeof this.options.validate_drag){if(!this.options.validate_drag(t,i))return this.active_node=null,this.target_node=null,void(this.target_direct=null)}for(var n=i.children,h=n.length,l=null,_=Number.MAX_VALUE,a=null,r="_last_";h--;)if((l=n[h]).direction==s&&l.id!=t.id){var d=l.get_location().y-o;d>0&&d<_&&(_=d,a=l,r="_first_")}a&&(r=a.id),this.jm.move_node(t.id,r,i.id,s)}this.active_node=null,this.target_node=null,this.target_direct=null}jm_event_handle(t,i){t===e.default.event_type.resize&&this.resize()}}const h=new e.default.plugin("draggable_node",function(t,e){var i=new n(t,e);i.init(),t.add_event_listener(function(t,e){i.jm_event_handle.call(i,t,e)})});e.default.register_plugin(h),exports.DraggableNode=n,exports.default=n,exports.draggable_plugin=h;
|
|
9
9
|
//# sourceMappingURL=jsmind.draggable-node.js.map
|