Source: Graph.js

/** A graph
 * @constructor
 */
hui.ui.Graph = function(options) {
  this.options = options;
  this.name = options.name;
  this.element = hui.get(options.element);
  this.ready = false;
  this.defered = [];

  this.impl = hui.ui.Graph.D3;

  hui.ui.extend(this);
  hui.log('Initializing implementation...');
  this.impl.init(this);
  if (options.source) {
    options.source.listen(this);
  }
};

hui.ui.Graph.prototype = {
  setData : function(data) {
    this._defer(function() {
      this.impl.setData(data);
    }.bind(this));
  },
  _defer : function(func) {
    if (this.ready) {
      func();
    } else {
      hui.log('Defering function');
      this.defered.push(func);
    }
  },
  /** @private */
  $objectsLoaded : function(data) {
    hui.log('Data loaded');
    this.setData(data);
  },
  /** @private */
  implIsReady : function() {
    hui.log('Implementation is ready!');
    this.ready = true;
    for (var i=0; i < this.defered.length; i++) {
      this.defered[i]();
    }
  },
  /** @private */
  implNodeWasClicked : function(node) {
    this.fire('clickNode',node);
  },
  /** @private */
  $sourceShouldRefresh : function() {
    return hui.dom.isVisible(this.element);
  },
  refresh : function() {
    if (this.options.source) {
      this.options.source.refresh();
    }
  },
  show : function() {
    this.element.style.display='block';
    this.refresh();
  },
  hide : function() {
    this.element.style.display='none';
  },
  /** @private */
  $visibilityChanged : function() {
    if (this.options.source && hui.dom.isVisible(this.element)) {
      // If there is a source, make sure it is initially
      this.options.source.refreshFirst();
    }
  },
  /** @private */
  $$layout : function() {
    hui.log('graph.layoutChanged');
    window.setTimeout(function(){
      this.impl.resize(this.element.parentNode.clientWidth,this.element.parentNode.clientHeight);
    }.bind(this),100);
  }
};

