Alice

 view release on metacpan or  search on metacpan

share/static/alice.js  view on Meta::CPAN


});
Alice.Connection = {
  gotoLogin: function() {
    window.location = "/login";
  },

  connect: function(cb) {
    if (this.reconnect_count > 3) {
      this.aborting = true;
      this.changeStatus("ok");

      if (this.type == "websocket") {
        this.application.activeWindow().showAlert("WebSocket connection failed, falling back...");
        this.application.connection = new Alice.Connection.XHR(this.application);
        this.application.connection.connect();
        return;
      }

      this.application.activeWindow().showAlert("Alice server is not responding (<a href='javascript:alice.connection.reconnect()'>reconnect</a>)");
      return;
    }
    this.pings = [];
    this.closeConnection();
    this.len = 0;
    this.reconnect_count++;

    this.changeStatus("loading");
    this._connect(cb);
  },

  changeStatus: function(classname) {
    $('connection_status').className = classname;
  },

  reconnect: function () {
    this.reconnecting = true;
    this.reconnect_count = 0;
    this.connect();
  },

  handleException: function(request, exception) {
    this.application.log("encountered an error with stream.");
    this.application.log(exception);
    this.connected = false;
    if (!this.aborting)
      setTimeout(this.connect.bind(this), 2000);
    else
      this.changeStatus("ok");
  },

  handleComplete: function(transport) {
    this.application.log("connection was closed cleanly.");
    this.connected = false;
    if (!this.aborting)
      setTimeout(this.connect.bind(this), 2000);
    else
      this.changeStatus("ok");
  },

  processQueue: function(data) {
    try {
      var queue = data.queue;
      var length = queue.length;
      for (var i=0; i<length; i++) {
        if (queue[i].type == "identify") {
          this.id = queue[i].id;
        }
        else if (queue[i].type == "action")
          this.application.handleAction(queue[i]);
        else if (queue[i].type == "message") {
          if (queue[i].timestamp)
            queue[i].timestamp = Alice.epochToLocal(queue[i].timestamp, this.application.options.timeformat);
          this.application.displayMessage(queue[i]);
        }
        else if (queue[i].type == "chunk") {
          this.application.displayChunk(queue[i]);
        }
      }
    }
    catch (e) {
      this.application.log(e.toString());
    }
  },

  get: function(path, callback) {
    new Ajax.Request(path, {
      method: 'get',
      on401: this.gotoLogin,
      onSuccess: callback
    });
  },

  sendTabOrder: function (windows) {
    new Ajax.Request('/tabs', {
      method: 'post',
      on401: this.gotoLogin,
      parameters: {tabs: windows}
    });
  },

  requestChunk: function (win, limit, max) {
    if (!this.connected) return;
    this.sendMessage({
      source: win,
      msg: "/chunk " + limit + " " + max,
    });
  }
};
Alice.Connection.WebSocket = Class.create(Alice.Connection, {
  initialize: function(application) {
    this.type = "websocket";
    this.application = application;
    this.connected = false;
    this.aborting = false;
    this.request = null;
    this.reconnect_count = 0;
    this.reconnecting = false;
  },

  _connect: function(cb) {
    var now = new Date();
    this.application.log("opening new websocket stream");
    this.changeStatus("ok");
    var parameters = Object.toQueryString({
      t: now.getTime() / 1000,
      tab: this.application.activeWindow().id
    });
    var protocol = (window.location.protocol.match(/^https/) ? "wss://" : "ws://");
    var url = protocol + window.location.host + "/wsstream?" + parameters;
    this.request = new WebSocket(url);
    this.request.onopen = function(){
      this.connected = true;
      setTimeout(cb, 100);
    }.bind(this);
    this.request.onmessage = this.handleUpdate.bind(this);
    this.request.onerror = this.handleException.bind(this);
    this.request.onclose = this.handleComplete.bind(this);
  },

  handleUpdate: function(e) {
    var data = e.data.evalJSON();
    this.processQueue(data);
  },

  sendMessage: function(form) {
    if (!this.connected) return false;

    var params = form;
    if (form.nodeName && form.nodeName == "FORM") {
      params = Form.serialize(form, true);
    }

    params['stream'] = this.id;
    this.request.send(Object.toJSON(params));
    return true;
  },

  closeConnection: function() {
    this.aborting = true;
    if (this.request) this.request.close();
    this.aborting = false;
  },

  closeWindow: function(win) {
    this.sendMessage({source: win.id, msg: "/close"});
  },

  handleException: function(exception) {
    this.application.log("encountered an error with stream.");
    this.application.log(exception);
    this.connected = false;
    if (!this.aborting)
      setTimeout(this.connect.bind(this), 2000);
    else
      this.changeStatus("ok");
  },

  requestWindow: function(title, windowId, message) {
    this.sendMessage({source: windowId, msg: "/create " + title});
    if (message) {
      setTimeout(function() {
        this.application.displayMessage(message)
      }.bind(this), 1000);
    }
  }

});
Alice.Connection.XHR = Class.create(Alice.Connection, {
  initialize: function(application) {
    this.type = "xhr";
    this.pings = [];
    this.pingLimit = 10;
    this.seperator = "--xalicex\n";
    this.len = 0;

    this.application = application;
    this.connected = false;
    this.aborting = false;
    this.request = null;
    this.reconnect_count = 0;
    this.reconnecting = false;
  },

  _connect: function(cb) {
    setTimeout(function () {
    var now = new Date();
    this.application.log("opening new xhr stream");
    this.changeStatus("ok");
    this.request = new Ajax.Request('/stream', {
      method: 'get',
      parameters: {
        t: now.getTime() / 1000,
        tab: this.application.activeWindow().id
      },
      on401: this.gotoLogin,
      on500: this.gotoLogin,
      on502: this.gotoLogin,
      on503: this.gotoLogin,
      onException: this.handleException.bind(this),
      onInteractive: function(transport) {
        if (!this.connected) {
          this.connected = true;
          setTimeout(cb, 0);
        }
        this.handleUpdate(transport);
      }.bind(this),
      onComplete: this.handleComplete.bind(this)
    });
    }.bind(this), this.application.loadDelay);
  },

  handleUpdate: function(transport) {
    if (this.reconnecting) {
      this.application.activeWindow().showHappyAlert("Reconnected to the Alice server");
      this.reconnecting = false;
    }

    this.reconnect_count = 0;

    var time = new Date();
    var data = transport.responseText.slice(this.len);
    var start, end;
    start = data.indexOf(this.seperator);

    if (start > -1) {
      start += this.seperator.length;
      end = data.indexOf(this.seperator, start);
      if (end == -1) return;
    }
    else return;

    this.len += (end + this.seperator.length) - start;
    data = data.slice(start, end);
    var data = data.evalJSON();

    this.processQueue(data);

    if (data.time) {
      var lag = this.addPing(time / 1000 -  data.time);

      if (lag > 5) {
        this.application.log("lag is over 5s, reconnecting.");
        this.connect();
      }
    }
  },

  addPing: function(ping) {
    this.pings.push(ping);
    if (this.pings.length > this.pingLimit)
      this.pings.shift();

    var lag = this.lag();
    return lag;
  },

  lag: function() {
    if (!this.pings.length) return 0;
    return this.pings.inject(0, function (acc, n) {return acc + n}) / this.pings.length;
  },

  sendMessage: function(form) {
    if (!this.connected) return false;

    var params;
    if (form.nodeName && form.nodeName == "FORM") {
      params = Form.serialize(form);
    }
    else {
      params = form;
    }

    params['stream'] = this.id;

    new Ajax.Request('/say', {
      method: 'post',
      parameters: params,
      on401: this.gotoLogin,
      onException: function (request, exception) {
        alert("There was an error sending a message.");
      }
    });

    return true;
  },

  closeConnection: function() {
    this.aborting = true;
    if (this.request && this.request.transport)
      this.request.transport.abort();
    this.aborting = false;
  },

  closeWindow: function(win) {
    new Ajax.Request('/say', {
      method: 'post',



( run in 0.513 second using v1.01-cache-2.11-cpan-99c4e6809bf )