summaryrefslogblamecommitdiff
path: root/public/js/mv/connect.js
blob: 0fee5e70b48864d44561a9913e5888054ca99868 (plain) (tree)
1
             

















                                                                                  
                                                           










































                                                                                                                   




                                                






                                                                                                           

                                  























































































                                                                                                       
"use strict";
var mv = function(mv) {
  mv.socket = {
    connect: connect
  };
  /* If we're updating due to something we received, then don't broadcast back. */
  var netrendering = false;
  /* Our ID */
  var id = 0;
  /* List of all users */
  var users = {};
  /* The user status box */
  var usersStatus = d3.select("#users-status");
  /* io.socket's socket */
  var socket;
  function connect() {
    socket = io.connect('http://localhost:3000');
    socket.on("connect", function() { console.log("CONNECT", arguments); });
    socket.on("disconnect", function() { console.log("DISCONNECT", arguments); });
    d3.select("#connect-status").style("display", "block");
    socket.emit('login');
    /*
     * Protocol:
     *  selflogin -> id (I)
     *  login -> id (I), nick (S)
     *  nickset -> id (I), nick (S)
     *  users -> { id -> {nick (S), filters ({dim (S) -> filter (*)}) } }
     *  logout -> id (I)
     */
    socket.on('selflogin', function(d) {
      /* Acknowledged that we logged in */
      /* Take note of our ID. */
      id = d.id;
    });
    socket.on('login', function(d) {
      /* Someone else logging in */
      users[d.id] = { nick: d.nick, filters: {} };
      updateUsersStatus();
    });
    socket.on('users', function(d) {
      /* We've got a list of all users. */
      /* The server is always right. */
      users = d.users;
      updateUsersStatus();
    });
    socket.on('nickset', function(d) {
      /* Someone, possibly us, changed their nick. */
      users[d.id].nick = d.nick;
      updateUsersStatus();
    });
    socket.on('filterset', function(d) {
      /* Someone changed their filter */
      users[d.id].filters = d.filters;
      /* Use the variable netrendering to denote that we're rendering due to a change received from the network. */
      netrendering = true;
      setOwnFilters(d.filters);
      netrendering = false;
    });
    socket.on('logout', function(d) {
      /* Someone disconnected, take them off the list. */
      delete users[d.id];
      updateUsersStatus();
    });
    /*
     * Remember the old renderlet
     * FIXME: Find a more elegant way to do this
     */
    var f = dc.renderlet();
    dc.renderlet(function() {
      /* Hook a listener into dc's rendering routine. If it rerenders, broadcast the change. */
      if (netrendering) {
        /* If we rendered due a change we received, don't broadcast it again. That would be A Bad Thing. */
        return;
      }
      socket.emit("filter", { filters: mv.charter.filters() });
      /* Call the old renderlet */
      f();
    });
  }
  function setOwnFilters(filters) {
    /* See if there's any difference - if there isn't, don't update. */
    var change = false;
    var key;
    /* Check for keys in the filters to apply which are not in our charts. */
    for (key in filters) {
      if (!(key in mv.charts))
        continue;
      var filter = mv.charts[key].filter();
      if (typeof(filter) == "array") {
        /* Crossfilter uses arrays to filter ranges. Exactly the first two elements are significant. */
        if (filter[0] == filters[key][0] &&
            filter[1] == filters[key][1]) {
          continue;
        }
      } else if (filter == filters[key]) {
        continue;
      }
      /* This filter differs. Apply it. */
      change = true;
      mv.charts[key].filter(filters[key]);
    }
    /* Check for keys in our charts which are not in the filters to apply. */
    for (key in mv.charts) {
      if (mv.charts[key].filter() != null) {
        if (key in filters) {
          /* This has already been handled above */
          continue;
        }
        /* There is no longer a filter applying here, clear it. */
        change = true;
        mv.charts[key].filterAll();
      }
    }
    if (change) {
      dc.redrawAll();
    }
  }
  function updateUsersStatus() {
    /* Convert the user list to a form suitable for a d3 selection */
    var data = [];
    for (var uid in users) { users[uid].id = uid; data.push(users[uid]); }
    /* Data join */
    var userlist = usersStatus.selectAll(".user")
      .data(data, function(d) { return d.id; });
    /* Enter */
    userlist
      .enter().append("li").attr("class", "user")
    ;
    /* Update */
    userlist
      .each(function(d,i) {
        var elm = d3.select(this);
        console.log("Userlist para appending", d,i);
        var name = elm.select(".name");
        var nick = d.nick == null ? "Anonymous User " + d.id : d.nick;
        if (d.id == id) {
          /* This is us! We can edit our name. */
          if (name.empty()) {
            console.log("Found our entry. id:", id, "datum", d);
            name = elm.append("input").attr("class", "name")
              .attr("type", "text")
              .attr("placeholder", "Enter name here")
              .on("change", function () {
                /* A d3 select must be dereferenced twice to access the DOM entity */
                socket.emit("nick", { nick: name[0][0].value });
              })
            ;
          }
          name.attr("value", nick);
        } else {
          /* This is someone else. We can't edit their name. */
          if (name.empty()) {
            name = elm.append("p").attr("class", "name");
          }
          name.text(nick);
        }
      })
    ;
    /* Remove */
    userlist
      .exit().remove()
    ;
  }
  return mv;
}(mv || {});