/** @namespace */
hui.ui.Graph.D3 = {
  init : function(parent) {
    this.parent = parent;
    var self = this;
    hui.require(hui.ui.getURL('lib/d3.v3/d3.v3.js'),function() {
      self._init();
      parent.implIsReady();
    });
  },
  resize : function(width,height) {
    if (this.vis) {
      this.vis.attr('width',width);
      this.vis.attr('height',height);
    }
    if (this.layout) {
      this.layout.size([width,height]);
      this.layout.start();
    }
  },
  _init : function() {
    hui.log('Creating visualization...');
    var w = this.parent.element.clientWidth,
      h = this.parent.element.clientHeight,
      fill = d3.scale.category20();

    this.vis = d3.select(this.parent.element)
      .append("svg:svg")
      .attr("width", w)
      .attr("height", h);

  },
  _onClickNode : function(node) {
    this.parent.implNodeWasClicked(node);
  },
  _findById : function(nodes,id) {
    for (var i = nodes.length - 1; i >= 0; i--){
      if (nodes[i].id===id) {
        return i;
      }
    }
    return null;
  },
  _convert : function(data) {
    var nodes = data.nodes;
    data.links = data.edges;
    for (var i = data.links.length - 1; i >= 0; i--){
      var link = data.links[i];
      link.source = this._findById(nodes,link.from);
      link.target = this._findById(nodes,link.to);
    }
    return data;
  },

  clear : function() {
    if (this.layout) {
      this.layout.stop();
      this.vis.remove();
      this._init();
    }
  },

  setData : function(data) {
    this.clear();
    var w = this.parent.element.clientWidth,
      h = this.parent.element.clientHeight;
    var json = this._convert(data);

    var force = this.layout = d3.layout.force()
      .charge(-200)
      .gravity(0.10)
      .distance(100)
      .nodes(json.nodes)
      .links(json.links)
      .size([w, h]);
    var link = this.vis.selectAll("line.link")
      .data(json.links)
      .enter().append("svg:line")
      .attr("class", "hui_graph_link")
      .style("stroke-width", function(d) { return d.label=='Friends' ? 3 : 1; })
      .attr("x1", function(d) { return d.source.x; })
      .attr("y1", function(d) { return d.source.y; })
      .attr("x2", function(d) { return d.target.x; })
      .attr("y2", function(d) { return d.target.y; });

    var node = this.vis.selectAll("circle.node")
      .data(json.nodes)
      .enter()
      .append("svg:g")
      .attr('class','hui_node')
      .attr("cx", function(d) { return d.x; })
      .attr("cy", function(d) { return d.y; })
      .style("fill",'none')
      .call(force.drag);
      node.on('click',this._onClickNode.bind(this));
    var self = this;
    node.each(function(individual) {
      var x = d3.select(this);
      var icon = self.buildIcon(individual.icon,x);
    });
//      node.attr("transform", "translate("+(w*Math.random())+","+(h*Math.random())+")")

      /*var circle = node
        .append('svg:circle').attr('r',10)
        .attr("class", "node")
        //.attr("cx", function(d) { return d.x; })
            //.attr("cy", function(d) { return d.y; })
          .style("fill", function(d) { return fill(d.group); })
            ;*/
    var text = node
      .append('svg:text')
      .attr('class','hui_graph_label')
      .attr("dx", "13")
      .attr("dy", "5")
    .text(function(d) { return d.label; });

    node.append("svg:title").text(function(d) { return d.name; });

      this.vis.style("opacity", 1e-6)
        .transition()
      .duration(2000)
      .style("opacity", 1);

    force.on("tick", function() {
      link.attr("x1", function(d) { return d.source.x; })
        .attr("y1", function(d) { return d.source.y; })
        .attr("x2", function(d) { return d.target.x; })
        .attr("y2", function(d) { return d.target.y; });

        node.attr("transform", function(d) { return "translate("+d.x+","+d.y+")"; });
    });
    force.start();
    hui.log('Starting...');
  },
  buildIcon : function(icon,parent) {
    var node;
    if (icon=='monochrome/person') {
      node = parent.append('svg:path').attr('class','hui_graph_icon');
      node.attr('d','M-9.315,10c0,0-0.575-2.838,1.863-3.951c1.763-0.799,2.174-0.949,2.512-1.2 c0.138-0.087,0.263-0.198,0.438-0.351c0.661-0.561,0.562-1.324,1.038-1.562c0.474-0.225,0.424,0.238,0.524,0 c0.101-0.225-0.075-1.799,0-1.551c0.062,0.252-0.863-1.636-0.901-2.611C-3.888-2.439-4.702-2.99-4.613-3.651 c0.212-1.513,1.472-2.322,1.472-2.322s-2.423-0.454-1.36-1.478c1.062-1.012,1.474-1.4,2.6-2.076c1.138-0.663,2.674-0.599,4.163,0 C3.749-8.914,4.124-8.489,4.61-7.602c0.425,0.762,0.45,1.326,0.413,1.813C4.986-5.314,5.049-4.926,5.049-4.926 s0.499,0.112,0.513,0.837c0.013,0.687-0.175,1.699-0.551,2.162C4.861-1.752,4.599-1.114,4.197-0.264 C3.812,0.574,3.3,1.898,3.3,1.898s0.012,0.725,0,0.926c-0.039,0.45,0.649,0.012,0.962,0.512c0.312,0.501,0.1,0.85,0.799,1.162 c0.688,0.312,2.639,1.562,3.588,2.151C9.762,7.337,9.262,10,9.262,10H-9.315z').attr('fill-rule','evenodd');
    } else if (icon=='monochrome/folder') {
      node = parent.append('svg:g').attr('class','hui_graph_icon');
      node.append('svg:polygon').attr('fill','#fff').attr('points','9.464,-3.309 9.464,-6.384 -0.354,-6.384 -3.433,-9.462 -9.462,-9.462 -9.462,-3.309 -11.153,-3.309 -9.329,9.462 9.331,9.462 11.153,-3.309');
      node.append('svg:polygon').attr('points','-9.999,-2.309 -8.461,8.462 8.464,8.462 10.001,-2.309');
      node.append('svg:polygon').attr('points','8.464,-5.384 -0.769,-5.384 -3.846,-8.462 -8.461,-8.462 -8.461,-5.384 -8.461,-3.846 8.464,-3.846');
      //node.append('svg:rect').attr('cx','0').attr('cy','0').attr('r','12').attr('fill','#fff');
      //node.append('svg:polygon').attr('points','-10,-2 -8.461,8 8.461,8 10,-2');
      //node.append('svg:polygon').attr('points','8,-5 -0.77,-5 -3.846,-8 -8,-8 -8,-5.384 -8,-4 8,-4');
    } else if (icon=='monochrome/image') {
      node = parent.append('svg:g').attr('class','hui_graph_icon');
      node.append('svg:rect').attr('x','-11').attr('y','-9').attr('width','22').attr('height','18').attr('fill','#fff');
      node.append('svg:path').attr('d','M8-6V6H-8V-6H8 M10-8h-20V8h20V-8L10-8z');
      node.append('svg:circle').attr('cx','-2.818').attr('cy','-2.693').attr('r','1.875');
      node.append('svg:path').attr('d','M-7,5H7v-5.033L4.271-3.625L1.585,2.18L0,0.561c0,0-2.193,0.814-2.917,2.064c-1.151-0.625-1.776,0-1.776,0L-7,5z');
    } else if (icon=='monochrome/news') {
      node = parent.append('svg:g').attr('class','hui_graph_icon');
      node.append('svg:path').attr('d','M10.523-9.273l-1.25-1.25C8.967-10.831,8.559-11,8.125-11s-0.842,0.169-1.148,0.477L5.625-9.173 l-1.352-1.351C3.968-10.83,3.56-11,3.125-11c-0.367,0-0.727,0.126-1.014,0.354L0-8.956l-2.109-1.688 C-2.396-10.873-2.757-11-3.124-11c-0.435,0-0.843,0.169-1.149,0.477l-1.352,1.351l-1.352-1.351C-7.283-10.831-7.691-11-8.125-11 s-0.842,0.169-1.148,0.477l-1.25,1.25C-10.831-8.967-11-8.559-11-8.125v16.25c0,0.434,0.169,0.841,0.477,1.148l1.25,1.25 c0.307,0.307,0.715,0.476,1.148,0.476s0.842-0.169,1.148-0.476l1.352-1.351l1.352,1.351c0.309,0.308,0.716,0.476,1.149,0.476 c0.366,0,0.726-0.125,1.012-0.354L0,8.956l2.109,1.688C2.394,10.873,2.755,11,3.125,11c0.44,0,0.852-0.172,1.157-0.485l1.343-1.342 l1.352,1.351c0.307,0.307,0.715,0.476,1.148,0.476s0.842-0.169,1.148-0.476l1.25-1.25C10.831,8.966,11,8.559,11,8.125v-16.25 C11-8.559,10.831-8.967,10.523-9.273z').attr('fill','#fff');
      node.append('svg:path').attr('d','M9.816-8.566l-1.25-1.25c-0.244-0.244-0.639-0.244-0.883,0L5.625-7.759L3.566-9.816c-0.225-0.226-0.583-0.245-0.832-0.047 L0-7.675l-2.734-2.188c-0.248-0.198-0.606-0.179-0.832,0.047l-2.059,2.058l-2.059-2.058c-0.244-0.244-0.639-0.244-0.883,0 l-1.25,1.25C-9.934-8.449-10-8.291-10-8.125v16.25c0,0.166,0.066,0.324,0.184,0.441l1.25,1.25c0.244,0.244,0.639,0.244,0.883,0 l2.059-2.058l2.059,2.058c0.226,0.225,0.584,0.244,0.832,0.047L0,7.676l2.734,2.188C2.85,9.956,2.987,10,3.125,10 c0.161,0,0.321-0.061,0.441-0.184l2.059-2.058l2.059,2.058c0.244,0.244,0.639,0.244,0.883,0l1.25-1.25 C9.934,8.449,10,8.291,10,8.125v-16.25C10-8.291,9.934-8.449,9.816-8.566z M-1.25,3.75H-7.5V-2.5h6.25V3.75z M7.5,3.75H0V2.5h7.5 V3.75z M7.5,1.25H0V0h7.5V1.25z M7.5-1.25H0V-2.5h7.5V-1.25z M7.5-3.75h-15V-5h15V-3.75z');
    } else if (icon=='monochrome/warning') {
      node = parent.append('svg:g').attr('class','hui_graph_icon');
      node.append('svg:path').attr('d','M10.672,7.15L2.321-9.432C1.913-10.386,0.984-11-0.057-11c-0.882,0-1.693,0.445-2.173,1.19 c-0.099,0.156-0.176,0.309-0.234,0.459l-8.152,16.403C-10.867,7.46-11,7.929-11,8.411C-11,9.839-9.841,11-8.416,11H8.416 C9.841,11,11,9.839,11,8.411C11,7.969,10.887,7.533,10.672,7.15z').attr('fill','#fff');
      node.append('svg:path').attr('d','M9.8,7.639L1.411-9.013C1.175-9.592,0.607-10-0.057-10c-0.558,0-1.049,0.291-1.333,0.731 c-0.062,0.1-0.116,0.206-0.156,0.316L-9.744,7.543C-9.908,7.795-10,8.091-10,8.411C-10,9.286-9.294,10-8.416,10H8.416 C9.291,10,10,9.286,10,8.411C10,8.13,9.928,7.867,9.8,7.639 M1.613-5.456L1.166,3.906H-1.15l-0.444-9.362H1.613z M0.009,8.869 h-0.02c-1.077,0-1.808-0.792-1.808-1.855c0-1.1,0.751-1.855,1.827-1.855c1.077,0,1.788,0.756,1.81,1.855 C1.818,8.077,1.107,8.869,0.009,8.869');
    } else if (icon=='monochrome/globe') {
      node = parent.append('svg:g').attr('class','hui_graph_icon');
      node.append('svg:circle').attr('cx','0').attr('cy','0').attr('r','11').attr('fill','#fff');
      node.append('svg:path').attr('d','M0.048-9.91c-5.496,0-9.955,4.456-9.955,9.954S-5.448,10,0.048,10 S10,5.542,10,0.044S5.544-9.91,0.048-9.91 M6.974-5.686C7.083-5.721,7.47-5.287,7.556-5.406c0.113-0.149-0.407-0.515-0.407-0.668 c0-0.21,0.978,0.964,1.032,1.057c0.013,0.021-0.27-0.136-0.276-0.108C7.883-4.966,7.852-4.809,7.813-4.65 C7.402-4.612,6.57-5.55,6.974-5.686 M2.82,4.227c-0.755,0.929-0.313,1.975-1.71,2.39C0.392,6.831,0.7,8.332-0.506,7.959 c0.069,0.07,0.069,0.171,0.239,0.28c-0.266,0.19-0.57,0.228-0.885,0.322c0.077,0.088,0.285,0.672,0.468,0.605 C-2.344,10-2.666,6.858-3.027,6c-0.271-0.653-1.199-0.954-1.596-1.555C-4.869,4.069-5.025,3.651-5.262,3.27 c-0.565-0.908,0.843-1.993,0.208-2.791C-5.307,0.159-5.762,0.667-6.25,0.026c-0.127-0.167-0.172-0.534-0.226-0.732 C-6.811-0.622-7.312-1.351-7.42-1.242c-0.375,0.375-0.934-0.514-0.891-0.845c0.128-0.971-0.375-1.522,0.148-2.444 c0.793-1.392,1.738-2.674,3.156-3.475c0.827-0.467,2.369-1.355,3.358-1.085C-1.83-9.02-3.658-8.137-3.457-8.079 c0.175,0.052,0.582-0.202,0.69,0.064c0.003,0.118-0.025,0.225-0.089,0.323c0.203,0.299,1.448-0.978,1.707-1.078 c0.506-0.2,0.744,0.327,1.312,0.132c0.396-0.135,0.213,0.623,0.172,0.623c0.066,0,0.53-0.203,0.503,0.042 C0.77-7.379-1.377-8.058-1.604-7.367c-0.061,0.188,0.697-0.175,0.82-0.063c-0.051,0.083-0.109,0.16-0.173,0.236 c0.059,0.117,0.523,0.265,0.573,0.158C-0.434-6.93-1.781-6.76-2.012-6.664c-0.393,0.162-1.07,0.585-1.436,0.911 c-0.217,0.197-0.126,0.482-0.376,0.659c-0.274,0.194-0.561,0.381-0.811,0.608C-4.96-4.19-4.667-3.286-5.028-3.119 c0.102-0.047-0.356-1.584-0.97-0.84C-6.243-3.661-6.733-3.787-7.11-3.42c-0.335,0.327-0.454,0.969-0.391,1.412 c0.149,1.05,0.815-0.293,1.221-0.293c0.271,0-0.46,0.972-0.247,1.128C-6.086-0.85-5.801-1.399-5.96-0.417 c-0.236,1.433,2.149,0.1,2.071-0.053c0.112,0.226-0.282,0.441-0.009,0.629c0.11,0.075,0.096-0.386,0.17-0.464 c0.24-0.251,0.339,0.118,0.551,0.202c0.269,0.11,1.516,0.09,1.595,0.495c0.206,1.011,2.215,0.385,1.633,1.951 c0.065-0.041,0.134-0.066,0.208-0.078c0.485,0.127,0.933,0.55,1.366,0.498C2.539,2.651,3.563,3.32,2.82,4.227 M7.309,0.806 c-1.176-1.099-1.178-3.391-0.12-4.486c-0.12-0.761,0.75-0.87,1.163-0.548c0.3,0.23,0.628,0.978,0.741,1.332 c0.683,2.116,0.578,4.827-0.563,6.78C8.935,3.006,8.911,2.22,9.108,1.319C9.436-0.147,7.744,1.213,7.309,0.806');
    } else if (icon=='monochrome/dot') {
      node = parent.append('svg:g').attr('class','hui_graph_icon');
      node.append('svg:circle').attr('cx','0').attr('cy','0').attr('r','11').attr('fill','#fff');
      node.append('svg:circle').attr('cx','0').attr('cy','0').attr('r','10');
    } else if (icon=='monochrome/page') {
      node = parent.append('svg:g').attr('class','hui_graph_icon');
      node.append('svg:polygon').attr('fill','#fff').attr('points','-9,11 -9,-11 3,-11 9,-5 9,11');
      node.append('svg:polygon').attr('points','3,-9.5 3,-5 7.5,-5');
      node.append('svg:polygon').attr('points','-8,-10 2,-10 2,-4 8,-4 8,10 -8,10');

      node.append('svg:rect').attr('x','-6').attr('y','-8').attr('width','6').attr('height','1').attr('fill','#fff');
      node.append('svg:rect').attr('x','-6').attr('y','-5').attr('width','6').attr('height','1').attr('fill','#fff');
      node.append('svg:rect').attr('x','-6').attr('y','-2').attr('width','12').attr('height','1').attr('fill','#fff');
      node.append('svg:rect').attr('x','-6').attr('y','7').attr('width','12').attr('height','1').attr('fill','#fff');
      node.append('svg:rect').attr('x','-6').attr('y','4').attr('width','12').attr('height','1').attr('fill','#fff');
      node.append('svg:rect').attr('x','-6').attr('y','1').attr('width','12').attr('height','1').attr('fill','#fff');
    } else if (icon=='monochrome/hierarchy') {
      node = parent.append('svg:g').attr('class','hui_graph_icon');
      node.append('svg:path').attr('fill','#fff').attr('d','M8.574,4L3.527,0.971V-11h-7V0.939L-8.575,4H-11v7h22V4H8.574z');
      node.append('svg:polygon').attr('points','8.297,5 2.526,1.537 2.526,-2.5 0.651,-2.5 0.651,-5 2.526,-5 2.526,-10 -2.474,-10 -2.474,-5 -0.599,-5 -0.599,-2.5 -2.474,-2.5 -2.474,1.505 -8.298,5 -10,5 -10,10 -5,10 -5,5 -5.868,5 -1.702,2.5 -0.599,2.5 -0.599,5 -2.474,5 -2.474,10 2.526,10 2.526,5 0.651,5 0.651,2.5 1.702,2.5 5.869,5 5,5 5,10 10,10 10,5');
    } else if (icon=='monochrome/email') {
      node = parent.append('svg:g').attr('class','hui_graph_icon');
      node.append('svg:path').attr('fill','#fff').attr('d','M10.727-1.695C10.727-7.086,6.645-11,1.02-11c-6.696,0-11.746,5.137-11.746,11.949 C-10.727,7.549-5.713,11-0.76,11c2.283,0,3.872-0.314,5.668-1.121L5.707,9.52L4.701,6.158C8.271,5.922,10.727,2.76,10.727-1.695z');
      node.append('svg:path').attr('d','M4.498,8.967C2.773,9.742,1.281,10-0.76,10c-4.771,0-8.967-3.418-8.967-9.051C-9.727-4.912-5.445-10,1.02-10 c5.086,0,8.707,3.479,8.707,8.305c0,4.225-2.355,6.869-5.459,6.869c-1.35,0-2.328-0.719-2.471-2.211H1.74 C0.82,4.398-0.445,5.174-1.996,5.174c-1.84,0-3.219-1.408-3.219-3.764c0-3.535,2.613-6.695,6.754-6.695 c1.262,0,2.699,0.314,3.391,0.688L4.066,0.748c-0.258,1.695-0.059,2.473,0.748,2.5c1.234,0.057,2.787-1.523,2.787-4.855 c0-3.766-2.414-6.639-6.867-6.639c-4.426,0-8.248,3.42-8.248,8.938c0,4.828,3.045,7.529,7.328,7.529 c1.467,0,3.045-0.318,4.193-0.893L4.498,8.967z M1.969-2.9C1.74-2.957,1.422-3.016,1.078-3.016c-1.898,0-3.391,1.867-3.391,4.08 c0,1.092,0.488,1.781,1.408,1.781c1.092,0,2.213-1.35,2.443-3.018L1.969-2.9z');
    } else {
      node = parent.append('svg:g').attr('class','hui_graph_icon');
      node.append('svg:polygon').attr('fill','#fff').attr('points','0.09,-11 -9.182,-11 -9.182,11 9.182,11 9.182,-1.909');
      node.append('svg:polygon').attr('points','0.91,-8.182 0.91,-2.727 6.365,-2.727');
      node.append('svg:polygon').attr('points','-8.182,-10 -0.91,-10 -0.91,-0.909 8.182,-0.909 8.182,10 -8.182,10');
    }

  }
};