| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027 | 
							- /*
 
-  * Released under BSD License
 
-  * Copyright (c) 2014-2021 hizzgdev@163.com
 
-  *
 
-  * Project Home:
 
-  *   https://github.com/hizzgdev/jsmind/
 
-  */
 
- ; (function ($w) {
 
-     'use strict';
 
-     // set 'jsMind' as the library name.
 
-     // __name__ should be a const value, Never try to change it easily.
 
-     var __name__ = 'jsMind';
 
-     // library version
 
-     var __version__ = '0.4.6';
 
-     // author
 
-     var __author__ = 'hizzgdev@163.com';
 
-     // an noop function define
 
-     var _noop = function () { };
 
-     var logger = (typeof console === 'undefined') ? {
 
-         log: _noop, debug: _noop, error: _noop, warn: _noop, info: _noop
 
-     } : console;
 
-     // check global variables
 
-     if (typeof module === 'undefined' || !module.exports) {
 
-         if (typeof $w[__name__] != 'undefined') {
 
-             logger.log(__name__ + ' has been already exist.');
 
-             return;
 
-         }
 
-     }
 
-     // shortcut of methods in dom
 
-     var $d = $w.document;
 
-     var $g = function (id) { return $d.getElementById(id); };
 
-     var $c = function (tag) { return $d.createElement(tag); };
 
-     var $t = function (n, t) { if (n.hasChildNodes()) { n.firstChild.nodeValue = t; } else { n.appendChild($d.createTextNode(t)); } };
 
-     var $h = function (n, t) {
 
-         if (t instanceof HTMLElement) {
 
-             n.innerHTML = '';
 
-             n.appendChild(t);
 
-         } else {
 
-             n.innerHTML = t;
 
-         }
 
-     };
 
-     // detect isElement
 
-     var $i = function (el) { return !!el && (typeof el === 'object') && (el.nodeType === 1) && (typeof el.style === 'object') && (typeof el.ownerDocument === 'object'); };
 
-     if (typeof String.prototype.startsWith != 'function') { String.prototype.startsWith = function (p) { return this.slice(0, p.length) === p; }; }
 
-     var DEFAULT_OPTIONS = {
 
-         container: '',   // id of the container
 
-         editable: false, // you can change it in your options
 
-         theme: null,
 
-         mode: 'full',    // full or side
 
-         support_html: true,
 
-         view: {
 
-             engine: 'canvas',
 
-             hmargin: 100,
 
-             vmargin: 50,
 
-             line_width: 2,
 
-             line_color: '#555'
 
-         },
 
-         layout: {
 
-             hspace: 30,
 
-             vspace: 20,
 
-             pspace: 13
 
-         },
 
-         default_event_handle: {
 
-             enable_mousedown_handle: true,
 
-             enable_click_handle: true,
 
-             enable_dblclick_handle: true
 
-         },
 
-         shortcut: {
 
-             enable: true,
 
-             handles: {
 
-             },
 
-             mapping: {
 
-                 addchild: 45, // Insert
 
-                 addbrother: 13, // Enter
 
-                 editnode: 113,// F2
 
-                 delnode: 46, // Delete
 
-                 toggle: 32, // Space
 
-                 left: 37, // Left
 
-                 up: 38, // Up
 
-                 right: 39, // Right
 
-                 down: 40, // Down
 
-             }
 
-         },
 
-     };
 
-     // core object
 
-     var jm = function (options) {
 
-         jm.current = this;
 
-         this.version = __version__;
 
-         var opts = {};
 
-         jm.util.json.merge(opts, DEFAULT_OPTIONS);
 
-         jm.util.json.merge(opts, options);
 
-         if (!opts.container) {
 
-             logger.error('the options.container should not be null or empty.');
 
-             return;
 
-         }
 
-         this.options = opts;
 
-         this.inited = false;
 
-         this.mind = null;
 
-         this.event_handles = [];
 
-         this.init();
 
-     };
 
-     // ============= static object =============================================
 
-     jm.direction = { left: -1, center: 0, right: 1 };
 
-     jm.event_type = { show: 1, resize: 2, edit: 3, select: 4 };
 
-     jm.key = { meta: 1 << 13, ctrl: 1 << 12, alt: 1 << 11, shift: 1 << 10 };
 
-     jm.node = function (sId, iIndex, sTopic, oData, bIsRoot, oParent, eDirection, bExpanded) {
 
-         if (!sId) { logger.error('invalid nodeid'); return; }
 
-         if (typeof iIndex != 'number') { logger.error('invalid node index'); return; }
 
-         if (typeof bExpanded === 'undefined') { bExpanded = true; }
 
-         this.id = sId;
 
-         this.index = iIndex;
 
-         this.topic = sTopic;
 
-         this.data = oData || {};
 
-         this.isroot = bIsRoot;
 
-         this.parent = oParent;
 
-         this.direction = eDirection;
 
-         this.expanded = !!bExpanded;
 
-         this.children = [];
 
-         this._data = {};
 
-     };
 
-     jm.node.compare = function (node1, node2) {
 
-         // '-1' is alwary the last
 
-         var r = 0;
 
-         var i1 = node1.index;
 
-         var i2 = node2.index;
 
-         if (i1 >= 0 && i2 >= 0) {
 
-             r = i1 - i2;
 
-         } else if (i1 == -1 && i2 == -1) {
 
-             r = 0;
 
-         } else if (i1 == -1) {
 
-             r = 1;
 
-         } else if (i2 == -1) {
 
-             r = -1;
 
-         } else {
 
-             r = 0;
 
-         }
 
-         //logger.debug(i1+' <> '+i2+'  =  '+r);
 
-         return r;
 
-     };
 
-     jm.node.inherited = function (pnode, node) {
 
-         if (!!pnode && !!node) {
 
-             if (pnode.id === node.id) {
 
-                 return true;
 
-             }
 
-             if (pnode.isroot) {
 
-                 return true;
 
-             }
 
-             var pid = pnode.id;
 
-             var p = node;
 
-             while (!p.isroot) {
 
-                 p = p.parent;
 
-                 if (p.id === pid) {
 
-                     return true;
 
-                 }
 
-             }
 
-         }
 
-         return false;
 
-     };
 
-     jm.node.prototype = {
 
-         get_location: function () {
 
-             var vd = this._data.view;
 
-             return {
 
-                 x: vd.abs_x,
 
-                 y: vd.abs_y
 
-             };
 
-         },
 
-         get_size: function () {
 
-             var vd = this._data.view;
 
-             return {
 
-                 w: vd.width,
 
-                 h: vd.height
 
-             }
 
-         }
 
-     };
 
-     jm.mind = function () {
 
-         this.name = null;
 
-         this.author = null;
 
-         this.version = null;
 
-         this.root = null;
 
-         this.selected = null;
 
-         this.nodes = {};
 
-     };
 
-     jm.mind.prototype = {
 
-         get_node: function (nodeid) {
 
-             if (nodeid in this.nodes) {
 
-                 return this.nodes[nodeid];
 
-             } else {
 
-                 logger.warn('the node[id=' + nodeid + '] can not be found');
 
-                 return null;
 
-             }
 
-         },
 
-         set_root: function (nodeid, topic, data) {
 
-             if (this.root == null) {
 
-                 this.root = new jm.node(nodeid, 0, topic, data, true);
 
-                 this._put_node(this.root);
 
-             } else {
 
-                 logger.error('root node is already exist');
 
-             }
 
-         },
 
-         add_node: function (parent_node, nodeid, topic, data, idx, direction, expanded) {
 
-             if (!jm.util.is_node(parent_node)) {
 
-                 var the_parent_node = this.get_node(parent_node);
 
-                 if (!the_parent_node) {
 
-                     logger.error('the parent_node[id=' + parent_node + '] can not be found.');
 
-                     return null;
 
-                 } else {
 
-                     return this.add_node(the_parent_node, nodeid, topic, data, idx, direction, expanded);
 
-                 }
 
-             }
 
-             var nodeindex = idx || -1;
 
-             var node = null;
 
-             if (parent_node.isroot) {
 
-                 var d = jm.direction.right;
 
-                 if (isNaN(direction)) {
 
-                     var children = parent_node.children;
 
-                     var children_len = children.length;
 
-                     var r = 0;
 
-                     for (var i = 0; i < children_len; i++) { if (children[i].direction === jm.direction.left) { r--; } else { r++; } }
 
-                     d = (children_len > 1 && r > 0) ? jm.direction.left : jm.direction.right;
 
-                 } else {
 
-                     d = (direction != jm.direction.left) ? jm.direction.right : jm.direction.left;
 
-                 }
 
-                 node = new jm.node(nodeid, nodeindex, topic, data, false, parent_node, d, expanded);
 
-             } else {
 
-                 node = new jm.node(nodeid, nodeindex, topic, data, false, parent_node, parent_node.direction, expanded);
 
-             }
 
-             if (this._put_node(node)) {
 
-                 parent_node.children.push(node);
 
-                 this._reindex(parent_node);
 
-             } else {
 
-                 logger.error('fail, the nodeid \'' + node.id + '\' has been already exist.');
 
-                 node = null;
 
-             }
 
-             return node;
 
-         },
 
-         insert_node_before: function (node_before, nodeid, topic, data) {
 
-             if (!jm.util.is_node(node_before)) {
 
-                 var the_node_before = this.get_node(node_before);
 
-                 if (!the_node_before) {
 
-                     logger.error('the node_before[id=' + node_before + '] can not be found.');
 
-                     return null;
 
-                 } else {
 
-                     return this.insert_node_before(the_node_before, nodeid, topic, data);
 
-                 }
 
-             }
 
-             var node_index = node_before.index - 0.5;
 
-             return this.add_node(node_before.parent, nodeid, topic, data, node_index);
 
-         },
 
-         get_node_before: function (node) {
 
-             if (!jm.util.is_node(node)) {
 
-                 var the_node = this.get_node(node);
 
-                 if (!the_node) {
 
-                     logger.error('the node[id=' + node + '] can not be found.');
 
-                     return null;
 
-                 } else {
 
-                     return this.get_node_before(the_node);
 
-                 }
 
-             }
 
-             if (node.isroot) { return null; }
 
-             var idx = node.index - 2;
 
-             if (idx >= 0) {
 
-                 return node.parent.children[idx];
 
-             } else {
 
-                 return null;
 
-             }
 
-         },
 
-         insert_node_after: function (node_after, nodeid, topic, data) {
 
-             if (!jm.util.is_node(node_after)) {
 
-                 var the_node_after = this.get_node(node_after);
 
-                 if (!the_node_after) {
 
-                     logger.error('the node_after[id=' + node_after + '] can not be found.');
 
-                     return null;
 
-                 } else {
 
-                     return this.insert_node_after(the_node_after, nodeid, topic, data);
 
-                 }
 
-             }
 
-             var node_index = node_after.index + 0.5;
 
-             return this.add_node(node_after.parent, nodeid, topic, data, node_index);
 
-         },
 
-         get_node_after: function (node) {
 
-             if (!jm.util.is_node(node)) {
 
-                 var the_node = this.get_node(node);
 
-                 if (!the_node) {
 
-                     logger.error('the node[id=' + node + '] can not be found.');
 
-                     return null;
 
-                 } else {
 
-                     return this.get_node_after(the_node);
 
-                 }
 
-             }
 
-             if (node.isroot) { return null; }
 
-             var idx = node.index;
 
-             var brothers = node.parent.children;
 
-             if (brothers.length >= idx) {
 
-                 return node.parent.children[idx];
 
-             } else {
 
-                 return null;
 
-             }
 
-         },
 
-         move_node: function (node, beforeid, parentid, direction) {
 
-             if (!jm.util.is_node(node)) {
 
-                 var the_node = this.get_node(node);
 
-                 if (!the_node) {
 
-                     logger.error('the node[id=' + node + '] can not be found.');
 
-                     return null;
 
-                 } else {
 
-                     return this.move_node(the_node, beforeid, parentid, direction);
 
-                 }
 
-             }
 
-             if (!parentid) {
 
-                 parentid = node.parent.id;
 
-             }
 
-             return this._move_node(node, beforeid, parentid, direction);
 
-         },
 
-         _flow_node_direction: function (node, direction) {
 
-             if (typeof direction === 'undefined') {
 
-                 direction = node.direction;
 
-             } else {
 
-                 node.direction = direction;
 
-             }
 
-             var len = node.children.length;
 
-             while (len--) {
 
-                 this._flow_node_direction(node.children[len], direction);
 
-             }
 
-         },
 
-         _move_node_internal: function (node, beforeid) {
 
-             if (!!node && !!beforeid) {
 
-                 if (beforeid == '_last_') {
 
-                     node.index = -1;
 
-                     this._reindex(node.parent);
 
-                 } else if (beforeid == '_first_') {
 
-                     node.index = 0;
 
-                     this._reindex(node.parent);
 
-                 } else {
 
-                     var node_before = (!!beforeid) ? this.get_node(beforeid) : null;
 
-                     if (node_before != null && node_before.parent != null && node_before.parent.id == node.parent.id) {
 
-                         node.index = node_before.index - 0.5;
 
-                         this._reindex(node.parent);
 
-                     }
 
-                 }
 
-             }
 
-             return node;
 
-         },
 
-         _move_node: function (node, beforeid, parentid, direction) {
 
-             if (!!node && !!parentid) {
 
-                 if (node.parent.id != parentid) {
 
-                     // remove from parent's children
 
-                     var sibling = node.parent.children;
 
-                     var si = sibling.length;
 
-                     while (si--) {
 
-                         if (sibling[si].id == node.id) {
 
-                             sibling.splice(si, 1);
 
-                             break;
 
-                         }
 
-                     }
 
-                     node.parent = this.get_node(parentid);
 
-                     node.parent.children.push(node);
 
-                 }
 
-                 if (node.parent.isroot) {
 
-                     if (direction == jm.direction.left) {
 
-                         node.direction = direction;
 
-                     } else {
 
-                         node.direction = jm.direction.right;
 
-                     }
 
-                 } else {
 
-                     node.direction = node.parent.direction;
 
-                 }
 
-                 this._move_node_internal(node, beforeid);
 
-                 this._flow_node_direction(node);
 
-             }
 
-             return node;
 
-         },
 
-         remove_node: function (node) {
 
-             if (!jm.util.is_node(node)) {
 
-                 var the_node = this.get_node(node);
 
-                 if (!the_node) {
 
-                     logger.error('the node[id=' + node + '] can not be found.');
 
-                     return false;
 
-                 } else {
 
-                     return this.remove_node(the_node);
 
-                 }
 
-             }
 
-             if (!node) {
 
-                 logger.error('fail, the node can not be found');
 
-                 return false;
 
-             }
 
-             if (node.isroot) {
 
-                 logger.error('fail, can not remove root node');
 
-                 return false;
 
-             }
 
-             if (this.selected != null && this.selected.id == node.id) {
 
-                 this.selected = null;
 
-             }
 
-             // clean all subordinate nodes
 
-             var children = node.children;
 
-             var ci = children.length;
 
-             while (ci--) {
 
-                 this.remove_node(children[ci]);
 
-             }
 
-             // clean all children
 
-             children.length = 0;
 
-             // remove from parent's children
 
-             var sibling = node.parent.children;
 
-             var si = sibling.length;
 
-             while (si--) {
 
-                 if (sibling[si].id == node.id) {
 
-                     sibling.splice(si, 1);
 
-                     break;
 
-                 }
 
-             }
 
-             // remove from global nodes
 
-             delete this.nodes[node.id];
 
-             // clean all properties
 
-             for (var k in node) {
 
-                 delete node[k];
 
-             }
 
-             // remove it's self
 
-             node = null;
 
-             //delete node;
 
-             return true;
 
-         },
 
-         _put_node: function (node) {
 
-             if (node.id in this.nodes) {
 
-                 logger.warn('the nodeid \'' + node.id + '\' has been already exist.');
 
-                 return false;
 
-             } else {
 
-                 this.nodes[node.id] = node;
 
-                 return true;
 
-             }
 
-         },
 
-         _reindex: function (node) {
 
-             if (node instanceof jm.node) {
 
-                 node.children.sort(jm.node.compare);
 
-                 for (var i = 0; i < node.children.length; i++) {
 
-                     node.children[i].index = i + 1;
 
-                 }
 
-             }
 
-         },
 
-     };
 
-     jm.format = {
 
-         node_tree: {
 
-             example: {
 
-                 "meta": {
 
-                     "name": __name__,
 
-                     "author": __author__,
 
-                     "version": __version__
 
-                 },
 
-                 "format": "node_tree",
 
-                 "data": { "id": "root", "topic": "jsMind Example" }
 
-             },
 
-             get_mind: function (source) {
 
-                 var df = jm.format.node_tree;
 
-                 var mind = new jm.mind();
 
-                 mind.name = source.meta.name;
 
-                 mind.author = source.meta.author;
 
-                 mind.version = source.meta.version;
 
-                 df._parse(mind, source.data);
 
-                 return mind;
 
-             },
 
-             get_data: function (mind) {
 
-                 var df = jm.format.node_tree;
 
-                 var json = {};
 
-                 json.meta = {
 
-                     name: mind.name,
 
-                     author: mind.author,
 
-                     version: mind.version
 
-                 };
 
-                 json.format = 'node_tree';
 
-                 json.data = df._buildnode(mind.root);
 
-                 return json;
 
-             },
 
-             _parse: function (mind, node_root) {
 
-                 var df = jm.format.node_tree;
 
-                 var data = df._extract_data(node_root);
 
-                 mind.set_root(node_root.id, node_root.topic, data);
 
-                 if ('children' in node_root) {
 
-                     var children = node_root.children;
 
-                     for (var i = 0; i < children.length; i++) {
 
-                         df._extract_subnode(mind, mind.root, children[i]);
 
-                     }
 
-                 }
 
-             },
 
-             _extract_data: function (node_json) {
 
-                 var data = {};
 
-                 for (var k in node_json) {
 
-                     if (k == 'id' || k == 'topic' || k == 'children' || k == 'direction' || k == 'expanded') {
 
-                         continue;
 
-                     }
 
-                     data[k] = node_json[k];
 
-                 }
 
-                 return data;
 
-             },
 
-             _extract_subnode: function (mind, node_parent, node_json) {
 
-                 var df = jm.format.node_tree;
 
-                 var data = df._extract_data(node_json);
 
-                 var d = null;
 
-                 if (node_parent.isroot) {
 
-                     d = node_json.direction == 'left' ? jm.direction.left : jm.direction.right;
 
-                 }
 
-                 var node = mind.add_node(node_parent, node_json.id, node_json.topic, data, null, d, node_json.expanded);
 
-                 if (!!node_json['children']) {
 
-                     var children = node_json.children;
 
-                     for (var i = 0; i < children.length; i++) {
 
-                         df._extract_subnode(mind, node, children[i]);
 
-                     }
 
-                 }
 
-             },
 
-             _buildnode: function (node) {
 
-                 var df = jm.format.node_tree;
 
-                 if (!(node instanceof jm.node)) { return; }
 
-                 var o = {
 
-                     id: node.id,
 
-                     topic: node.topic,
 
-                     expanded: node.expanded
 
-                 };
 
-                 if (!!node.parent && node.parent.isroot) {
 
-                     o.direction = node.direction == jm.direction.left ? 'left' : 'right';
 
-                 }
 
-                 if (node.data != null) {
 
-                     var node_data = node.data;
 
-                     for (var k in node_data) {
 
-                         o[k] = node_data[k];
 
-                     }
 
-                 }
 
-                 var children = node.children;
 
-                 if (children.length > 0) {
 
-                     o.children = [];
 
-                     for (var i = 0; i < children.length; i++) {
 
-                         o.children.push(df._buildnode(children[i]));
 
-                     }
 
-                 }
 
-                 return o;
 
-             }
 
-         },
 
-         node_array: {
 
-             example: {
 
-                 "meta": {
 
-                     "name": __name__,
 
-                     "author": __author__,
 
-                     "version": __version__
 
-                 },
 
-                 "format": "node_array",
 
-                 "data": [
 
-                     { "id": "root", "topic": "jsMind Example", "isroot": true }
 
-                 ]
 
-             },
 
-             get_mind: function (source) {
 
-                 var df = jm.format.node_array;
 
-                 var mind = new jm.mind();
 
-                 mind.name = source.meta.name;
 
-                 mind.author = source.meta.author;
 
-                 mind.version = source.meta.version;
 
-                 df._parse(mind, source.data);
 
-                 return mind;
 
-             },
 
-             get_data: function (mind) {
 
-                 var df = jm.format.node_array;
 
-                 var json = {};
 
-                 json.meta = {
 
-                     name: mind.name,
 
-                     author: mind.author,
 
-                     version: mind.version
 
-                 };
 
-                 json.format = 'node_array';
 
-                 json.data = [];
 
-                 df._array(mind, json.data);
 
-                 return json;
 
-             },
 
-             _parse: function (mind, node_array) {
 
-                 var df = jm.format.node_array;
 
-                 var narray = node_array.slice(0);
 
-                 // reverse array for improving looping performance
 
-                 narray.reverse();
 
-                 var root_id = df._extract_root(mind, narray);
 
-                 if (!!root_id) {
 
-                     df._extract_subnode(mind, root_id, narray);
 
-                 } else {
 
-                     logger.error('root node can not be found');
 
-                 }
 
-             },
 
-             _extract_root: function (mind, node_array) {
 
-                 var df = jm.format.node_array;
 
-                 var i = node_array.length;
 
-                 while (i--) {
 
-                     if ('isroot' in node_array[i] && node_array[i].isroot) {
 
-                         var root_json = node_array[i];
 
-                         var data = df._extract_data(root_json);
 
-                         mind.set_root(root_json.id, root_json.topic, data);
 
-                         node_array.splice(i, 1);
 
-                         return root_json.id;
 
-                     }
 
-                 }
 
-                 return null;
 
-             },
 
-             _extract_subnode: function (mind, parentid, node_array) {
 
-                 var df = jm.format.node_array;
 
-                 var i = node_array.length;
 
-                 var node_json = null;
 
-                 var data = null;
 
-                 var extract_count = 0;
 
-                 while (i--) {
 
-                     node_json = node_array[i];
 
-                     if (node_json.parentid == parentid) {
 
-                         data = df._extract_data(node_json);
 
-                         var d = null;
 
-                         var node_direction = node_json.direction;
 
-                         if (!!node_direction) {
 
-                             d = node_direction == 'left' ? jm.direction.left : jm.direction.right;
 
-                         }
 
-                         mind.add_node(parentid, node_json.id, node_json.topic, data, null, d, node_json.expanded);
 
-                         node_array.splice(i, 1);
 
-                         extract_count++;
 
-                         var sub_extract_count = df._extract_subnode(mind, node_json.id, node_array);
 
-                         if (sub_extract_count > 0) {
 
-                             // reset loop index after extract subordinate node
 
-                             i = node_array.length;
 
-                             extract_count += sub_extract_count;
 
-                         }
 
-                     }
 
-                 }
 
-                 return extract_count;
 
-             },
 
-             _extract_data: function (node_json) {
 
-                 var data = {};
 
-                 for (var k in node_json) {
 
-                     if (k == 'id' || k == 'topic' || k == 'parentid' || k == 'isroot' || k == 'direction' || k == 'expanded') {
 
-                         continue;
 
-                     }
 
-                     data[k] = node_json[k];
 
-                 }
 
-                 return data;
 
-             },
 
-             _array: function (mind, node_array) {
 
-                 var df = jm.format.node_array;
 
-                 df._array_node(mind.root, node_array);
 
-             },
 
-             _array_node: function (node, node_array) {
 
-                 var df = jm.format.node_array;
 
-                 if (!(node instanceof jm.node)) { return; }
 
-                 var o = {
 
-                     id: node.id,
 
-                     topic: node.topic,
 
-                     expanded: node.expanded
 
-                 };
 
-                 if (!!node.parent) {
 
-                     o.parentid = node.parent.id;
 
-                 }
 
-                 if (node.isroot) {
 
-                     o.isroot = true;
 
-                 }
 
-                 if (!!node.parent && node.parent.isroot) {
 
-                     o.direction = node.direction == jm.direction.left ? 'left' : 'right';
 
-                 }
 
-                 if (node.data != null) {
 
-                     var node_data = node.data;
 
-                     for (var k in node_data) {
 
-                         o[k] = node_data[k];
 
-                     }
 
-                 }
 
-                 node_array.push(o);
 
-                 var ci = node.children.length;
 
-                 for (var i = 0; i < ci; i++) {
 
-                     df._array_node(node.children[i], node_array);
 
-                 }
 
-             },
 
-         },
 
-         freemind: {
 
-             example: {
 
-                 "meta": {
 
-                     "name": __name__,
 
-                     "author": __author__,
 
-                     "version": __version__
 
-                 },
 
-                 "format": "freemind",
 
-                 "data": "<map version=\"1.0.1\"><node ID=\"root\" TEXT=\"freemind Example\"/></map>"
 
-             },
 
-             get_mind: function (source) {
 
-                 var df = jm.format.freemind;
 
-                 var mind = new jm.mind();
 
-                 mind.name = source.meta.name;
 
-                 mind.author = source.meta.author;
 
-                 mind.version = source.meta.version;
 
-                 var xml = source.data;
 
-                 var xml_doc = df._parse_xml(xml);
 
-                 var xml_root = df._find_root(xml_doc);
 
-                 df._load_node(mind, null, xml_root);
 
-                 return mind;
 
-             },
 
-             get_data: function (mind) {
 
-                 var df = jm.format.freemind;
 
-                 var json = {};
 
-                 json.meta = {
 
-                     name: mind.name,
 
-                     author: mind.author,
 
-                     version: mind.version
 
-                 };
 
-                 json.format = 'freemind';
 
-                 var xmllines = [];
 
-                 xmllines.push('<map version=\"1.0.1\">');
 
-                 df._buildmap(mind.root, xmllines);
 
-                 xmllines.push('</map>');
 
-                 json.data = xmllines.join(' ');
 
-                 return json;
 
-             },
 
-             _parse_xml: function (xml) {
 
-                 var xml_doc = null;
 
-                 if (window.DOMParser) {
 
-                     var parser = new DOMParser();
 
-                     xml_doc = parser.parseFromString(xml, 'text/xml');
 
-                 } else { // Internet Explorer
 
-                     xml_doc = new ActiveXObject('Microsoft.XMLDOM');
 
-                     xml_doc.async = false;
 
-                     xml_doc.loadXML(xml);
 
-                 }
 
-                 return xml_doc;
 
-             },
 
-             _find_root: function (xml_doc) {
 
-                 var nodes = xml_doc.childNodes;
 
-                 var node = null;
 
-                 var root = null;
 
-                 var n = null;
 
-                 for (var i = 0; i < nodes.length; i++) {
 
-                     n = nodes[i];
 
-                     if (n.nodeType == 1 && n.tagName == 'map') {
 
-                         node = n;
 
-                         break;
 
-                     }
 
-                 }
 
-                 if (!!node) {
 
-                     var ns = node.childNodes;
 
-                     node = null;
 
-                     for (var i = 0; i < ns.length; i++) {
 
-                         n = ns[i];
 
-                         if (n.nodeType == 1 && n.tagName == 'node') {
 
-                             node = n;
 
-                             break;
 
-                         }
 
-                     }
 
-                 }
 
-                 return node;
 
-             },
 
-             _load_node: function (mind, parent_id, xml_node) {
 
-                 var df = jm.format.freemind;
 
-                 var node_id = xml_node.getAttribute('ID');
 
-                 var node_topic = xml_node.getAttribute('TEXT');
 
-                 // look for richcontent
 
-                 if (node_topic == null) {
 
-                     var topic_children = xml_node.childNodes;
 
-                     var topic_child = null;
 
-                     for (var i = 0; i < topic_children.length; i++) {
 
-                         topic_child = topic_children[i];
 
-                         //logger.debug(topic_child.tagName);
 
-                         if (topic_child.nodeType == 1 && topic_child.tagName === 'richcontent') {
 
-                             node_topic = topic_child.textContent;
 
-                             break;
 
-                         }
 
-                     }
 
-                 }
 
-                 var node_data = df._load_attributes(xml_node);
 
-                 var node_expanded = ('expanded' in node_data) ? (node_data.expanded == 'true') : true;
 
-                 delete node_data.expanded;
 
-                 var node_position = xml_node.getAttribute('POSITION');
 
-                 var node_direction = null;
 
-                 if (!!node_position) {
 
-                     node_direction = node_position == 'left' ? jm.direction.left : jm.direction.right;
 
-                 }
 
-                 //logger.debug(node_position +':'+ node_direction);
 
-                 if (!!parent_id) {
 
-                     mind.add_node(parent_id, node_id, node_topic, node_data, null, node_direction, node_expanded);
 
-                 } else {
 
-                     mind.set_root(node_id, node_topic, node_data);
 
-                 }
 
-                 var children = xml_node.childNodes;
 
-                 var child = null;
 
-                 for (var i = 0; i < children.length; i++) {
 
-                     child = children[i];
 
-                     if (child.nodeType == 1 && child.tagName == 'node') {
 
-                         df._load_node(mind, node_id, child);
 
-                     }
 
-                 }
 
-             },
 
-             _load_attributes: function (xml_node) {
 
-                 var children = xml_node.childNodes;
 
-                 var attr = null;
 
-                 var attr_data = {};
 
-                 for (var i = 0; i < children.length; i++) {
 
-                     attr = children[i];
 
-                     if (attr.nodeType == 1 && attr.tagName === 'attribute') {
 
-                         attr_data[attr.getAttribute('NAME')] = attr.getAttribute('VALUE');
 
-                     }
 
-                 }
 
-                 return attr_data;
 
-             },
 
-             _buildmap: function (node, xmllines) {
 
-                 var df = jm.format.freemind;
 
-                 var pos = null;
 
-                 if (!!node.parent && node.parent.isroot) {
 
-                     pos = node.direction === jm.direction.left ? 'left' : 'right';
 
-                 }
 
-                 xmllines.push('<node');
 
-                 xmllines.push('ID=\"' + node.id + '\"');
 
-                 if (!!pos) {
 
-                     xmllines.push('POSITION=\"' + pos + '\"');
 
-                 }
 
-                 xmllines.push('TEXT=\"' + node.topic + '\">');
 
-                 // store expanded status as an attribute
 
-                 xmllines.push('<attribute NAME=\"expanded\" VALUE=\"' + node.expanded + '\"/>');
 
-                 // for attributes
 
-                 var node_data = node.data;
 
-                 if (node_data != null) {
 
-                     for (var k in node_data) {
 
-                         xmllines.push('<attribute NAME=\"' + k + '\" VALUE=\"' + node_data[k] + '\"/>');
 
-                     }
 
-                 }
 
-                 // for children
 
-                 var children = node.children;
 
-                 for (var i = 0; i < children.length; i++) {
 
-                     df._buildmap(children[i], xmllines);
 
-                 }
 
-                 xmllines.push('</node>');
 
-             },
 
-         },
 
-     };
 
-     // ============= utility object =============================================
 
-     jm.util = {
 
-         is_node: function (node) {
 
-             return !!node && node instanceof jm.node;
 
-         },
 
-         ajax: {
 
-             _xhr: function () {
 
-                 var xhr = null;
 
-                 if (window.XMLHttpRequest) {
 
-                     xhr = new XMLHttpRequest();
 
-                 } else {
 
-                     try {
 
-                         xhr = new ActiveXObject('Microsoft.XMLHTTP');
 
-                     } catch (e) { }
 
-                 }
 
-                 return xhr;
 
-             },
 
-             _eurl: function (url) {
 
-                 return encodeURIComponent(url);
 
-             },
 
-             request: function (url, param, method, callback, fail_callback) {
 
-                 var a = jm.util.ajax;
 
-                 var p = null;
 
-                 var tmp_param = [];
 
-                 for (var k in param) {
 
-                     tmp_param.push(a._eurl(k) + '=' + a._eurl(param[k]));
 
-                 }
 
-                 if (tmp_param.length > 0) {
 
-                     p = tmp_param.join('&');
 
-                 }
 
-                 var xhr = a._xhr();
 
-                 if (!xhr) { return; }
 
-                 xhr.onreadystatechange = function () {
 
-                     if (xhr.readyState == 4) {
 
-                         if (xhr.status == 200 || xhr.status == 0) {
 
-                             if (typeof callback === 'function') {
 
-                                 var data = jm.util.json.string2json(xhr.responseText);
 
-                                 if (data != null) {
 
-                                     callback(data);
 
-                                 } else {
 
-                                     callback(xhr.responseText);
 
-                                 }
 
-                             }
 
-                         } else {
 
-                             if (typeof fail_callback === 'function') {
 
-                                 fail_callback(xhr);
 
-                             } else {
 
-                                 logger.error('xhr request failed.', xhr);
 
-                             }
 
-                         }
 
-                     }
 
-                 }
 
-                 method = method || 'GET';
 
-                 xhr.open(method, url, true);
 
-                 xhr.setRequestHeader('If-Modified-Since', '0');
 
-                 if (method == 'POST') {
 
-                     xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded;charset=utf-8');
 
-                     xhr.send(p);
 
-                 } else {
 
-                     xhr.send();
 
-                 }
 
-             },
 
-             get: function (url, callback) {
 
-                 return jm.util.ajax.request(url, {}, 'GET', callback);
 
-             },
 
-             post: function (url, param, callback) {
 
-                 return jm.util.ajax.request(url, param, 'POST', callback);
 
-             }
 
-         },
 
-         dom: {
 
-             //target,eventType,handler
 
-             add_event: function (t, e, h) {
 
-                 if (!!t.addEventListener) {
 
-                     t.addEventListener(e, h, false);
 
-                 } else {
 
-                     t.attachEvent('on' + e, h);
 
-                 }
 
-             }
 
-         },
 
-         file: {
 
-             read: function (file_data, fn_callback) {
 
-                 var reader = new FileReader();
 
-                 reader.onload = function () {
 
-                     if (typeof fn_callback === 'function') {
 
-                         fn_callback(this.result, file_data.name);
 
-                     }
 
-                 };
 
-                 reader.readAsText(file_data);
 
-             },
 
-             save: function (file_data, type, name) {
 
-                 var blob;
 
-                 if (typeof $w.Blob === 'function') {
 
-                     blob = new Blob([file_data], { type: type });
 
-                 } else {
 
-                     var BlobBuilder = $w.BlobBuilder || $w.MozBlobBuilder || $w.WebKitBlobBuilder || $w.MSBlobBuilder;
 
-                     var bb = new BlobBuilder();
 
-                     bb.append(file_data);
 
-                     blob = bb.getBlob(type);
 
-                 }
 
-                 if (navigator.msSaveBlob) {
 
-                     navigator.msSaveBlob(blob, name);
 
-                 } else {
 
-                     var URL = $w.URL || $w.webkitURL;
 
-                     var bloburl = URL.createObjectURL(blob);
 
-                     var anchor = $c('a');
 
-                     if ('download' in anchor) {
 
-                         anchor.style.visibility = 'hidden';
 
-                         anchor.href = bloburl;
 
-                         anchor.download = name;
 
-                         $d.body.appendChild(anchor);
 
-                         var evt = $d.createEvent('MouseEvents');
 
-                         evt.initEvent('click', true, true);
 
-                         anchor.dispatchEvent(evt);
 
-                         $d.body.removeChild(anchor);
 
-                     } else {
 
-                         location.href = bloburl;
 
-                     }
 
-                 }
 
-             }
 
-         },
 
-         json: {
 
-             json2string: function (json) {
 
-                 if (!!JSON) {
 
-                     try {
 
-                         var json_str = JSON.stringify(json);
 
-                         return json_str;
 
-                     } catch (e) {
 
-                         logger.warn(e);
 
-                         logger.warn('can not convert to string');
 
-                         return null;
 
-                     }
 
-                 }
 
-             },
 
-             string2json: function (json_str) {
 
-                 if (!!JSON) {
 
-                     try {
 
-                         var json = JSON.parse(json_str);
 
-                         return json;
 
-                     } catch (e) {
 
-                         logger.warn(e);
 
-                         logger.warn('can not parse to json');
 
-                         return null;
 
-                     }
 
-                 }
 
-             },
 
-             merge: function (b, a) {
 
-                 for (var o in a) {
 
-                     if (o in b) {
 
-                         if (typeof b[o] === 'object' &&
 
-                             Object.prototype.toString.call(b[o]).toLowerCase() == '[object object]' &&
 
-                             !b[o].length) {
 
-                             jm.util.json.merge(b[o], a[o]);
 
-                         } else {
 
-                             b[o] = a[o];
 
-                         }
 
-                     } else {
 
-                         b[o] = a[o];
 
-                     }
 
-                 }
 
-                 return b;
 
-             }
 
-         },
 
-         uuid: {
 
-             newid: function () {
 
-                 return (new Date().getTime().toString(16) + Math.random().toString(16).substr(2)).substr(2, 16);
 
-             }
 
-         },
 
-         text: {
 
-             is_empty: function (s) {
 
-                 if (!s) { return true; }
 
-                 return s.replace(/\s*/, '').length == 0;
 
-             }
 
-         }
 
-     };
 
-     jm.prototype = {
 
-         init: function () {
 
-             if (this.inited) { return; }
 
-             this.inited = true;
 
-             var opts = this.options;
 
-             var opts_layout = {
 
-                 mode: opts.mode,
 
-                 hspace: opts.layout.hspace,
 
-                 vspace: opts.layout.vspace,
 
-                 pspace: opts.layout.pspace
 
-             }
 
-             var opts_view = {
 
-                 container: opts.container,
 
-                 support_html: opts.support_html,
 
-                 engine: opts.view.engine,
 
-                 hmargin: opts.view.hmargin,
 
-                 vmargin: opts.view.vmargin,
 
-                 line_width: opts.view.line_width,
 
-                 line_color: opts.view.line_color
 
-             };
 
-             // create instance of function provider
 
-             this.data = new jm.data_provider(this);
 
-             this.layout = new jm.layout_provider(this, opts_layout);
 
-             this.view = new jm.view_provider(this, opts_view);
 
-             this.shortcut = new jm.shortcut_provider(this, opts.shortcut);
 
-             this.data.init();
 
-             this.layout.init();
 
-             this.view.init();
 
-             this.shortcut.init();
 
-             this._event_bind();
 
-             jm.init_plugins(this);
 
-         },
 
-         enable_edit: function () {
 
-             this.options.editable = true;
 
-         },
 
-         disable_edit: function () {
 
-             this.options.editable = false;
 
-         },
 
-         // call enable_event_handle('dblclick')
 
-         // options are 'mousedown', 'click', 'dblclick'
 
-         enable_event_handle: function (event_handle) {
 
-             this.options.default_event_handle['enable_' + event_handle + '_handle'] = true;
 
-         },
 
-         // call disable_event_handle('dblclick')
 
-         // options are 'mousedown', 'click', 'dblclick'
 
-         disable_event_handle: function (event_handle) {
 
-             this.options.default_event_handle['enable_' + event_handle + '_handle'] = false;
 
-         },
 
-         get_editable: function () {
 
-             return this.options.editable;
 
-         },
 
-         set_theme: function (theme) {
 
-             var theme_old = this.options.theme;
 
-             this.options.theme = (!!theme) ? theme : null;
 
-             if (theme_old != this.options.theme) {
 
-                 this.view.reset_theme();
 
-                 this.view.reset_custom_style();
 
-             }
 
-         },
 
-         _event_bind: function () {
 
-             this.view.add_event(this, 'mousedown', this.mousedown_handle);
 
-             this.view.add_event(this, 'click', this.click_handle);
 
-             this.view.add_event(this, 'dblclick', this.dblclick_handle);
 
-         },
 
-         mousedown_handle: function (e) {
 
-             if (!this.options.default_event_handle['enable_mousedown_handle']) {
 
-                 return;
 
-             }
 
-             var element = e.target || event.srcElement;
 
-             var nodeid = this.view.get_binded_nodeid(element);
 
-             if (!!nodeid) {
 
-                 if (element.tagName.toLowerCase() == 'jmnode') {
 
-                     this.select_node(nodeid);
 
-                 }
 
-             } else {
 
-                 this.select_clear();
 
-             }
 
-         },
 
-         click_handle: function (e) {
 
-             if (!this.options.default_event_handle['enable_click_handle']) {
 
-                 return;
 
-             }
 
-             var element = e.target || event.srcElement;
 
-             var isexpander = this.view.is_expander(element);
 
-             if (isexpander) {
 
-                 var nodeid = this.view.get_binded_nodeid(element);
 
-                 if (!!nodeid) {
 
-                     this.toggle_node(nodeid);
 
-                 }
 
-             }
 
-         },
 
-         dblclick_handle: function (e) {
 
-             if (!this.options.default_event_handle['enable_dblclick_handle']) {
 
-                 return;
 
-             }
 
-             if (this.get_editable()) {
 
-                 var element = e.target || event.srcElement;
 
-                 var nodeid = this.view.get_binded_nodeid(element);
 
-                 if (!!nodeid) {
 
-                     this.begin_edit(nodeid);
 
-                 }
 
-             }
 
-         },
 
-         begin_edit: function (node) {
 
-             if (!jm.util.is_node(node)) {
 
-                 var the_node = this.get_node(node);
 
-                 if (!the_node) {
 
-                     logger.error('the node[id=' + node + '] can not be found.');
 
-                     return false;
 
-                 } else {
 
-                     return this.begin_edit(the_node);
 
-                 }
 
-             }
 
-             if (this.get_editable()) {
 
-                 this.view.edit_node_begin(node);
 
-             } else {
 
-                 logger.error('fail, this mind map is not editable.');
 
-                 return;
 
-             }
 
-         },
 
-         end_edit: function () {
 
-             this.view.edit_node_end();
 
-         },
 
-         toggle_node: function (node) {
 
-             if (!jm.util.is_node(node)) {
 
-                 var the_node = this.get_node(node);
 
-                 if (!the_node) {
 
-                     logger.error('the node[id=' + node + '] can not be found.');
 
-                     return;
 
-                 } else {
 
-                     return this.toggle_node(the_node);
 
-                 }
 
-             }
 
-             if (node.isroot) { return; }
 
-             this.view.save_location(node);
 
-             this.layout.toggle_node(node);
 
-             this.view.relayout();
 
-             this.view.restore_location(node);
 
-         },
 
-         expand_node: function (node) {
 
-             if (!jm.util.is_node(node)) {
 
-                 var the_node = this.get_node(node);
 
-                 if (!the_node) {
 
-                     logger.error('the node[id=' + node + '] can not be found.');
 
-                     return;
 
-                 } else {
 
-                     return this.expand_node(the_node);
 
-                 }
 
-             }
 
-             if (node.isroot) { return; }
 
-             this.view.save_location(node);
 
-             this.layout.expand_node(node);
 
-             this.view.relayout();
 
-             this.view.restore_location(node);
 
-         },
 
-         collapse_node: function (node) {
 
-             if (!jm.util.is_node(node)) {
 
-                 var the_node = this.get_node(node);
 
-                 if (!the_node) {
 
-                     logger.error('the node[id=' + node + '] can not be found.');
 
-                     return;
 
-                 } else {
 
-                     return this.collapse_node(the_node);
 
-                 }
 
-             }
 
-             if (node.isroot) { return; }
 
-             this.view.save_location(node);
 
-             this.layout.collapse_node(node);
 
-             this.view.relayout();
 
-             this.view.restore_location(node);
 
-         },
 
-         expand_all: function () {
 
-             this.layout.expand_all();
 
-             this.view.relayout();
 
-         },
 
-         collapse_all: function () {
 
-             this.layout.collapse_all();
 
-             this.view.relayout();
 
-         },
 
-         expand_to_depth: function (depth) {
 
-             this.layout.expand_to_depth(depth);
 
-             this.view.relayout();
 
-         },
 
-         _reset: function () {
 
-             this.view.reset();
 
-             this.layout.reset();
 
-             this.data.reset();
 
-         },
 
-         _show: function (mind) {
 
-             var m = mind || jm.format.node_array.example;
 
-             this.mind = this.data.load(m);
 
-             if (!this.mind) {
 
-                 logger.error('data.load error');
 
-                 return;
 
-             } else {
 
-                 logger.debug('data.load ok');
 
-             }
 
-             this.view.load();
 
-             logger.debug('view.load ok');
 
-             this.layout.layout();
 
-             logger.debug('layout.layout ok');
 
-             this.view.show(true);
 
-             logger.debug('view.show ok');
 
-             this.invoke_event_handle(jm.event_type.show, { data: [mind] });
 
-         },
 
-         show: function (mind) {
 
-             this._reset();
 
-             this._show(mind);
 
-         },
 
-         get_meta: function () {
 
-             return {
 
-                 name: this.mind.name,
 
-                 author: this.mind.author,
 
-                 version: this.mind.version
 
-             };
 
-         },
 
-         get_data: function (data_format) {
 
-             var df = data_format || 'node_tree';
 
-             return this.data.get_data(df);
 
-         },
 
-         get_root: function () {
 
-             return this.mind.root;
 
-         },
 
-         get_node: function (nodeid) {
 
-             return this.mind.get_node(nodeid);
 
-         },
 
-         add_node: function (parent_node, nodeid, topic, data) {
 
-             if (this.get_editable()) {
 
-                 var node = this.mind.add_node(parent_node, nodeid, topic, data);
 
-                 if (!!node) {
 
-                     this.view.add_node(node);
 
-                     this.layout.layout();
 
-                     this.view.show(false);
 
-                     this.view.reset_node_custom_style(node);
 
-                     this.expand_node(parent_node);
 
-                     this.invoke_event_handle(jm.event_type.edit, { evt: 'add_node', data: [parent_node.id, nodeid, topic, data], node: nodeid });
 
-                 }
 
-                 return node;
 
-             } else {
 
-                 logger.error('fail, this mind map is not editable');
 
-                 return null;
 
-             }
 
-         },
 
-         insert_node_before: function (node_before, nodeid, topic, data) {
 
-             if (this.get_editable()) {
 
-                 var beforeid = jm.util.is_node(node_before) ? node_before.id : node_before;
 
-                 var node = this.mind.insert_node_before(node_before, nodeid, topic, data);
 
-                 if (!!node) {
 
-                     this.view.add_node(node);
 
-                     this.layout.layout();
 
-                     this.view.show(false);
 
-                     this.invoke_event_handle(jm.event_type.edit, { evt: 'insert_node_before', data: [beforeid, nodeid, topic, data], node: nodeid });
 
-                 }
 
-                 return node;
 
-             } else {
 
-                 logger.error('fail, this mind map is not editable');
 
-                 return null;
 
-             }
 
-         },
 
-         insert_node_after: function (node_after, nodeid, topic, data) {
 
-             if (this.get_editable()) {
 
-                 var afterid = jm.util.is_node(node_after) ? node_after.id : node_after;
 
-                 var node = this.mind.insert_node_after(node_after, nodeid, topic, data);
 
-                 if (!!node) {
 
-                     this.view.add_node(node);
 
-                     this.layout.layout();
 
-                     this.view.show(false);
 
-                     this.invoke_event_handle(jm.event_type.edit, { evt: 'insert_node_after', data: [afterid, nodeid, topic, data], node: nodeid });
 
-                 }
 
-                 return node;
 
-             } else {
 
-                 logger.error('fail, this mind map is not editable');
 
-                 return null;
 
-             }
 
-         },
 
-         remove_node: function (node) {
 
-             if (!jm.util.is_node(node)) {
 
-                 var the_node = this.get_node(node);
 
-                 if (!the_node) {
 
-                     logger.error('the node[id=' + node + '] can not be found.');
 
-                     return false;
 
-                 } else {
 
-                     return this.remove_node(the_node);
 
-                 }
 
-             }
 
-             if (this.get_editable()) {
 
-                 if (node.isroot) {
 
-                     logger.error('fail, can not remove root node');
 
-                     return false;
 
-                 }
 
-                 var nodeid = node.id;
 
-                 var parentid = node.parent.id;
 
-                 var parent_node = this.get_node(parentid);
 
-                 this.view.save_location(parent_node);
 
-                 this.view.remove_node(node);
 
-                 this.mind.remove_node(node);
 
-                 this.layout.layout();
 
-                 this.view.show(false);
 
-                 this.view.restore_location(parent_node);
 
-                 this.invoke_event_handle(jm.event_type.edit, { evt: 'remove_node', data: [nodeid], node: parentid });
 
-                 return true;
 
-             } else {
 
-                 logger.error('fail, this mind map is not editable');
 
-                 return false;
 
-             }
 
-         },
 
-         update_node: function (nodeid, topic) {
 
-             if (this.get_editable()) {
 
-                 if (jm.util.text.is_empty(topic)) {
 
-                     logger.warn('fail, topic can not be empty');
 
-                     return;
 
-                 }
 
-                 var node = this.get_node(nodeid);
 
-                 if (!!node) {
 
-                     if (node.topic === topic) {
 
-                         logger.info('nothing changed');
 
-                         this.view.update_node(node);
 
-                         return;
 
-                     }
 
-                     node.topic = topic;
 
-                     this.view.update_node(node);
 
-                     this.layout.layout();
 
-                     this.view.show(false);
 
-                     this.invoke_event_handle(jm.event_type.edit, { evt: 'update_node', data: [nodeid, topic], node: nodeid });
 
-                 }
 
-             } else {
 
-                 logger.error('fail, this mind map is not editable');
 
-                 return;
 
-             }
 
-         },
 
-         move_node: function (nodeid, beforeid, parentid, direction) {
 
-             if (this.get_editable()) {
 
-                 var node = this.mind.move_node(nodeid, beforeid, parentid, direction);
 
-                 if (!!node) {
 
-                     this.view.update_node(node);
 
-                     this.layout.layout();
 
-                     this.view.show(false);
 
-                     this.invoke_event_handle(jm.event_type.edit, { evt: 'move_node', data: [nodeid, beforeid, parentid, direction], node: nodeid });
 
-                 }
 
-             } else {
 
-                 logger.error('fail, this mind map is not editable');
 
-                 return;
 
-             }
 
-         },
 
-         select_node: function (node) {
 
-             if (!jm.util.is_node(node)) {
 
-                 var the_node = this.get_node(node);
 
-                 if (!the_node) {
 
-                     logger.error('the node[id=' + node + '] can not be found.');
 
-                     return;
 
-                 } else {
 
-                     return this.select_node(the_node);
 
-                 }
 
-             }
 
-             if (!this.layout.is_visible(node)) {
 
-                 return;
 
-             }
 
-             this.mind.selected = node;
 
-             this.view.select_node(node);
 
-             this.invoke_event_handle(jm.event_type.select, { evt: 'select_node', data: [], node: node.id });
 
-         },
 
-         get_selected_node: function () {
 
-             if (!!this.mind) {
 
-                 return this.mind.selected;
 
-             } else {
 
-                 return null;
 
-             }
 
-         },
 
-         select_clear: function () {
 
-             if (!!this.mind) {
 
-                 this.mind.selected = null;
 
-                 this.view.select_clear();
 
-             }
 
-         },
 
-         is_node_visible: function (node) {
 
-             return this.layout.is_visible(node);
 
-         },
 
-         find_node_before: function (node) {
 
-             if (!jm.util.is_node(node)) {
 
-                 var the_node = this.get_node(node);
 
-                 if (!the_node) {
 
-                     logger.error('the node[id=' + node + '] can not be found.');
 
-                     return;
 
-                 } else {
 
-                     return this.find_node_before(the_node);
 
-                 }
 
-             }
 
-             if (node.isroot) { return null; }
 
-             var n = null;
 
-             if (node.parent.isroot) {
 
-                 var c = node.parent.children;
 
-                 var prev = null;
 
-                 var ni = null;
 
-                 for (var i = 0; i < c.length; i++) {
 
-                     ni = c[i];
 
-                     if (node.direction === ni.direction) {
 
-                         if (node.id === ni.id) {
 
-                             n = prev;
 
-                         }
 
-                         prev = ni;
 
-                     }
 
-                 }
 
-             } else {
 
-                 n = this.mind.get_node_before(node);
 
-             }
 
-             return n;
 
-         },
 
-         find_node_after: function (node) {
 
-             if (!jm.util.is_node(node)) {
 
-                 var the_node = this.get_node(node);
 
-                 if (!the_node) {
 
-                     logger.error('the node[id=' + node + '] can not be found.');
 
-                     return;
 
-                 } else {
 
-                     return this.find_node_after(the_node);
 
-                 }
 
-             }
 
-             if (node.isroot) { return null; }
 
-             var n = null;
 
-             if (node.parent.isroot) {
 
-                 var c = node.parent.children;
 
-                 var getthis = false;
 
-                 var ni = null;
 
-                 for (var i = 0; i < c.length; i++) {
 
-                     ni = c[i];
 
-                     if (node.direction === ni.direction) {
 
-                         if (getthis) {
 
-                             n = ni;
 
-                             break;
 
-                         }
 
-                         if (node.id === ni.id) {
 
-                             getthis = true;
 
-                         }
 
-                     }
 
-                 }
 
-             } else {
 
-                 n = this.mind.get_node_after(node);
 
-             }
 
-             return n;
 
-         },
 
-         set_node_color: function (nodeid, bgcolor, fgcolor) {
 
-             if (this.get_editable()) {
 
-                 var node = this.mind.get_node(nodeid);
 
-                 if (!!node) {
 
-                     if (!!bgcolor) {
 
-                         node.data['background-color'] = bgcolor;
 
-                     }
 
-                     if (!!fgcolor) {
 
-                         node.data['foreground-color'] = fgcolor;
 
-                     }
 
-                     this.view.reset_node_custom_style(node);
 
-                 }
 
-             } else {
 
-                 logger.error('fail, this mind map is not editable');
 
-                 return null;
 
-             }
 
-         },
 
-         set_node_font_style: function (nodeid, size, weight, style) {
 
-             if (this.get_editable()) {
 
-                 var node = this.mind.get_node(nodeid);
 
-                 if (!!node) {
 
-                     if (!!size) {
 
-                         node.data['font-size'] = size;
 
-                     }
 
-                     if (!!weight) {
 
-                         node.data['font-weight'] = weight;
 
-                     }
 
-                     if (!!style) {
 
-                         node.data['font-style'] = style;
 
-                     }
 
-                     this.view.reset_node_custom_style(node);
 
-                     this.view.update_node(node);
 
-                     this.layout.layout();
 
-                     this.view.show(false);
 
-                 }
 
-             } else {
 
-                 logger.error('fail, this mind map is not editable');
 
-                 return null;
 
-             }
 
-         },
 
-         set_node_background_image: function (nodeid, image, width, height, rotation) {
 
-             if (this.get_editable()) {
 
-                 var node = this.mind.get_node(nodeid);
 
-                 if (!!node) {
 
-                     if (!!image) {
 
-                         node.data['background-image'] = image;
 
-                     }
 
-                     if (!!width) {
 
-                         node.data['width'] = width;
 
-                     }
 
-                     if (!!height) {
 
-                         node.data['height'] = height;
 
-                     }
 
-                     if (!!rotation) {
 
-                         node.data['background-rotation'] = rotation;
 
-                     }
 
-                     this.view.reset_node_custom_style(node);
 
-                     this.view.update_node(node);
 
-                     this.layout.layout();
 
-                     this.view.show(false);
 
-                 }
 
-             } else {
 
-                 logger.error('fail, this mind map is not editable');
 
-                 return null;
 
-             }
 
-         },
 
-         set_node_background_rotation: function (nodeid, rotation) {
 
-             if (this.get_editable()) {
 
-                 var node = this.mind.get_node(nodeid);
 
-                 if (!!node) {
 
-                     if (!node.data['background-image']) {
 
-                         logger.error('fail, only can change rotation angle of node with background image');
 
-                         return null;
 
-                     }
 
-                     node.data['background-rotation'] = rotation;
 
-                     this.view.reset_node_custom_style(node);
 
-                     this.view.update_node(node);
 
-                     this.layout.layout();
 
-                     this.view.show(false);
 
-                 }
 
-             } else {
 
-                 logger.error('fail, this mind map is not editable');
 
-                 return null;
 
-             }
 
-         },
 
-         resize: function () {
 
-             this.view.resize();
 
-         },
 
-         // callback(type ,data)
 
-         add_event_listener: function (callback) {
 
-             if (typeof callback === 'function') {
 
-                 this.event_handles.push(callback);
 
-             }
 
-         },
 
-         clear_event_listener: function () {
 
-             this.event_handles = [];
 
-         },
 
-         invoke_event_handle: function (type, data) {
 
-             var j = this;
 
-             $w.setTimeout(function () {
 
-                 j._invoke_event_handle(type, data);
 
-             }, 0);
 
-         },
 
-         _invoke_event_handle: function (type, data) {
 
-             var l = this.event_handles.length;
 
-             for (var i = 0; i < l; i++) {
 
-                 this.event_handles[i](type, data);
 
-             }
 
-         }
 
-     };
 
-     // ============= data provider =============================================
 
-     jm.data_provider = function (jm) {
 
-         this.jm = jm;
 
-     };
 
-     jm.data_provider.prototype = {
 
-         init: function () {
 
-             logger.debug('data.init');
 
-         },
 
-         reset: function () {
 
-             logger.debug('data.reset');
 
-         },
 
-         load: function (mind_data) {
 
-             var df = null;
 
-             var mind = null;
 
-             if (typeof mind_data === 'object') {
 
-                 if (!!mind_data.format) {
 
-                     df = mind_data.format;
 
-                 } else {
 
-                     df = 'node_tree';
 
-                 }
 
-             } else {
 
-                 df = 'freemind';
 
-             }
 
-             if (df == 'node_array') {
 
-                 mind = jm.format.node_array.get_mind(mind_data);
 
-             } else if (df == 'node_tree') {
 
-                 mind = jm.format.node_tree.get_mind(mind_data);
 
-             } else if (df == 'freemind') {
 
-                 mind = jm.format.freemind.get_mind(mind_data);
 
-             } else {
 
-                 logger.warn('unsupported format');
 
-             }
 
-             return mind;
 
-         },
 
-         get_data: function (data_format) {
 
-             var data = null;
 
-             if (data_format == 'node_array') {
 
-                 data = jm.format.node_array.get_data(this.jm.mind);
 
-             } else if (data_format == 'node_tree') {
 
-                 data = jm.format.node_tree.get_data(this.jm.mind);
 
-             } else if (data_format == 'freemind') {
 
-                 data = jm.format.freemind.get_data(this.jm.mind);
 
-             } else {
 
-                 logger.error('unsupported ' + data_format + ' format');
 
-             }
 
-             return data;
 
-         },
 
-     };
 
-     // ============= layout provider ===========================================
 
-     jm.layout_provider = function (jm, options) {
 
-         this.opts = options;
 
-         this.jm = jm;
 
-         this.isside = (this.opts.mode == 'side');
 
-         this.bounds = null;
 
-         this.cache_valid = false;
 
-     };
 
-     jm.layout_provider.prototype = {
 
-         init: function () {
 
-             logger.debug('layout.init');
 
-         },
 
-         reset: function () {
 
-             logger.debug('layout.reset');
 
-             this.bounds = { n: 0, s: 0, w: 0, e: 0 };
 
-         },
 
-         layout: function () {
 
-             logger.debug('layout.layout');
 
-             this.layout_direction();
 
-             this.layout_offset();
 
-         },
 
-         layout_direction: function () {
 
-             this._layout_direction_root();
 
-         },
 
-         _layout_direction_root: function () {
 
-             var node = this.jm.mind.root;
 
-             // logger.debug(node);
 
-             var layout_data = null;
 
-             if ('layout' in node._data) {
 
-                 layout_data = node._data.layout;
 
-             } else {
 
-                 layout_data = {};
 
-                 node._data.layout = layout_data;
 
-             }
 
-             var children = node.children;
 
-             var children_count = children.length;
 
-             layout_data.direction = jm.direction.center;
 
-             layout_data.side_index = 0;
 
-             if (this.isside) {
 
-                 var i = children_count;
 
-                 while (i--) {
 
-                     this._layout_direction_side(children[i], jm.direction.right, i);
 
-                 }
 
-             } else {
 
-                 var i = children_count;
 
-                 var subnode = null;
 
-                 while (i--) {
 
-                     subnode = children[i];
 
-                     if (subnode.direction == jm.direction.left) {
 
-                         this._layout_direction_side(subnode, jm.direction.left, i);
 
-                     } else {
 
-                         this._layout_direction_side(subnode, jm.direction.right, i);
 
-                     }
 
-                 }
 
-                 /*
 
-                 var boundary = Math.ceil(children_count/2);
 
-                 var i = children_count;
 
-                 while(i--){
 
-                     if(i>=boundary){
 
-                         this._layout_direction_side(children[i],jm.direction.left, children_count-i-1);
 
-                     }else{
 
-                         this._layout_direction_side(children[i],jm.direction.right, i);
 
-                     }
 
-                 }*/
 
-             }
 
-         },
 
-         _layout_direction_side: function (node, direction, side_index) {
 
-             var layout_data = null;
 
-             if ('layout' in node._data) {
 
-                 layout_data = node._data.layout;
 
-             } else {
 
-                 layout_data = {};
 
-                 node._data.layout = layout_data;
 
-             }
 
-             var children = node.children;
 
-             var children_count = children.length;
 
-             layout_data.direction = direction;
 
-             layout_data.side_index = side_index;
 
-             var i = children_count;
 
-             while (i--) {
 
-                 this._layout_direction_side(children[i], direction, i);
 
-             }
 
-         },
 
-         layout_offset: function () {
 
-             var node = this.jm.mind.root;
 
-             var layout_data = node._data.layout;
 
-             layout_data.offset_x = 0;
 
-             layout_data.offset_y = 0;
 
-             layout_data.outer_height = 0;
 
-             var children = node.children;
 
-             var i = children.length;
 
-             var left_nodes = [];
 
-             var right_nodes = [];
 
-             var subnode = null;
 
-             while (i--) {
 
-                 subnode = children[i];
 
-                 if (subnode._data.layout.direction == jm.direction.right) {
 
-                     right_nodes.unshift(subnode);
 
-                 } else {
 
-                     left_nodes.unshift(subnode);
 
-                 }
 
-             }
 
-             layout_data.left_nodes = left_nodes;
 
-             layout_data.right_nodes = right_nodes;
 
-             layout_data.outer_height_left = this._layout_offset_subnodes(left_nodes);
 
-             layout_data.outer_height_right = this._layout_offset_subnodes(right_nodes);
 
-             this.bounds.e = node._data.view.width / 2;
 
-             this.bounds.w = 0 - this.bounds.e;
 
-             //logger.debug(this.bounds.w);
 
-             this.bounds.n = 0;
 
-             this.bounds.s = Math.max(layout_data.outer_height_left, layout_data.outer_height_right);
 
-         },
 
-         // layout both the x and y axis
 
-         _layout_offset_subnodes: function (nodes) {
 
-             var total_height = 0;
 
-             var nodes_count = nodes.length;
 
-             var i = nodes_count;
 
-             var node = null;
 
-             var node_outer_height = 0;
 
-             var layout_data = null;
 
-             var base_y = 0;
 
-             var pd = null; // parent._data
 
-             while (i--) {
 
-                 node = nodes[i];
 
-                 layout_data = node._data.layout;
 
-                 if (pd == null) {
 
-                     pd = node.parent._data;
 
-                 }
 
-                 node_outer_height = this._layout_offset_subnodes(node.children);
 
-                 if (!node.expanded) {
 
-                     node_outer_height = 0;
 
-                     this.set_visible(node.children, false);
 
-                 }
 
-                 node_outer_height = Math.max(node._data.view.height, node_outer_height);
 
-                 layout_data.outer_height = node_outer_height;
 
-                 layout_data.offset_y = base_y - node_outer_height / 2;
 
-                 layout_data.offset_x = this.opts.hspace * layout_data.direction + pd.view.width * (pd.layout.direction + layout_data.direction) / 2;
 
-                 if (!node.parent.isroot) {
 
-                     layout_data.offset_x += this.opts.pspace * layout_data.direction;
 
-                 }
 
-                 base_y = base_y - node_outer_height - this.opts.vspace;
 
-                 total_height += node_outer_height;
 
-             }
 
-             if (nodes_count > 1) {
 
-                 total_height += this.opts.vspace * (nodes_count - 1);
 
-             }
 
-             i = nodes_count;
 
-             var middle_height = total_height / 2;
 
-             while (i--) {
 
-                 node = nodes[i];
 
-                 node._data.layout.offset_y += middle_height;
 
-             }
 
-             return total_height;
 
-         },
 
-         // layout the y axis only, for collapse/expand a node
 
-         _layout_offset_subnodes_height: function (nodes) {
 
-             var total_height = 0;
 
-             var nodes_count = nodes.length;
 
-             var i = nodes_count;
 
-             var node = null;
 
-             var node_outer_height = 0;
 
-             var layout_data = null;
 
-             var base_y = 0;
 
-             var pd = null; // parent._data
 
-             while (i--) {
 
-                 node = nodes[i];
 
-                 layout_data = node._data.layout;
 
-                 if (pd == null) {
 
-                     pd = node.parent._data;
 
-                 }
 
-                 node_outer_height = this._layout_offset_subnodes_height(node.children);
 
-                 if (!node.expanded) {
 
-                     node_outer_height = 0;
 
-                 }
 
-                 node_outer_height = Math.max(node._data.view.height, node_outer_height);
 
-                 layout_data.outer_height = node_outer_height;
 
-                 layout_data.offset_y = base_y - node_outer_height / 2;
 
-                 base_y = base_y - node_outer_height - this.opts.vspace;
 
-                 total_height += node_outer_height;
 
-             }
 
-             if (nodes_count > 1) {
 
-                 total_height += this.opts.vspace * (nodes_count - 1);
 
-             }
 
-             i = nodes_count;
 
-             var middle_height = total_height / 2;
 
-             while (i--) {
 
-                 node = nodes[i];
 
-                 node._data.layout.offset_y += middle_height;
 
-                 //logger.debug(node.topic);
 
-                 //logger.debug(node._data.layout.offset_y);
 
-             }
 
-             return total_height;
 
-         },
 
-         get_node_offset: function (node) {
 
-             var layout_data = node._data.layout;
 
-             var offset_cache = null;
 
-             if (('_offset_' in layout_data) && this.cache_valid) {
 
-                 offset_cache = layout_data._offset_;
 
-             } else {
 
-                 offset_cache = { x: -1, y: -1 };
 
-                 layout_data._offset_ = offset_cache;
 
-             }
 
-             if (offset_cache.x == -1 || offset_cache.y == -1) {
 
-                 var x = layout_data.offset_x;
 
-                 var y = layout_data.offset_y;
 
-                 if (!node.isroot) {
 
-                     var offset_p = this.get_node_offset(node.parent);
 
-                     x += offset_p.x;
 
-                     y += offset_p.y;
 
-                 }
 
-                 offset_cache.x = x;
 
-                 offset_cache.y = y;
 
-             }
 
-             return offset_cache;
 
-         },
 
-         get_node_point: function (node) {
 
-             var view_data = node._data.view;
 
-             var offset_p = this.get_node_offset(node);
 
-             //logger.debug(offset_p);
 
-             var p = {};
 
-             p.x = offset_p.x + view_data.width * (node._data.layout.direction - 1) / 2;
 
-             p.y = offset_p.y - view_data.height / 2;
 
-             //logger.debug(p);
 
-             return p;
 
-         },
 
-         get_node_point_in: function (node) {
 
-             var p = this.get_node_offset(node);
 
-             return p;
 
-         },
 
-         get_node_point_out: function (node) {
 
-             var layout_data = node._data.layout;
 
-             var pout_cache = null;
 
-             if (('_pout_' in layout_data) && this.cache_valid) {
 
-                 pout_cache = layout_data._pout_;
 
-             } else {
 
-                 pout_cache = { x: -1, y: -1 };
 
-                 layout_data._pout_ = pout_cache;
 
-             }
 
-             if (pout_cache.x == -1 || pout_cache.y == -1) {
 
-                 if (node.isroot) {
 
-                     pout_cache.x = 0;
 
-                     pout_cache.y = 0;
 
-                 } else {
 
-                     var view_data = node._data.view;
 
-                     var offset_p = this.get_node_offset(node);
 
-                     pout_cache.x = offset_p.x + (view_data.width + this.opts.pspace) * node._data.layout.direction;
 
-                     pout_cache.y = offset_p.y;
 
-                     //logger.debug('pout');
 
-                     //logger.debug(pout_cache);
 
-                 }
 
-             }
 
-             return pout_cache;
 
-         },
 
-         get_expander_point: function (node) {
 
-             var p = this.get_node_point_out(node);
 
-             var ex_p = {};
 
-             if (node._data.layout.direction == jm.direction.right) {
 
-                 ex_p.x = p.x - this.opts.pspace;
 
-             } else {
 
-                 ex_p.x = p.x;
 
-             }
 
-             ex_p.y = p.y - Math.ceil(this.opts.pspace / 2);
 
-             return ex_p;
 
-         },
 
-         get_min_size: function () {
 
-             var nodes = this.jm.mind.nodes;
 
-             var node = null;
 
-             var pout = null;
 
-             for (var nodeid in nodes) {
 
-                 node = nodes[nodeid];
 
-                 pout = this.get_node_point_out(node);
 
-                 if (pout.x > this.bounds.e) { this.bounds.e = pout.x; }
 
-                 if (pout.x < this.bounds.w) { this.bounds.w = pout.x; }
 
-             }
 
-             return {
 
-                 w: this.bounds.e - this.bounds.w,
 
-                 h: this.bounds.s - this.bounds.n
 
-             }
 
-         },
 
-         toggle_node: function (node) {
 
-             if (node.isroot) {
 
-                 return;
 
-             }
 
-             if (node.expanded) {
 
-                 this.collapse_node(node);
 
-             } else {
 
-                 this.expand_node(node);
 
-             }
 
-         },
 
-         expand_node: function (node) {
 
-             node.expanded = true;
 
-             this.part_layout(node);
 
-             this.set_visible(node.children, true);
 
-             this.jm.invoke_event_handle(jm.event_type.show, { evt: 'expand_node', data: [], node: node.id });
 
-         },
 
-         collapse_node: function (node) {
 
-             node.expanded = false;
 
-             this.part_layout(node);
 
-             this.set_visible(node.children, false);
 
-             this.jm.invoke_event_handle(jm.event_type.show, { evt: 'collapse_node', data: [], node: node.id });
 
-         },
 
-         expand_all: function () {
 
-             var nodes = this.jm.mind.nodes;
 
-             var c = 0;
 
-             var node;
 
-             for (var nodeid in nodes) {
 
-                 node = nodes[nodeid];
 
-                 if (!node.expanded) {
 
-                     node.expanded = true;
 
-                     c++;
 
-                 }
 
-             }
 
-             if (c > 0) {
 
-                 var root = this.jm.mind.root;
 
-                 this.part_layout(root);
 
-                 this.set_visible(root.children, true);
 
-             }
 
-         },
 
-         collapse_all: function () {
 
-             var nodes = this.jm.mind.nodes;
 
-             var c = 0;
 
-             var node;
 
-             for (var nodeid in nodes) {
 
-                 node = nodes[nodeid];
 
-                 if (node.expanded && !node.isroot) {
 
-                     node.expanded = false;
 
-                     c++;
 
-                 }
 
-             }
 
-             if (c > 0) {
 
-                 var root = this.jm.mind.root;
 
-                 this.part_layout(root);
 
-                 this.set_visible(root.children, true);
 
-             }
 
-         },
 
-         expand_to_depth: function (target_depth, curr_nodes, curr_depth) {
 
-             if (target_depth < 1) { return; }
 
-             var nodes = curr_nodes || this.jm.mind.root.children;
 
-             var depth = curr_depth || 1;
 
-             var i = nodes.length;
 
-             var node = null;
 
-             while (i--) {
 
-                 node = nodes[i];
 
-                 if (depth < target_depth) {
 
-                     if (!node.expanded) {
 
-                         this.expand_node(node);
 
-                     }
 
-                     this.expand_to_depth(target_depth, node.children, depth + 1);
 
-                 }
 
-                 if (depth == target_depth) {
 
-                     if (node.expanded) {
 
-                         this.collapse_node(node);
 
-                     }
 
-                 }
 
-             }
 
-         },
 
-         part_layout: function (node) {
 
-             var root = this.jm.mind.root;
 
-             if (!!root) {
 
-                 var root_layout_data = root._data.layout;
 
-                 if (node.isroot) {
 
-                     root_layout_data.outer_height_right = this._layout_offset_subnodes_height(root_layout_data.right_nodes);
 
-                     root_layout_data.outer_height_left = this._layout_offset_subnodes_height(root_layout_data.left_nodes);
 
-                 } else {
 
-                     if (node._data.layout.direction == jm.direction.right) {
 
-                         root_layout_data.outer_height_right = this._layout_offset_subnodes_height(root_layout_data.right_nodes);
 
-                     } else {
 
-                         root_layout_data.outer_height_left = this._layout_offset_subnodes_height(root_layout_data.left_nodes);
 
-                     }
 
-                 }
 
-                 this.bounds.s = Math.max(root_layout_data.outer_height_left, root_layout_data.outer_height_right);
 
-                 this.cache_valid = false;
 
-             } else {
 
-                 logger.warn('can not found root node');
 
-             }
 
-         },
 
-         set_visible: function (nodes, visible) {
 
-             var i = nodes.length;
 
-             var node = null;
 
-             var layout_data = null;
 
-             while (i--) {
 
-                 node = nodes[i];
 
-                 layout_data = node._data.layout;
 
-                 if (node.expanded) {
 
-                     this.set_visible(node.children, visible);
 
-                 } else {
 
-                     this.set_visible(node.children, false);
 
-                 }
 
-                 if (!node.isroot) {
 
-                     node._data.layout.visible = visible;
 
-                 }
 
-             }
 
-         },
 
-         is_expand: function (node) {
 
-             return node.expanded;
 
-         },
 
-         is_visible: function (node) {
 
-             var layout_data = node._data.layout;
 
-             if (('visible' in layout_data) && !layout_data.visible) {
 
-                 return false;
 
-             } else {
 
-                 return true;
 
-             }
 
-         }
 
-     };
 
-     jm.graph_canvas = function (view) {
 
-         this.opts = view.opts;
 
-         this.e_canvas = $c('canvas');
 
-         this.e_canvas.className = 'jsmind';
 
-         this.canvas_ctx = this.e_canvas.getContext('2d');
 
-         this.size = { w: 0, h: 0 };
 
-     };
 
-     jm.graph_canvas.prototype = {
 
-         element: function () {
 
-             return this.e_canvas;
 
-         },
 
-         set_size: function (w, h) {
 
-             this.size.w = w;
 
-             this.size.h = h;
 
-             this.e_canvas.width = w;
 
-             this.e_canvas.height = h;
 
-         },
 
-         clear: function () {
 
-             this.canvas_ctx.clearRect(0, 0, this.size.w, this.size.h);
 
-         },
 
-         draw_line: function (pout, pin, offset) {
 
-             var ctx = this.canvas_ctx;
 
-             ctx.strokeStyle = this.opts.line_color;
 
-             ctx.lineWidth = this.opts.line_width;
 
-             ctx.lineCap = 'round';
 
-             this._bezier_to(ctx,
 
-                 pin.x + offset.x,
 
-                 pin.y + offset.y,
 
-                 pout.x + offset.x,
 
-                 pout.y + offset.y);
 
-         },
 
-         copy_to: function (dest_canvas_ctx, callback) {
 
-             dest_canvas_ctx.drawImage(this.e_canvas, 0, 0);
 
-             !!callback && callback();
 
-         },
 
-         _bezier_to: function (ctx, x1, y1, x2, y2) {
 
-             ctx.beginPath();
 
-             ctx.moveTo(x1, y1);
 
-             ctx.bezierCurveTo(x1 + (x2 - x1) * 2 / 3, y1, x1, y2, x2, y2);
 
-             ctx.stroke();
 
-         },
 
-         _line_to: function (ctx, x1, y1, x2, y2) {
 
-             ctx.beginPath();
 
-             ctx.moveTo(x1, y1);
 
-             ctx.lineTo(x2, y2);
 
-             ctx.stroke();
 
-         }
 
-     };
 
-     jm.graph_svg = function (view) {
 
-         this.view = view;
 
-         this.opts = view.opts;
 
-         this.e_svg = jm.graph_svg.c('svg');
 
-         this.e_svg.setAttribute('class', 'jsmind');
 
-         this.size = { w: 0, h: 0 };
 
-         this.lines = [];
 
-     };
 
-     jm.graph_svg.c = function (tag) {
 
-         return $d.createElementNS('http://www.w3.org/2000/svg', tag);
 
-     };
 
-     jm.graph_svg.prototype = {
 
-         element: function () {
 
-             return this.e_svg;
 
-         },
 
-         set_size: function (w, h) {
 
-             this.size.w = w;
 
-             this.size.h = h;
 
-             this.e_svg.setAttribute('width', w);
 
-             this.e_svg.setAttribute('height', h);
 
-         },
 
-         clear: function () {
 
-             var len = this.lines.length;
 
-             while (len--) {
 
-                 this.e_svg.removeChild(this.lines[len]);
 
-             }
 
-             this.lines.length = 0;
 
-         },
 
-         draw_line: function (pout, pin, offset) {
 
-             var line = jm.graph_svg.c('path');
 
-             line.setAttribute('stroke', this.opts.line_color);
 
-             line.setAttribute('stroke-width', this.opts.line_width);
 
-             line.setAttribute('fill', 'transparent');
 
-             this.lines.push(line);
 
-             this.e_svg.appendChild(line);
 
-             this._bezier_to(line, pin.x + offset.x, pin.y + offset.y, pout.x + offset.x, pout.y + offset.y);
 
-         },
 
-         copy_to: function (dest_canvas_ctx, callback) {
 
-             var img = new Image();
 
-             img.onload = function () {
 
-                 dest_canvas_ctx.drawImage(img, 0, 0);
 
-                 !!callback && callback();
 
-             }
 
-             img.src = 'data:image/svg+xml;base64,' + btoa(new XMLSerializer().serializeToString(this.e_svg));
 
-         },
 
-         _bezier_to: function (path, x1, y1, x2, y2) {
 
-             path.setAttribute('d', 'M' + x1 + ' ' + y1 + ' C ' + (x1 + (x2 - x1) * 2 / 3) + ' ' + y1 + ', ' + x1 + ' ' + y2 + ', ' + x2 + ' ' + y2);
 
-         },
 
-         _line_to: function (path, x1, y1, x2, y2) {
 
-             path.setAttribute('d', 'M ' + x1 + ' ' + y1 + ' L ' + x2 + ' ' + y2);
 
-         }
 
-     };
 
-     // view provider
 
-     jm.view_provider = function (jm, options) {
 
-         this.opts = options;
 
-         this.jm = jm;
 
-         this.layout = jm.layout;
 
-         this.container = null;
 
-         this.e_panel = null;
 
-         this.e_nodes = null;
 
-         this.size = { w: 0, h: 0 };
 
-         this.selected_node = null;
 
-         this.editing_node = null;
 
-         this.graph = null;
 
-     };
 
-     jm.view_provider.prototype = {
 
-         init: function () {
 
-             logger.debug('view.init');
 
-             this.container = $i(this.opts.container) ? this.opts.container : $g(this.opts.container);
 
-             if (!this.container) {
 
-                 logger.error('the options.view.container was not be found in dom');
 
-                 return;
 
-             }
 
-             this.e_panel = $c('div');
 
-             this.e_nodes = $c('jmnodes');
 
-             this.e_editor = $c('input');
 
-             this.graph = this.opts.engine.toLowerCase() === 'svg' ? new jm.graph_svg(this) : new jm.graph_canvas(this);
 
-             this.e_panel.className = 'jsmind-inner';
 
-             this.e_panel.tabIndex = 1;
 
-             this.e_panel.appendChild(this.graph.element());
 
-             this.e_panel.appendChild(this.e_nodes);
 
-             this.e_editor.className = 'jsmind-editor';
 
-             this.e_editor.type = 'text';
 
-             this.actualZoom = 1;
 
-             this.zoomStep = 0.1;
 
-             this.minZoom = 0.5;
 
-             this.maxZoom = 2;
 
-             var v = this;
 
-             jm.util.dom.add_event(this.e_editor, 'keydown', function (e) {
 
-                 var evt = e || event;
 
-                 if (evt.keyCode == 13) { v.edit_node_end(); evt.stopPropagation(); }
 
-             });
 
-             jm.util.dom.add_event(this.e_editor, 'blur', function (e) {
 
-                 v.edit_node_end();
 
-             });
 
-             this.container.appendChild(this.e_panel);
 
-         },
 
-         add_event: function (obj, event_name, event_handle) {
 
-             jm.util.dom.add_event(this.e_nodes, event_name, function (e) {
 
-                 var evt = e || event;
 
-                 event_handle.call(obj, evt);
 
-             });
 
-         },
 
-         get_binded_nodeid: function (element) {
 
-             if (element == null) {
 
-                 return null;
 
-             }
 
-             var tagName = element.tagName.toLowerCase();
 
-             if (tagName == 'jmnodes' || tagName == 'body' || tagName == 'html') {
 
-                 return null;
 
-             }
 
-             if (tagName == 'jmnode' || tagName == 'jmexpander') {
 
-                 return element.getAttribute('nodeid');
 
-             } else {
 
-                 return this.get_binded_nodeid(element.parentElement);
 
-             }
 
-         },
 
-         is_expander: function (element) {
 
-             return (element.tagName.toLowerCase() == 'jmexpander');
 
-         },
 
-         reset: function () {
 
-             logger.debug('view.reset');
 
-             this.selected_node = null;
 
-             this.clear_lines();
 
-             this.clear_nodes();
 
-             this.reset_theme();
 
-         },
 
-         reset_theme: function () {
 
-             var theme_name = this.jm.options.theme;
 
-             if (!!theme_name) {
 
-                 this.e_nodes.className = 'theme-' + theme_name;
 
-             } else {
 
-                 this.e_nodes.className = '';
 
-             }
 
-         },
 
-         reset_custom_style: function () {
 
-             var nodes = this.jm.mind.nodes;
 
-             for (var nodeid in nodes) {
 
-                 this.reset_node_custom_style(nodes[nodeid]);
 
-             }
 
-         },
 
-         load: function () {
 
-             logger.debug('view.load');
 
-             this.init_nodes();
 
-         },
 
-         expand_size: function () {
 
-             var min_size = this.layout.get_min_size();
 
-             var min_width = min_size.w + this.opts.hmargin * 2;
 
-             var min_height = min_size.h + this.opts.vmargin * 2;
 
-             var client_w = this.e_panel.clientWidth;
 
-             var client_h = this.e_panel.clientHeight;
 
-             if (client_w < min_width) { client_w = min_width; }
 
-             if (client_h < min_height) { client_h = min_height; }
 
-             this.size.w = client_w;
 
-             this.size.h = client_h;
 
-         },
 
-         init_nodes_size: function (node) {
 
-             var view_data = node._data.view;
 
-             view_data.width = view_data.element.clientWidth;
 
-             view_data.height = view_data.element.clientHeight;
 
-         },
 
-         init_nodes: function () {
 
-             var nodes = this.jm.mind.nodes;
 
-             var doc_frag = $d.createDocumentFragment();
 
-             for (var nodeid in nodes) {
 
-                 this.create_node_element(nodes[nodeid], doc_frag);
 
-             }
 
-             this.e_nodes.appendChild(doc_frag);
 
-             for (var nodeid in nodes) {
 
-                 this.init_nodes_size(nodes[nodeid]);
 
-             }
 
-         },
 
-         add_node: function (node) {
 
-             this.create_node_element(node, this.e_nodes);
 
-             this.init_nodes_size(node);
 
-         },
 
-         create_node_element: function (node, parent_node) {
 
-             var view_data = null;
 
-             if ('view' in node._data) {
 
-                 view_data = node._data.view;
 
-             } else {
 
-                 view_data = {};
 
-                 node._data.view = view_data;
 
-             }
 
-             var d = $c('jmnode');
 
-             if (node.isroot) {
 
-                 d.className = 'root';
 
-             } else {
 
-                 var d_e = $c('jmexpander');
 
-                 $t(d_e, '-');
 
-                 d_e.setAttribute('nodeid', node.id);
 
-                 d_e.style.visibility = 'hidden';
 
-                 parent_node.appendChild(d_e);
 
-                 view_data.expander = d_e;
 
-             }
 
-             if (!!node.topic) {
 
-                 if (this.opts.support_html) {
 
-                     $h(d, node.topic);
 
-                 } else {
 
-                     $t(d, node.topic);
 
-                 }
 
-             }
 
-             d.setAttribute('nodeid', node.id);
 
-             d.style.visibility = 'hidden';
 
-             this._reset_node_custom_style(d, node.data);
 
-             parent_node.appendChild(d);
 
-             view_data.element = d;
 
-         },
 
-         remove_node: function (node) {
 
-             if (this.selected_node != null && this.selected_node.id == node.id) {
 
-                 this.selected_node = null;
 
-             }
 
-             if (this.editing_node != null && this.editing_node.id == node.id) {
 
-                 node._data.view.element.removeChild(this.e_editor);
 
-                 this.editing_node = null;
 
-             }
 
-             var children = node.children;
 
-             var i = children.length;
 
-             while (i--) {
 
-                 this.remove_node(children[i]);
 
-             }
 
-             if (node._data.view) {
 
-                 var element = node._data.view.element;
 
-                 var expander = node._data.view.expander;
 
-                 this.e_nodes.removeChild(element);
 
-                 this.e_nodes.removeChild(expander);
 
-                 node._data.view.element = null;
 
-                 node._data.view.expander = null;
 
-             }
 
-         },
 
-         update_node: function (node) {
 
-             var view_data = node._data.view;
 
-             var element = view_data.element;
 
-             if (!!node.topic) {
 
-                 if (this.opts.support_html) {
 
-                     $h(element, node.topic);
 
-                 } else {
 
-                     $t(element, node.topic);
 
-                 }
 
-             }
 
-             view_data.width = element.clientWidth;
 
-             view_data.height = element.clientHeight;
 
-         },
 
-         select_node: function (node) {
 
-             if (!!this.selected_node) {
 
-                 this.selected_node._data.view.element.className =
 
-                     this.selected_node._data.view.element.className.replace(/\s*selected\b/i, '');
 
-                 this.reset_node_custom_style(this.selected_node);
 
-             }
 
-             if (!!node) {
 
-                 this.selected_node = node;
 
-                 node._data.view.element.className += ' selected';
 
-                 this.clear_node_custom_style(node);
 
-             }
 
-         },
 
-         select_clear: function () {
 
-             this.select_node(null);
 
-         },
 
-         get_editing_node: function () {
 
-             return this.editing_node;
 
-         },
 
-         is_editing: function () {
 
-             return (!!this.editing_node);
 
-         },
 
-         edit_node_begin: function (node) {
 
-             if (!node.topic) {
 
-                 logger.warn("don't edit image nodes");
 
-                 return;
 
-             }
 
-             if (this.editing_node != null) {
 
-                 this.edit_node_end();
 
-             }
 
-             this.editing_node = node;
 
-             var view_data = node._data.view;
 
-             var element = view_data.element;
 
-             var topic = node.topic;
 
-             var ncs = getComputedStyle(element);
 
-             this.e_editor.value = topic;
 
-             this.e_editor.style.width = (element.clientWidth - parseInt(ncs.getPropertyValue('padding-left')) - parseInt(ncs.getPropertyValue('padding-right'))) + 'px';
 
-             element.innerHTML = '';
 
-             element.appendChild(this.e_editor);
 
-             element.style.zIndex = 5;
 
-             this.e_editor.focus();
 
-             this.e_editor.select();
 
-         },
 
-         edit_node_end: function () {
 
-             if (this.editing_node != null) {
 
-                 var node = this.editing_node;
 
-                 this.editing_node = null;
 
-                 var view_data = node._data.view;
 
-                 var element = view_data.element;
 
-                 var topic = this.e_editor.value;
 
-                 element.style.zIndex = 'auto';
 
-                 element.removeChild(this.e_editor);
 
-                 if (jm.util.text.is_empty(topic) || node.topic === topic) {
 
-                     if (this.opts.support_html) {
 
-                         $h(element, node.topic);
 
-                     } else {
 
-                         $t(element, node.topic);
 
-                     }
 
-                 } else {
 
-                     this.jm.update_node(node.id, topic);
 
-                 }
 
-             }
 
-         },
 
-         get_view_offset: function () {
 
-             var bounds = this.layout.bounds;
 
-             var _x = (this.size.w - bounds.e - bounds.w) / 2;
 
-             var _y = this.size.h / 2;
 
-             return { x: _x, y: _y };
 
-         },
 
-         resize: function () {
 
-             this.graph.set_size(1, 1);
 
-             this.e_nodes.style.width = '1px';
 
-             this.e_nodes.style.height = '1px';
 
-             this.expand_size();
 
-             this._show();
 
-         },
 
-         _show: function () {
 
-             this.graph.set_size(this.size.w, this.size.h);
 
-             this.e_nodes.style.width = this.size.w + 'px';
 
-             this.e_nodes.style.height = this.size.h + 'px';
 
-             this.show_nodes();
 
-             this.show_lines();
 
-             //this.layout.cache_valid = true;
 
-             this.jm.invoke_event_handle(jm.event_type.resize, { data: [] });
 
-         },
 
-         zoomIn: function () {
 
-             return this.setZoom(this.actualZoom + this.zoomStep);
 
-         },
 
-         zoomOut: function () {
 
-             return this.setZoom(this.actualZoom - this.zoomStep);
 
-         },
 
-         setZoom: function (zoom) {
 
-             if ((zoom < this.minZoom) || (zoom > this.maxZoom)) {
 
-                 return false;
 
-             }
 
-             this.actualZoom = zoom;
 
-             for (var i = 0; i < this.e_panel.children.length; i++) {
 
-                 this.e_panel.children[i].style.transform = 'scale(' + zoom + ')';
 
-             };
 
-             this.show(true);
 
-             return true;
 
-         },
 
-         _center_root: function () {
 
-             // center root node
 
-             var outer_w = this.e_panel.clientWidth;
 
-             var outer_h = this.e_panel.clientHeight;
 
-             if (this.size.w > outer_w) {
 
-                 var _offset = this.get_view_offset();
 
-                 this.e_panel.scrollLeft = _offset.x - outer_w / 2;
 
-             }
 
-             if (this.size.h > outer_h) {
 
-                 this.e_panel.scrollTop = (this.size.h - outer_h) / 2;
 
-             }
 
-         },
 
-         show: function (keep_center) {
 
-             logger.debug('view.show');
 
-             this.expand_size();
 
-             this._show();
 
-             if (!!keep_center) {
 
-                 this._center_root();
 
-             }
 
-         },
 
-         relayout: function () {
 
-             this.expand_size();
 
-             this._show();
 
-         },
 
-         save_location: function (node) {
 
-             var vd = node._data.view;
 
-             vd._saved_location = {
 
-                 x: parseInt(vd.element.style.left) - this.e_panel.scrollLeft,
 
-                 y: parseInt(vd.element.style.top) - this.e_panel.scrollTop,
 
-             };
 
-         },
 
-         restore_location: function (node) {
 
-             var vd = node._data.view;
 
-             this.e_panel.scrollLeft = parseInt(vd.element.style.left) - vd._saved_location.x;
 
-             this.e_panel.scrollTop = parseInt(vd.element.style.top) - vd._saved_location.y;
 
-         },
 
-         clear_nodes: function () {
 
-             var mind = this.jm.mind;
 
-             if (mind == null) {
 
-                 return;
 
-             }
 
-             var nodes = mind.nodes;
 
-             var node = null;
 
-             for (var nodeid in nodes) {
 
-                 node = nodes[nodeid];
 
-                 node._data.view.element = null;
 
-                 node._data.view.expander = null;
 
-             }
 
-             this.e_nodes.innerHTML = '';
 
-         },
 
-         show_nodes: function () {
 
-             var nodes = this.jm.mind.nodes;
 
-             var node = null;
 
-             var node_element = null;
 
-             var expander = null;
 
-             var p = null;
 
-             var p_expander = null;
 
-             var expander_text = '-';
 
-             var view_data = null;
 
-             var _offset = this.get_view_offset();
 
-             for (var nodeid in nodes) {
 
-                 node = nodes[nodeid];
 
-                 view_data = node._data.view;
 
-                 node_element = view_data.element;
 
-                 expander = view_data.expander;
 
-                 if (!this.layout.is_visible(node)) {
 
-                     node_element.style.display = 'none';
 
-                     expander.style.display = 'none';
 
-                     continue;
 
-                 }
 
-                 this.reset_node_custom_style(node);
 
-                 p = this.layout.get_node_point(node);
 
-                 view_data.abs_x = _offset.x + p.x;
 
-                 view_data.abs_y = _offset.y + p.y;
 
-                 node_element.style.left = (_offset.x + p.x) + 'px';
 
-                 node_element.style.top = (_offset.y + p.y) + 'px';
 
-                 node_element.style.display = '';
 
-                 node_element.style.visibility = 'visible';
 
-                 if (!node.isroot && node.children.length > 0) {
 
-                     expander_text = node.expanded ? '-' : '+';
 
-                     p_expander = this.layout.get_expander_point(node);
 
-                     expander.style.left = (_offset.x + p_expander.x) + 'px';
 
-                     expander.style.top = (_offset.y + p_expander.y) + 'px';
 
-                     expander.style.display = '';
 
-                     expander.style.visibility = 'visible';
 
-                     $t(expander, expander_text);
 
-                 }
 
-                 // hide expander while all children have been removed
 
-                 if (!node.isroot && node.children.length == 0) {
 
-                     expander.style.display = 'none';
 
-                     expander.style.visibility = 'hidden';
 
-                 }
 
-             }
 
-         },
 
-         reset_node_custom_style: function (node) {
 
-             this._reset_node_custom_style(node._data.view.element, node.data);
 
-         },
 
-         _reset_node_custom_style: function (node_element, node_data) {
 
-             if ('background-color' in node_data) {
 
-                 node_element.style.backgroundColor = node_data['background-color'];
 
-             }
 
-             if ('foreground-color' in node_data) {
 
-                 node_element.style.color = node_data['foreground-color'];
 
-             }
 
-             if ('width' in node_data) {
 
-                 node_element.style.width = node_data['width'] + 'px';
 
-             }
 
-             if ('height' in node_data) {
 
-                 node_element.style.height = node_data['height'] + 'px';
 
-             }
 
-             if ('font-size' in node_data) {
 
-                 node_element.style.fontSize = node_data['font-size'] + 'px';
 
-             }
 
-             if ('font-weight' in node_data) {
 
-                 node_element.style.fontWeight = node_data['font-weight'];
 
-             }
 
-             if ('font-style' in node_data) {
 
-                 node_element.style.fontStyle = node_data['font-style'];
 
-             }
 
-             if ('background-image' in node_data) {
 
-                 var backgroundImage = node_data['background-image'];
 
-                 if (backgroundImage.startsWith('data') && node_data['width'] && node_data['height']) {
 
-                     var img = new Image();
 
-                     img.onload = function () {
 
-                         var c = $c('canvas');
 
-                         c.width = node_element.clientWidth;
 
-                         c.height = node_element.clientHeight;
 
-                         var img = this;
 
-                         if (c.getContext) {
 
-                             var ctx = c.getContext('2d');
 
-                             ctx.drawImage(img, 2, 2, node_element.clientWidth, node_element.clientHeight);
 
-                             var scaledImageData = c.toDataURL();
 
-                             node_element.style.backgroundImage = 'url(' + scaledImageData + ')';
 
-                         }
 
-                     };
 
-                     img.src = backgroundImage;
 
-                 } else {
 
-                     node_element.style.backgroundImage = 'url(' + backgroundImage + ')';
 
-                 }
 
-                 node_element.style.backgroundSize = '99%';
 
-                 if ('background-rotation' in node_data) {
 
-                     node_element.style.transform = 'rotate(' + node_data['background-rotation'] + 'deg)';
 
-                 }
 
-             }
 
-         },
 
-         clear_node_custom_style: function (node) {
 
-             var node_element = node._data.view.element;
 
-             node_element.style.backgroundColor = "";
 
-             node_element.style.color = "";
 
-         },
 
-         clear_lines: function () {
 
-             this.graph.clear();
 
-         },
 
-         show_lines: function () {
 
-             this.clear_lines();
 
-             var nodes = this.jm.mind.nodes;
 
-             var node = null;
 
-             var pin = null;
 
-             var pout = null;
 
-             var _offset = this.get_view_offset();
 
-             for (var nodeid in nodes) {
 
-                 node = nodes[nodeid];
 
-                 if (!!node.isroot) { continue; }
 
-                 if (('visible' in node._data.layout) && !node._data.layout.visible) { continue; }
 
-                 pin = this.layout.get_node_point_in(node);
 
-                 pout = this.layout.get_node_point_out(node.parent);
 
-                 this.graph.draw_line(pout, pin, _offset);
 
-             }
 
-         },
 
-     };
 
-     // shortcut provider
 
-     jm.shortcut_provider = function (jm, options) {
 
-         this.jm = jm;
 
-         this.opts = options;
 
-         this.mapping = options.mapping;
 
-         this.handles = options.handles;
 
-         this._newid = null;
 
-         this._mapping = {};
 
-     };
 
-     jm.shortcut_provider.prototype = {
 
-         init: function () {
 
-             jm.util.dom.add_event(this.jm.view.e_panel, 'keydown', this.handler.bind(this));
 
-             this.handles['addchild'] = this.handle_addchild;
 
-             this.handles['addbrother'] = this.handle_addbrother;
 
-             this.handles['editnode'] = this.handle_editnode;
 
-             this.handles['delnode'] = this.handle_delnode;
 
-             this.handles['toggle'] = this.handle_toggle;
 
-             this.handles['up'] = this.handle_up;
 
-             this.handles['down'] = this.handle_down;
 
-             this.handles['left'] = this.handle_left;
 
-             this.handles['right'] = this.handle_right;
 
-             for (var handle in this.mapping) {
 
-                 if (!!this.mapping[handle] && (handle in this.handles)) {
 
-                     this._mapping[this.mapping[handle]] = this.handles[handle];
 
-                 }
 
-             }
 
-             if (typeof this.opts.id_generator === 'function') {
 
-                 this._newid = this.opts.id_generator;
 
-             } else {
 
-                 this._newid = jm.util.uuid.newid;
 
-             }
 
-         },
 
-         enable_shortcut: function () {
 
-             this.opts.enable = true;
 
-         },
 
-         disable_shortcut: function () {
 
-             this.opts.enable = false;
 
-         },
 
-         handler: function (e) {
 
-             if (e.which == 9) { e.preventDefault(); } //prevent tab to change focus in browser
 
-             if (this.jm.view.is_editing()) { return; }
 
-             var evt = e || event;
 
-             if (!this.opts.enable) { return true; }
 
-             var kc = evt.keyCode + (evt.metaKey << 13) + (evt.ctrlKey << 12) + (evt.altKey << 11) + (evt.shiftKey << 10);
 
-             if (kc in this._mapping) {
 
-                 this._mapping[kc].call(this, this.jm, e);
 
-             }
 
-         },
 
-         handle_addchild: function (_jm, e) {
 
-             var selected_node = _jm.get_selected_node();
 
-             if (!!selected_node) {
 
-                 var nodeid = this._newid();
 
-                 var node = _jm.add_node(selected_node, nodeid, 'New Node');
 
-                 if (!!node) {
 
-                     _jm.select_node(nodeid);
 
-                     _jm.begin_edit(nodeid);
 
-                 }
 
-             }
 
-         },
 
-         handle_addbrother: function (_jm, e) {
 
-             var selected_node = _jm.get_selected_node();
 
-             if (!!selected_node && !selected_node.isroot) {
 
-                 var nodeid = this._newid();
 
-                 var node = _jm.insert_node_after(selected_node, nodeid, 'New Node');
 
-                 if (!!node) {
 
-                     _jm.select_node(nodeid);
 
-                     _jm.begin_edit(nodeid);
 
-                 }
 
-             }
 
-         },
 
-         handle_editnode: function (_jm, e) {
 
-             var selected_node = _jm.get_selected_node();
 
-             if (!!selected_node) {
 
-                 _jm.begin_edit(selected_node);
 
-             }
 
-         },
 
-         handle_delnode: function (_jm, e) {
 
-             var selected_node = _jm.get_selected_node();
 
-             if (!!selected_node && !selected_node.isroot) {
 
-                 _jm.select_node(selected_node.parent);
 
-                 _jm.remove_node(selected_node);
 
-             }
 
-         },
 
-         handle_toggle: function (_jm, e) {
 
-             var evt = e || event;
 
-             var selected_node = _jm.get_selected_node();
 
-             if (!!selected_node) {
 
-                 _jm.toggle_node(selected_node.id);
 
-                 evt.stopPropagation();
 
-                 evt.preventDefault();
 
-             }
 
-         },
 
-         handle_up: function (_jm, e) {
 
-             var evt = e || event;
 
-             var selected_node = _jm.get_selected_node();
 
-             if (!!selected_node) {
 
-                 var up_node = _jm.find_node_before(selected_node);
 
-                 if (!up_node) {
 
-                     var np = _jm.find_node_before(selected_node.parent);
 
-                     if (!!np && np.children.length > 0) {
 
-                         up_node = np.children[np.children.length - 1];
 
-                     }
 
-                 }
 
-                 if (!!up_node) {
 
-                     _jm.select_node(up_node);
 
-                 }
 
-                 evt.stopPropagation();
 
-                 evt.preventDefault();
 
-             }
 
-         },
 
-         handle_down: function (_jm, e) {
 
-             var evt = e || event;
 
-             var selected_node = _jm.get_selected_node();
 
-             if (!!selected_node) {
 
-                 var down_node = _jm.find_node_after(selected_node);
 
-                 if (!down_node) {
 
-                     var np = _jm.find_node_after(selected_node.parent);
 
-                     if (!!np && np.children.length > 0) {
 
-                         down_node = np.children[0];
 
-                     }
 
-                 }
 
-                 if (!!down_node) {
 
-                     _jm.select_node(down_node);
 
-                 }
 
-                 evt.stopPropagation();
 
-                 evt.preventDefault();
 
-             }
 
-         },
 
-         handle_left: function (_jm, e) {
 
-             this._handle_direction(_jm, e, jm.direction.left);
 
-         },
 
-         handle_right: function (_jm, e) {
 
-             this._handle_direction(_jm, e, jm.direction.right);
 
-         },
 
-         _handle_direction: function (_jm, e, d) {
 
-             var evt = e || event;
 
-             var selected_node = _jm.get_selected_node();
 
-             var node = null;
 
-             if (!!selected_node) {
 
-                 if (selected_node.isroot) {
 
-                     var c = selected_node.children;
 
-                     var children = [];
 
-                     for (var i = 0; i < c.length; i++) {
 
-                         if (c[i].direction === d) {
 
-                             children.push(i);
 
-                         }
 
-                     }
 
-                     node = c[children[Math.floor((children.length - 1) / 2)]];
 
-                 }
 
-                 else if (selected_node.direction === d) {
 
-                     var children = selected_node.children;
 
-                     var childrencount = children.length;
 
-                     if (childrencount > 0) {
 
-                         node = children[Math.floor((childrencount - 1) / 2)];
 
-                     }
 
-                 } else {
 
-                     node = selected_node.parent;
 
-                 }
 
-                 if (!!node) {
 
-                     _jm.select_node(node);
 
-                 }
 
-                 evt.stopPropagation();
 
-                 evt.preventDefault();
 
-             }
 
-         },
 
-     };
 
-     // plugin
 
-     jm.plugin = function (name, init) {
 
-         this.name = name;
 
-         this.init = init;
 
-     };
 
-     jm.plugins = [];
 
-     jm.register_plugin = function (plugin) {
 
-         if (plugin instanceof jm.plugin) {
 
-             jm.plugins.push(plugin);
 
-         }
 
-     };
 
-     jm.init_plugins = function (sender) {
 
-         $w.setTimeout(function () {
 
-             jm._init_plugins(sender);
 
-         }, 0);
 
-     };
 
-     jm._init_plugins = function (sender) {
 
-         var l = jm.plugins.length;
 
-         var fn_init = null;
 
-         for (var i = 0; i < l; i++) {
 
-             fn_init = jm.plugins[i].init;
 
-             if (typeof fn_init === 'function') {
 
-                 fn_init(sender);
 
-             }
 
-         }
 
-     };
 
-     // quick way
 
-     jm.show = function (options, mind) {
 
-         var _jm = new jm(options);
 
-         _jm.show(mind);
 
-         return _jm;
 
-     };
 
-     // export jsmind
 
-     if (typeof module !== 'undefined' && typeof exports === 'object') {
 
-         module.exports = jm;
 
-     } else if (typeof define === 'function' && (define.amd || define.cmd)) {
 
-         define(function () { return jm; });
 
-     } else {
 
-         $w[__name__] = jm;
 
-     }
 
- })(typeof window !== 'undefined' ? window : global);
 
 
  |