Source: js/update.js

'use strict';

/**
 * Create a transition if necessary.
 * @private
 * @param   {D3Selection}    selection
 * @param   {string}         type       duration = options.transition[type]
 * @param   {string}         name
 * @returns {ge.GraphEditor}
 * @see [d3.transition]{@link https://github.com/d3/d3-transition/blob/master/README.md#api-reference}
 */
ge.GraphEditor.prototype.transition = function transition(selection, type, name) {
	var duration = this.options.transition[type];
	if(!duration) {
		return selection;
	}
	/*if(this.state.simulation) {
		return selection;
	}*/
	return selection
		.transition(name)
		.duration(duration);
};


/*ge.GraphEditor.prototype.getNodeSize = function getNodeSize(node) {
	if(node.title === node.prevTitle) {
		return false;
	}

	node.prevTitle = node.title;

	this.tmpText.text(node.title);
	var bbox = this.tmpText.node().getBBox();
	var width;

	var words = node.title.split(/\s+/);

	if(words.length <= 1) {
		width = bbox.width;
	}
	else {
		width = Math.sqrt(bbox.width * bbox.height);

		var line = '';
		var lineWidth, prevLineWidth = 0, maxLineWidth = 0;
		var lines = 0;
		var i = 0;

		while(i < words.length) {
			if(line === '') {
				line = words[i];
				this.tmpText.text(line);
				prevLineWidth = this.tmpText.node().getComputedTextLength();
				++i;
			}
			else {
				line = line.concat(' ', words[i]);
				this.tmpText.text(line);
				lineWidth = this.tmpText.node().getComputedTextLength();
				if(lineWidth > width) {
					maxLineWidth = Math.max(maxLineWidth, prevLineWidth);
					++lines;
					line = '';
				}
				else {
					prevLineWidth = lineWidth;
					++i;
				}
			}
		}

		width = Math.max(
			maxLineWidth,
			prevLineWidth,
			bbox.height * lines * 1.5
		);
	}

	width = Math.max(width, this.options.node.size.min);

	node.textSize = width;
	node.textSize += 2 * (node.textSize % 2) + this.options.node.border;
	node.textOffset = Math.floor(-node.textSize / 2);
	node.size = node.textSize / 1.4142;

	return true;
};*/

/**
 * Handle graph SVG element resize.
 * @returns {ge.GraphEditor}
 */
ge.GraphEditor.prototype.resized = function resized() {
	this.updateExtent();
	this.zoom.translateTo(this.container, 0, 0);
	return this;
};

/**
 * Update force simulation.
 * @private
 * @returns {ge.GraphEditor}
 */
ge.GraphEditor.prototype.updateSimulation = function updateSimulation() {
	if(this.state.simulation) {
		this.state.simulation = this.options.simulation
			.create.call(
				this,
				this.state.simulation,
				this.data.nodes,
				this.data.links
			)
			.alpha(1);
	}
	return this;
};

/**
 * Update bounding box.
 * @private
 * @param   {Node}           [node]  Moved node.
 * @returns {ge.GraphEditor}
 */
ge.GraphEditor.prototype.updateBBox = function updateBBox(node) {
	var data = this.data.nodes;
	var padding = this.options.bbox.padding;

	if(node === undefined) {
		var left = d3.min(data, function(d) { return d.x; });
		var up = d3.min(data, function(d) { return d.y; });
		var right = d3.max(data, function(d) { return d.x + d.width; });
		var down = d3.max(data, function(d) { return d.y + d.height; });

		this.bbox[0][0] = (left || 0) - padding;
		this.bbox[1][0] = (right || 0) + padding;
		this.bbox[0][1] = (up || 0) - padding;
		this.bbox[1][1] = (down || 0) + padding;
	}
	else {
		//console.log(node.x, node.y, node.width, node.height, padding);
		this.bbox[0][0] = Math.min(this.bbox[0][0], node.x - padding);
		this.bbox[0][1] = Math.min(this.bbox[0][1], node.y - padding);
		this.bbox[1][0] = Math.max(
			this.bbox[1][0],
			node.x + node.width + padding
		);
		this.bbox[1][1] = Math.max(
			this.bbox[1][1],
			node.y + node.height + padding
		);
	}

	this.updateExtent();
	this.zoom.translateExtent(this.bbox);
	return this;
};

/**
 * Update zoom behavior extent.
 * @private
 * @returns {ge.GraphEditor}
 */
ge.GraphEditor.prototype.updateExtent = function updateExtent() {
	var bbox = this.svg.node().getBoundingClientRect();
	var extent = [
		[ 0, 0 ],
		[ bbox.width, bbox.height ]
	];
	this.zoom.extent(extent);
	return this;
};

