You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2930 lines
101 KiB
2930 lines
101 KiB
/* |
|
* Released under BSD License |
|
* Copyright (c) 2014-2016 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){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:{ |
|
hmargin:100, |
|
vmargin:50, |
|
line_width:2, |
|
line_color:'#3598DB' |
|
}, |
|
layout:{ |
|
hspace:50, |
|
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.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_before); |
|
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 == jsMind.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":"Welcome to EAF MindMap"} |
|
}, |
|
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('children' in node_json){ |
|
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":"Welcome to EAF MindMap", "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); |
|
} |
|
} |
|
}, |
|
|
|
canvas:{ |
|
bezierto: 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(); |
|
}, |
|
lineto : function(ctx,x1,y1,x2,y2){ |
|
ctx.beginPath(); |
|
ctx.moveTo(x1,y1); |
|
ctx.lineTo(x2,y2); |
|
ctx.stroke(); |
|
}, |
|
clear:function(ctx,x,y,w,h){ |
|
ctx.clearRect(x,y,w,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, |
|
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){ |
|
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); |
|
}, |
|
|
|
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); |
|
} |
|
}, |
|
|
|
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); |
|
//logger.debug(pout.x); |
|
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); |
|
}, |
|
|
|
collapse_node:function(node){ |
|
node.expanded = false; |
|
this.part_layout(node); |
|
this.set_visible(node.children,false); |
|
}, |
|
|
|
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; |
|
} |
|
}, |
|
}; |
|
|
|
// 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.e_canvas = null; |
|
|
|
this.canvas_ctx = null; |
|
this.size = {w:0,h:0}; |
|
|
|
this.selected_node = null; |
|
this.editing_node = 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_canvas = $c('canvas'); |
|
this.e_nodes = $c('jmnodes'); |
|
this.e_editor = $c('input'); |
|
|
|
this.e_panel.className = 'jsmind-inner'; |
|
this.e_panel.appendChild(this.e_canvas); |
|
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); |
|
|
|
this.init_canvas(); |
|
}, |
|
|
|
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_canvas:function(){ |
|
var ctx = this.e_canvas.getContext('2d'); |
|
this.canvas_ctx = ctx; |
|
}, |
|
|
|
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.e_canvas.width = 1; |
|
this.e_canvas.height = 1; |
|
this.e_nodes.style.width = '1px'; |
|
this.e_nodes.style.height = '1px'; |
|
|
|
this.expand_size(); |
|
this._show(); |
|
}, |
|
|
|
_show:function(){ |
|
this.e_canvas.width = this.size.w; |
|
this.e_canvas.height = 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:[]}); |
|
}, |
|
|
|
zoom_in: function() { |
|
return this.set_zoom(this.actualZoom + this.zoomStep); |
|
}, |
|
|
|
zoom_out: function() { |
|
return this.set_zoom(this.actualZoom - this.zoomStep); |
|
}, |
|
|
|
set_zoom: 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(canvas_ctx){ |
|
var ctx = canvas_ctx || this.canvas_ctx; |
|
jm.util.canvas.clear(ctx,0,0,this.size.w,this.size.h); |
|
}, |
|
|
|
show_lines:function(canvas_ctx, fill_background){ |
|
this.clear_lines(canvas_ctx); |
|
|
|
if (fill_background) { |
|
// Draw background, use to save screenshot. |
|
var ctx = canvas_ctx || this.canvas_ctx; |
|
ctx.fillStyle = window.getComputedStyle(document.getElementById("jsmind_container"), null).backgroundColor.toString(); |
|
ctx.fillRect(0, 0, this.size.w, this.size.h); |
|
} |
|
|
|
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.draw_line(pout,pin,_offset,canvas_ctx); |
|
} |
|
}, |
|
|
|
draw_line:function(pin,pout,offset,canvas_ctx){ |
|
var ctx = canvas_ctx || this.canvas_ctx; |
|
ctx.strokeStyle = this.opts.line_color; |
|
ctx.lineWidth = this.opts.line_width; |
|
ctx.lineCap = 'round'; |
|
|
|
jm.util.canvas.bezierto( |
|
ctx, |
|
pin.x + offset.x, |
|
pin.y + offset.y, |
|
pout.x + offset.x, |
|
pout.y + offset.y); |
|
}, |
|
}; |
|
|
|
// shortcut provider |
|
jm.shortcut_provider= function(jm, options){ |
|
this.jm = jm; |
|
this.opts = options; |
|
this.mapping = options.mapping; |
|
this.handles = options.handles; |
|
this._mapping = {}; |
|
}; |
|
|
|
jm.shortcut_provider.prototype = { |
|
init : function(){ |
|
jm.util.dom.add_event($d,'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]; |
|
} |
|
} |
|
}, |
|
|
|
enable_shortcut : function(){ |
|
this.opts.enable = true; |
|
}, |
|
|
|
disable_shortcut : function(){ |
|
this.opts.enable = false; |
|
}, |
|
|
|
handler : function(e){ |
|
if(this.jm.view.is_editing()){return;} |
|
var evt = e || event; |
|
if(!this.opts.enable){return true;} |
|
var kc = evt.keyCode; |
|
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 = jm.util.uuid.newid(); |
|
var node = _jm.add_node(selected_node, nodeid, 'Topic'); |
|
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 = jm.util.uuid.newid(); |
|
var node = _jm.insert_node_after(selected_node, nodeid, 'Topic'); |
|
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);
|
|
|