1 /** 2 * Creates a new node. 3 * 4 * @constructor 5 */ 6 mindmaps.Node = function() { 7 this.id = mindmaps.Util.getId(); 8 this.parent = null; 9 this.children = new mindmaps.NodeMap(); 10 this.text = { 11 caption : "New Idea", 12 font : { 13 style : "normal", 14 weight : "normal", 15 decoration : "none", 16 /** unit: pixel */ 17 size : 15, 18 color : "#000000" 19 } 20 }; 21 this.offset = new mindmaps.Point(); 22 this.foldChildren = false; 23 this.branchColor = "#000000"; 24 }; 25 26 /** 27 * Creates a deep copy of this node, where all nodes have a new IDs. 28 * 29 * @returns {mindmaps.Node} the cloned node 30 */ 31 mindmaps.Node.prototype.clone = function() { 32 var clone = new mindmaps.Node(); 33 var text = { 34 caption : this.text.caption 35 }; 36 var font = { 37 weight : this.text.font.weight, 38 style : this.text.font.style, 39 decoration : this.text.font.decoration, 40 size : this.text.font.size, 41 color : this.text.font.color 42 }; 43 text.font = font; 44 clone.text = text; 45 clone.offset = this.offset.clone(); 46 clone.foldChildren = this.foldChildren; 47 clone.branchColor = this.branchColor; 48 49 this.forEachChild(function(child) { 50 var childClone = child.clone(); 51 clone.addChild(childClone); 52 }); 53 54 return clone; 55 }; 56 57 /** 58 * Creates a new node object from JSON String. 59 * 60 * @param {String} json 61 * @returns {mindmaps.Node} 62 */ 63 mindmaps.Node.fromJSON = function(json) { 64 return mindmaps.Node.fromObject(JSON.parse(json)); 65 }; 66 67 /** 68 * Creates a new node object from a generic object. 69 * 70 * @param {Object} obj 71 * @returns {mindmaps.Node} 72 */ 73 mindmaps.Node.fromObject = function(obj) { 74 var node = new mindmaps.Node(); 75 node.id = obj.id; 76 node.text = obj.text; 77 node.offset = mindmaps.Point.fromObject(obj.offset); 78 node.foldChildren = obj.foldChildren; 79 node.branchColor = obj.branchColor; 80 81 // extract all children from array of objects 82 obj.children.forEach(function(child) { 83 var childNode = mindmaps.Node.fromObject(child); 84 node.addChild(childNode); 85 }); 86 87 return node; 88 }; 89 90 /** 91 * Returns a presentation of this node and its children ready for serialization. 92 * Called by JSON.stringify(). 93 * 94 * @private 95 */ 96 mindmaps.Node.prototype.toJSON = function() { 97 // TODO see if we cant improve this 98 // http://ejohn.org/blog/ecmascript-5-strict-mode-json-and-more/ 99 // copy all children into array 100 var self = this; 101 var children = (function() { 102 var result = []; 103 self.forEachChild(function(child) { 104 result.push(child.toJSON()); 105 }); 106 return result; 107 })(); 108 109 var obj = { 110 id : this.id, 111 // store parent as id since we have to avoid circular references 112 parentId : this.parent ? this.parent.id : null, 113 text : this.text, 114 offset : this.offset, 115 foldChildren : this.foldChildren, 116 branchColor : this.branchColor, 117 children : children 118 }; 119 120 return obj; 121 }; 122 123 /** 124 * Creates a JSON representation of the node. 125 * 126 * @returns {String} 127 */ 128 mindmaps.Node.prototype.serialize = function() { 129 return JSON.stringify(this); 130 }; 131 132 /** 133 * Adds a child to the node. 134 * 135 * @param {mindmaps.Node} node 136 */ 137 mindmaps.Node.prototype.addChild = function(node) { 138 node.parent = this; 139 this.children.add(node); 140 }; 141 142 /** 143 * Removes a direct child. 144 * 145 * @param {mindmaps.Node} node 146 */ 147 mindmaps.Node.prototype.removeChild = function(node) { 148 node.parent = null; 149 this.children.remove(node); 150 }; 151 152 /** 153 * Returns whether this node is a root. 154 * 155 * @returns {Boolean} 156 */ 157 mindmaps.Node.prototype.isRoot = function() { 158 return this.parent === null; 159 }; 160 161 /** 162 * Returns whether this node is a leaf. 163 * 164 * @returns {Boolean} 165 */ 166 mindmaps.Node.prototype.isLeaf = function() { 167 return this.children.size() === 0; 168 }; 169 170 /** 171 * Returns the parent node. 172 * 173 * @returns {mindmaps.Node} 174 */ 175 mindmaps.Node.prototype.getParent = function() { 176 return this.parent; 177 }; 178 179 /** 180 * Returns the root if this node is part of a tree structure, otherwise it 181 * returns itself. 182 * 183 * @returns {mindmaps.Node} The root of the tree structure. 184 */ 185 mindmaps.Node.prototype.getRoot = function() { 186 var root = this; 187 while (root.parent) { 188 root = root.parent; 189 } 190 191 return root; 192 }; 193 194 /** 195 * Gets the position of the node relative to the root. 196 * 197 * @returns {Number} 198 */ 199 mindmaps.Node.prototype.getPosition = function() { 200 var pos = this.offset.clone(); 201 var node = this.parent; 202 203 while (node) { 204 pos.add(node.offset); 205 node = node.parent; 206 } 207 return pos; 208 }; 209 210 /** 211 * Gets the depth of the node. Root has a depth of 0. 212 * 213 * @returns {Number} 214 */ 215 mindmaps.Node.prototype.getDepth = function() { 216 var node = this.parent; 217 var depth = 0; 218 219 while (node) { 220 depth++; 221 node = node.parent; 222 } 223 224 return depth; 225 }; 226 227 /** 228 * Gets the children of the node. Traverses the whole sub tree if recursive is 229 * true. 230 * 231 * @param recursive 232 * @returns {Array} 233 * @deprecated 234 */ 235 mindmaps.Node.prototype.getChildren = function(recursive) { 236 var nodes = []; 237 238 this.children.each(function(node) { 239 if (recursive) { 240 var childNodes = node.getChildren(true); 241 childNodes.forEach(function(child) { 242 nodes.push(child); 243 }); 244 } 245 nodes.push(node); 246 }); 247 248 return nodes; 249 }; 250 251 /** 252 * Iterator. Traverses all child nodes. 253 * 254 * @param {Function} func 255 */ 256 mindmaps.Node.prototype.forEachChild = function(func) { 257 this.children.each(func); 258 }; 259 260 /** 261 * Iterator. Traverses all child nodes recursively. 262 * 263 * @param {Function} func 264 */ 265 mindmaps.Node.prototype.forEachDescendant = function(func) { 266 this.children.each(function(node) { 267 func(node); 268 node.forEachDescendant(func); 269 }); 270 }; 271 272 /** 273 * Sets the caption for the node 274 * 275 * @param {String} caption 276 */ 277 mindmaps.Node.prototype.setCaption = function(caption) { 278 this.text.caption = caption; 279 }; 280 281 /** 282 * Gets the caption for the node. 283 * 284 * @returns {String} 285 */ 286 mindmaps.Node.prototype.getCaption = function() { 287 return this.text.caption; 288 }; 289 290 /** 291 * Tests (depth-first) whether the other node is a descendant of this node. 292 * 293 * @param {mindmaps.Node} other 294 * @returns {Boolean} true if descendant, false otherwise. 295 */ 296 mindmaps.Node.prototype.isDescendant = function(other) { 297 function test(node) { 298 var children = node.children.values(); 299 for ( var i = 0, len = children.length; i < len; i++) { 300 var child = children[i]; 301 if (test(child)) { 302 return true; 303 } 304 305 if (child === other) { 306 return true; 307 } 308 } 309 return false; 310 } 311 312 return test(this); 313 };