/**
 * Update a link.
 * @param   {Reference}       link  Changed link.
 * @returns {ge.GraphEditor}
 */
ge.GraphEditor.prototype.updateLink = function updateLink(link) {
	var self = this;
	var transition = function(selection) {
		return self.transition(selection, 'drag', 'update-link');
	};
	link = self.getElement(link);
	var def = d3.select(link.datum().defSelector);
	ge.Link.updateSelection(link, def, self.options.link, transition);
	return this;
};


/**
 * Update a node. If it was moved or resized, update its links.
 * @param   {Reference}       node  Changed node.
 * @returns {ge.GraphEditor}
 */
ge.GraphEditor.prototype.updateNode = function updateNode(node) {
	var self = this;

	node = this.getElement(node);

	var d = node.datum();
	var prevTransform = node.attr('transform');
	var nextTransform = 'translate('.concat(d.x, ',', d.y, ')');
	var prevWidth = d.width;
	var prevHeight = d.height;

	var transition = function(selection) {
		return self.transition(selection, 'drag', 'update-node');
	};

	ge.Node.updateSelection(node, transition, this.textSize);

	if(nextTransform !== prevTransform
	   || d.width !== prevWidth
	   || d.height !== prevHeight) {
		node = d;

		var updateLink = function(d) {
			return d.source === node || d.target === node;
		};

		var defs = this.defs.filter(updateLink);
		var links = this.links.filter(updateLink);

		ge.Link.updateSelection(links, defs, self.options.link, transition);

		/*transition(defs)
			.attr('d', function() { return d.shape.path(d); });

		transition(links.select('path'))
			.attr('d', function(d) { return d.path; });

		var offset = opt.link.text.offset;
		var anchor = opt.link.text.anchor;

		transition(links.select('textPath'))
			.attr('startOffset', function(d) { return offset[d.flip]; })
			.attr('text-anchor', function(d) { return anchor[d.flip]; });*/

		this.updateBBox(node);
	}

	return this;
};

/*ge.GraphEditor.prototype.updateSize = function updateSize(nodes, links) {
	nodes = nodes || this.nodes;
	links = links || this.links;
	var opt = this.options;
	var sizeScale = this.state.sizeScale;

	links.style('stroke-width', function(d) {
		return d.size * sizeScale + 'px';
	});

	links.select('text')
		.style(
			'stroke-width',
			opt.textStrokeWidth * sizeScale + 'px'
		)
		.style(
			'font-size',
			opt.linkFontSize * sizeScale + 'px'
		);

	nodes.select('circle')
		.attr('r', function(d) { return d.size * sizeScale; })
		.style(
			'stroke-width',
			opt.nodeStrokeWidth * sizeScale + 'px'
		);

	nodes.select('text')
		.style(
			'font-size',
			opt.fontSize * sizeScale + 'px'
		)
		.style(
			'stroke-width',
			opt.textStrokeWidth * sizeScale + 'px'
		);

	return this;
};*/

/**
 * Update everything.
 * @param   {boolean}        [simulation=false]  True if called from the simulation tick handler.
 * @returns {ge.GraphEditor}
 */
ge.GraphEditor.prototype.update = function update(simulation) {
	var self = this;
	var opt = this.options;
	var ttype = simulation ? 'simulation' : 'drag';

	var transition = function(selection) {
		return self.transition(selection, ttype, 'update');
	};

	this.nodes = this.nodes.data(this.data.nodes, ge.id);
	this.nodes.exit().remove();
	var newNodes = this.nodes.enter()
		.append('g')
		.classed(opt.css.node, true);
	ge.Node.initSelection(newNodes);
	this.nodes = newNodes.merge(this.nodes);
	ge.Node.updateSelection(this.nodes, transition, this.textSize);
	this.nodeEvents(newNodes);

	this.defs = this.defs.data(this.data.links, ge.id);
	this.defs.exit().remove();
	var newDefs = this.defs.enter().append('path');

	this.links = this.links.data(this.data.links, ge.id);
	this.links.exit().remove();
	var newLinks = this.links.enter()
		.append('g')
		.classed(opt.css.link, true);

	ge.Link.initSelection(newLinks, newDefs, opt.link);
	this.links = this.links.merge(newLinks);
	this.defs = this.defs.merge(newDefs);
	ge.Link.updateSelection(this.links, this.defs, opt.link, transition);
	this.linkEvents(newLinks);

	//this.updateSize(newNodes, newLinks);
	this.updateBBox();

	if(!simulation) {
		this.updateSimulation();
		this.selectNode(this.state.selectedNode, true);
	}

	return this;
};