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 };