Source: transform/highlightHostTransformation.js

/**
 * Constructs a HighlightHostTransformation that highlights the specified host
 * 
 * @classdesc
 * 
 * <p>
 * HighlightHostTransformation "highlights" a set of hosts by removing all edges
 * not incident on the set of highlighted nodes. The highlighted hosts are drawn
 * with a border to distinguish them from unhighlighted ones.
 * </p>
 * 
 * <p>
 * In the case that the set of hosts to highlight is empty, this transformation
 * does nothing. In the case that a specified host does not exist, it is
 * ignored.
 * </p>
 * 
 * @constructor
 * @extends Transformation
 * @param {String} host The host to highlight
 */
function HighlightHostTransformation() {

    /** @private */
    this.hosts = {};

    /** @private */
    this.hiddenHosts = {};

    /** @private */
    this.hideHostTransformations = [];
}

// HighlightMotifTransformation extends Transformation
HighlightHostTransformation.prototype = Object.create(Transformation.prototype);
HighlightHostTransformation.prototype.constructor = HighlightHostTransformation;

HighlightHostTransformation.prototype.isHighlighted = function(host) {
    return !!this.hosts[host];
};

HighlightHostTransformation.prototype.isHidden = function(host) {
    return !!this.hiddenHosts[host];
};

/**
 * Gets the highlighted host(s)
 * 
 * @returns {Array<String>} A list of highlighted hosts
 */
HighlightHostTransformation.prototype.getHosts = function() {
    return Object.keys(this.hosts);
};

/**
 * Adds a host to the set of hosts to highlight.
 * 
 * @param {String} host
 */
HighlightHostTransformation.prototype.addHost = function(host) {
    this.hosts[host] = true;
};

/**
 * Removes a host from the set of hosts to highlight. In the case that the
 * provided host isn't in the set of hosts to highlight, this method does
 * nothing.
 * 
 * @param {String} host
 */
HighlightHostTransformation.prototype.removeHost = function(host) {
    delete this.hosts[host];
};

/**
 * Toggles a host to and from the set of hosts to highlight. In other words, if
 * a host is currently in the set of hosts to highlight, it is removed and if it
 * isn't in that set, it is added to that set.
 * 
 * @param {String} host
 */
HighlightHostTransformation.prototype.toggleHost = function(host) {
    if (!this.hosts[host]) {
        this.hosts[host] = true;
    }
    else {
        delete this.hosts[host];
    }
};

/**
 * Removes all hosts that are to be highlighted. No hosts will be highlighted by
 * this transformation after running this method.
 */
HighlightHostTransformation.prototype.clearHosts = function() {
    this.hosts = {};
};

/**
 * <p>
 * Gets the hosts that are hidden by the transformation.
 * </p>
 * 
 * <p>
 * When hosts are highlighted, irrelevant hosts will be hidden. This method
 * returns those implicitly hidden hosts (not the hosts that are specified to be
 * hidden). Since the hosts to be hidden are only calculated when the transform
 * method is invoked, this method will return the implicity hidden hosts from
 * the last call to transform. If transform has yet to be called, this method
 * returns an empty array.
 * </p>
 * 
 * @returns {Array<String>} The array of hosts.
 */
HighlightHostTransformation.prototype.getHiddenHosts = function() {
    return Object.keys(this.hiddenHosts);
};

/**
 * Overrides {@link Transformation#transform}
 */
HighlightHostTransformation.prototype.transform = function(model) {
    var graph = model.getGraph();
    this.hiddenHosts = {};
    this.hideHostTransformations = [];

    var numHosts = 0;
    for (var key in this.hosts) {
        numHosts++;
        var head = graph.getHead(key);
        if (head != null) {
            var vn = model.getVisualNodeByNode(head);
            vn.setHighlight(true);
        }
    }

    if (numHosts == 0) {
        return;
    }

    var hosts = graph.getHosts();
    for (var i = 0; i < hosts.length; i++) {
        var host = hosts[i];
        if (this.hosts[host]) {
            continue;
        }

        var communicated = {};
        var numCommunicated = 0;

        var curr = graph.getHead(host).getNext();
        while (!curr.isTail()) {
            var families = curr.getFamily();
            var keep = false;

            for (var j = 0; j < families.length; j++) {
                var family = families[j];
                keep |= this.hosts[family.getHost()];

                if (this.hosts[family.getHost()] && !communicated[family.getHost()]) {
                    communicated[family.getHost()] = true;
                    numCommunicated++;
                }
            }

            if (!keep) {
                model.addHiddenEdgeToFamily(curr);
                curr = curr.getPrev();
                curr.getNext().remove();
            }
            curr = curr.getNext();
        }

        if (numCommunicated != numHosts) {
            var hideHostTransformation = new HideHostTransformation(host);
            hideHostTransformation.transform(model);
            this.hideHostTransformations.push(hideHostTransformation);
            this.hiddenHosts[host] = true;
        }

    }

    model.update();
};