view sxe-document.js @ 5:03ef53b969bd

Add XMPP support.
author Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
date Tue, 31 Jan 2012 01:09:41 +0100
parents
children 24aa8dccb170
line wrap: on
line source

'use strict';

var Document = function(initiator, name, host, domId, prolog) {
  this.initiator = initiator;
  this.name = name;
  this.host = host;

  this.prolog = prolog || 'data:application/xhtml+xml,%3C%3Fxml%20version%3D%271.0%27%3F%3E%0A%3C%21DOCTYPE%20html%3E%0A';
  this.state = 'not-started';
  this.records = {};
  this.dom = document.getElementById(domId);
};

Document.prototype = {
  add: function(jid, child) {
    var record = new Record(jid, child);

    if (record.rid in this.records)
      console.log('duplicate new');

    this.records[record.rid] = record;
    record.toDOM(this.records, this.dom);
  },
  update: function(jid, child) {
    var target = child.getAttributeNS(null, 'target');

    if (!(target in this.records))
      return; // Ignore it.

    var record = this.records[target];
    console.log(record, child);
    child.setAttributeNS(null, 'rid', target);

    var version = +child.getAttributeNS(null, 'version');
    if (this.state === 'getting-state')
      record.version = version;
    else
      record.version++;

    if (record.version === version) {
      var type = child.getAttributeNS(null, 'type');
      if (type === 'text' || type === 'attr' || type === 'comment') {
        var chdata = child.getAttributeNS(null, 'chdata');
        var replacefrom = +child.getAttributeNS(null, 'replacefrom');
        var replacen = +child.getAttributeNS(null, 'replacen');
        if (chdata && replacefrom && replacen) {
          var string = record.chdata.substr(0, replacefrom);
          string += chdata;
          string += record.chdata.substr(replacefrom + replacen);
          child.removeAttributeNS(null, 'replacefrom');
          child.removeAttributeNS(null, 'replacen');
          child.setAttributeNS(null, 'chdata', string);
        }
      }
      record.update(jid, child);
      record.toDOM(this.records, this.dom);
    } else
      ; // Not sure I understand correctly.
  },
  remove: function(jid, child) {
    var rid = child.getAttributeNS(null, 'target');
    this.records[rid].remove(this.records);
    delete this.records[rid];
  },
  processState: function(jid, elements) {
    var i = 0;
    var first = elements[0];
    if (first.localName === 'document-begin') {
      if (this.state !== 'not-started')
        return; //TODO: the session has already started.
      i = 1;
      this.prolog = first.getAttributeNS(null, 'prolog');
      this.state = 'getting-session';
    }

    //TODO: if the session isn’t started, should ignore changes?

    for (; i < elements.length; i++) {
      var child = elements[i];
      var change = child.localName;

      switch (change) {
        case 'new':
          doc.add(jid, child);
          break;
        case 'set':
          doc.update(jid, child);
          break;
        case 'remove':
          doc.remove(jid, child);
          break;
        case 'document-end':
          this.state = 'started';
          break;
      }
    }
  },
  createState: function(root, parent, state) {
    var children = root.childNodes;
    for (var i = 0; i < children.length; i++) {
      var child = children[i];
      var element = document.createElementNS(Lightstring.NS.sxe, 'new');
      var rid = Lightstring.newId('GUID');
      element.setAttributeNS(null, 'rid', rid);

      if (parent)
        element.setAttributeNS(null, 'parent', parent);

      switch (child.nodeType) {
        case 1:
          element.setAttributeNS(null, 'type', 'element');
          element.setAttributeNS(null, 'ns', child.namespaceURI);
          element.setAttributeNS(null, 'name', child.localName);
          state.push(element);

          //TODO: move that elsewhere, or make it prettier.
          var convertAttr = function(attr) {
            var element = document.createElementNS(Lightstring.NS.sxe, 'new');
            var arid = Lightstring.newId('GUID');
            element.setAttributeNS(null, 'rid', arid);
            element.setAttributeNS(null, 'parent', rid);
            if (attr.namespaceURI)
              element.setAttributeNS(null, 'ns', attr.namespaceURI);
            element.setAttributeNS(null, 'name', attr.localName);
            element.setAttributeNS(null, 'chdata', attr.textContent);
            state.push(element);
          };

          for (var j = 0; j < child.attributes.length; j++)
            convertAttr(child.attributes[j]);

          state = this.createState(child, rid, state);
          break;

        case 3:
          element.setAttributeNS(null, 'type', 'text');
          element.setAttributeNS(null, 'chdata', child.textContent);
          state.push(element);
          break;

        case 7:
          element.setAttributeNS(null, 'type', 'processinginstruction');
          element.setAttributeNS(null, 'pitarget', child.target);
          element.setAttributeNS(null, 'pidata', child.data);
          state.push(element);
          break;

        case 8:
          element.setAttributeNS(null, 'type', 'comment');
          element.setAttributeNS(null, 'chdata', child.textContent);
          state.push(element);
          break;
      }
    }
    return state;
  }
};