jsmind.js 103 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934
  1. /*
  2. * Released under BSD License
  3. * Copyright (c) 2014-2016 hizzgdev@163.com
  4. *
  5. * Project Home:
  6. * https://github.com/hizzgdev/jsmind/
  7. */
  8. ;
  9. (function($w) {
  10. 'use strict';
  11. // set 'jsMind' as the library name.
  12. // __name__ should be a const value, Never try to change it easily.
  13. var __name__ = 'jsMind';
  14. // library version
  15. var __version__ = '0.4.6';
  16. // author
  17. var __author__ = 'hizzgdev@163.com';
  18. // an noop function define
  19. var _noop = function() {};
  20. var logger = (typeof console === 'undefined') ? {
  21. log: _noop,
  22. debug: _noop,
  23. error: _noop,
  24. warn: _noop,
  25. info: _noop
  26. } : console;
  27. // check global variables
  28. if (typeof module === 'undefined' || !module.exports) {
  29. if (typeof $w[__name__] != 'undefined') {
  30. logger.log(__name__ + ' has been already exist.');
  31. return;
  32. }
  33. }
  34. // shortcut of methods in dom
  35. var $d = $w.document;
  36. var $g = function(id) { return $d.getElementById(id); };
  37. var $c = function(tag) { return $d.createElement(tag); };
  38. var $t = function(n, t) { if (n.hasChildNodes()) { n.firstChild.nodeValue = t; } else { n.appendChild($d.createTextNode(t)); } };
  39. var $h = function(n, t) {
  40. if (t instanceof HTMLElement) {
  41. n.innerHTML = '';
  42. n.appendChild(t)
  43. } else {
  44. n.innerHTML = t;
  45. }
  46. };
  47. // detect isElement
  48. var $i = function(el) { return !!el && (typeof el === 'object') && (el.nodeType === 1) && (typeof el.style === 'object') && (typeof el.ownerDocument === 'object'); };
  49. if (typeof String.prototype.startsWith != 'function') { String.prototype.startsWith = function(p) { return this.slice(0, p.length) === p; }; }
  50. var DEFAULT_OPTIONS = {
  51. container: '', // id of the container
  52. editable: false, // you can change it in your options
  53. theme: null,
  54. mode: 'full', // full or side
  55. support_html: true,
  56. view: {
  57. hmargin: 100,
  58. vmargin: 50,
  59. line_width: 2,
  60. line_color: '#555'
  61. },
  62. layout: {
  63. hspace: 30,
  64. vspace: 20,
  65. pspace: 13
  66. },
  67. default_event_handle: {
  68. enable_mousedown_handle: true,
  69. enable_click_handle: true,
  70. enable_dblclick_handle: true
  71. },
  72. shortcut: {
  73. enable: true,
  74. handles: {},
  75. mapping: {
  76. addchild: 45, // Insert
  77. addbrother: 13, // Enter
  78. editnode: 113, // F2
  79. delnode: 46, // Delete
  80. toggle: 32, // Space
  81. left: 37, // Left
  82. up: 38, // Up
  83. right: 39, // Right
  84. down: 40, // Down
  85. }
  86. },
  87. };
  88. // core object
  89. var jm = function(options) {
  90. jm.current = this;
  91. this.version = __version__;
  92. var opts = {};
  93. jm.util.json.merge(opts, DEFAULT_OPTIONS);
  94. jm.util.json.merge(opts, options);
  95. if (!opts.container) {
  96. logger.error('the options.container should not be null or empty.');
  97. return;
  98. }
  99. this.options = opts;
  100. this.inited = false;
  101. this.mind = null;
  102. this.event_handles = [];
  103. this.init();
  104. };
  105. // ============= static object =============================================
  106. jm.direction = { left: -1, center: 0, right: 1 };
  107. jm.event_type = { show: 1, resize: 2, edit: 3, select: 4 };
  108. jm.node = function(sId, iIndex, sTopic, oData, bIsRoot, oParent, eDirection, bExpanded) {
  109. if (!sId) { logger.error('invalid nodeid'); return; }
  110. if (typeof iIndex != 'number') { logger.error('invalid node index'); return; }
  111. if (typeof bExpanded === 'undefined') { bExpanded = true; }
  112. this.id = sId;
  113. this.index = iIndex;
  114. this.topic = sTopic;
  115. this.data = oData || {};
  116. this.isroot = bIsRoot;
  117. this.parent = oParent;
  118. this.direction = eDirection;
  119. this.expanded = !!bExpanded;
  120. this.children = [];
  121. this._data = {};
  122. };
  123. jm.node.compare = function(node1, node2) {
  124. // '-1' is alwary the last
  125. var r = 0;
  126. var i1 = node1.index;
  127. var i2 = node2.index;
  128. if (i1 >= 0 && i2 >= 0) {
  129. r = i1 - i2;
  130. } else if (i1 == -1 && i2 == -1) {
  131. r = 0;
  132. } else if (i1 == -1) {
  133. r = 1;
  134. } else if (i2 == -1) {
  135. r = -1;
  136. } else {
  137. r = 0;
  138. }
  139. //logger.debug(i1+' <> '+i2+' = '+r);
  140. return r;
  141. };
  142. jm.node.inherited = function(pnode, node) {
  143. if (!!pnode && !!node) {
  144. if (pnode.id === node.id) {
  145. return true;
  146. }
  147. if (pnode.isroot) {
  148. return true;
  149. }
  150. var pid = pnode.id;
  151. var p = node;
  152. while (!p.isroot) {
  153. p = p.parent;
  154. if (p.id === pid) {
  155. return true;
  156. }
  157. }
  158. }
  159. return false;
  160. };
  161. jm.node.prototype = {
  162. get_location: function() {
  163. var vd = this._data.view;
  164. return {
  165. x: vd.abs_x,
  166. y: vd.abs_y
  167. };
  168. },
  169. get_size: function() {
  170. var vd = this._data.view;
  171. return {
  172. w: vd.width,
  173. h: vd.height
  174. }
  175. }
  176. };
  177. jm.mind = function() {
  178. this.name = null;
  179. this.author = null;
  180. this.version = null;
  181. this.root = null;
  182. this.selected = null;
  183. this.nodes = {};
  184. };
  185. jm.mind.prototype = {
  186. get_node: function(nodeid) {
  187. if (nodeid in this.nodes) {
  188. return this.nodes[nodeid];
  189. } else {
  190. logger.warn('the node[id=' + nodeid + '] can not be found');
  191. return null;
  192. }
  193. },
  194. set_root: function(nodeid, topic, data) {
  195. if (this.root == null) {
  196. this.root = new jm.node(nodeid, 0, topic, data, true);
  197. this._put_node(this.root);
  198. } else {
  199. logger.error('root node is already exist');
  200. }
  201. },
  202. add_node: function(parent_node, nodeid, topic, data, idx, direction, expanded) {
  203. if (!jm.util.is_node(parent_node)) {
  204. var the_parent_node = this.get_node(parent_node);
  205. if (!the_parent_node) {
  206. logger.error('the parent_node[id=' + parent_node + '] can not be found.');
  207. return null;
  208. } else {
  209. return this.add_node(the_parent_node, nodeid, topic, data, idx, direction, expanded);
  210. }
  211. }
  212. var nodeindex = idx || -1;
  213. var node = null;
  214. if (parent_node.isroot) {
  215. var d = jm.direction.right;
  216. if (isNaN(direction)) {
  217. var children = parent_node.children;
  218. var children_len = children.length;
  219. var r = 0;
  220. for (var i = 0; i < children_len; i++) { if (children[i].direction === jm.direction.left) { r--; } else { r++; } }
  221. d = (children_len > 1 && r > 0) ? jm.direction.left : jm.direction.right
  222. } else {
  223. d = (direction != jm.direction.left) ? jm.direction.right : jm.direction.left;
  224. }
  225. node = new jm.node(nodeid, nodeindex, topic, data, false, parent_node, d, expanded);
  226. } else {
  227. node = new jm.node(nodeid, nodeindex, topic, data, false, parent_node, parent_node.direction, expanded);
  228. }
  229. if (this._put_node(node)) {
  230. parent_node.children.push(node);
  231. this._reindex(parent_node);
  232. } else {
  233. logger.error('fail, the nodeid \'' + node.id + '\' has been already exist.');
  234. node = null;
  235. }
  236. return node;
  237. },
  238. insert_node_before: function(node_before, nodeid, topic, data) {
  239. if (!jm.util.is_node(node_before)) {
  240. var the_node_before = this.get_node(node_before);
  241. if (!the_node_before) {
  242. logger.error('the node_before[id=' + node_before + '] can not be found.');
  243. return null;
  244. } else {
  245. return this.insert_node_before(the_node_before, nodeid, topic, data);
  246. }
  247. }
  248. var node_index = node_before.index - 0.5;
  249. return this.add_node(node_before.parent, nodeid, topic, data, node_index);
  250. },
  251. get_node_before: function(node) {
  252. if (!jm.util.is_node(node)) {
  253. var the_node = this.get_node(node);
  254. if (!the_node) {
  255. logger.error('the node[id=' + node + '] can not be found.');
  256. return null;
  257. } else {
  258. return this.get_node_before(the_node);
  259. }
  260. }
  261. if (node.isroot) { return null; }
  262. var idx = node.index - 2;
  263. if (idx >= 0) {
  264. return node.parent.children[idx];
  265. } else {
  266. return null;
  267. }
  268. },
  269. insert_node_after: function(node_after, nodeid, topic, data) {
  270. if (!jm.util.is_node(node_after)) {
  271. var the_node_after = this.get_node(node_before);
  272. if (!the_node_after) {
  273. logger.error('the node_after[id=' + node_after + '] can not be found.');
  274. return null;
  275. } else {
  276. return this.insert_node_after(the_node_after, nodeid, topic, data);
  277. }
  278. }
  279. var node_index = node_after.index + 0.5;
  280. return this.add_node(node_after.parent, nodeid, topic, data, node_index);
  281. },
  282. get_node_after: function(node) {
  283. if (!jm.util.is_node(node)) {
  284. var the_node = this.get_node(node);
  285. if (!the_node) {
  286. logger.error('the node[id=' + node + '] can not be found.');
  287. return null;
  288. } else {
  289. return this.get_node_after(the_node);
  290. }
  291. }
  292. if (node.isroot) { return null; }
  293. var idx = node.index;
  294. var brothers = node.parent.children;
  295. if (brothers.length >= idx) {
  296. return node.parent.children[idx];
  297. } else {
  298. return null;
  299. }
  300. },
  301. move_node: function(node, beforeid, parentid, direction) {
  302. if (!jm.util.is_node(node)) {
  303. var the_node = this.get_node(node);
  304. if (!the_node) {
  305. logger.error('the node[id=' + node + '] can not be found.');
  306. return null;
  307. } else {
  308. return this.move_node(the_node, beforeid, parentid, direction);
  309. }
  310. }
  311. if (!parentid) {
  312. parentid = node.parent.id;
  313. }
  314. return this._move_node(node, beforeid, parentid, direction);
  315. },
  316. _flow_node_direction: function(node, direction) {
  317. if (typeof direction === 'undefined') {
  318. direction = node.direction;
  319. } else {
  320. node.direction = direction;
  321. }
  322. var len = node.children.length;
  323. while (len--) {
  324. this._flow_node_direction(node.children[len], direction);
  325. }
  326. },
  327. _move_node_internal: function(node, beforeid) {
  328. if (!!node && !!beforeid) {
  329. if (beforeid == '_last_') {
  330. node.index = -1;
  331. this._reindex(node.parent);
  332. } else if (beforeid == '_first_') {
  333. node.index = 0;
  334. this._reindex(node.parent);
  335. } else {
  336. var node_before = (!!beforeid) ? this.get_node(beforeid) : null;
  337. if (node_before != null && node_before.parent != null && node_before.parent.id == node.parent.id) {
  338. node.index = node_before.index - 0.5;
  339. this._reindex(node.parent);
  340. }
  341. }
  342. }
  343. return node;
  344. },
  345. _move_node: function(node, beforeid, parentid, direction) {
  346. if (!!node && !!parentid) {
  347. if (node.parent.id != parentid) {
  348. // remove from parent's children
  349. var sibling = node.parent.children;
  350. var si = sibling.length;
  351. while (si--) {
  352. if (sibling[si].id == node.id) {
  353. sibling.splice(si, 1);
  354. break;
  355. }
  356. }
  357. node.parent = this.get_node(parentid);
  358. node.parent.children.push(node);
  359. }
  360. if (node.parent.isroot) {
  361. if (direction == jsMind.direction.left) {
  362. node.direction = direction;
  363. } else {
  364. node.direction = jm.direction.right;
  365. }
  366. } else {
  367. node.direction = node.parent.direction;
  368. }
  369. this._move_node_internal(node, beforeid);
  370. this._flow_node_direction(node);
  371. }
  372. return node;
  373. },
  374. remove_node: function(node) {
  375. if (!jm.util.is_node(node)) {
  376. var the_node = this.get_node(node);
  377. if (!the_node) {
  378. logger.error('the node[id=' + node + '] can not be found.');
  379. return false;
  380. } else {
  381. return this.remove_node(the_node);
  382. }
  383. }
  384. if (!node) {
  385. logger.error('fail, the node can not be found');
  386. return false;
  387. }
  388. if (node.isroot) {
  389. logger.error('fail, can not remove root node');
  390. return false;
  391. }
  392. if (this.selected != null && this.selected.id == node.id) {
  393. this.selected = null;
  394. }
  395. // clean all subordinate nodes
  396. var children = node.children;
  397. var ci = children.length;
  398. while (ci--) {
  399. this.remove_node(children[ci]);
  400. }
  401. // clean all children
  402. children.length = 0;
  403. // remove from parent's children
  404. var sibling = node.parent.children;
  405. var si = sibling.length;
  406. while (si--) {
  407. if (sibling[si].id == node.id) {
  408. sibling.splice(si, 1);
  409. break;
  410. }
  411. }
  412. // remove from global nodes
  413. delete this.nodes[node.id];
  414. // clean all properties
  415. for (var k in node) {
  416. delete node[k];
  417. }
  418. // remove it's self
  419. node = null;
  420. //delete node;
  421. return true;
  422. },
  423. _put_node: function(node) {
  424. if (node.id in this.nodes) {
  425. logger.warn('the nodeid \'' + node.id + '\' has been already exist.');
  426. return false;
  427. } else {
  428. this.nodes[node.id] = node;
  429. return true;
  430. }
  431. },
  432. _reindex: function(node) {
  433. if (node instanceof jm.node) {
  434. node.children.sort(jm.node.compare);
  435. for (var i = 0; i < node.children.length; i++) {
  436. node.children[i].index = i + 1;
  437. }
  438. }
  439. },
  440. };
  441. jm.format = {
  442. node_tree: {
  443. example: {
  444. "meta": {
  445. "name": __name__,
  446. "author": __author__,
  447. "version": __version__
  448. },
  449. "format": "node_tree",
  450. "data": { "id": "root", "topic": "jsMind Example" }
  451. },
  452. get_mind: function(source) {
  453. var df = jm.format.node_tree;
  454. var mind = new jm.mind();
  455. mind.name = source.meta.name;
  456. mind.author = source.meta.author;
  457. mind.version = source.meta.version;
  458. df._parse(mind, source.data);
  459. return mind;
  460. },
  461. get_data: function(mind) {
  462. var df = jm.format.node_tree;
  463. var json = {};
  464. json.meta = {
  465. name: mind.name,
  466. author: mind.author,
  467. version: mind.version
  468. };
  469. json.format = 'node_tree';
  470. json.data = df._buildnode(mind.root);
  471. return json;
  472. },
  473. _parse: function(mind, node_root) {
  474. var df = jm.format.node_tree;
  475. var data = df._extract_data(node_root);
  476. mind.set_root(node_root.id, node_root.topic, data);
  477. if ('children' in node_root) {
  478. var children = node_root.children;
  479. for (var i = 0; i < children.length; i++) {
  480. df._extract_subnode(mind, mind.root, children[i]);
  481. }
  482. }
  483. },
  484. _extract_data: function(node_json) {
  485. var data = {};
  486. for (var k in node_json) {
  487. if (k == 'id' || k == 'topic' || k == 'children' || k == 'direction' || k == 'expanded') {
  488. continue;
  489. }
  490. data[k] = node_json[k];
  491. }
  492. return data;
  493. },
  494. _extract_subnode: function(mind, node_parent, node_json) {
  495. var df = jm.format.node_tree;
  496. var data = df._extract_data(node_json);
  497. var d = null;
  498. if (node_parent.isroot) {
  499. d = node_json.direction == 'left' ? jm.direction.left : jm.direction.right;
  500. }
  501. var node = mind.add_node(node_parent, node_json.id, node_json.topic, data, null, d, node_json.expanded);
  502. if ('children' in node_json) {
  503. var children = node_json.children;
  504. for (var i = 0; i < children.length; i++) {
  505. df._extract_subnode(mind, node, children[i]);
  506. }
  507. }
  508. },
  509. _buildnode: function(node) {
  510. var df = jm.format.node_tree;
  511. if (!(node instanceof jm.node)) { return; }
  512. var o = {
  513. id: node.id,
  514. topic: node.topic,
  515. expanded: node.expanded
  516. };
  517. if (!!node.parent && node.parent.isroot) {
  518. o.direction = node.direction == jm.direction.left ? 'left' : 'right';
  519. }
  520. if (node.data != null) {
  521. var node_data = node.data;
  522. for (var k in node_data) {
  523. o[k] = node_data[k];
  524. }
  525. }
  526. var children = node.children;
  527. if (children.length > 0) {
  528. o.children = [];
  529. for (var i = 0; i < children.length; i++) {
  530. o.children.push(df._buildnode(children[i]));
  531. }
  532. }
  533. return o;
  534. }
  535. },
  536. node_array: {
  537. example: {
  538. "meta": {
  539. "name": __name__,
  540. "author": __author__,
  541. "version": __version__
  542. },
  543. "format": "node_array",
  544. "data": [
  545. { "id": "root", "topic": "jsMind Example", "isroot": true }
  546. ]
  547. },
  548. get_mind: function(source) {
  549. var df = jm.format.node_array;
  550. var mind = new jm.mind();
  551. mind.name = source.meta.name;
  552. mind.author = source.meta.author;
  553. mind.version = source.meta.version;
  554. df._parse(mind, source.data);
  555. return mind;
  556. },
  557. get_data: function(mind) {
  558. var df = jm.format.node_array;
  559. var json = {};
  560. json.meta = {
  561. name: mind.name,
  562. author: mind.author,
  563. version: mind.version
  564. };
  565. json.format = 'node_array';
  566. json.data = [];
  567. df._array(mind, json.data);
  568. return json;
  569. },
  570. _parse: function(mind, node_array) {
  571. var df = jm.format.node_array;
  572. var narray = node_array.slice(0);
  573. // reverse array for improving looping performance
  574. narray.reverse();
  575. var root_id = df._extract_root(mind, narray);
  576. if (!!root_id) {
  577. df._extract_subnode(mind, root_id, narray);
  578. } else {
  579. logger.error('root node can not be found');
  580. }
  581. },
  582. _extract_root: function(mind, node_array) {
  583. var df = jm.format.node_array;
  584. var i = node_array.length;
  585. while (i--) {
  586. if ('isroot' in node_array[i] && node_array[i].isroot) {
  587. var root_json = node_array[i];
  588. var data = df._extract_data(root_json);
  589. mind.set_root(root_json.id, root_json.topic, data);
  590. node_array.splice(i, 1);
  591. return root_json.id;
  592. }
  593. }
  594. return null;
  595. },
  596. _extract_subnode: function(mind, parentid, node_array) {
  597. var df = jm.format.node_array;
  598. var i = node_array.length;
  599. var node_json = null;
  600. var data = null;
  601. var extract_count = 0;
  602. while (i--) {
  603. node_json = node_array[i];
  604. if (node_json.parentid == parentid) {
  605. data = df._extract_data(node_json);
  606. var d = null;
  607. var node_direction = node_json.direction;
  608. if (!!node_direction) {
  609. d = node_direction == 'left' ? jm.direction.left : jm.direction.right;
  610. }
  611. mind.add_node(parentid, node_json.id, node_json.topic, data, null, d, node_json.expanded);
  612. node_array.splice(i, 1);
  613. extract_count++;
  614. var sub_extract_count = df._extract_subnode(mind, node_json.id, node_array);
  615. if (sub_extract_count > 0) {
  616. // reset loop index after extract subordinate node
  617. i = node_array.length;
  618. extract_count += sub_extract_count;
  619. }
  620. }
  621. }
  622. return extract_count;
  623. },
  624. _extract_data: function(node_json) {
  625. var data = {};
  626. for (var k in node_json) {
  627. if (k == 'id' || k == 'topic' || k == 'parentid' || k == 'isroot' || k == 'direction' || k == 'expanded') {
  628. continue;
  629. }
  630. data[k] = node_json[k];
  631. }
  632. return data;
  633. },
  634. _array: function(mind, node_array) {
  635. var df = jm.format.node_array;
  636. df._array_node(mind.root, node_array);
  637. },
  638. _array_node: function(node, node_array) {
  639. var df = jm.format.node_array;
  640. if (!(node instanceof jm.node)) { return; }
  641. var o = {
  642. id: node.id,
  643. topic: node.topic,
  644. expanded: node.expanded
  645. };
  646. if (!!node.parent) {
  647. o.parentid = node.parent.id;
  648. }
  649. if (node.isroot) {
  650. o.isroot = true;
  651. }
  652. if (!!node.parent && node.parent.isroot) {
  653. o.direction = node.direction == jm.direction.left ? 'left' : 'right';
  654. }
  655. if (node.data != null) {
  656. var node_data = node.data;
  657. for (var k in node_data) {
  658. o[k] = node_data[k];
  659. }
  660. }
  661. node_array.push(o);
  662. var ci = node.children.length;
  663. for (var i = 0; i < ci; i++) {
  664. df._array_node(node.children[i], node_array);
  665. }
  666. },
  667. },
  668. freemind: {
  669. example: {
  670. "meta": {
  671. "name": __name__,
  672. "author": __author__,
  673. "version": __version__
  674. },
  675. "format": "freemind",
  676. "data": "<map version=\"1.0.1\"><node ID=\"root\" TEXT=\"freemind Example\"/></map>"
  677. },
  678. get_mind: function(source) {
  679. var df = jm.format.freemind;
  680. var mind = new jm.mind();
  681. mind.name = source.meta.name;
  682. mind.author = source.meta.author;
  683. mind.version = source.meta.version;
  684. var xml = source.data;
  685. var xml_doc = df._parse_xml(xml);
  686. var xml_root = df._find_root(xml_doc);
  687. df._load_node(mind, null, xml_root);
  688. return mind;
  689. },
  690. get_data: function(mind) {
  691. var df = jm.format.freemind;
  692. var json = {};
  693. json.meta = {
  694. name: mind.name,
  695. author: mind.author,
  696. version: mind.version
  697. };
  698. json.format = 'freemind';
  699. var xmllines = [];
  700. xmllines.push('<map version=\"1.0.1\">');
  701. df._buildmap(mind.root, xmllines);
  702. xmllines.push('</map>');
  703. json.data = xmllines.join(' ');
  704. return json;
  705. },
  706. _parse_xml: function(xml) {
  707. var xml_doc = null;
  708. if (window.DOMParser) {
  709. var parser = new DOMParser();
  710. xml_doc = parser.parseFromString(xml, 'text/xml');
  711. } else { // Internet Explorer
  712. xml_doc = new ActiveXObject('Microsoft.XMLDOM');
  713. xml_doc.async = false;
  714. xml_doc.loadXML(xml);
  715. }
  716. return xml_doc;
  717. },
  718. _find_root: function(xml_doc) {
  719. var nodes = xml_doc.childNodes;
  720. var node = null;
  721. var root = null;
  722. var n = null;
  723. for (var i = 0; i < nodes.length; i++) {
  724. n = nodes[i];
  725. if (n.nodeType == 1 && n.tagName == 'map') {
  726. node = n;
  727. break;
  728. }
  729. }
  730. if (!!node) {
  731. var ns = node.childNodes;
  732. node = null;
  733. for (var i = 0; i < ns.length; i++) {
  734. n = ns[i];
  735. if (n.nodeType == 1 && n.tagName == 'node') {
  736. node = n;
  737. break;
  738. }
  739. }
  740. }
  741. return node;
  742. },
  743. _load_node: function(mind, parent_id, xml_node) {
  744. var df = jm.format.freemind;
  745. var node_id = xml_node.getAttribute('ID');
  746. var node_topic = xml_node.getAttribute('TEXT');
  747. // look for richcontent
  748. if (node_topic == null) {
  749. var topic_children = xml_node.childNodes;
  750. var topic_child = null;
  751. for (var i = 0; i < topic_children.length; i++) {
  752. topic_child = topic_children[i];
  753. //logger.debug(topic_child.tagName);
  754. if (topic_child.nodeType == 1 && topic_child.tagName === 'richcontent') {
  755. node_topic = topic_child.textContent;
  756. break;
  757. }
  758. }
  759. }
  760. var node_data = df._load_attributes(xml_node);
  761. var node_expanded = ('expanded' in node_data) ? (node_data.expanded == 'true') : true;
  762. delete node_data.expanded;
  763. var node_position = xml_node.getAttribute('POSITION');
  764. var node_direction = null;
  765. if (!!node_position) {
  766. node_direction = node_position == 'left' ? jm.direction.left : jm.direction.right;
  767. }
  768. //logger.debug(node_position +':'+ node_direction);
  769. if (!!parent_id) {
  770. mind.add_node(parent_id, node_id, node_topic, node_data, null, node_direction, node_expanded);
  771. } else {
  772. mind.set_root(node_id, node_topic, node_data);
  773. }
  774. var children = xml_node.childNodes;
  775. var child = null;
  776. for (var i = 0; i < children.length; i++) {
  777. child = children[i];
  778. if (child.nodeType == 1 && child.tagName == 'node') {
  779. df._load_node(mind, node_id, child);
  780. }
  781. }
  782. },
  783. _load_attributes: function(xml_node) {
  784. var children = xml_node.childNodes;
  785. var attr = null;
  786. var attr_data = {};
  787. for (var i = 0; i < children.length; i++) {
  788. attr = children[i];
  789. if (attr.nodeType == 1 && attr.tagName === 'attribute') {
  790. attr_data[attr.getAttribute('NAME')] = attr.getAttribute('VALUE');
  791. }
  792. }
  793. return attr_data;
  794. },
  795. _buildmap: function(node, xmllines) {
  796. var df = jm.format.freemind;
  797. var pos = null;
  798. if (!!node.parent && node.parent.isroot) {
  799. pos = node.direction === jm.direction.left ? 'left' : 'right';
  800. }
  801. xmllines.push('<node');
  802. xmllines.push('ID=\"' + node.id + '\"');
  803. if (!!pos) {
  804. xmllines.push('POSITION=\"' + pos + '\"');
  805. }
  806. xmllines.push('TEXT=\"' + node.topic + '\">');
  807. // store expanded status as an attribute
  808. xmllines.push('<attribute NAME=\"expanded\" VALUE=\"' + node.expanded + '\"/>');
  809. // for attributes
  810. var node_data = node.data;
  811. if (node_data != null) {
  812. for (var k in node_data) {
  813. xmllines.push('<attribute NAME=\"' + k + '\" VALUE=\"' + node_data[k] + '\"/>');
  814. }
  815. }
  816. // for children
  817. var children = node.children;
  818. for (var i = 0; i < children.length; i++) {
  819. df._buildmap(children[i], xmllines);
  820. }
  821. xmllines.push('</node>');
  822. },
  823. },
  824. };
  825. // ============= utility object =============================================
  826. jm.util = {
  827. is_node: function(node) {
  828. return !!node && node instanceof jm.node;
  829. },
  830. ajax: {
  831. _xhr: function() {
  832. var xhr = null;
  833. if (window.XMLHttpRequest) {
  834. xhr = new XMLHttpRequest();
  835. } else {
  836. try {
  837. xhr = new ActiveXObject('Microsoft.XMLHTTP');
  838. } catch (e) {}
  839. }
  840. return xhr;
  841. },
  842. _eurl: function(url) {
  843. return encodeURIComponent(url);
  844. },
  845. request: function(url, param, method, callback, fail_callback) {
  846. var a = jm.util.ajax;
  847. var p = null;
  848. var tmp_param = [];
  849. for (var k in param) {
  850. tmp_param.push(a._eurl(k) + '=' + a._eurl(param[k]));
  851. }
  852. if (tmp_param.length > 0) {
  853. p = tmp_param.join('&');
  854. }
  855. var xhr = a._xhr();
  856. if (!xhr) { return; }
  857. xhr.onreadystatechange = function() {
  858. if (xhr.readyState == 4) {
  859. if (xhr.status == 200 || xhr.status == 0) {
  860. if (typeof callback === 'function') {
  861. var data = jm.util.json.string2json(xhr.responseText);
  862. if (data != null) {
  863. callback(data);
  864. } else {
  865. callback(xhr.responseText);
  866. }
  867. }
  868. } else {
  869. if (typeof fail_callback === 'function') {
  870. fail_callback(xhr);
  871. } else {
  872. logger.error('xhr request failed.', xhr);
  873. }
  874. }
  875. }
  876. }
  877. method = method || 'GET';
  878. xhr.open(method, url, true);
  879. xhr.setRequestHeader('If-Modified-Since', '0');
  880. if (method == 'POST') {
  881. xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded;charset=utf-8');
  882. xhr.send(p);
  883. } else {
  884. xhr.send();
  885. }
  886. },
  887. get: function(url, callback) {
  888. return jm.util.ajax.request(url, {}, 'GET', callback);
  889. },
  890. post: function(url, param, callback) {
  891. return jm.util.ajax.request(url, param, 'POST', callback);
  892. }
  893. },
  894. dom: {
  895. //target,eventType,handler
  896. add_event: function(t, e, h) {
  897. if (!!t.addEventListener) {
  898. t.addEventListener(e, h, false);
  899. } else {
  900. t.attachEvent('on' + e, h);
  901. }
  902. }
  903. },
  904. canvas: {
  905. bezierto: function(ctx, x1, y1, x2, y2) {
  906. ctx.beginPath();
  907. ctx.moveTo(x1, y1);
  908. ctx.bezierCurveTo(x1 + (x2 - x1) * 2 / 3, y1, x1, y2, x2, y2);
  909. ctx.stroke();
  910. },
  911. lineto: function(ctx, x1, y1, x2, y2) {
  912. ctx.beginPath();
  913. ctx.moveTo(x1, y1);
  914. ctx.lineTo(x2, y2);
  915. ctx.stroke();
  916. },
  917. clear: function(ctx, x, y, w, h) {
  918. ctx.clearRect(x, y, w, h);
  919. }
  920. },
  921. file: {
  922. read: function(file_data, fn_callback) {
  923. var reader = new FileReader();
  924. reader.onload = function() {
  925. if (typeof fn_callback === 'function') {
  926. fn_callback(this.result, file_data.name);
  927. }
  928. };
  929. reader.readAsText(file_data);
  930. },
  931. save: function(file_data, type, name) {
  932. var blob;
  933. if (typeof $w.Blob === 'function') {
  934. blob = new Blob([file_data], { type: type });
  935. } else {
  936. var BlobBuilder = $w.BlobBuilder || $w.MozBlobBuilder || $w.WebKitBlobBuilder || $w.MSBlobBuilder;
  937. var bb = new BlobBuilder();
  938. bb.append(file_data);
  939. blob = bb.getBlob(type);
  940. }
  941. if (navigator.msSaveBlob) {
  942. navigator.msSaveBlob(blob, name);
  943. } else {
  944. var URL = $w.URL || $w.webkitURL;
  945. var bloburl = URL.createObjectURL(blob);
  946. var anchor = $c('a');
  947. if ('download' in anchor) {
  948. anchor.style.visibility = 'hidden';
  949. anchor.href = bloburl;
  950. anchor.download = name;
  951. $d.body.appendChild(anchor);
  952. var evt = $d.createEvent('MouseEvents');
  953. evt.initEvent('click', true, true);
  954. anchor.dispatchEvent(evt);
  955. $d.body.removeChild(anchor);
  956. } else {
  957. location.href = bloburl;
  958. }
  959. }
  960. }
  961. },
  962. json: {
  963. json2string: function(json) {
  964. if (!!JSON) {
  965. try {
  966. var json_str = JSON.stringify(json);
  967. return json_str;
  968. } catch (e) {
  969. logger.warn(e);
  970. logger.warn('can not convert to string');
  971. return null;
  972. }
  973. }
  974. },
  975. string2json: function(json_str) {
  976. if (!!JSON) {
  977. try {
  978. var json = JSON.parse(json_str);
  979. return json;
  980. } catch (e) {
  981. logger.warn(e);
  982. logger.warn('can not parse to json');
  983. return null;
  984. }
  985. }
  986. },
  987. merge: function(b, a) {
  988. for (var o in a) {
  989. if (o in b) {
  990. if (typeof b[o] === 'object' &&
  991. Object.prototype.toString.call(b[o]).toLowerCase() == '[object object]' &&
  992. !b[o].length) {
  993. jm.util.json.merge(b[o], a[o]);
  994. } else {
  995. b[o] = a[o];
  996. }
  997. } else {
  998. b[o] = a[o];
  999. }
  1000. }
  1001. return b;
  1002. }
  1003. },
  1004. uuid: {
  1005. newid: function() {
  1006. return (new Date().getTime().toString(16) + Math.random().toString(16).substr(2)).substr(2, 16);
  1007. }
  1008. },
  1009. text: {
  1010. is_empty: function(s) {
  1011. if (!s) { return true; }
  1012. return s.replace(/\s*/, '').length == 0;
  1013. }
  1014. }
  1015. };
  1016. jm.prototype = {
  1017. init: function() {
  1018. if (this.inited) { return; }
  1019. this.inited = true;
  1020. var opts = this.options;
  1021. var opts_layout = {
  1022. mode: opts.mode,
  1023. hspace: opts.layout.hspace,
  1024. vspace: opts.layout.vspace,
  1025. pspace: opts.layout.pspace
  1026. }
  1027. var opts_view = {
  1028. container: opts.container,
  1029. support_html: opts.support_html,
  1030. hmargin: opts.view.hmargin,
  1031. vmargin: opts.view.vmargin,
  1032. line_width: opts.view.line_width,
  1033. line_color: opts.view.line_color
  1034. };
  1035. // create instance of function provider
  1036. this.data = new jm.data_provider(this);
  1037. this.layout = new jm.layout_provider(this, opts_layout);
  1038. this.view = new jm.view_provider(this, opts_view);
  1039. this.shortcut = new jm.shortcut_provider(this, opts.shortcut);
  1040. this.data.init();
  1041. this.layout.init();
  1042. this.view.init();
  1043. this.shortcut.init();
  1044. this._event_bind();
  1045. jm.init_plugins(this);
  1046. },
  1047. enable_edit: function() {
  1048. this.options.editable = true;
  1049. },
  1050. disable_edit: function() {
  1051. this.options.editable = false;
  1052. },
  1053. // call enable_event_handle('dblclick')
  1054. // options are 'mousedown', 'click', 'dblclick'
  1055. enable_event_handle: function(event_handle) {
  1056. this.options.default_event_handle['enable_' + event_handle + '_handle'] = true;
  1057. },
  1058. // call disable_event_handle('dblclick')
  1059. // options are 'mousedown', 'click', 'dblclick'
  1060. disable_event_handle: function(event_handle) {
  1061. this.options.default_event_handle['enable_' + event_handle + '_handle'] = false;
  1062. },
  1063. get_editable: function() {
  1064. return this.options.editable;
  1065. },
  1066. set_theme: function(theme) {
  1067. var theme_old = this.options.theme;
  1068. this.options.theme = (!!theme) ? theme : null;
  1069. if (theme_old != this.options.theme) {
  1070. this.view.reset_theme();
  1071. this.view.reset_custom_style();
  1072. }
  1073. },
  1074. _event_bind: function() {
  1075. this.view.add_event(this, 'mousedown', this.mousedown_handle);
  1076. this.view.add_event(this, 'click', this.click_handle);
  1077. this.view.add_event(this, 'dblclick', this.dblclick_handle);
  1078. },
  1079. mousedown_handle: function(e) {
  1080. if (!this.options.default_event_handle['enable_mousedown_handle']) {
  1081. return;
  1082. }
  1083. var element = e.target || event.srcElement;
  1084. var nodeid = this.view.get_binded_nodeid(element);
  1085. if (!!nodeid) {
  1086. this.select_node(nodeid);
  1087. } else {
  1088. this.select_clear();
  1089. }
  1090. },
  1091. click_handle: function(e) {
  1092. if (!this.options.default_event_handle['enable_click_handle']) {
  1093. return;
  1094. }
  1095. var element = e.target || event.srcElement;
  1096. var isexpander = this.view.is_expander(element);
  1097. if (isexpander) {
  1098. var nodeid = this.view.get_binded_nodeid(element);
  1099. if (!!nodeid) {
  1100. this.toggle_node(nodeid);
  1101. }
  1102. }
  1103. },
  1104. dblclick_handle: function(e) {
  1105. if (!this.options.default_event_handle['enable_dblclick_handle']) {
  1106. return;
  1107. }
  1108. if (this.get_editable()) {
  1109. var element = e.target || event.srcElement;
  1110. var nodeid = this.view.get_binded_nodeid(element);
  1111. if (!!nodeid) {
  1112. this.begin_edit(nodeid);
  1113. }
  1114. }
  1115. },
  1116. begin_edit: function(node) {
  1117. if (!jm.util.is_node(node)) {
  1118. var the_node = this.get_node(node);
  1119. if (!the_node) {
  1120. logger.error('the node[id=' + node + '] can not be found.');
  1121. return false;
  1122. } else {
  1123. return this.begin_edit(the_node);
  1124. }
  1125. }
  1126. if (this.get_editable()) {
  1127. this.view.edit_node_begin(node);
  1128. } else {
  1129. logger.error('fail, this mind map is not editable.');
  1130. return;
  1131. }
  1132. },
  1133. end_edit: function() {
  1134. this.view.edit_node_end();
  1135. },
  1136. toggle_node: function(node) {
  1137. if (!jm.util.is_node(node)) {
  1138. var the_node = this.get_node(node);
  1139. if (!the_node) {
  1140. logger.error('the node[id=' + node + '] can not be found.');
  1141. return;
  1142. } else {
  1143. return this.toggle_node(the_node);
  1144. }
  1145. }
  1146. if (node.isroot) { return; }
  1147. this.view.save_location(node);
  1148. this.layout.toggle_node(node);
  1149. this.view.relayout();
  1150. this.view.restore_location(node);
  1151. },
  1152. expand_node: function(node) {
  1153. if (!jm.util.is_node(node)) {
  1154. var the_node = this.get_node(node);
  1155. if (!the_node) {
  1156. logger.error('the node[id=' + node + '] can not be found.');
  1157. return;
  1158. } else {
  1159. return this.expand_node(the_node);
  1160. }
  1161. }
  1162. if (node.isroot) { return; }
  1163. this.view.save_location(node);
  1164. this.layout.expand_node(node);
  1165. this.view.relayout();
  1166. this.view.restore_location(node);
  1167. },
  1168. collapse_node: function(node) {
  1169. if (!jm.util.is_node(node)) {
  1170. var the_node = this.get_node(node);
  1171. if (!the_node) {
  1172. logger.error('the node[id=' + node + '] can not be found.');
  1173. return;
  1174. } else {
  1175. return this.collapse_node(the_node);
  1176. }
  1177. }
  1178. if (node.isroot) { return; }
  1179. this.view.save_location(node);
  1180. this.layout.collapse_node(node);
  1181. this.view.relayout();
  1182. this.view.restore_location(node);
  1183. },
  1184. expand_all: function() {
  1185. this.layout.expand_all();
  1186. this.view.relayout();
  1187. },
  1188. collapse_all: function() {
  1189. this.layout.collapse_all();
  1190. this.view.relayout();
  1191. },
  1192. expand_to_depth: function(depth) {
  1193. this.layout.expand_to_depth(depth);
  1194. this.view.relayout();
  1195. },
  1196. _reset: function() {
  1197. this.view.reset();
  1198. this.layout.reset();
  1199. this.data.reset();
  1200. },
  1201. _show: function(mind) {
  1202. var m = mind || jm.format.node_array.example;
  1203. this.mind = this.data.load(m);
  1204. if (!this.mind) {
  1205. logger.error('data.load error');
  1206. return;
  1207. } else {
  1208. logger.debug('data.load ok');
  1209. }
  1210. this.view.load();
  1211. logger.debug('view.load ok');
  1212. this.layout.layout();
  1213. logger.debug('layout.layout ok');
  1214. this.view.show(true);
  1215. logger.debug('view.show ok');
  1216. this.invoke_event_handle(jm.event_type.show, { data: [mind] });
  1217. },
  1218. show: function(mind) {
  1219. this._reset();
  1220. this._show(mind);
  1221. },
  1222. get_meta: function() {
  1223. return {
  1224. name: this.mind.name,
  1225. author: this.mind.author,
  1226. version: this.mind.version
  1227. };
  1228. },
  1229. get_data: function(data_format) {
  1230. var df = data_format || 'node_tree';
  1231. return this.data.get_data(df);
  1232. },
  1233. get_root: function() {
  1234. return this.mind.root;
  1235. },
  1236. get_node: function(nodeid) {
  1237. return this.mind.get_node(nodeid);
  1238. },
  1239. add_node: function(parent_node, nodeid, topic, data) {
  1240. if (this.get_editable()) {
  1241. var node = this.mind.add_node(parent_node, nodeid, topic, data);
  1242. if (!!node) {
  1243. this.view.add_node(node);
  1244. this.layout.layout();
  1245. this.view.show(false);
  1246. this.view.reset_node_custom_style(node);
  1247. this.expand_node(parent_node);
  1248. this.invoke_event_handle(jm.event_type.edit, { evt: 'add_node', data: [parent_node.id, nodeid, topic, data], node: nodeid });
  1249. }
  1250. return node;
  1251. } else {
  1252. logger.error('fail, this mind map is not editable');
  1253. return null;
  1254. }
  1255. },
  1256. insert_node_before: function(node_before, nodeid, topic, data) {
  1257. if (this.get_editable()) {
  1258. var beforeid = jm.util.is_node(node_before) ? node_before.id : node_before;
  1259. var node = this.mind.insert_node_before(node_before, nodeid, topic, data);
  1260. if (!!node) {
  1261. this.view.add_node(node);
  1262. this.layout.layout();
  1263. this.view.show(false);
  1264. this.invoke_event_handle(jm.event_type.edit, { evt: 'insert_node_before', data: [beforeid, nodeid, topic, data], node: nodeid });
  1265. }
  1266. return node;
  1267. } else {
  1268. logger.error('fail, this mind map is not editable');
  1269. return null;
  1270. }
  1271. },
  1272. insert_node_after: function(node_after, nodeid, topic, data) {
  1273. if (this.get_editable()) {
  1274. var afterid = jm.util.is_node(node_after) ? node_after.id : node_after;
  1275. var node = this.mind.insert_node_after(node_after, nodeid, topic, data);
  1276. if (!!node) {
  1277. this.view.add_node(node);
  1278. this.layout.layout();
  1279. this.view.show(false);
  1280. this.invoke_event_handle(jm.event_type.edit, { evt: 'insert_node_after', data: [afterid, nodeid, topic, data], node: nodeid });
  1281. }
  1282. return node;
  1283. } else {
  1284. logger.error('fail, this mind map is not editable');
  1285. return null;
  1286. }
  1287. },
  1288. remove_node: function(node) {
  1289. if (!jm.util.is_node(node)) {
  1290. var the_node = this.get_node(node);
  1291. if (!the_node) {
  1292. logger.error('the node[id=' + node + '] can not be found.');
  1293. return false;
  1294. } else {
  1295. return this.remove_node(the_node);
  1296. }
  1297. }
  1298. if (this.get_editable()) {
  1299. if (node.isroot) {
  1300. logger.error('fail, can not remove root node');
  1301. return false;
  1302. }
  1303. var nodeid = node.id;
  1304. var parentid = node.parent.id;
  1305. var parent_node = this.get_node(parentid);
  1306. this.view.save_location(parent_node);
  1307. this.view.remove_node(node);
  1308. this.mind.remove_node(node);
  1309. this.layout.layout();
  1310. this.view.show(false);
  1311. this.view.restore_location(parent_node);
  1312. this.invoke_event_handle(jm.event_type.edit, { evt: 'remove_node', data: [nodeid], node: parentid });
  1313. return true;
  1314. } else {
  1315. logger.error('fail, this mind map is not editable');
  1316. return false;
  1317. }
  1318. },
  1319. update_node: function(nodeid, topic) {
  1320. if (this.get_editable()) {
  1321. if (jm.util.text.is_empty(topic)) {
  1322. logger.warn('fail, topic can not be empty');
  1323. return;
  1324. }
  1325. var node = this.get_node(nodeid);
  1326. if (!!node) {
  1327. if (node.topic === topic) {
  1328. logger.info('nothing changed');
  1329. this.view.update_node(node);
  1330. return;
  1331. }
  1332. node.topic = topic;
  1333. this.view.update_node(node);
  1334. this.layout.layout();
  1335. this.view.show(false);
  1336. this.invoke_event_handle(jm.event_type.edit, { evt: 'update_node', data: [nodeid, topic], node: nodeid });
  1337. }
  1338. } else {
  1339. logger.error('fail, this mind map is not editable');
  1340. return;
  1341. }
  1342. },
  1343. move_node: function(nodeid, beforeid, parentid, direction) {
  1344. if (this.get_editable()) {
  1345. var node = this.mind.move_node(nodeid, beforeid, parentid, direction);
  1346. if (!!node) {
  1347. this.view.update_node(node);
  1348. this.layout.layout();
  1349. this.view.show(false);
  1350. this.invoke_event_handle(jm.event_type.edit, { evt: 'move_node', data: [nodeid, beforeid, parentid, direction], node: nodeid });
  1351. }
  1352. } else {
  1353. logger.error('fail, this mind map is not editable');
  1354. return;
  1355. }
  1356. },
  1357. select_node: function(node) {
  1358. if (!jm.util.is_node(node)) {
  1359. var the_node = this.get_node(node);
  1360. if (!the_node) {
  1361. logger.error('the node[id=' + node + '] can not be found.');
  1362. return;
  1363. } else {
  1364. return this.select_node(the_node);
  1365. }
  1366. }
  1367. if (!this.layout.is_visible(node)) {
  1368. return;
  1369. }
  1370. this.mind.selected = node;
  1371. this.view.select_node(node);
  1372. },
  1373. get_selected_node: function() {
  1374. if (!!this.mind) {
  1375. return this.mind.selected;
  1376. } else {
  1377. return null;
  1378. }
  1379. },
  1380. select_clear: function() {
  1381. if (!!this.mind) {
  1382. this.mind.selected = null;
  1383. this.view.select_clear();
  1384. }
  1385. },
  1386. is_node_visible: function(node) {
  1387. return this.layout.is_visible(node);
  1388. },
  1389. find_node_before: function(node) {
  1390. if (!jm.util.is_node(node)) {
  1391. var the_node = this.get_node(node);
  1392. if (!the_node) {
  1393. logger.error('the node[id=' + node + '] can not be found.');
  1394. return;
  1395. } else {
  1396. return this.find_node_before(the_node);
  1397. }
  1398. }
  1399. if (node.isroot) { return null; }
  1400. var n = null;
  1401. if (node.parent.isroot) {
  1402. var c = node.parent.children;
  1403. var prev = null;
  1404. var ni = null;
  1405. for (var i = 0; i < c.length; i++) {
  1406. ni = c[i];
  1407. if (node.direction === ni.direction) {
  1408. if (node.id === ni.id) {
  1409. n = prev;
  1410. }
  1411. prev = ni;
  1412. }
  1413. }
  1414. } else {
  1415. n = this.mind.get_node_before(node);
  1416. }
  1417. return n;
  1418. },
  1419. find_node_after: function(node) {
  1420. if (!jm.util.is_node(node)) {
  1421. var the_node = this.get_node(node);
  1422. if (!the_node) {
  1423. logger.error('the node[id=' + node + '] can not be found.');
  1424. return;
  1425. } else {
  1426. return this.find_node_after(the_node);
  1427. }
  1428. }
  1429. if (node.isroot) { return null; }
  1430. var n = null;
  1431. if (node.parent.isroot) {
  1432. var c = node.parent.children;
  1433. var getthis = false;
  1434. var ni = null;
  1435. for (var i = 0; i < c.length; i++) {
  1436. ni = c[i];
  1437. if (node.direction === ni.direction) {
  1438. if (getthis) {
  1439. n = ni;
  1440. break;
  1441. }
  1442. if (node.id === ni.id) {
  1443. getthis = true;
  1444. }
  1445. }
  1446. }
  1447. } else {
  1448. n = this.mind.get_node_after(node);
  1449. }
  1450. return n;
  1451. },
  1452. set_node_color: function(nodeid, bgcolor, fgcolor) {
  1453. if (this.get_editable()) {
  1454. var node = this.mind.get_node(nodeid);
  1455. if (!!node) {
  1456. if (!!bgcolor) {
  1457. node.data['background-color'] = bgcolor;
  1458. }
  1459. if (!!fgcolor) {
  1460. node.data['foreground-color'] = fgcolor;
  1461. }
  1462. this.view.reset_node_custom_style(node);
  1463. }
  1464. } else {
  1465. logger.error('fail, this mind map is not editable');
  1466. return null;
  1467. }
  1468. },
  1469. set_node_font_style: function(nodeid, size, weight, style) {
  1470. if (this.get_editable()) {
  1471. var node = this.mind.get_node(nodeid);
  1472. if (!!node) {
  1473. if (!!size) {
  1474. node.data['font-size'] = size;
  1475. }
  1476. if (!!weight) {
  1477. node.data['font-weight'] = weight;
  1478. }
  1479. if (!!style) {
  1480. node.data['font-style'] = style;
  1481. }
  1482. this.view.reset_node_custom_style(node);
  1483. this.view.update_node(node);
  1484. this.layout.layout();
  1485. this.view.show(false);
  1486. }
  1487. } else {
  1488. logger.error('fail, this mind map is not editable');
  1489. return null;
  1490. }
  1491. },
  1492. set_node_background_image: function(nodeid, image, width, height, rotation) {
  1493. if (this.get_editable()) {
  1494. var node = this.mind.get_node(nodeid);
  1495. if (!!node) {
  1496. if (!!image) {
  1497. node.data['background-image'] = image;
  1498. }
  1499. if (!!width) {
  1500. node.data['width'] = width;
  1501. }
  1502. if (!!height) {
  1503. node.data['height'] = height;
  1504. }
  1505. if (!!rotation) {
  1506. node.data['background-rotation'] = rotation;
  1507. }
  1508. this.view.reset_node_custom_style(node);
  1509. this.view.update_node(node);
  1510. this.layout.layout();
  1511. this.view.show(false);
  1512. }
  1513. } else {
  1514. logger.error('fail, this mind map is not editable');
  1515. return null;
  1516. }
  1517. },
  1518. set_node_background_rotation: function(nodeid, rotation) {
  1519. if (this.get_editable()) {
  1520. var node = this.mind.get_node(nodeid);
  1521. if (!!node) {
  1522. if (!node.data['background-image']) {
  1523. logger.error('fail, only can change rotation angle of node with background image');
  1524. return null;
  1525. }
  1526. node.data['background-rotation'] = rotation;
  1527. this.view.reset_node_custom_style(node);
  1528. this.view.update_node(node);
  1529. this.layout.layout();
  1530. this.view.show(false);
  1531. }
  1532. } else {
  1533. logger.error('fail, this mind map is not editable');
  1534. return null;
  1535. }
  1536. },
  1537. resize: function() {
  1538. this.view.resize();
  1539. },
  1540. // callback(type ,data)
  1541. add_event_listener: function(callback) {
  1542. if (typeof callback === 'function') {
  1543. this.event_handles.push(callback);
  1544. }
  1545. },
  1546. invoke_event_handle: function(type, data) {
  1547. var j = this;
  1548. $w.setTimeout(function() {
  1549. j._invoke_event_handle(type, data);
  1550. }, 0);
  1551. },
  1552. _invoke_event_handle: function(type, data) {
  1553. var l = this.event_handles.length;
  1554. for (var i = 0; i < l; i++) {
  1555. this.event_handles[i](type, data);
  1556. }
  1557. }
  1558. };
  1559. // ============= data provider =============================================
  1560. jm.data_provider = function(jm) {
  1561. this.jm = jm;
  1562. };
  1563. jm.data_provider.prototype = {
  1564. init: function() {
  1565. logger.debug('data.init');
  1566. },
  1567. reset: function() {
  1568. logger.debug('data.reset');
  1569. },
  1570. load: function(mind_data) {
  1571. var df = null;
  1572. var mind = null;
  1573. if (typeof mind_data === 'object') {
  1574. if (!!mind_data.format) {
  1575. df = mind_data.format;
  1576. } else {
  1577. df = 'node_tree';
  1578. }
  1579. } else {
  1580. df = 'freemind';
  1581. }
  1582. if (df == 'node_array') {
  1583. mind = jm.format.node_array.get_mind(mind_data);
  1584. } else if (df == 'node_tree') {
  1585. mind = jm.format.node_tree.get_mind(mind_data);
  1586. } else if (df == 'freemind') {
  1587. mind = jm.format.freemind.get_mind(mind_data);
  1588. } else {
  1589. logger.warn('unsupported format');
  1590. }
  1591. return mind;
  1592. },
  1593. get_data: function(data_format) {
  1594. var data = null;
  1595. if (data_format == 'node_array') {
  1596. data = jm.format.node_array.get_data(this.jm.mind);
  1597. } else if (data_format == 'node_tree') {
  1598. data = jm.format.node_tree.get_data(this.jm.mind);
  1599. } else if (data_format == 'freemind') {
  1600. data = jm.format.freemind.get_data(this.jm.mind);
  1601. } else {
  1602. logger.error('unsupported ' + data_format + ' format');
  1603. }
  1604. return data;
  1605. },
  1606. };
  1607. // ============= layout provider ===========================================
  1608. jm.layout_provider = function(jm, options) {
  1609. this.opts = options;
  1610. this.jm = jm;
  1611. this.isside = (this.opts.mode == 'side');
  1612. this.bounds = null;
  1613. this.cache_valid = false;
  1614. };
  1615. jm.layout_provider.prototype = {
  1616. init: function() {
  1617. logger.debug('layout.init');
  1618. },
  1619. reset: function() {
  1620. logger.debug('layout.reset');
  1621. this.bounds = { n: 0, s: 0, w: 0, e: 0 };
  1622. },
  1623. layout: function() {
  1624. logger.debug('layout.layout');
  1625. this.layout_direction();
  1626. this.layout_offset();
  1627. },
  1628. layout_direction: function() {
  1629. this._layout_direction_root();
  1630. },
  1631. _layout_direction_root: function() {
  1632. var node = this.jm.mind.root;
  1633. // logger.debug(node);
  1634. var layout_data = null;
  1635. if ('layout' in node._data) {
  1636. layout_data = node._data.layout;
  1637. } else {
  1638. layout_data = {};
  1639. node._data.layout = layout_data;
  1640. }
  1641. var children = node.children;
  1642. var children_count = children.length;
  1643. layout_data.direction = jm.direction.center;
  1644. layout_data.side_index = 0;
  1645. if (this.isside) {
  1646. var i = children_count;
  1647. while (i--) {
  1648. this._layout_direction_side(children[i], jm.direction.right, i);
  1649. }
  1650. } else {
  1651. var i = children_count;
  1652. var subnode = null;
  1653. while (i--) {
  1654. subnode = children[i];
  1655. if (subnode.direction == jm.direction.left) {
  1656. this._layout_direction_side(subnode, jm.direction.left, i);
  1657. } else {
  1658. this._layout_direction_side(subnode, jm.direction.right, i);
  1659. }
  1660. }
  1661. /*
  1662. var boundary = Math.ceil(children_count/2);
  1663. var i = children_count;
  1664. while(i--){
  1665. if(i>=boundary){
  1666. this._layout_direction_side(children[i],jm.direction.left, children_count-i-1);
  1667. }else{
  1668. this._layout_direction_side(children[i],jm.direction.right, i);
  1669. }
  1670. }*/
  1671. }
  1672. },
  1673. _layout_direction_side: function(node, direction, side_index) {
  1674. var layout_data = null;
  1675. if ('layout' in node._data) {
  1676. layout_data = node._data.layout;
  1677. } else {
  1678. layout_data = {};
  1679. node._data.layout = layout_data;
  1680. }
  1681. var children = node.children;
  1682. var children_count = children.length;
  1683. layout_data.direction = direction;
  1684. layout_data.side_index = side_index;
  1685. var i = children_count;
  1686. while (i--) {
  1687. this._layout_direction_side(children[i], direction, i);
  1688. }
  1689. },
  1690. layout_offset: function() {
  1691. var node = this.jm.mind.root;
  1692. var layout_data = node._data.layout;
  1693. layout_data.offset_x = 0;
  1694. layout_data.offset_y = 0;
  1695. layout_data.outer_height = 0;
  1696. var children = node.children;
  1697. var i = children.length;
  1698. var left_nodes = [];
  1699. var right_nodes = [];
  1700. var subnode = null;
  1701. while (i--) {
  1702. subnode = children[i];
  1703. if (subnode._data.layout.direction == jm.direction.right) {
  1704. right_nodes.unshift(subnode);
  1705. } else {
  1706. left_nodes.unshift(subnode);
  1707. }
  1708. }
  1709. layout_data.left_nodes = left_nodes;
  1710. layout_data.right_nodes = right_nodes;
  1711. layout_data.outer_height_left = this._layout_offset_subnodes(left_nodes);
  1712. layout_data.outer_height_right = this._layout_offset_subnodes(right_nodes);
  1713. this.bounds.e = node._data.view.width / 2;
  1714. this.bounds.w = 0 - this.bounds.e;
  1715. //logger.debug(this.bounds.w);
  1716. this.bounds.n = 0;
  1717. this.bounds.s = Math.max(layout_data.outer_height_left, layout_data.outer_height_right);
  1718. },
  1719. // layout both the x and y axis
  1720. _layout_offset_subnodes: function(nodes) {
  1721. var total_height = 0;
  1722. var nodes_count = nodes.length;
  1723. var i = nodes_count;
  1724. var node = null;
  1725. var node_outer_height = 0;
  1726. var layout_data = null;
  1727. var base_y = 0;
  1728. var pd = null; // parent._data
  1729. while (i--) {
  1730. node = nodes[i];
  1731. layout_data = node._data.layout;
  1732. if (pd == null) {
  1733. pd = node.parent._data;
  1734. }
  1735. node_outer_height = this._layout_offset_subnodes(node.children);
  1736. if (!node.expanded) {
  1737. node_outer_height = 0;
  1738. this.set_visible(node.children, false);
  1739. }
  1740. node_outer_height = Math.max(node._data.view.height, node_outer_height);
  1741. layout_data.outer_height = node_outer_height;
  1742. layout_data.offset_y = base_y - node_outer_height / 2;
  1743. layout_data.offset_x = this.opts.hspace * layout_data.direction + pd.view.width * (pd.layout.direction + layout_data.direction) / 2;
  1744. if (!node.parent.isroot) {
  1745. layout_data.offset_x += this.opts.pspace * layout_data.direction;
  1746. }
  1747. base_y = base_y - node_outer_height - this.opts.vspace;
  1748. total_height += node_outer_height;
  1749. }
  1750. if (nodes_count > 1) {
  1751. total_height += this.opts.vspace * (nodes_count - 1);
  1752. }
  1753. i = nodes_count;
  1754. var middle_height = total_height / 2;
  1755. while (i--) {
  1756. node = nodes[i];
  1757. node._data.layout.offset_y += middle_height;
  1758. }
  1759. return total_height;
  1760. },
  1761. // layout the y axis only, for collapse/expand a node
  1762. _layout_offset_subnodes_height: function(nodes) {
  1763. var total_height = 0;
  1764. var nodes_count = nodes.length;
  1765. var i = nodes_count;
  1766. var node = null;
  1767. var node_outer_height = 0;
  1768. var layout_data = null;
  1769. var base_y = 0;
  1770. var pd = null; // parent._data
  1771. while (i--) {
  1772. node = nodes[i];
  1773. layout_data = node._data.layout;
  1774. if (pd == null) {
  1775. pd = node.parent._data;
  1776. }
  1777. node_outer_height = this._layout_offset_subnodes_height(node.children);
  1778. if (!node.expanded) {
  1779. node_outer_height = 0;
  1780. }
  1781. node_outer_height = Math.max(node._data.view.height, node_outer_height);
  1782. layout_data.outer_height = node_outer_height;
  1783. layout_data.offset_y = base_y - node_outer_height / 2;
  1784. base_y = base_y - node_outer_height - this.opts.vspace;
  1785. total_height += node_outer_height;
  1786. }
  1787. if (nodes_count > 1) {
  1788. total_height += this.opts.vspace * (nodes_count - 1);
  1789. }
  1790. i = nodes_count;
  1791. var middle_height = total_height / 2;
  1792. while (i--) {
  1793. node = nodes[i];
  1794. node._data.layout.offset_y += middle_height;
  1795. //logger.debug(node.topic);
  1796. //logger.debug(node._data.layout.offset_y);
  1797. }
  1798. return total_height;
  1799. },
  1800. get_node_offset: function(node) {
  1801. var layout_data = node._data.layout;
  1802. var offset_cache = null;
  1803. if (('_offset_' in layout_data) && this.cache_valid) {
  1804. offset_cache = layout_data._offset_;
  1805. } else {
  1806. offset_cache = { x: -1, y: -1 };
  1807. layout_data._offset_ = offset_cache;
  1808. }
  1809. if (offset_cache.x == -1 || offset_cache.y == -1) {
  1810. var x = layout_data.offset_x;
  1811. var y = layout_data.offset_y;
  1812. if (!node.isroot) {
  1813. var offset_p = this.get_node_offset(node.parent);
  1814. x += offset_p.x;
  1815. y += offset_p.y;
  1816. }
  1817. offset_cache.x = x;
  1818. offset_cache.y = y;
  1819. }
  1820. return offset_cache;
  1821. },
  1822. get_node_point: function(node) {
  1823. var view_data = node._data.view;
  1824. var offset_p = this.get_node_offset(node);
  1825. //logger.debug(offset_p);
  1826. var p = {};
  1827. p.x = offset_p.x + view_data.width * (node._data.layout.direction - 1) / 2;
  1828. p.y = offset_p.y - view_data.height / 2;
  1829. //logger.debug(p);
  1830. return p;
  1831. },
  1832. get_node_point_in: function(node) {
  1833. var p = this.get_node_offset(node);
  1834. return p;
  1835. },
  1836. get_node_point_out: function(node) {
  1837. var layout_data = node._data.layout;
  1838. var pout_cache = null;
  1839. if (('_pout_' in layout_data) && this.cache_valid) {
  1840. pout_cache = layout_data._pout_;
  1841. } else {
  1842. pout_cache = { x: -1, y: -1 };
  1843. layout_data._pout_ = pout_cache;
  1844. }
  1845. if (pout_cache.x == -1 || pout_cache.y == -1) {
  1846. if (node.isroot) {
  1847. pout_cache.x = 0;
  1848. pout_cache.y = 0;
  1849. } else {
  1850. var view_data = node._data.view;
  1851. var offset_p = this.get_node_offset(node);
  1852. pout_cache.x = offset_p.x + (view_data.width + this.opts.pspace) * node._data.layout.direction;
  1853. pout_cache.y = offset_p.y;
  1854. //logger.debug('pout');
  1855. //logger.debug(pout_cache);
  1856. }
  1857. }
  1858. return pout_cache;
  1859. },
  1860. get_expander_point: function(node) {
  1861. var p = this.get_node_point_out(node);
  1862. var ex_p = {};
  1863. if (node._data.layout.direction == jm.direction.right) {
  1864. ex_p.x = p.x - this.opts.pspace;
  1865. } else {
  1866. ex_p.x = p.x;
  1867. }
  1868. ex_p.y = p.y - Math.ceil(this.opts.pspace / 2);
  1869. return ex_p;
  1870. },
  1871. get_min_size: function() {
  1872. var nodes = this.jm.mind.nodes;
  1873. var node = null;
  1874. var pout = null;
  1875. for (var nodeid in nodes) {
  1876. node = nodes[nodeid];
  1877. pout = this.get_node_point_out(node);
  1878. //logger.debug(pout.x);
  1879. if (pout.x > this.bounds.e) { this.bounds.e = pout.x; }
  1880. if (pout.x < this.bounds.w) { this.bounds.w = pout.x; }
  1881. }
  1882. return {
  1883. w: this.bounds.e - this.bounds.w,
  1884. h: this.bounds.s - this.bounds.n
  1885. }
  1886. },
  1887. toggle_node: function(node) {
  1888. if (node.isroot) {
  1889. return;
  1890. }
  1891. if (node.expanded) {
  1892. this.collapse_node(node);
  1893. } else {
  1894. this.expand_node(node);
  1895. }
  1896. },
  1897. expand_node: function(node) {
  1898. node.expanded = true;
  1899. this.part_layout(node);
  1900. this.set_visible(node.children, true);
  1901. },
  1902. collapse_node: function(node) {
  1903. node.expanded = false;
  1904. this.part_layout(node);
  1905. this.set_visible(node.children, false);
  1906. },
  1907. expand_all: function() {
  1908. var nodes = this.jm.mind.nodes;
  1909. var c = 0;
  1910. var node;
  1911. for (var nodeid in nodes) {
  1912. node = nodes[nodeid];
  1913. if (!node.expanded) {
  1914. node.expanded = true;
  1915. c++;
  1916. }
  1917. }
  1918. if (c > 0) {
  1919. var root = this.jm.mind.root;
  1920. this.part_layout(root);
  1921. this.set_visible(root.children, true);
  1922. }
  1923. },
  1924. collapse_all: function() {
  1925. var nodes = this.jm.mind.nodes;
  1926. var c = 0;
  1927. var node;
  1928. for (var nodeid in nodes) {
  1929. node = nodes[nodeid];
  1930. if (node.expanded && !node.isroot) {
  1931. node.expanded = false
  1932. c++;
  1933. }
  1934. }
  1935. if (c > 0) {
  1936. var root = this.jm.mind.root;
  1937. this.part_layout(root);
  1938. this.set_visible(root.children, true);
  1939. }
  1940. },
  1941. expand_to_depth: function(target_depth, curr_nodes, curr_depth) {
  1942. if (target_depth < 1) { return; }
  1943. var nodes = curr_nodes || this.jm.mind.root.children;
  1944. var depth = curr_depth || 1;
  1945. var i = nodes.length;
  1946. var node = null;
  1947. while (i--) {
  1948. node = nodes[i];
  1949. if (depth < target_depth) {
  1950. if (!node.expanded) {
  1951. this.expand_node(node);
  1952. }
  1953. this.expand_to_depth(target_depth, node.children, depth + 1);
  1954. }
  1955. if (depth == target_depth) {
  1956. if (node.expanded) {
  1957. this.collapse_node(node);
  1958. }
  1959. }
  1960. }
  1961. },
  1962. part_layout: function(node) {
  1963. var root = this.jm.mind.root;
  1964. if (!!root) {
  1965. var root_layout_data = root._data.layout;
  1966. if (node.isroot) {
  1967. root_layout_data.outer_height_right = this._layout_offset_subnodes_height(root_layout_data.right_nodes);
  1968. root_layout_data.outer_height_left = this._layout_offset_subnodes_height(root_layout_data.left_nodes);
  1969. } else {
  1970. if (node._data.layout.direction == jm.direction.right) {
  1971. root_layout_data.outer_height_right = this._layout_offset_subnodes_height(root_layout_data.right_nodes);
  1972. } else {
  1973. root_layout_data.outer_height_left = this._layout_offset_subnodes_height(root_layout_data.left_nodes);
  1974. }
  1975. }
  1976. this.bounds.s = Math.max(root_layout_data.outer_height_left, root_layout_data.outer_height_right);
  1977. this.cache_valid = false;
  1978. } else {
  1979. logger.warn('can not found root node');
  1980. }
  1981. },
  1982. set_visible: function(nodes, visible) {
  1983. var i = nodes.length;
  1984. var node = null;
  1985. var layout_data = null;
  1986. while (i--) {
  1987. node = nodes[i];
  1988. layout_data = node._data.layout;
  1989. if (node.expanded) {
  1990. this.set_visible(node.children, visible);
  1991. } else {
  1992. this.set_visible(node.children, false);
  1993. }
  1994. if (!node.isroot) {
  1995. node._data.layout.visible = visible;
  1996. }
  1997. }
  1998. },
  1999. is_expand: function(node) {
  2000. return node.expanded;
  2001. },
  2002. is_visible: function(node) {
  2003. var layout_data = node._data.layout;
  2004. if (('visible' in layout_data) && !layout_data.visible) {
  2005. return false;
  2006. } else {
  2007. return true;
  2008. }
  2009. },
  2010. };
  2011. // view provider
  2012. jm.view_provider = function(jm, options) {
  2013. this.opts = options;
  2014. this.jm = jm;
  2015. this.layout = jm.layout;
  2016. this.container = null;
  2017. this.e_panel = null;
  2018. this.e_nodes = null;
  2019. this.e_canvas = null;
  2020. this.canvas_ctx = null;
  2021. this.size = { w: 0, h: 0 };
  2022. this.selected_node = null;
  2023. this.editing_node = null;
  2024. };
  2025. jm.view_provider.prototype = {
  2026. init: function() {
  2027. logger.debug('view.init');
  2028. this.container = $i(this.opts.container) ? this.opts.container : $g(this.opts.container);
  2029. if (!this.container) {
  2030. logger.error('the options.view.container was not be found in dom');
  2031. return;
  2032. }
  2033. this.e_panel = $c('div');
  2034. this.e_canvas = $c('canvas');
  2035. this.e_nodes = $c('jmnodes');
  2036. this.e_editor = $c('input');
  2037. this.e_panel.className = 'jsmind-inner';
  2038. this.e_panel.appendChild(this.e_canvas);
  2039. this.e_panel.appendChild(this.e_nodes);
  2040. this.e_editor.className = 'jsmind-editor';
  2041. this.e_editor.type = 'text';
  2042. this.actualZoom = 1;
  2043. this.zoomStep = 0.1;
  2044. this.minZoom = 0.5;
  2045. this.maxZoom = 2;
  2046. var v = this;
  2047. jm.util.dom.add_event(this.e_editor, 'keydown', function(e) {
  2048. var evt = e || event;
  2049. if (evt.keyCode == 13) { v.edit_node_end();
  2050. evt.stopPropagation(); }
  2051. });
  2052. jm.util.dom.add_event(this.e_editor, 'blur', function(e) {
  2053. v.edit_node_end();
  2054. });
  2055. this.container.appendChild(this.e_panel);
  2056. this.init_canvas();
  2057. },
  2058. add_event: function(obj, event_name, event_handle) {
  2059. jm.util.dom.add_event(this.e_nodes, event_name, function(e) {
  2060. var evt = e || event;
  2061. event_handle.call(obj, evt);
  2062. });
  2063. },
  2064. get_binded_nodeid: function(element) {
  2065. if (element == null) {
  2066. return null;
  2067. }
  2068. var tagName = element.tagName.toLowerCase();
  2069. if (tagName == 'jmnodes' || tagName == 'body' || tagName == 'html') {
  2070. return null;
  2071. }
  2072. if (tagName == 'jmnode' || tagName == 'jmexpander') {
  2073. return element.getAttribute('nodeid');
  2074. } else {
  2075. return this.get_binded_nodeid(element.parentElement);
  2076. }
  2077. },
  2078. is_expander: function(element) {
  2079. return (element.tagName.toLowerCase() == 'jmexpander');
  2080. },
  2081. reset: function() {
  2082. logger.debug('view.reset');
  2083. this.selected_node = null;
  2084. this.clear_lines();
  2085. this.clear_nodes();
  2086. this.reset_theme();
  2087. },
  2088. reset_theme: function() {
  2089. var theme_name = this.jm.options.theme;
  2090. if (!!theme_name) {
  2091. this.e_nodes.className = 'theme-' + theme_name;
  2092. } else {
  2093. this.e_nodes.className = '';
  2094. }
  2095. },
  2096. reset_custom_style: function() {
  2097. var nodes = this.jm.mind.nodes;
  2098. for (var nodeid in nodes) {
  2099. this.reset_node_custom_style(nodes[nodeid]);
  2100. }
  2101. },
  2102. load: function() {
  2103. logger.debug('view.load');
  2104. this.init_nodes();
  2105. },
  2106. expand_size: function() {
  2107. var min_size = this.layout.get_min_size();
  2108. var min_width = min_size.w + this.opts.hmargin * 2;
  2109. var min_height = min_size.h + this.opts.vmargin * 2;
  2110. var client_w = this.e_panel.clientWidth;
  2111. var client_h = this.e_panel.clientHeight;
  2112. if (client_w < min_width) { client_w = min_width; }
  2113. if (client_h < min_height) { client_h = min_height; }
  2114. this.size.w = client_w;
  2115. this.size.h = client_h;
  2116. },
  2117. init_canvas: function() {
  2118. var ctx = this.e_canvas.getContext('2d');
  2119. this.canvas_ctx = ctx;
  2120. },
  2121. init_nodes_size: function(node) {
  2122. var view_data = node._data.view;
  2123. view_data.width = view_data.element.clientWidth;
  2124. view_data.height = view_data.element.clientHeight;
  2125. },
  2126. init_nodes: function() {
  2127. var nodes = this.jm.mind.nodes;
  2128. var doc_frag = $d.createDocumentFragment();
  2129. for (var nodeid in nodes) {
  2130. this.create_node_element(nodes[nodeid], doc_frag);
  2131. }
  2132. this.e_nodes.appendChild(doc_frag);
  2133. for (var nodeid in nodes) {
  2134. this.init_nodes_size(nodes[nodeid]);
  2135. }
  2136. },
  2137. add_node: function(node) {
  2138. this.create_node_element(node, this.e_nodes);
  2139. this.init_nodes_size(node);
  2140. },
  2141. create_node_element: function(node, parent_node) {
  2142. var view_data = null;
  2143. if ('view' in node._data) {
  2144. view_data = node._data.view;
  2145. } else {
  2146. view_data = {};
  2147. node._data.view = view_data;
  2148. }
  2149. var d = $c('jmnode');
  2150. if (node.isroot) {
  2151. d.className = 'root';
  2152. } else {
  2153. var d_e = $c('jmexpander');
  2154. $t(d_e, '-');
  2155. d_e.setAttribute('nodeid', node.id);
  2156. d_e.style.visibility = 'hidden';
  2157. parent_node.appendChild(d_e);
  2158. view_data.expander = d_e;
  2159. }
  2160. if (!!node.topic) {
  2161. if (this.opts.support_html) {
  2162. $h(d, node.topic);
  2163. } else {
  2164. $t(d, node.topic);
  2165. }
  2166. }
  2167. d.setAttribute('nodeid', node.id);
  2168. d.style.visibility = 'hidden';
  2169. this._reset_node_custom_style(d, node.data);
  2170. parent_node.appendChild(d);
  2171. view_data.element = d;
  2172. },
  2173. remove_node: function(node) {
  2174. if (this.selected_node != null && this.selected_node.id == node.id) {
  2175. this.selected_node = null;
  2176. }
  2177. if (this.editing_node != null && this.editing_node.id == node.id) {
  2178. node._data.view.element.removeChild(this.e_editor);
  2179. this.editing_node = null;
  2180. }
  2181. var children = node.children;
  2182. var i = children.length;
  2183. while (i--) {
  2184. this.remove_node(children[i]);
  2185. }
  2186. if (node._data.view) {
  2187. var element = node._data.view.element;
  2188. var expander = node._data.view.expander;
  2189. this.e_nodes.removeChild(element);
  2190. this.e_nodes.removeChild(expander);
  2191. node._data.view.element = null;
  2192. node._data.view.expander = null;
  2193. }
  2194. },
  2195. update_node: function(node) {
  2196. var view_data = node._data.view;
  2197. var element = view_data.element;
  2198. if (!!node.topic) {
  2199. if (this.opts.support_html) {
  2200. $h(element, node.topic);
  2201. } else {
  2202. $t(element, node.topic);
  2203. }
  2204. }
  2205. view_data.width = element.clientWidth;
  2206. view_data.height = element.clientHeight;
  2207. },
  2208. select_node: function(node) {
  2209. if (!!this.selected_node) {
  2210. this.selected_node._data.view.element.className =
  2211. this.selected_node._data.view.element.className.replace(/\s*selected\b/i, '');
  2212. this.reset_node_custom_style(this.selected_node);
  2213. }
  2214. if (!!node) {
  2215. this.selected_node = node;
  2216. node._data.view.element.className += ' selected';
  2217. this.clear_node_custom_style(node);
  2218. }
  2219. },
  2220. select_clear: function() {
  2221. this.select_node(null);
  2222. },
  2223. get_editing_node: function() {
  2224. return this.editing_node;
  2225. },
  2226. is_editing: function() {
  2227. return (!!this.editing_node);
  2228. },
  2229. edit_node_begin: function(node) {
  2230. if (!node.topic) {
  2231. logger.warn("don't edit image nodes");
  2232. return;
  2233. }
  2234. if (this.editing_node != null) {
  2235. this.edit_node_end();
  2236. }
  2237. this.editing_node = node;
  2238. var view_data = node._data.view;
  2239. var element = view_data.element;
  2240. var topic = node.topic;
  2241. var ncs = getComputedStyle(element);
  2242. this.e_editor.value = topic;
  2243. this.e_editor.style.width = (element.clientWidth - parseInt(ncs.getPropertyValue('padding-left')) - parseInt(ncs.getPropertyValue('padding-right'))) + 'px';
  2244. element.innerHTML = '';
  2245. element.appendChild(this.e_editor);
  2246. element.style.zIndex = 5;
  2247. this.e_editor.focus();
  2248. this.e_editor.select();
  2249. },
  2250. edit_node_end: function() {
  2251. if (this.editing_node != null) {
  2252. var node = this.editing_node;
  2253. this.editing_node = null;
  2254. var view_data = node._data.view;
  2255. var element = view_data.element;
  2256. var topic = this.e_editor.value;
  2257. element.style.zIndex = 'auto';
  2258. element.removeChild(this.e_editor);
  2259. if (jm.util.text.is_empty(topic) || node.topic === topic) {
  2260. if (this.opts.support_html) {
  2261. $h(element, node.topic);
  2262. } else {
  2263. $t(element, node.topic);
  2264. }
  2265. } else {
  2266. this.jm.update_node(node.id, topic);
  2267. }
  2268. }
  2269. },
  2270. get_view_offset: function() {
  2271. var bounds = this.layout.bounds;
  2272. var _x = (this.size.w - bounds.e - bounds.w) / 2;
  2273. var _y = this.size.h / 2;
  2274. return { x: _x, y: _y };
  2275. },
  2276. resize: function() {
  2277. this.e_canvas.width = 1;
  2278. this.e_canvas.height = 1;
  2279. this.e_nodes.style.width = '1px';
  2280. this.e_nodes.style.height = '1px';
  2281. this.expand_size();
  2282. this._show();
  2283. },
  2284. _show: function() {
  2285. this.e_canvas.width = this.size.w;
  2286. this.e_canvas.height = this.size.h;
  2287. this.e_nodes.style.width = this.size.w + 'px';
  2288. this.e_nodes.style.height = this.size.h + 'px';
  2289. this.show_nodes();
  2290. this.show_lines();
  2291. //this.layout.cache_valid = true;
  2292. this.jm.invoke_event_handle(jm.event_type.resize, { data: [] });
  2293. },
  2294. zoomIn: function() {
  2295. return this.setZoom(this.actualZoom + this.zoomStep);
  2296. },
  2297. zoomOut: function() {
  2298. return this.setZoom(this.actualZoom - this.zoomStep);
  2299. },
  2300. setZoom: function(zoom) {
  2301. if ((zoom < this.minZoom) || (zoom > this.maxZoom)) {
  2302. return false;
  2303. }
  2304. this.actualZoom = zoom;
  2305. for (var i = 0; i < this.e_panel.children.length; i++) {
  2306. this.e_panel.children[i].style.transform = 'scale(' + zoom + ')';
  2307. };
  2308. this.show(true);
  2309. return true;
  2310. },
  2311. _center_root: function() {
  2312. // center root node
  2313. var outer_w = this.e_panel.clientWidth;
  2314. var outer_h = this.e_panel.clientHeight;
  2315. if (this.size.w > outer_w) {
  2316. var _offset = this.get_view_offset();
  2317. this.e_panel.scrollLeft = _offset.x - outer_w / 2;
  2318. }
  2319. if (this.size.h > outer_h) {
  2320. this.e_panel.scrollTop = (this.size.h - outer_h) / 2;
  2321. }
  2322. },
  2323. show: function(keep_center) {
  2324. logger.debug('view.show');
  2325. this.expand_size();
  2326. this._show();
  2327. if (!!keep_center) {
  2328. this._center_root();
  2329. }
  2330. },
  2331. relayout: function() {
  2332. this.expand_size();
  2333. this._show();
  2334. },
  2335. save_location: function(node) {
  2336. var vd = node._data.view;
  2337. vd._saved_location = {
  2338. x: parseInt(vd.element.style.left) - this.e_panel.scrollLeft,
  2339. y: parseInt(vd.element.style.top) - this.e_panel.scrollTop,
  2340. };
  2341. },
  2342. restore_location: function(node) {
  2343. var vd = node._data.view;
  2344. this.e_panel.scrollLeft = parseInt(vd.element.style.left) - vd._saved_location.x;
  2345. this.e_panel.scrollTop = parseInt(vd.element.style.top) - vd._saved_location.y;
  2346. },
  2347. clear_nodes: function() {
  2348. var mind = this.jm.mind;
  2349. if (mind == null) {
  2350. return;
  2351. }
  2352. var nodes = mind.nodes;
  2353. var node = null;
  2354. for (var nodeid in nodes) {
  2355. node = nodes[nodeid];
  2356. node._data.view.element = null;
  2357. node._data.view.expander = null;
  2358. }
  2359. this.e_nodes.innerHTML = '';
  2360. },
  2361. show_nodes: function() {
  2362. var nodes = this.jm.mind.nodes;
  2363. var node = null;
  2364. var node_element = null;
  2365. var expander = null;
  2366. var p = null;
  2367. var p_expander = null;
  2368. var expander_text = '-';
  2369. var view_data = null;
  2370. var _offset = this.get_view_offset();
  2371. for (var nodeid in nodes) {
  2372. node = nodes[nodeid];
  2373. view_data = node._data.view;
  2374. node_element = view_data.element;
  2375. expander = view_data.expander;
  2376. if (!this.layout.is_visible(node)) {
  2377. node_element.style.display = 'none';
  2378. expander.style.display = 'none';
  2379. continue;
  2380. }
  2381. this.reset_node_custom_style(node);
  2382. p = this.layout.get_node_point(node);
  2383. view_data.abs_x = _offset.x + p.x;
  2384. view_data.abs_y = _offset.y + p.y;
  2385. node_element.style.left = (_offset.x + p.x) + 'px';
  2386. node_element.style.top = (_offset.y + p.y) + 'px';
  2387. node_element.style.display = '';
  2388. node_element.style.visibility = 'visible';
  2389. if (!node.isroot && node.children.length > 0) {
  2390. expander_text = node.expanded ? '-' : '+';
  2391. p_expander = this.layout.get_expander_point(node);
  2392. expander.style.left = (_offset.x + p_expander.x) + 'px';
  2393. expander.style.top = (_offset.y + p_expander.y) + 'px';
  2394. expander.style.display = '';
  2395. expander.style.visibility = 'visible';
  2396. $t(expander, expander_text);
  2397. }
  2398. // hide expander while all children have been removed
  2399. if (!node.isroot && node.children.length == 0) {
  2400. expander.style.display = 'none';
  2401. expander.style.visibility = 'hidden';
  2402. }
  2403. }
  2404. },
  2405. reset_node_custom_style: function(node) {
  2406. this._reset_node_custom_style(node._data.view.element, node.data);
  2407. },
  2408. _reset_node_custom_style: function(node_element, node_data) {
  2409. if ('background-color' in node_data) {
  2410. node_element.style.backgroundColor = node_data['background-color'];
  2411. }
  2412. if ('foreground-color' in node_data) {
  2413. node_element.style.color = node_data['foreground-color'];
  2414. }
  2415. if ('width' in node_data) {
  2416. node_element.style.width = node_data['width'] + 'px';
  2417. }
  2418. if ('height' in node_data) {
  2419. node_element.style.height = node_data['height'] + 'px';
  2420. }
  2421. if ('font-size' in node_data) {
  2422. node_element.style.fontSize = node_data['font-size'] + 'px';
  2423. }
  2424. if ('font-weight' in node_data) {
  2425. node_element.style.fontWeight = node_data['font-weight'];
  2426. }
  2427. if ('font-style' in node_data) {
  2428. node_element.style.fontStyle = node_data['font-style'];
  2429. }
  2430. if ('background-image' in node_data) {
  2431. var backgroundImage = node_data['background-image'];
  2432. if (backgroundImage.startsWith('data') && node_data['width'] && node_data['height']) {
  2433. var img = new Image();
  2434. img.onload = function() {
  2435. var c = $c('canvas');
  2436. c.width = node_element.clientWidth;
  2437. c.height = node_element.clientHeight;
  2438. var img = this;
  2439. if (c.getContext) {
  2440. var ctx = c.getContext('2d');
  2441. ctx.drawImage(img, 2, 2, node_element.clientWidth, node_element.clientHeight);
  2442. var scaledImageData = c.toDataURL();
  2443. node_element.style.backgroundImage = 'url(' + scaledImageData + ')';
  2444. }
  2445. };
  2446. img.src = backgroundImage;
  2447. } else {
  2448. node_element.style.backgroundImage = 'url(' + backgroundImage + ')';
  2449. }
  2450. node_element.style.backgroundSize = '99%';
  2451. if ('background-rotation' in node_data) {
  2452. node_element.style.transform = 'rotate(' + node_data['background-rotation'] + 'deg)';
  2453. }
  2454. }
  2455. },
  2456. clear_node_custom_style: function(node) {
  2457. var node_element = node._data.view.element;
  2458. node_element.style.backgroundColor = "";
  2459. node_element.style.color = "";
  2460. },
  2461. clear_lines: function(canvas_ctx) {
  2462. var ctx = canvas_ctx || this.canvas_ctx;
  2463. jm.util.canvas.clear(ctx, 0, 0, this.size.w, this.size.h);
  2464. },
  2465. show_lines: function(canvas_ctx) {
  2466. this.clear_lines(canvas_ctx);
  2467. var nodes = this.jm.mind.nodes;
  2468. var node = null;
  2469. var pin = null;
  2470. var pout = null;
  2471. var _offset = this.get_view_offset();
  2472. for (var nodeid in nodes) {
  2473. node = nodes[nodeid];
  2474. if (!!node.isroot) { continue; }
  2475. if (('visible' in node._data.layout) && !node._data.layout.visible) { continue; }
  2476. pin = this.layout.get_node_point_in(node);
  2477. pout = this.layout.get_node_point_out(node.parent);
  2478. this.draw_line(pout, pin, _offset, canvas_ctx);
  2479. }
  2480. },
  2481. draw_line: function(pin, pout, offset, canvas_ctx) {
  2482. var ctx = canvas_ctx || this.canvas_ctx;
  2483. ctx.strokeStyle = this.opts.line_color;
  2484. ctx.lineWidth = this.opts.line_width;
  2485. ctx.lineCap = 'round';
  2486. jm.util.canvas.bezierto(
  2487. ctx,
  2488. pin.x + offset.x,
  2489. pin.y + offset.y,
  2490. pout.x + offset.x,
  2491. pout.y + offset.y);
  2492. },
  2493. };
  2494. // shortcut provider
  2495. jm.shortcut_provider = function(jm, options) {
  2496. this.jm = jm;
  2497. this.opts = options;
  2498. this.mapping = options.mapping;
  2499. this.handles = options.handles;
  2500. this._mapping = {};
  2501. };
  2502. jm.shortcut_provider.prototype = {
  2503. init: function() {
  2504. jm.util.dom.add_event($d, 'keydown', this.handler.bind(this));
  2505. this.handles['addchild'] = this.handle_addchild;
  2506. this.handles['addbrother'] = this.handle_addbrother;
  2507. this.handles['editnode'] = this.handle_editnode;
  2508. this.handles['delnode'] = this.handle_delnode;
  2509. this.handles['toggle'] = this.handle_toggle;
  2510. this.handles['up'] = this.handle_up;
  2511. this.handles['down'] = this.handle_down;
  2512. this.handles['left'] = this.handle_left;
  2513. this.handles['right'] = this.handle_right;
  2514. for (var handle in this.mapping) {
  2515. if (!!this.mapping[handle] && (handle in this.handles)) {
  2516. this._mapping[this.mapping[handle]] = this.handles[handle];
  2517. }
  2518. }
  2519. },
  2520. enable_shortcut: function() {
  2521. this.opts.enable = true;
  2522. },
  2523. disable_shortcut: function() {
  2524. this.opts.enable = false;
  2525. },
  2526. handler: function(e) {
  2527. if (this.jm.view.is_editing()) { return; }
  2528. var evt = e || event;
  2529. if (!this.opts.enable) { return true; }
  2530. var kc = evt.keyCode;
  2531. if (kc in this._mapping) {
  2532. this._mapping[kc].call(this, this.jm, e);
  2533. }
  2534. },
  2535. handle_addchild: function(_jm, e) {
  2536. var selected_node = _jm.get_selected_node();
  2537. if (!!selected_node) {
  2538. var nodeid = jm.util.uuid.newid();
  2539. var node = _jm.add_node(selected_node, nodeid, 'New Node');
  2540. if (!!node) {
  2541. _jm.select_node(nodeid);
  2542. _jm.begin_edit(nodeid);
  2543. }
  2544. }
  2545. },
  2546. handle_addbrother: function(_jm, e) {
  2547. var selected_node = _jm.get_selected_node();
  2548. if (!!selected_node && !selected_node.isroot) {
  2549. var nodeid = jm.util.uuid.newid();
  2550. var node = _jm.insert_node_after(selected_node, nodeid, 'New Node');
  2551. if (!!node) {
  2552. _jm.select_node(nodeid);
  2553. _jm.begin_edit(nodeid);
  2554. }
  2555. }
  2556. },
  2557. handle_editnode: function(_jm, e) {
  2558. var selected_node = _jm.get_selected_node();
  2559. if (!!selected_node) {
  2560. _jm.begin_edit(selected_node);
  2561. }
  2562. },
  2563. handle_delnode: function(_jm, e) {
  2564. var selected_node = _jm.get_selected_node();
  2565. if (!!selected_node && !selected_node.isroot) {
  2566. _jm.select_node(selected_node.parent);
  2567. _jm.remove_node(selected_node);
  2568. }
  2569. },
  2570. handle_toggle: function(_jm, e) {
  2571. var evt = e || event;
  2572. var selected_node = _jm.get_selected_node();
  2573. if (!!selected_node) {
  2574. _jm.toggle_node(selected_node.id);
  2575. evt.stopPropagation();
  2576. evt.preventDefault();
  2577. }
  2578. },
  2579. handle_up: function(_jm, e) {
  2580. var evt = e || event;
  2581. var selected_node = _jm.get_selected_node();
  2582. if (!!selected_node) {
  2583. var up_node = _jm.find_node_before(selected_node);
  2584. if (!up_node) {
  2585. var np = _jm.find_node_before(selected_node.parent);
  2586. if (!!np && np.children.length > 0) {
  2587. up_node = np.children[np.children.length - 1];
  2588. }
  2589. }
  2590. if (!!up_node) {
  2591. _jm.select_node(up_node);
  2592. }
  2593. evt.stopPropagation();
  2594. evt.preventDefault();
  2595. }
  2596. },
  2597. handle_down: function(_jm, e) {
  2598. var evt = e || event;
  2599. var selected_node = _jm.get_selected_node();
  2600. if (!!selected_node) {
  2601. var down_node = _jm.find_node_after(selected_node);
  2602. if (!down_node) {
  2603. var np = _jm.find_node_after(selected_node.parent);
  2604. if (!!np && np.children.length > 0) {
  2605. down_node = np.children[0];
  2606. }
  2607. }
  2608. if (!!down_node) {
  2609. _jm.select_node(down_node);
  2610. }
  2611. evt.stopPropagation();
  2612. evt.preventDefault();
  2613. }
  2614. },
  2615. handle_left: function(_jm, e) {
  2616. this._handle_direction(_jm, e, jm.direction.left);
  2617. },
  2618. handle_right: function(_jm, e) {
  2619. this._handle_direction(_jm, e, jm.direction.right);
  2620. },
  2621. _handle_direction: function(_jm, e, d) {
  2622. var evt = e || event;
  2623. var selected_node = _jm.get_selected_node();
  2624. var node = null;
  2625. if (!!selected_node) {
  2626. if (selected_node.isroot) {
  2627. var c = selected_node.children;
  2628. var children = [];
  2629. for (var i = 0; i < c.length; i++) {
  2630. if (c[i].direction === d) {
  2631. children.push(i)
  2632. }
  2633. }
  2634. node = c[children[Math.floor((children.length - 1) / 2)]];
  2635. } else if (selected_node.direction === d) {
  2636. var children = selected_node.children;
  2637. var childrencount = children.length;
  2638. if (childrencount > 0) {
  2639. node = children[Math.floor((childrencount - 1) / 2)]
  2640. }
  2641. } else {
  2642. node = selected_node.parent;
  2643. }
  2644. if (!!node) {
  2645. _jm.select_node(node);
  2646. }
  2647. evt.stopPropagation();
  2648. evt.preventDefault();
  2649. }
  2650. },
  2651. };
  2652. // plugin
  2653. jm.plugin = function(name, init) {
  2654. this.name = name;
  2655. this.init = init;
  2656. };
  2657. jm.plugins = [];
  2658. jm.register_plugin = function(plugin) {
  2659. if (plugin instanceof jm.plugin) {
  2660. jm.plugins.push(plugin);
  2661. }
  2662. };
  2663. jm.init_plugins = function(sender) {
  2664. $w.setTimeout(function() {
  2665. jm._init_plugins(sender);
  2666. }, 0);
  2667. };
  2668. jm._init_plugins = function(sender) {
  2669. var l = jm.plugins.length;
  2670. var fn_init = null;
  2671. for (var i = 0; i < l; i++) {
  2672. fn_init = jm.plugins[i].init;
  2673. if (typeof fn_init === 'function') {
  2674. fn_init(sender);
  2675. }
  2676. }
  2677. };
  2678. // quick way
  2679. jm.show = function(options, mind) {
  2680. var _jm = new jm(options);
  2681. _jm.show(mind);
  2682. return _jm;
  2683. };
  2684. // export jsmind
  2685. if (typeof module !== 'undefined' && typeof exports === 'object') {
  2686. module.exports = jm;
  2687. } else if (typeof define === 'function' && (define.amd || define.cmd)) {
  2688. define(function() { return jm; });
  2689. } else {
  2690. $w[__name__] = jm;
  2691. }
  2692. })(typeof window !== 'undefined' ? window : global);