jsmind.menu.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439
  1. /*
  2. * Released under BSD License
  3. * Copyright (c) 2019-2020 Allen_sun_js@hotmail.com
  4. *
  5. * Project Home:
  6. * https://github.com/allensunjian
  7. */
  8. (function ($w, temp) {
  9. var obj = null
  10. var Jm = $w[temp], name = 'menu', $d = $w['document'], menuEvent = 'oncontextmenu', clickEvent = 'onclick', $c = function (tag) { return $d.createElement(tag); }, _noop = function () { }, logger = (typeof console === 'undefined') ? { log: _noop, debug: _noop, error: _noop, warn: _noop, info: _noop } : console;
  11. var $t = function (n, t) { if (n.hasChildNodes()) { n.firstChild.nodeValue = t; } else { n.appendChild($d.createTextNode(t)); } };
  12. var $h = function (n, t) {
  13. if (t instanceof HTMLElement) {
  14. t.innerHTML = "";
  15. n.appendChild(t)
  16. } else {
  17. n.innerHTML = t;
  18. }
  19. };
  20. if (!Jm || Jm[name]) return;
  21. Jm.menu = function (_jm) {
  22. obj = this
  23. this._get_menu_options(_jm, function () {
  24. this.init(_jm);
  25. this._mount_events()
  26. })
  27. }
  28. Jm.menu.prototype = {
  29. defaultDataMap: {
  30. funcMap: {
  31. edit: {
  32. isDepNode: true,
  33. // defaultFn不受到中台变量的控制,始终会先于fn去执行
  34. defaultFn: function (node) {
  35. var f = this._menu_default_mind_methods._menu_begin_edit.call(this.jm);
  36. f && this._menu_default_mind_methods._menu_edit_node_begin(this.jm.view, node);
  37. },
  38. fn: _noop,
  39. text: 'edit node'
  40. },
  41. addChild: {
  42. isDepNode: true,
  43. fn: function (nodeid,text) {
  44. var selected_node = this.get_selected_node();
  45. if (selected_node) {
  46. var node = this.add_node(selected_node, nodeid, text);
  47. if (node) {
  48. this.select_node(nodeid);
  49. this.begin_edit(nodeid);
  50. obj._mount_events();
  51. }
  52. }
  53. },
  54. text: 'append child'
  55. },
  56. addBrother: {
  57. isDepNode: true,
  58. fn: function (nodeid,text) {
  59. var selected_node = this.get_selected_node();
  60. if (selected_node && !selected_node.isroot) {
  61. var node = this.insert_node_after(selected_node, nodeid, text);
  62. if (node) {
  63. this.select_node(nodeid);
  64. this.begin_edit(nodeid);
  65. obj._mount_events();
  66. }
  67. }
  68. },
  69. text: 'append brother'
  70. },
  71. delete: {
  72. isDepNode: true,
  73. fn: function () {
  74. this.shortcut.handle_delnode.call(this.shortcut, this);
  75. },
  76. text: 'delete node'
  77. },
  78. showAll: {
  79. sDepNode: false,
  80. fn: function () {
  81. this.expand_all(this)
  82. },
  83. text: 'show all'
  84. },
  85. hideAll: {
  86. isDepNode: false,
  87. fn: function () {
  88. this.collapse_all(this)
  89. },
  90. text: 'hide all'
  91. },
  92. screenshot: {
  93. isDepNode: false,
  94. fn: function () {
  95. if (!this.screenshot) {
  96. logger.error('[jsmind] screenshot dependent on jsmind.screenshot.js !');
  97. return;
  98. }
  99. this.screenshot.shootDownload();
  100. },
  101. text: 'load mind picture'
  102. },
  103. showNode: {
  104. isDepNode: true,
  105. fn: function (node) {
  106. this.expand_node(node);
  107. },
  108. text: 'show target node'
  109. },
  110. hideNode: {
  111. isDepNode: true,
  112. fn: function (node) {
  113. this.collapse_node(node);
  114. },
  115. text: 'hide target node'
  116. },
  117. },
  118. menuStl: {
  119. 'width': '150px',
  120. 'padding': '12px 0',
  121. 'position': 'fixed',
  122. 'z-index': '10',
  123. 'background': '#fff',
  124. 'box-shadow': '0 2px 12px 0 rgba(0,0,0,0.1)',
  125. 'border-radius': '5px',
  126. 'font-size': '12px',
  127. 'display': 'none'
  128. },
  129. menuItemStl:{
  130. padding: '5px 15px',
  131. cursor: 'pointer',
  132. display: 'block',
  133. 'text-align': 'center',
  134. 'transition':'all .2s'
  135. },
  136. injectionList:['edit','addChild','delete']
  137. },
  138. init: function (_jm) {
  139. this._create_menu(_jm);
  140. this._get_injectionList(_jm);
  141. this.menuOpts.switchMidStage && Jm.util.dom.add_event(_jm.view.e_editor , 'blur', function () {
  142. this._menu_default_mind_methods._menu_edit_node_end.call(_jm.view);
  143. if(typeof this.menuOpts.editCaller == 'function') {
  144. this.menuOpts.editCaller($w.menu._update_node_info, this._menu_default_mind_methods._menu_update_edit_node)
  145. return
  146. }
  147. this._menu_default_mind_methods._menu_update_edit_node();
  148. }.bind(this));
  149. },
  150. _event_contextMenu (e) {
  151. e.preventDefault();
  152. this.menu.style.left = e.clientX + 'px';
  153. this.menu.style.top = e.clientY + 'px';
  154. this.menu.style.display = 'block';
  155. this.selected_node = this.jm.get_selected_node();
  156. } ,
  157. _event_hideMenu() {
  158. this.menu.style.display = 'none'
  159. },
  160. _mount_events () {
  161. var items = document.getElementsByTagName('jmnode')
  162. for(let i = 0; i < items.length; i++) {
  163. items[i][menuEvent] = this._event_contextMenu.bind(this);
  164. }
  165. $w[clickEvent] = this._event_hideMenu.bind(this);
  166. },
  167. _create_menu (_jm) {
  168. var d = $c('menu');
  169. this._set_menu_wrap_syl(d);
  170. this.menu = d;
  171. this.e_panel = _jm.view.e_panel;
  172. this.e_panel.appendChild(d);
  173. },
  174. _create_menu_item (j, text, fn, isDepNode,cb, defaultFn) {
  175. var d = $c('menu-item'),_this = this;
  176. this._set_menu_item_syl(d);
  177. d.innerText = text;
  178. d.addEventListener('click', function () {
  179. if (this.selected_node || !isDepNode) {
  180. defaultFn.call(_this, this.selected_node);
  181. if (!_this._get_mid_opts()) {
  182. cb(this.selected_node, _noop)
  183. fn.call(j,Jm.util.uuid.newid(), this.menuOpts.newNodeText || 'New Node');
  184. return;
  185. }
  186. cb(this.selected_node,_this._mid_stage_next(function () {
  187. var retArgs = [this.selected_node],
  188. argus = Array.prototype.slice.call(arguments[0],0);
  189. argus[1] = this.menuOpts.newNodeText || 'New Node';
  190. if (argus[0]) {
  191. retArgs = argus
  192. }
  193. fn.apply(j,retArgs);
  194. }.bind(this)))
  195. return
  196. }
  197. alert(this.menuOpts.tipContent || 'Continue with node selected!')
  198. }.bind(this))
  199. d.addEventListener('mouseover', function () {
  200. d.style.background = 'rgb(179, 216, 255)'
  201. }.bind(this))
  202. d.addEventListener('mouseleave', function () {
  203. d.style.background = '#fff'
  204. }.bind(this))
  205. return d
  206. },
  207. _set_menu_wrap_syl (d) {
  208. var os = this._get_option_sty('menu',this._get_mixin_sty);
  209. d.style.cssText = this._format_cssText(os);
  210. },
  211. _set_menu_item_syl (d) {
  212. var os = this._get_option_sty('menuItem',this._get_mixin_sty);
  213. d.style.cssText = this._format_cssText(os)
  214. },
  215. _format_cssText (o) {
  216. var text = '';
  217. Object.keys(o).forEach(function (k) {
  218. text += k +':'+o[k] +';'
  219. })
  220. return text;
  221. },
  222. _empty_object (o) {
  223. return Object.keys(o).length == 0? true :false
  224. },
  225. _get_option_sty (type, fn) {
  226. var sty = this.menuOpts.style,
  227. menu = this.defaultDataMap.menuStl,
  228. menuItem = this.defaultDataMap.menuItemStl,
  229. o = {menu,menuItem}
  230. if (!sty) return o[type];
  231. if (!sty[type]) return o[type];
  232. if (!sty[type] || this._empty_object(sty[type])) return o[type];
  233. return fn( o[type],sty[type])
  234. },
  235. _get_mixin_sty (dSty, oSty) {
  236. var o = {};
  237. Object.keys(oSty).forEach(function (k) {
  238. o[k] = oSty[k];
  239. })
  240. Object.keys(dSty).forEach(function (k) {
  241. if (!o[k]) o[k] = dSty[k];
  242. })
  243. return o
  244. },
  245. _get_menu_options (j, fn) {
  246. var options = j.options;
  247. if (!options.menuOpts) return;
  248. if (!options.menuOpts.showMenu) return;
  249. this.menuOpts = j.options.menuOpts
  250. fn.call(this)
  251. },
  252. _get_injectionDetail () {
  253. var iLs = this.menuOpts.injectionList,
  254. dLs = this.defaultDataMap.injectionList;
  255. if (!iLs) return dLs;
  256. if (!Array.isArray(iLs)) {
  257. logger.error('[jsmind] injectionList must be a Array');
  258. return;
  259. }
  260. if (iLs.length == 0) return dLs;
  261. return iLs
  262. },
  263. _get_injectionList (j) {
  264. var list = this._get_injectionDetail(),
  265. _this = this;
  266. list.forEach(function (k) {
  267. var o = null,
  268. text = "",
  269. callback = _noop,
  270. defaultFn = _noop;
  271. if (typeof k == 'object') {
  272. o = _this.defaultDataMap.funcMap[k.target];
  273. text = k.text;
  274. k.callback && (callback = k.callback);
  275. } else {
  276. o = _this.defaultDataMap.funcMap[k];
  277. text = o.text;
  278. }
  279. if (o.defaultFn) defaultFn = o.defaultFn;
  280. _this.menu.appendChild(_this._create_menu_item(j ,text, o.fn, o.isDepNode,callback, defaultFn));
  281. })
  282. },
  283. _get_mid_opts () {
  284. var b = this.menuOpts.switchMidStage;
  285. if (!b) return false;
  286. if (typeof b !== 'boolean') {
  287. logger.error('[jsmind] switchMidStage must be Boolean');
  288. return false;
  289. }
  290. return b
  291. },
  292. _switch_view_db_event () {
  293. Jm.prototype.dblclick_handle = _noop;
  294. Jm.shortcut_provider.prototype.handler = _noop;
  295. Jm.view_provider.prototype.edit_node_end = _noop;
  296. },
  297. _mid_stage_next (fn) {
  298. return function () {
  299. fn(arguments);
  300. }
  301. },
  302. _reset_mind_event_edit () {},
  303. _menu_default_mind_methods: {
  304. _menu_begin_edit: function () {
  305. var f = this.get_editable();
  306. if (!f) {
  307. logger.error('fail, this mind map is not editable.');
  308. }
  309. return f;
  310. },
  311. _menu_edit_node_begin (scope, node) {
  312. if (!node.topic) {
  313. logger.warn("don't edit image nodes");
  314. return;
  315. }
  316. if (scope.editing_node != null) {
  317. this._menu_default_mind_methods._menu_edit_node_end.call(scope);
  318. }
  319. scope.editing_node = node;
  320. var view_data = node._data.view;
  321. var element = view_data.element;
  322. var topic = node.topic;
  323. var ncs = getComputedStyle(element);
  324. scope.e_editor.value = topic;
  325. scope.e_editor.style.width = (element.clientWidth - parseInt(ncs.getPropertyValue('padding-left')) - parseInt(ncs.getPropertyValue('padding-right'))) + 'px';
  326. element.innerHTML = '';
  327. element.appendChild(scope.e_editor);
  328. element.style.zIndex = 5;
  329. scope.e_editor.focus();
  330. scope.e_editor.select();
  331. },
  332. _menu_edit_node_end: function () {
  333. if (this.editing_node != null) {
  334. var node = this.editing_node;
  335. this.editing_node = null;
  336. var view_data = node._data.view;
  337. var element = view_data.element;
  338. var topic = this.e_editor.value;
  339. element.style.zIndex = 'auto';
  340. element.removeChild(this.e_editor);
  341. $w.menu._update_node_info = {id: node.id, topic: topic};
  342. if (Jm.util.text.is_empty(topic) || node.topic === topic) {
  343. if (this.opts.support_html) {
  344. $h(element, node.topic);
  345. } else {
  346. $t(element, node.topic);
  347. }
  348. }
  349. }
  350. },
  351. _menu_update_edit_node: function () {
  352. var info = $w.menu._update_node_info;
  353. $w.menu.jm.update_node(info.id, info.topic);
  354. }
  355. }
  356. }
  357. var plugin = new Jm.plugin('menu',function (_jm) {
  358. $w.menu = new Jm.menu(_jm);
  359. $w.menu.jm = _jm;
  360. if($w.menu.menuOpts) _jm.menu = $w.menu;
  361. })
  362. Jm.register_plugin(plugin)
  363. function preventMindEventDefault() {
  364. Jm.menu.prototype._switch_view_db_event();
  365. }
  366. Jm.preventMindEventDefault = preventMindEventDefault
  367. // eslint-disable-next-line no-unused-vars
  368. function reBuild () {
  369. if (obj !== null) {
  370. obj._mount_events()
  371. }
  372. }
  373. // export reBuild
  374. if (typeof module !== 'undefined' && typeof exports === 'object') {
  375. module.exports = {
  376. reBuild,
  377. init: function (opt) {
  378. Jm = opt
  379. }
  380. };
  381. }
  382. // exports = {
  383. // reBuild,
  384. // // init: function (opt) {
  385. // // console.log(opt)
  386. // // }
  387. // };
  388. // $w['render'] = function () {
  389. // if (obj !== null) {
  390. // obj._mount_events()
  391. // }
  392. // }
  393. })(window, 'jsMind')