<commit_before>/*! Licensed under MIT, https://github.com/sofish/pen */
(function(doc) {

  var Pen, FakePen, utils = {};

  // type detect
  utils.is = function(obj, type) {
    return Object.prototype.toString.call(obj).slice(8, -1) === type;
  };

  // copy props from a obj
  utils.copy = function(defaults, source) {
    for(var p in source) {
      if(source.hasOwnProperty(p)) {
        var val = source[p];
        defaults[p] = this.is(val, 'Object') ? this.copy({}, val) :
          this.is(val, 'Array') ? this.copy([], val) : val;
      }
    }
    return defaults;
  };

  // log
  utils.log = function(message, force) {
    if(window._pen_debug_mode_on || force) console.log('%cPEN DEBUGGER: %c' + message, 'font-family:arial,sans-serif;color:#1abf89;line-height:2em;', 'font-family:cursor,monospace;color:#333;');
  };

  // shift a function
  utils.shift = function(key, fn, time) {
    time = time || 50;
    var queue = this['_shift_fn' + key], timeout = 'shift_timeout' + key, current;
    if ( queue ) {
      queue.concat([fn, time]);
    }
    else {
      queue = [[fn, time]];
    }
    current = queue.pop();
    clearTimeout(this[timeout]);
    this[timeout] = setTimeout(function() {
      current[0]();
    }, time);
  };

  // merge: make it easy to have a fallback
  utils.merge = function(config) {

    // default settings
    var defaults = {
      class: 'pen',
      debug: false,
      stay: config.stay || !config.debug,
      stayMsg: 'Are you going to leave here?',
      textarea: '<textarea name="content"></textarea>',
      list: [
        'blockquote', 'h2', 'h3', 'p', 'code', 'insertorderedlist', 'insertunorderedlist', 'inserthorizontalrule',
        'indent', 'outdent', 'bold', 'italic', 'underline', 'createlink'
      ]
    };

    // user-friendly config
    if(config.nodeType === 1) {
      defaults.editor = config;
    } else if(config.match && config.match(/^#[\S]+$/)) {
      defaults.editor = doc.getElementById(config.slice(1));
    } else {
      defaults = utils.copy(defaults, config);
    }

    return defaults;
  };

  Pen = function(config) {

    if(!config) return utils.log('can\'t find config', true);

    // merge user config
    var defaults = utils.merge(config);

    if(defaults.editor.nodeType !== 1) return utils.log('can\'t find editor');
    if(defaults.debug) window._pen_debug_mode_on = true;

    var editor = defaults.editor;

    // set default class
    editor.classList.add(defaults.class);

    // set contenteditable
    var editable = editor.getAttribute('contenteditable');
    if(!editable) editor.setAttribute('contenteditable', 'true');

    // assign config
    this.config = defaults;

    // save the selection obj
    this._sel = doc.getSelection();

    // map actions
    this.actions();

    // enable toolbar
    this.toolbar();

    // enable markdown covert
    if (this.markdown) {
      this.markdown.init(this);
    }

    // stay on the page
    if (this.config.stay) {
      this.stay(this.config);
    }
  };

  // node effects
  Pen.prototype._effectNode = function(el, returnAsNodeName) {
    var nodes = [];
    while(el !== this.config.editor) {
      if(el.nodeName.match(/(?:[pubia]|h[1-6]|blockquote|[uo]l|li)/i)) {
        nodes.push(returnAsNodeName ? el.nodeName.toLowerCase() : el);
      }
      el = el.parentNode;
    }
    return nodes;
  };

  // remove style attr
  Pen.prototype.nostyle = function() {
    var els = this.config.editor.querySelectorAll('[style]');
    [].slice.call(els).forEach(function(item) {
      item.removeAttribute('style');
    });
    return this;
  };

  Pen.prototype.toolbar = function() {

    var that = this, icons = '';

    for(var i = 0, list = this.config.list; i < list.length; i++) {
      var name = list[i], klass = 'pen-icon icon-' + name;
      icons += '' + (name.match(/^h[1-6]|p$/i) ? name.toUpperCase() : '') + '';
      if((name === 'createlink')) icons += '<input class="pen-input" placeholder="http://" />';
    }

    var menu = doc.createElement('div');
    menu.setAttribute('class', this.config.class + '-menu pen-menu');
    menu.innerHTML = icons;
    menu.style.display = 'none';

    doc.body.appendChild((this._menu = menu));

    var setpos = function() {
      if(menu.style.display === 'block') that.menu();
    };

    // change menu offset when window resize / scroll
    window.addEventListener('resize', setpos);
    window.addEventListener('scroll', setpos);

    var editor = this.config.editor;
    var toggle = function() {

      if(that._isDestroyed) return;

      utils.shift('toggle_menu', function() {
        var range = that._sel;
        if(!range.isCollapsed) {
          //show menu
          that._range = range.getRangeAt(0);
          that.menu().highlight();
        } else {
          //hide menu
          that._menu.style.display = 'none';
        }
      }, 200);
    };

    // toggle toolbar on mouse select
    editor.addEventListener('mouseup', toggle);

    // toggle toolbar on key select
    editor.addEventListener('keyup', toggle);

    // toggle toolbar on key select
    menu.addEventListener('click', function(e) {
      var action = e.target.getAttribute('data-action');

      if(!action) return;

      var apply = function(value) {
        that._sel.removeAllRanges();
        that._sel.addRange(that._range);
        that._actions(action, value);
        that._range = that._sel.getRangeAt(0);
        that.highlight().nostyle().menu();
      };

      // create link
      if(action === 'createlink') {
        var input = menu.getElementsByTagName('input')[0], createlink;

        input.style.display = 'block';
        input.focus();

        createlink = function(input) {
          input.style.display = 'none';
          if(input.value) return apply(input.value.replace(/(^\s+)|(\s+$)/g, '').replace(/^(?!http:\/\/|https:\/\/)(.*)$/, 'http://$1'));
          action = 'unlink';
          apply();
        };

        input.onkeypress = function(e) {
          if(e.which === 13) return createlink(e.target);
        };

        return input.onkeypress;
      }

      apply();
    });

    return this;
  };

  // highlight menu
  Pen.prototype.highlight = function() {
    var node = this._sel.focusNode
      , effects = this._effectNode(node)
      , menu = this._menu
      , linkInput = menu.querySelector('input')
      , highlight;

    // remove all highlights
    [].slice.call(menu.querySelectorAll('.active')).forEach(function(el) {
      el.classList.remove('active');
    });

    // display link input if createlink enabled
    if (linkInput) linkInput.style.display = 'none';

    highlight = function(str) {
      var selector = '.icon-' + str
        , el = menu.querySelector(selector);
      return el && el.classList.add('active');
    };

    effects.forEach(function(item) {
      var tag = item.nodeName.toLowerCase();
      switch(tag) {
        case 'a':
          return (menu.querySelector('input').value = item.href), highlight('createlink');
        case 'i':
          return highlight('italic');
        case 'u':
          return highlight('underline');
        case 'b':
          return highlight('bold');
        case 'code':
          return highlight('code');
        case 'ul':
          return highlight('insertunorderedlist');
        case 'ol':
          return highlight('insertorderedlist');
        case 'ol':
          return highlight('insertorderedlist');
        case 'li':
          return highlight('indent');
        default :
          highlight(tag);
      }
    });

    return this;
  };

  Pen.prototype.actions = function() {
    var that = this, reg, block, overall, insert;

    // allow command list
    reg = {
      block: /^(?:p|h[1-6]|blockquote|pre)$/,
      inline: /^(?:bold|italic|underline|insertorderedlist|insertunorderedlist|indent|outdent)$/,
      source: /^(?:insertimage|createlink|unlink)$/,
      insert: /^(?:inserthorizontalrule|insert)$/,
      wrap: /^(?:code)$/
    };

    overall = function(cmd, val) {
      var message = ' to exec 「' + cmd + '」 command' + (val ? (' with value: ' + val) : '');
      if(document.execCommand(cmd, false, val) && that.config.debug) {
        utils.log('success' + message);
      } else {
        utils.log('fail' + message);
      }
    };

    insert = function(name) {
      var range = that._sel.getRangeAt(0)
        , node = range.startContainer;

      while(node.nodeType !== 1) {
        node = node.parentNode;
      }

      range.selectNode(node);
      range.collapse(false);
      return overall(name);
    };

    block = function(name) {
      if(that._effectNode(that._sel.getRangeAt(0).startContainer, true).indexOf(name) !== -1) {
        if(name === 'blockquote') return document.execCommand('outdent', false, null);
        name = 'p';
      }
      return overall('formatblock', name);
    };

    wrap = function(tag) {
      var val = '<'+tag+'>'+ document.getSelection() +'</'+tag+'>';
      return overall('insertHTML', val);
    }

    this._actions = function(name, value) {
      if(name.match(reg.block)) {
        block(name);
      } else if(name.match(reg.inline) || name.match(reg.source)) {
        overall(name, value);
      } else if(name.match(reg.insert)) {
        insert(name);
      } else if(name.match(reg.wrap)) {
        wrap(name);
      } else {
        if(this.config.debug) utils.log('can not find command function for name: ' + name + (value ? (', value: ' + value) : ''));
      }
    };

    return this;
  };

  // show menu
  Pen.prototype.menu = function() {

    var offset = this._range.getBoundingClientRect()
      , top = offset.top - 10
      , left = offset.left + (offset.width / 2)
      , menu = this._menu;

    // display block to caculate it's width & height
    menu.style.display = 'block';
    menu.style.top = top - menu.clientHeight + 'px';
    menu.style.left = left - (menu.clientWidth/2) + 'px';

    return this;
  };

  Pen.prototype.stay = function(config) {
    var that = this;
    if (!window.onbeforeunload) {
      window.onbeforeunload = function() {
        if(!that._isDestroyed) return config.stayMsg;
      };
    }
  };

  Pen.prototype.destroy = function(isAJoke) {
    var destroy = isAJoke ? false : true
      , attr = isAJoke ? 'setAttribute' : 'removeAttribute';

    if(!isAJoke) {
      this._sel.removeAllRanges();
      this._menu.style.display = 'none';
    }
    this._isDestroyed = destroy;
    this.config.editor[attr]('contenteditable', '');

    return this;
  };

  Pen.prototype.rebuild = function() {
    return this.destroy('it\'s a joke');
  };

  // a fallback for old browers
  FakePen = function(config) {
    if(!config) return utils.log('can\'t find config', true);

    var defaults = utils.merge(config)
      , klass = defaults.editor.getAttribute('class');

    klass = klass ? klass.replace(/\bpen\b/g, '') + ' pen-textarea ' + defaults.class : 'pen pen-textarea';
    defaults.editor.setAttribute('class', klass);
    defaults.editor.innerHTML = defaults.textarea;
    return defaults.editor;
  };

  // make it accessible
  this.Pen = doc.getSelection ? Pen : FakePen;

}(document));
<commit_msg>Linking allows anchors and relative paths<commit_after>/*! Licensed under MIT, https://github.com/sofish/pen */
(function(doc) {

  var Pen, FakePen, utils = {};

  // type detect
  utils.is = function(obj, type) {
    return Object.prototype.toString.call(obj).slice(8, -1) === type;
  };

  // copy props from a obj
  utils.copy = function(defaults, source) {
    for(var p in source) {
      if(source.hasOwnProperty(p)) {
        var val = source[p];
        defaults[p] = this.is(val, 'Object') ? this.copy({}, val) :
          this.is(val, 'Array') ? this.copy([], val) : val;
      }
    }
    return defaults;
  };

  // log
  utils.log = function(message, force) {
    if(window._pen_debug_mode_on || force) console.log('%cPEN DEBUGGER: %c' + message, 'font-family:arial,sans-serif;color:#1abf89;line-height:2em;', 'font-family:cursor,monospace;color:#333;');
  };

  // shift a function
  utils.shift = function(key, fn, time) {
    time = time || 50;
    var queue = this['_shift_fn' + key], timeout = 'shift_timeout' + key, current;
    if ( queue ) {
      queue.concat([fn, time]);
    }
    else {
      queue = [[fn, time]];
    }
    current = queue.pop();
    clearTimeout(this[timeout]);
    this[timeout] = setTimeout(function() {
      current[0]();
    }, time);
  };

  // merge: make it easy to have a fallback
  utils.merge = function(config) {

    // default settings
    var defaults = {
      class: 'pen',
      debug: false,
      stay: config.stay || !config.debug,
      stayMsg: 'Are you going to leave here?',
      textarea: '<textarea name="content"></textarea>',
      list: [
        'blockquote', 'h2', 'h3', 'p', 'code', 'insertorderedlist', 'insertunorderedlist', 'inserthorizontalrule',
        'indent', 'outdent', 'bold', 'italic', 'underline', 'createlink'
      ]
    };

    // user-friendly config
    if(config.nodeType === 1) {
      defaults.editor = config;
    } else if(config.match && config.match(/^#[\S]+$/)) {
      defaults.editor = doc.getElementById(config.slice(1));
    } else {
      defaults = utils.copy(defaults, config);
    }

    return defaults;
  };

  Pen = function(config) {

    if(!config) return utils.log('can\'t find config', true);

    // merge user config
    var defaults = utils.merge(config);

    if(defaults.editor.nodeType !== 1) return utils.log('can\'t find editor');
    if(defaults.debug) window._pen_debug_mode_on = true;

    var editor = defaults.editor;

    // set default class
    editor.classList.add(defaults.class);

    // set contenteditable
    var editable = editor.getAttribute('contenteditable');
    if(!editable) editor.setAttribute('contenteditable', 'true');

    // assign config
    this.config = defaults;

    // save the selection obj
    this._sel = doc.getSelection();

    // map actions
    this.actions();

    // enable toolbar
    this.toolbar();

    // enable markdown covert
    if (this.markdown) {
      this.markdown.init(this);
    }

    // stay on the page
    if (this.config.stay) {
      this.stay(this.config);
    }
  };

  // node effects
  Pen.prototype._effectNode = function(el, returnAsNodeName) {
    var nodes = [];
    while(el !== this.config.editor) {
      if(el.nodeName.match(/(?:[pubia]|h[1-6]|blockquote|[uo]l|li)/i)) {
        nodes.push(returnAsNodeName ? el.nodeName.toLowerCase() : el);
      }
      el = el.parentNode;
    }
    return nodes;
  };

  // remove style attr
  Pen.prototype.nostyle = function() {
    var els = this.config.editor.querySelectorAll('[style]');
    [].slice.call(els).forEach(function(item) {
      item.removeAttribute('style');
    });
    return this;
  };

  Pen.prototype.toolbar = function() {

    var that = this, icons = '';

    for(var i = 0, list = this.config.list; i < list.length; i++) {
      var name = list[i], klass = 'pen-icon icon-' + name;
      icons += '' + (name.match(/^h[1-6]|p$/i) ? name.toUpperCase() : '') + '';
      if((name === 'createlink')) icons += '<input class="pen-input" placeholder="http://" />';
    }

    var menu = doc.createElement('div');
    menu.setAttribute('class', this.config.class + '-menu pen-menu');
    menu.innerHTML = icons;
    menu.style.display = 'none';

    doc.body.appendChild((this._menu = menu));

    var setpos = function() {
      if(menu.style.display === 'block') that.menu();
    };

    // change menu offset when window resize / scroll
    window.addEventListener('resize', setpos);
    window.addEventListener('scroll', setpos);

    var editor = this.config.editor;
    var toggle = function() {

      if(that._isDestroyed) return;

      utils.shift('toggle_menu', function() {
        var range = that._sel;
        if(!range.isCollapsed) {
          //show menu
          that._range = range.getRangeAt(0);
          that.menu().highlight();
        } else {
          //hide menu
          that._menu.style.display = 'none';
        }
      }, 200);
    };

    // toggle toolbar on mouse select
    editor.addEventListener('mouseup', toggle);

    // toggle toolbar on key select
    editor.addEventListener('keyup', toggle);

    // toggle toolbar on key select
    menu.addEventListener('click', function(e) {
      var action = e.target.getAttribute('data-action');

      if(!action) return;

      var apply = function(value) {
        that._sel.removeAllRanges();
        that._sel.addRange(that._range);
        that._actions(action, value);
        that._range = that._sel.getRangeAt(0);
        that.highlight().nostyle().menu();
      };

      // create link
      if(action === 'createlink') {
        var input = menu.getElementsByTagName('input')[0], createlink;

        input.style.display = 'block';
        input.focus();

        createlink = function(input) {
          input.style.display = 'none';
          if(input.value) return apply(input.value.replace(/(^\s+)|(\s+$)/g, '').replace(/^(?!\w+?:\/\/|\/|\.\/|\?|#)(.*)$/, 'http://$1'));
          action = 'unlink';
          apply();
        };

        input.onkeypress = function(e) {
          if(e.which === 13) return createlink(e.target);
        };

        return input.onkeypress;
      }

      apply();
    });

    return this;
  };

  // highlight menu
  Pen.prototype.highlight = function() {
    var node = this._sel.focusNode
      , effects = this._effectNode(node)
      , menu = this._menu
      , linkInput = menu.querySelector('input')
      , highlight;

    // remove all highlights
    [].slice.call(menu.querySelectorAll('.active')).forEach(function(el) {
      el.classList.remove('active');
    });

    // display link input if createlink enabled
    if (linkInput) linkInput.style.display = 'none';

    highlight = function(str) {
      var selector = '.icon-' + str
        , el = menu.querySelector(selector);
      return el && el.classList.add('active');
    };

    effects.forEach(function(item) {
      var tag = item.nodeName.toLowerCase();
      switch(tag) {
        case 'a':
          return (menu.querySelector('input').value = item.getAttribute('href')), highlight('createlink');
        case 'i':
          return highlight('italic');
        case 'u':
          return highlight('underline');
        case 'b':
          return highlight('bold');
        case 'code':
          return highlight('code');
        case 'ul':
          return highlight('insertunorderedlist');
        case 'ol':
          return highlight('insertorderedlist');
        case 'ol':
          return highlight('insertorderedlist');
        case 'li':
          return highlight('indent');
        default :
          highlight(tag);
      }
    });

    return this;
  };

  Pen.prototype.actions = function() {
    var that = this, reg, block, overall, insert;

    // allow command list
    reg = {
      block: /^(?:p|h[1-6]|blockquote|pre)$/,
      inline: /^(?:bold|italic|underline|insertorderedlist|insertunorderedlist|indent|outdent)$/,
      source: /^(?:insertimage|createlink|unlink)$/,
      insert: /^(?:inserthorizontalrule|insert)$/,
      wrap: /^(?:code)$/
    };

    overall = function(cmd, val) {
      var message = ' to exec 「' + cmd + '」 command' + (val ? (' with value: ' + val) : '');
      if(document.execCommand(cmd, false, val) && that.config.debug) {
        utils.log('success' + message);
      } else {
        utils.log('fail' + message);
      }
    };

    insert = function(name) {
      var range = that._sel.getRangeAt(0)
        , node = range.startContainer;

      while(node.nodeType !== 1) {
        node = node.parentNode;
      }

      range.selectNode(node);
      range.collapse(false);
      return overall(name);
    };

    block = function(name) {
      if(that._effectNode(that._sel.getRangeAt(0).startContainer, true).indexOf(name) !== -1) {
        if(name === 'blockquote') return document.execCommand('outdent', false, null);
        name = 'p';
      }
      return overall('formatblock', name);
    };

    wrap = function(tag) {
      var val = '<'+tag+'>'+ document.getSelection() +'</'+tag+'>';
      return overall('insertHTML', val);
    }

    this._actions = function(name, value) {
      if(name.match(reg.block)) {
        block(name);
      } else if(name.match(reg.inline) || name.match(reg.source)) {
        overall(name, value);
      } else if(name.match(reg.insert)) {
        insert(name);
      } else if(name.match(reg.wrap)) {
        wrap(name);
      } else {
        if(this.config.debug) utils.log('can not find command function for name: ' + name + (value ? (', value: ' + value) : ''));
      }
    };

    return this;
  };

  // show menu
  Pen.prototype.menu = function() {

    var offset = this._range.getBoundingClientRect()
      , top = offset.top - 10
      , left = offset.left + (offset.width / 2)
      , menu = this._menu;

    // display block to caculate it's width & height
    menu.style.display = 'block';
    menu.style.top = top - menu.clientHeight + 'px';
    menu.style.left = left - (menu.clientWidth/2) + 'px';

    return this;
  };

  Pen.prototype.stay = function(config) {
    var that = this;
    if (!window.onbeforeunload) {
      window.onbeforeunload = function() {
        if(!that._isDestroyed) return config.stayMsg;
      };
    }
  };

  Pen.prototype.destroy = function(isAJoke) {
    var destroy = isAJoke ? false : true
      , attr = isAJoke ? 'setAttribute' : 'removeAttribute';

    if(!isAJoke) {
      this._sel.removeAllRanges();
      this._menu.style.display = 'none';
    }
    this._isDestroyed = destroy;
    this.config.editor[attr]('contenteditable', '');

    return this;
  };

  Pen.prototype.rebuild = function() {
    return this.destroy('it\'s a joke');
  };

  // a fallback for old browers
  FakePen = function(config) {
    if(!config) return utils.log('can\'t find config', true);

    var defaults = utils.merge(config)
      , klass = defaults.editor.getAttribute('class');

    klass = klass ? klass.replace(/\bpen\b/g, '') + ' pen-textarea ' + defaults.class : 'pen pen-textarea';
    defaults.editor.setAttribute('class', klass);
    defaults.editor.innerHTML = defaults.textarea;
    return defaults.editor;
  };

  // make it accessible
  this.Pen = doc.getSelection ? Pen : FakePen;

}(document));
<|endoftext|>


<commit_before>var nodetypes = {};
var linktypes = {};
var channeltypes = {};

function Model() {
    this.nodes = Kefir.emitter();
    this.targets = Kefir.emitter();
    this.renderers = Kefir.emitter();

    var chain = function(prev, next) {
        return { prev: prev, next: next };
    };

    Kefir.combine([ this.nodes,
                    this.targets.scan(chain),
                    this.renderers.scan(chain) ]).onValue(
        function(value) {
            var node = value[0], last_target = value[1],
                                 last_renderer = value[2];
            console.log('update');
            console.log(node, last_target, last_renderer);
            var last_target = last_target,
                last_renderer = last_renderer;
            while (last_target.prev) {
                last_target = last_target.prev;
                while (last_renderer.prev) {
                    last_renderer = last_renderer.prev;
                    console.log(node, last_target, last_renderer);
                }
            }
        }
    );

    var nodes = this.nodes;
    var targets = this.targets;
    var renderers = this.renderers;

    targets.emit('t-abcdefgh-1');
    renderers.emit('r-abcdefgh-1');
    renderers.emit('r-abcdefgh-2');
    targets.emit('t-abcdefgh-2');
    nodes.emit('n-a');
    nodes.emit('n-b');
    nodes.emit('n-c');
    nodes.emit('n-d');
    renderers.emit('r-efgh');
    nodes.emit('n-e');
    targets.emit('t-fgh');
    nodes.emit('n-f');
    targets.emit('t-gh-1');
    targets.emit('t-gh-2');
    nodes.emit('n-g');
    renderers.emit('r-h');
    nodes.emit('n-h');
}
Model.prototype.attach = function(elm) {
    this.targets.emit(elm);
    return this;
}
Model.prototype.add = function(node) {
    this.nodes.emit(node);
    return this;
}
Model.prototype.start = function() {
    return this;
}

function Node(type, name) {
    this.type = type || 'core/empty';
    var def = nodetypes[this.type];
    if (!def) throw new Error('Node type' + this.type + ' is not registered!');
    this.def = def;

    this.name = name || def.name || 'Unnamed';
    this.inlets = [];
    this.outlets = [];
    this.def = def;
}

function Link(type, name) {
    this.type = type || 'core/direct';
    var def = linktypes[this.type];
    if (!def) throw new Error('Node type' + this.type + ' is not registered!');
    this.def = def;

    this.name = name || def.name || '';
    this.start = null;
    this.end = [];
}

function Channel(type, name) {
    this.type = type || 'core/bool';
    var def = linktypes[this.type];
    if (!def) throw new Error('Channel type' + this.type + ' is not registered!');
    this.def = def;

    this.name = name || def.name || 'Unnamed';
    this.node = null;
}

function nodetype(id, def) {
    nodetypes[name] = def;
}

function linktype(id, def) {
    linktypes[name] = def;
}

function channeltype(id, def) {
    channeltypes[name] = def;
}
<commit_msg>better render loop with recursion<commit_after>var nodetypes = {};
var linktypes = {};
var channeltypes = {};

function Model() {
    this.nodes = Kefir.emitter();
    this.targets = Kefir.emitter();
    this.renderers = Kefir.emitter();

    function rev_cons(prev, cur) {
        return [ cur, Array.isArray(prev) ? prev : [ prev, null ] ];
    };

    function walk_cons(cell, f) {
        if (!cell) return;
        if (cell[0]) f(cell[0]);
        walk_cons(cell[1], f);
    }

    Kefir.combine([ this.nodes,
                    this.targets.scan(rev_cons),
                    this.renderers.scan(rev_cons) ]).onValue(
        function(value) {
            var node = value[0], targets = value[1],
                                 renderers = value[2];
            console.log('update');
            walk_cons(targets, function(target) {
                walk_cons(renderers, function(renderer) {
                    console.log(node, target, renderer);
                });
            });
        }
    );

    var nodes = this.nodes;
    var targets = this.targets;
    var renderers = this.renderers;

    targets.emit('t-1');
    renderers.emit('r-1');
    renderers.emit('r-2');
    targets.emit('t-2');
    nodes.emit('n-a');
    nodes.emit('n-b');
    nodes.emit('n-c');
    nodes.emit('n-d');
    renderers.emit('r-3');
    nodes.emit('n-e');
    targets.emit('t-3');
    nodes.emit('n-f');
    targets.emit('t-4');
    targets.emit('t-5');
    nodes.emit('n-g');
    renderers.emit('r-4');
    nodes.emit('n-h');
}
Model.prototype.attach = function(elm) {
    this.targets.emit(elm);
    return this;
}
Model.prototype.add = function(node) {
    this.nodes.emit(node);
    return this;
}
Model.prototype.start = function() {
    return this;
}

function Node(type, name) {
    this.type = type || 'core/empty';
    var def = nodetypes[this.type];
    if (!def) throw new Error('Node type' + this.type + ' is not registered!');
    this.def = def;

    this.name = name || def.name || 'Unnamed';
    this.inlets = [];
    this.outlets = [];
    this.def = def;
}

function Link(type, name) {
    this.type = type || 'core/direct';
    var def = linktypes[this.type];
    if (!def) throw new Error('Node type' + this.type + ' is not registered!');
    this.def = def;

    this.name = name || def.name || '';
    this.start = null;
    this.end = [];
}

function Channel(type, name) {
    this.type = type || 'core/bool';
    var def = linktypes[this.type];
    if (!def) throw new Error('Channel type' + this.type + ' is not registered!');
    this.def = def;

    this.name = name || def.name || 'Unnamed';
    this.node = null;
}

function nodetype(id, def) {
    nodetypes[name] = def;
}

function linktype(id, def) {
    linktypes[name] = def;
}

function channeltype(id, def) {
    channeltypes[name] = def;
}
<|endoftext|>


<commit_before>(function(exports) {
  var isArray = Array.isArray || function(arg) {
    return Object.prototype.toString.call(arg) == '[object Array]';
  }

  exports.server = function(host, port, user, pass) {
    var http;
    var xmlHTTP;

    var urlUtils;

    var decodeJSON = true;
    var currDatabase;
    var staleDefault = false;
    var globalCookies = {};

    function throwIfNoCurrDB() {
      if(!currDatabase) {
        throw 'Must setDatabase() first.';
      }
    }

    if(typeof XMLHttpRequest === 'function') {
      xmlHTTP = new XMLHttpRequest();
    }
    else if(typeof ActiveXObject === 'function') {
      xmlHTTP = new ActiveXObject('Microsoft.XMLHTTP');
    }
    else if(typeof require === 'function') {
      http = require('http');
      urlUtils = require('url');
    }
    else {
      throw 'whoops';
    }

    function onResponse(httpCode, headers, body, callback) {
      var resp = {
        _HTTP: {
          status: httpCode
        },
        headers: headers
      };

      if(typeof body === 'string') {
        resp.body = (body.length > 0 && decodeJSON) ? JSON.parse(body) : body;
      }

      if(typeof callback === 'function') {
        callback(resp);
      }
    }

    function procPacket(method, path, data, headers, callback) {
      var cookieString = '';

      headers = headers || {};

      if(!headers['Content-Type']) {
        headers['Content-Type'] = 'application/json';
      }

      if(data && typeof data !== 'string') {
        data = JSON.stringify(data);
      }

      //deal with global cookies
      for(var key in globalCookies) {
        if(globalCookies.hasOwnProperty(key)) {
          cookieString += key + '=' + globalCookies[key] + ';';
        }
      }

      if(cookieString) {
        if(!headers['Cookie']) {
          headers['Cookie'] = cookieString;
        }
        else {
          headers['Cookie'] += cookieString;
        }
      }

      if(http) {
        // Node.JS http module
        headers['User-Agent'] = 'Sag-JS/0.1'; //can't set this in browsers

        var req = http.request(
          {
            method: method,
            host: host,
            port: port,
            path: path,
            headers: headers
          },
          function(res) {
            var resBody;

            res.setEncoding('utf8');

            if(method !== 'HEAD') {
              resBody = '';

              res.on('data', function(chunk) {
                resBody += chunk;
              });
            }

            res.on('end', function() {
              onResponse(res.statusCode, res.headers, resBody, callback);
            });
          }
        );

        req.on('error', function(e) {
          console.log('problem with request: ' + e);
        });

        if(data) {
          req.write(data);
        }

        req.end();
      }
      else if(xmlHTTP) {
        // Browser xhr magik
        xmlHTTP.onreadystatechange = function() {
          if(this.readyState === 4 && this.status > 0) {
            var headers = {};
            var rawHeaders = this.getAllResponseHeaders().split('\n');

            for(var i in rawHeaders) {
              rawHeaders[i] = rawHeaders[i].split(': ');

              if(rawHeaders[i][1]) {
                headers[rawHeaders[i][0]] = rawHeaders[i][1];
              }
            }

            onResponse(this.status, headers, this.response, callback);
          }
        };

        xmlHTTP.open(method, 'http://' + host + ':' + port + path);

        for(var k in headers) {
          if(headers.hasOwnProperty(k)) {
            xmlHTTP.setRequestHeader(k, headers[k]);
          }
        }

        xmlHTTP.send(data || null);
      }
      else {
        throw 'coder fail';
      }
    }

    function setURLParameter(url, key, value) {
      if(typeof url !== 'string') {
        throw 'URLs must be a string';
      }

      if(urlUtils) {
        //node.js
        url = urlUtils.parse(url);

        url.search = ((url.search) ? url.search + '&' : '?')
                      + key + '=' + value;

        url = urlUtils.format(url);
      }
      else {
        //browser
        url = url.split('?');

        url[1] = ((url[1]) ? url[1] + '&' : '') + key + '=' + value;

        url = url.join('?');
      }

      return url;
    }

    var publicThat = {
      get: function(opts) {
        throwIfNoCurrDB();

        if(typeof opts !== 'object') {
          throw 'invalid opts object';
        }

        if(typeof opts.url !== 'string') {
          throw 'invalid url type';
        }

        if(opts.callback && typeof opts.callback !== 'function') {
          throw 'invalid callback type';
        }

        if(opts.url.substr(0, 1) !== '/') {
          opts.url = '/' + opts.url;
        }

        if(staleDefault) {
          opts.url = setURLParameter(opts.url, 'stale', 'ok');
        }

        procPacket(
          'GET',
          '/' + currDatabase + opts.url,
          null,
          null,
          opts.callback
        );
      },

      post: function(opts) {
        var path;

        throwIfNoCurrDB();

        if(typeof opts !== 'object') {
          throw 'invalid opts object';
        }

        if(opts.data === null || opts.data === undefined) {
          throw 'you must specify data to POST';
        }

        path = '/' + currDatabase;

        if(opts.path) {
          if(typeof opts.path !== 'string') {
            throw 'Invalid path type (must be a string).';
          }

          if(opts.path.substr(0, 1) !== '/') {
            path += '/';
          }

          path += opts.path;
        }

        procPacket('POST', path, opts.data, null, opts.callback);
      },

      decode: function(d) {
        decodeJSON = !! d;
        return publicThat;
      },

      setDatabase: function(db) {
        if(typeof db !== 'string' || db.length <= 0) {
          throw 'invalid database name';
        }

        currDatabase = db;

        return publicThat;
      },

      currentDatabase: function() {
        return currDatabase;
      },

      getAllDatabases: function(callback) {
        procPacket('GET', '/_all_dbs', null, null, callback);
      },

      getStats: function(callback) {
        if(!currDatabase) {
          throw 'Must setDatabase() first.';
        }

        procPacket('GET', '/_stats', null, null, callback);
      },

      generateIDs: function(opts) {
        var url = '/_uuids';

        if(typeof opts !== 'object') {
          throw 'Missing required opts.';
        }

        if(typeof opts.count === 'number') {
          url += '?count=' + opts.count;
        }
        else if(typeof opts.count !== 'undefined') {
          throw 'Invalid count type';
        }

        procPacket('GET', url, null, null, opts.callback);
      },

      put: function(opts) {
        if(typeof opts !== 'object') {
          throw 'Missing required opts.';
        }

        throwIfNoCurrDB();

        if(opts.callback && typeof opts.callback !== 'function') {
          throw 'Invalid callback';
        }

        if(!opts.id) {
          throw 'Must specify an id.';
        }

        if(typeof opts.id !== 'string') {
          throw 'Invalid id type (must be a string).';
        }

        if(!opts.data) {
          throw 'Invalid data: must specify data (a document) to PUT.';
        }
        else {
          if(!opts.data._id) {
            throw 'No _id specified in the data.';
          }

          if(typeof opts.data._id !== 'string') {
            throw 'Invalid _id specific (must be a string).';
          }

          if(typeof opts.data === 'object' || isArray(opts.data)) {
            opts.data = JSON.stringify(opts.data);
          }
          else if(typeof opts.data !== 'string') {
            throw 'Invalid data: must be a string of JSON or an object or array to be encoded as JSON.';
          }
        }

        procPacket(
          'PUT',
          '/' + currDatabase + '/' + opts.id,
          opts.data,
          null,
          opts.callback
        );
      },

      delete: function(id, rev, callback) {
        throwIfNoCurrDB();

        if(!id || typeof id !== 'string') {
          throw 'Invalid id';
        }

        if(!rev || typeof rev !== 'string') {
          throw 'Invalid rev';
        }

        if(callback && typeof callback !== 'function') {
          throw 'Invalid callback type';
        }

        procPacket(
          'DELETE',
          '/' + currDatabase + '/' + id,
          null,
          { 'If-Match': rev },
          callback
        );
      },

      head: function(opts) {
        if(typeof opts !== 'object') {
          throw 'Missing required opts.';
        }

        throwIfNoCurrDB();

        if(typeof opts.url !== 'string' || !opts.url) {
          throw 'Invalid URL provided';
        }

        if(opts.url.substr(0, 1) !== '/') {
          opts.url = '/' + opts.url;
        }

        if(opts.callback && typeof opts.callback !== 'function') {
          throw 'Invalid callback type';
        }

        procPacket(
          'HEAD',
          '/' + currDatabase + opts.url,
          null,
          null,
          opts.callback
        );
      },

      getSession: function(callback) {
        if(callback && typeof callback !== 'function') {
          throw 'Invalid callback type';
        }

        procPacket('GET', '/_session', null, null, callback);
      },

      bulk: function(opts) {
        var data = {};

        if(typeof opts !== 'object') {
          throw 'Missing required opts.';
        }

        throwIfNoCurrDB();

        if(!opts.docs || !isArray(opts.docs)) {
          throw 'Invalid docs provided.';
        }

        if(opts.callback && typeof opts.callback !== 'function') {
          throw 'Invalid callback type';
        }

        if(opts.allOrNothing) {
          data.all_or_nothing = !!opts.allOrNothing;
        }

        data.docs = opts.docs;

        procPacket(
          'POST',
          '/' + currDatabase + '/_bulk_docs',
          data,
          null,
          opts.callback
        );
      },

      compact: function(opts) {
        var url;

        if(typeof opts !== 'object') {
          throw 'Missing required opts.';
        }

        throwIfNoCurrDB();

        url = '/' + currDatabase + '/_compact';

        if(opts.viewName) {
          if(typeof opts.viewName !== 'string') {
            throw 'Invalid viewName provided.';
          }

          url += '/' + opts.viewName;
        }

        if(opts.callback && typeof opts.callback !== 'function') {
          throw 'Invalid callback type.';
        }

        procPacket('POST', url, null, null, opts.callback);
      },

      copy: function(opts) {
        if(typeof opts !== 'object') {
          throw 'Missing required opts.';
        }

        throwIfNoCurrDB();

        if(!opts.srcID || typeof opts.srcID !== 'string') {
          throw 'Invalid srcID.';
        }

        if(!opts.dstID || typeof opts.dstID !== 'string') {
          throw 'Invalid dstID.';
        }

        if(opts.dstRev) {
          if(typeof opts.dstRev !== 'string') {
            throw 'Invalid dstRev.';
          }

          opts.dstID += '?rev=' + opts.dstRev;
        }

        if(opts.callback && typeof opts.callback !== 'function') {
          throw 'Invalid callback type.';
        }

        procPacket(
          'COPY',
          '/' + currDatabase + '/' + opts.srcID,
          null,
          { Destination: opts.dstID },
          opts.callback
        );
      },

      setStaleDefault: function(isIt) {
        staleDefault = !!isIt;

        return publicThat;
      },

      createDatabase: function(name, callback) {
        if(!name || typeof name !== 'string') {
          throw 'Invalid database name.';
        }

        if(callback && typeof callback !== 'function') {
          throw 'Invalid callback type.';
        }

        procPacket('PUT', '/' + name, null, null, callback);
      },

      deleteDatabase: function(name, callback) {
        if(!name || typeof name !== 'string') {
          throw 'Invalid database name.';
        }

        if(callback && typeof callback !== 'function') {
          throw 'Invalid callback type.';
        }

        procPacket('DELETE', '/' + name, null, null, callback);
      },

      setAttachment: function(opts) {
        var url;

        throwIfNoCurrDB();

        if(!opts.name || typeof opts.name !== 'string') {
          throw 'Invalid attachment name.';
        }

        if(!opts.data || typeof opts.data !== 'string') {
          throw 'Invalid attachment data - remember to serialize it to a string!';
        }

        if(!opts.contentType || typeof opts.contentType !== 'string') {
          throw 'Invalid contentType.';
        }

        if(!opts.docID || typeof opts.docID !== 'string') {
          throw 'Invalid docID.';
        }

        if(opts.rev && typeof opts.rev !== 'string') {
          throw 'Invalid attachment revID.';
        }

        if(opts.callback && typeof opts.callback !== 'function') {
          throw 'Invalid callback type.';
        }

        url = '/' + currDatabase + '/' + opts.docID + '/' + opts.name;

        if(opts.revID) {
          url += '?rev=' + opts.revID;
        }

        procPacket(
          'PUT',
          url,
          opts.data,
          { 'Content-Type': opts.contentType },
          opts.callback
        );
      },

      setCookie: function(key, value) {
        if(!key || typeof key !== 'string') {
          throw 'Invalid cookie key.';
        }

        if(value !== null && typeof value !== 'string') {
          throw 'Invalid non-string and non-null cookie value.';
        }

        if(value === null) {
          delete globalCookies[key];
        }
        else {
          globalCookies[key] = value;
        }

        return publicThat;
      },

      getCookie: function(key) {
        if(!key || typeof key !== 'string') {
          throw 'Invalid cookie key.';
        }

        return globalCookies[key];
      }
    };

    return publicThat;
  };
})((typeof exports === 'object') ? exports : (this.sag = {}));
<commit_msg>Code cleaning.<commit_after>(function(exports) {
  var isArray = Array.isArray || function(arg) {
    return Object.prototype.toString.call(arg) == '[object Array]';
  }

  exports.server = function(host, port, user, pass) {
    var http;
    var xmlHTTP;

    var urlUtils;

    var decodeJSON = true;
    var currDatabase;
    var staleDefault = false;
    var globalCookies = {};

    function throwIfNoCurrDB() {
      if(!currDatabase) {
        throw 'Must setDatabase() first.';
      }
    }

    if(typeof XMLHttpRequest === 'function') {
      xmlHTTP = new XMLHttpRequest();
    }
    else if(typeof ActiveXObject === 'function') {
      xmlHTTP = new ActiveXObject('Microsoft.XMLHTTP');
    }
    else if(typeof require === 'function') {
      http = require('http');
      urlUtils = require('url');
    }
    else {
      throw 'whoops';
    }

    function onResponse(httpCode, headers, body, callback) {
      var resp = {
        _HTTP: {
          status: httpCode
        },
        headers: headers
      };

      if(typeof body === 'string') {
        resp.body = (body.length > 0 && decodeJSON) ? JSON.parse(body) : body;
      }

      if(typeof callback === 'function') {
        callback(resp);
      }
    }

    function procPacket(method, path, data, headers, callback) {
      var cookieStr = '';

      headers = headers || {};

      if(!headers['Content-Type']) {
        headers['Content-Type'] = 'application/json';
      }

      if(data && typeof data !== 'string') {
        data = JSON.stringify(data);
      }

      //deal with global cookies
      for(var key in globalCookies) {
        if(globalCookies.hasOwnProperty(key)) {
          cookieStr += key + '=' + globalCookies[key] + ';';
        }
      }

      if(cookieStr) {
        headers.Cookie = ((headers.Cookie) ? headers.Cookie : '') + cookieStr;
      }

      if(http) {
        // Node.JS http module
        headers['User-Agent'] = 'Sag-JS/0.1'; //can't set this in browsers

        var req = http.request(
          {
            method: method,
            host: host,
            port: port,
            path: path,
            headers: headers
          },
          function(res) {
            var resBody;

            res.setEncoding('utf8');

            if(method !== 'HEAD') {
              resBody = '';

              res.on('data', function(chunk) {
                resBody += chunk;
              });
            }

            res.on('end', function() {
              onResponse(res.statusCode, res.headers, resBody, callback);
            });
          }
        );

        req.on('error', function(e) {
          console.log('problem with request: ' + e);
        });

        if(data) {
          req.write(data);
        }

        req.end();
      }
      else if(xmlHTTP) {
        // Browser xhr magik
        xmlHTTP.onreadystatechange = function() {
          if(this.readyState === 4 && this.status > 0) {
            var headers = {};
            var rawHeaders = this.getAllResponseHeaders().split('\n');

            for(var i in rawHeaders) {
              rawHeaders[i] = rawHeaders[i].split(': ');

              if(rawHeaders[i][1]) {
                headers[rawHeaders[i][0]] = rawHeaders[i][1];
              }
            }

            onResponse(this.status, headers, this.response, callback);
          }
        };

        xmlHTTP.open(method, 'http://' + host + ':' + port + path);

        for(var k in headers) {
          if(headers.hasOwnProperty(k)) {
            xmlHTTP.setRequestHeader(k, headers[k]);
          }
        }

        xmlHTTP.send(data || null);
      }
      else {
        throw 'coder fail';
      }
    }

    function setURLParameter(url, key, value) {
      if(typeof url !== 'string') {
        throw 'URLs must be a string';
      }

      if(urlUtils) {
        //node.js
        url = urlUtils.parse(url);

        url.search = ((url.search) ? url.search + '&' : '?')
                      + key + '=' + value;

        url = urlUtils.format(url);
      }
      else {
        //browser
        url = url.split('?');

        url[1] = ((url[1]) ? url[1] + '&' : '') + key + '=' + value;

        url = url.join('?');
      }

      return url;
    }

    var publicThat = {
      get: function(opts) {
        throwIfNoCurrDB();

        if(typeof opts !== 'object') {
          throw 'invalid opts object';
        }

        if(typeof opts.url !== 'string') {
          throw 'invalid url type';
        }

        if(opts.callback && typeof opts.callback !== 'function') {
          throw 'invalid callback type';
        }

        if(opts.url.substr(0, 1) !== '/') {
          opts.url = '/' + opts.url;
        }

        if(staleDefault) {
          opts.url = setURLParameter(opts.url, 'stale', 'ok');
        }

        procPacket(
          'GET',
          '/' + currDatabase + opts.url,
          null,
          null,
          opts.callback
        );
      },

      post: function(opts) {
        var path;

        throwIfNoCurrDB();

        if(typeof opts !== 'object') {
          throw 'invalid opts object';
        }

        if(opts.data === null || opts.data === undefined) {
          throw 'you must specify data to POST';
        }

        path = '/' + currDatabase;

        if(opts.path) {
          if(typeof opts.path !== 'string') {
            throw 'Invalid path type (must be a string).';
          }

          if(opts.path.substr(0, 1) !== '/') {
            path += '/';
          }

          path += opts.path;
        }

        procPacket('POST', path, opts.data, null, opts.callback);
      },

      decode: function(d) {
        decodeJSON = !! d;
        return publicThat;
      },

      setDatabase: function(db) {
        if(typeof db !== 'string' || db.length <= 0) {
          throw 'invalid database name';
        }

        currDatabase = db;

        return publicThat;
      },

      currentDatabase: function() {
        return currDatabase;
      },

      getAllDatabases: function(callback) {
        procPacket('GET', '/_all_dbs', null, null, callback);
      },

      getStats: function(callback) {
        if(!currDatabase) {
          throw 'Must setDatabase() first.';
        }

        procPacket('GET', '/_stats', null, null, callback);
      },

      generateIDs: function(opts) {
        var url = '/_uuids';

        if(typeof opts !== 'object') {
          throw 'Missing required opts.';
        }

        if(typeof opts.count === 'number') {
          url += '?count=' + opts.count;
        }
        else if(typeof opts.count !== 'undefined') {
          throw 'Invalid count type';
        }

        procPacket('GET', url, null, null, opts.callback);
      },

      put: function(opts) {
        if(typeof opts !== 'object') {
          throw 'Missing required opts.';
        }

        throwIfNoCurrDB();

        if(opts.callback && typeof opts.callback !== 'function') {
          throw 'Invalid callback';
        }

        if(!opts.id) {
          throw 'Must specify an id.';
        }

        if(typeof opts.id !== 'string') {
          throw 'Invalid id type (must be a string).';
        }

        if(!opts.data) {
          throw 'Invalid data: must specify data (a document) to PUT.';
        }
        else {
          if(!opts.data._id) {
            throw 'No _id specified in the data.';
          }

          if(typeof opts.data._id !== 'string') {
            throw 'Invalid _id specific (must be a string).';
          }

          if(typeof opts.data === 'object' || isArray(opts.data)) {
            opts.data = JSON.stringify(opts.data);
          }
          else if(typeof opts.data !== 'string') {
            throw 'Invalid data: must be a string of JSON or an object or array to be encoded as JSON.';
          }
        }

        procPacket(
          'PUT',
          '/' + currDatabase + '/' + opts.id,
          opts.data,
          null,
          opts.callback
        );
      },

      delete: function(id, rev, callback) {
        throwIfNoCurrDB();

        if(!id || typeof id !== 'string') {
          throw 'Invalid id';
        }

        if(!rev || typeof rev !== 'string') {
          throw 'Invalid rev';
        }

        if(callback && typeof callback !== 'function') {
          throw 'Invalid callback type';
        }

        procPacket(
          'DELETE',
          '/' + currDatabase + '/' + id,
          null,
          { 'If-Match': rev },
          callback
        );
      },

      head: function(opts) {
        if(typeof opts !== 'object') {
          throw 'Missing required opts.';
        }

        throwIfNoCurrDB();

        if(typeof opts.url !== 'string' || !opts.url) {
          throw 'Invalid URL provided';
        }

        if(opts.url.substr(0, 1) !== '/') {
          opts.url = '/' + opts.url;
        }

        if(opts.callback && typeof opts.callback !== 'function') {
          throw 'Invalid callback type';
        }

        procPacket(
          'HEAD',
          '/' + currDatabase + opts.url,
          null,
          null,
          opts.callback
        );
      },

      getSession: function(callback) {
        if(callback && typeof callback !== 'function') {
          throw 'Invalid callback type';
        }

        procPacket('GET', '/_session', null, null, callback);
      },

      bulk: function(opts) {
        var data = {};

        if(typeof opts !== 'object') {
          throw 'Missing required opts.';
        }

        throwIfNoCurrDB();

        if(!opts.docs || !isArray(opts.docs)) {
          throw 'Invalid docs provided.';
        }

        if(opts.callback && typeof opts.callback !== 'function') {
          throw 'Invalid callback type';
        }

        if(opts.allOrNothing) {
          data.all_or_nothing = !!opts.allOrNothing;
        }

        data.docs = opts.docs;

        procPacket(
          'POST',
          '/' + currDatabase + '/_bulk_docs',
          data,
          null,
          opts.callback
        );
      },

      compact: function(opts) {
        var url;

        if(typeof opts !== 'object') {
          throw 'Missing required opts.';
        }

        throwIfNoCurrDB();

        url = '/' + currDatabase + '/_compact';

        if(opts.viewName) {
          if(typeof opts.viewName !== 'string') {
            throw 'Invalid viewName provided.';
          }

          url += '/' + opts.viewName;
        }

        if(opts.callback && typeof opts.callback !== 'function') {
          throw 'Invalid callback type.';
        }

        procPacket('POST', url, null, null, opts.callback);
      },

      copy: function(opts) {
        if(typeof opts !== 'object') {
          throw 'Missing required opts.';
        }

        throwIfNoCurrDB();

        if(!opts.srcID || typeof opts.srcID !== 'string') {
          throw 'Invalid srcID.';
        }

        if(!opts.dstID || typeof opts.dstID !== 'string') {
          throw 'Invalid dstID.';
        }

        if(opts.dstRev) {
          if(typeof opts.dstRev !== 'string') {
            throw 'Invalid dstRev.';
          }

          opts.dstID += '?rev=' + opts.dstRev;
        }

        if(opts.callback && typeof opts.callback !== 'function') {
          throw 'Invalid callback type.';
        }

        procPacket(
          'COPY',
          '/' + currDatabase + '/' + opts.srcID,
          null,
          { Destination: opts.dstID },
          opts.callback
        );
      },

      setStaleDefault: function(isIt) {
        staleDefault = !!isIt;

        return publicThat;
      },

      createDatabase: function(name, callback) {
        if(!name || typeof name !== 'string') {
          throw 'Invalid database name.';
        }

        if(callback && typeof callback !== 'function') {
          throw 'Invalid callback type.';
        }

        procPacket('PUT', '/' + name, null, null, callback);
      },

      deleteDatabase: function(name, callback) {
        if(!name || typeof name !== 'string') {
          throw 'Invalid database name.';
        }

        if(callback && typeof callback !== 'function') {
          throw 'Invalid callback type.';
        }

        procPacket('DELETE', '/' + name, null, null, callback);
      },

      setAttachment: function(opts) {
        var url;

        throwIfNoCurrDB();

        if(!opts.name || typeof opts.name !== 'string') {
          throw 'Invalid attachment name.';
        }

        if(!opts.data || typeof opts.data !== 'string') {
          throw 'Invalid attachment data - remember to serialize it to a string!';
        }

        if(!opts.contentType || typeof opts.contentType !== 'string') {
          throw 'Invalid contentType.';
        }

        if(!opts.docID || typeof opts.docID !== 'string') {
          throw 'Invalid docID.';
        }

        if(opts.rev && typeof opts.rev !== 'string') {
          throw 'Invalid attachment revID.';
        }

        if(opts.callback && typeof opts.callback !== 'function') {
          throw 'Invalid callback type.';
        }

        url = '/' + currDatabase + '/' + opts.docID + '/' + opts.name;

        if(opts.revID) {
          url += '?rev=' + opts.revID;
        }

        procPacket(
          'PUT',
          url,
          opts.data,
          { 'Content-Type': opts.contentType },
          opts.callback
        );
      },

      setCookie: function(key, value) {
        if(!key || typeof key !== 'string') {
          throw 'Invalid cookie key.';
        }

        if(value !== null && typeof value !== 'string') {
          throw 'Invalid non-string and non-null cookie value.';
        }

        if(value === null) {
          delete globalCookies[key];
        }
        else {
          globalCookies[key] = value;
        }

        return publicThat;
      },

      getCookie: function(key) {
        if(!key || typeof key !== 'string') {
          throw 'Invalid cookie key.';
        }

        return globalCookies[key];
      }
    };

    return publicThat;
  };
})((typeof exports === 'object') ? exports : (this.sag = {}));
<|endoftext|>


<commit_before>(function(exports) {
  exports.server = function(host, port, user, pass) {
    var http;
    var xmlHTTP;

    var decodeJSON = true;
    var currDatabase;

    if(typeof XMLHttpRequest === 'function') {
      xmlHTTP = new XMLHttpRequest();
    }
    else if(typeof ActiveXObject === 'function') {
      xmlHTTP = new ActiveXObject('Microsoft.XMLHTTP');
    }
    else if(typeof require === 'function') {
      http = require('http');
    }
    else {
      throw 'whoops';
    }

    function onResponse(httpCode, headers, body, callback) {
      var resp = {
        _HTTP: {
          status: httpCode
        },
        headers: headers
      };

      if(typeof body === 'string') {
        resp.body = (body.length > 0 && decodeJSON) ? JSON.parse(body) : body;
      }

      callback(resp);
    }

    function procPacket(method, path, data, headers, callback) {
      headers = headers || {};

      if(!headers['Content-Type']) {
        headers['Content-Type'] = 'application/json';
      }

      if(http) {
        // Node.JS http module
        http.request(
          {
            method: method,
            host: host,
            port: port,
            url: path,
            headers: headers
          },
          function(res) {
            var resBody = '';

            res.setEncoding('utf8');

            res.on('data', function(chunk) {
              resBody += chunk;
            });

            res.on('end', function() {
              onResponse(res.statusCode, res.headers, resBody, callback);
            });
          }
        ).on('error', function(e) {
          console.log('problem with request: ' + e);
        }).end();
      }
      else if(xmlHTTP) {
        // Browser xhr magik
        xmlHTTP.onreadystatechange = function() {
          if(this.readyState === 4 && this.status > 0) {
            var headers = {};
            var rawHeaders = this.getAllResponseHeaders().split('\n');

            for(var i in rawHeaders) {
              rawHeaders[i] = rawHeaders[i].split(': ');

              if(rawHeaders[i][1]) {
                headers[rawHeaders[i][0]] = rawHeaders[i][1];
              }
            }

            onResponse(this.status, headers, this.response, callback);
          }
        };

        xmlHTTP.open(method, 'http://' + host + ':' + port + path);

        for(var k in headers) {
          if(headers.hasOwnProperty(k)) {
            xmlHTTP.setRequestHeader(k, headers[k]);
          }
        }

        xmlHTTP.send();
      }
      else {
        throw 'coder fail';
      }
    }

    var publicThat = {
      get: function(opts) {
        if(!currDatabase) {
          throw 'You must call setDatabase() first.';
        }

        if(typeof opts !== 'object') {
          throw 'invalid opts object';
        }

        if(typeof opts.url !== 'string') {
          throw 'invalid url type';
        }

        if(opts.callback && typeof opts.callback !== 'function') {
          throw 'invalid callback type';
        }

        if(opts.url.substr(0, 1) !== '/') {
          opts.url = '/' + opts.url;
        }

        procPacket('GET', '/' + currDatabase + opts.url, null, null, opts.callback);
      },

      decode: function(d) {
        decodeJSON = !! d;
        return publicThat;
      },

      setDatabase: function(db) {
        if(typeof db !== 'string' || db.length <= 0) {
          throw 'invalid database name';
        }

        currDatabase = db;

        return publicThat;
      }
    };

    return publicThat;
  };
})((typeof exports === 'object') ? exports : (this.sag = {}));
<commit_msg>fixing typo<commit_after>(function(exports) {
  exports.server = function(host, port, user, pass) {
    var http;
    var xmlHTTP;

    var decodeJSON = true;
    var currDatabase;

    if(typeof XMLHttpRequest === 'function') {
      xmlHTTP = new XMLHttpRequest();
    }
    else if(typeof ActiveXObject === 'function') {
      xmlHTTP = new ActiveXObject('Microsoft.XMLHTTP');
    }
    else if(typeof require === 'function') {
      http = require('http');
    }
    else {
      throw 'whoops';
    }

    function onResponse(httpCode, headers, body, callback) {
      var resp = {
        _HTTP: {
          status: httpCode
        },
        headers: headers
      };

      if(typeof body === 'string') {
        resp.body = (body.length > 0 && decodeJSON) ? JSON.parse(body) : body;
      }

      callback(resp);
    }

    function procPacket(method, path, data, headers, callback) {
      headers = headers || {};

      headers['User-Agent'] = 'Sag-JS/0.1';

      if(!headers['Content-Type']) {
        headers['Content-Type'] = 'application/json';
      }

      if(http) {
        // Node.JS http module
        http.request(
          {
            method: method,
            host: host,
            port: port,
            path: path,
            headers: headers
          },
          function(res) {
            var resBody = '';

            res.setEncoding('utf8');

            res.on('data', function(chunk) {
              resBody += chunk;
            });

            res.on('end', function() {
              onResponse(res.statusCode, res.headers, resBody, callback);
            });
          }
        ).on('error', function(e) {
          console.log('problem with request: ' + e);
        }).end();
      }
      else if(xmlHTTP) {
        // Browser xhr magik
        xmlHTTP.onreadystatechange = function() {
          if(this.readyState === 4 && this.status > 0) {
            var headers = {};
            var rawHeaders = this.getAllResponseHeaders().split('\n');

            for(var i in rawHeaders) {
              rawHeaders[i] = rawHeaders[i].split(': ');

              if(rawHeaders[i][1]) {
                headers[rawHeaders[i][0]] = rawHeaders[i][1];
              }
            }

            onResponse(this.status, headers, this.response, callback);
          }
        };

        xmlHTTP.open(method, 'http://' + host + ':' + port + path);

        for(var k in headers) {
          if(headers.hasOwnProperty(k)) {
            xmlHTTP.setRequestHeader(k, headers[k]);
          }
        }

        xmlHTTP.send();
      }
      else {
        throw 'coder fail';
      }
    }

    var publicThat = {
      get: function(opts) {
        if(!currDatabase) {
          throw 'You must call setDatabase() first.';
        }

        if(typeof opts !== 'object') {
          throw 'invalid opts object';
        }

        if(typeof opts.url !== 'string') {
          throw 'invalid url type';
        }

        if(opts.callback && typeof opts.callback !== 'function') {
          throw 'invalid callback type';
        }

        if(opts.url.substr(0, 1) !== '/') {
          opts.url = '/' + opts.url;
        }

        procPacket('GET', '/' + currDatabase + opts.url, null, null, opts.callback);
      },

      decode: function(d) {
        decodeJSON = !! d;
        return publicThat;
      },

      setDatabase: function(db) {
        if(typeof db !== 'string' || db.length <= 0) {
          throw 'invalid database name';
        }

        currDatabase = db;

        return publicThat;
      }
    };

    return publicThat;
  };
})((typeof exports === 'object') ? exports : (this.sag = {}));
<|endoftext|>


<commit_before>var http = require("http");
var fs  = require("fs");
var path = require("path");<commit_msg>Added create server function<commit_after>var http = require("http");
var fs  = require("fs");
var path = require("path");

http.createServer(function(req, res){

}).listen(4000); <|endoftext|>


<commit_before>
function Route(sourceAddress, destinationAddress, deployRate = 1) {
  this.sourceAddress = sourceAddress;
  this.destinationAddress = destinationAddress;
  this.deployRate = deployRate;

  this.sendCount = 0;
  this.receivedCount = 0;
  this.receivedStepCount = 0;
  this.transitCount = 0;
  this.efficiency = NaN;

  this.reset = function reset() {
    this.sendCount = 0;
    this.receivedCount = 0;
    this.receivedStepCount = 0;
    this.transitCount = 0;
    this.efficiency = NaN;
  }
}

function createSim(graph) {
  var self = {};

  // Number of steps the simulation has run
  self.simStep = 0;

  // Duration of the last simulation run
  self.simDuration = 0;

  // Routes to deploy packet on
  self.routes = {};

  // Keep track of setTimeout id
  self.timerId = null;

  // Simulation state
  self.running = false;

  function shuffleArray(a) {
    for (let i = a.length; i; i--) {
      let j = Math.floor(Math.random() * i);
      [a[i - 1], a[j]] = [a[j], a[i - 1]];
    }
  }

  self.reset = function reset() {
    var intNodes = graph.getIntNodes();
    var intLinks = graph.getIntLinks();

    for (var i = 0; i < intLinks.length; i++) {
      intLinks[i].o.reset();
    }

    for (var i = 0; i < intNodes.length; i++) {
      intNodes[i].o.reset();
    }

    self.simStep = 0;
    self.simDuration = 0;

    self.clearRouteCounter();

    graph.redraw();
  }

  function num(v, suffix = '') {
    if (isNaN(v)) return '-';
    if (v === 100) return "100%";
    return (v.toFixed(1) + suffix);
  }

  function updateSimStatistics() {
    var packets_unicast = 0;
    var packets_broadcast = 0;

    for (var id in self.routes) {
      self.routes[id].transitCount = 0;
    }

    function countPackets(node, packets) {
      for (var j = 0; j < packets.length; j += 1) {
        var packet = packets[j];
        if ('deployedAtStep' in packet) {
          var id = packet.sourceAddress + '=>' + packet.destinationAddress;
          if (id in self.routes && packet.destinationAddress !== node.mac) {
            self.routes[id].transitCount += 1;
          }
        } else {
          var isBroadcast = (packet.receiverAddress === BROADCAST_MAC);
          packets_unicast += !isBroadcast;
          packets_broadcast += isBroadcast;
        }
      }
    }

    var intNodes = graph.getIntNodes();
    for (var i = 0; i < intNodes.length; i += 1) {
      var node = intNodes[i].o;
      countPackets(node, node.incoming);
      countPackets(node, node.outgoing);
    }

    var routesSend = 0;
    var routesReceived = 0;
    var routesLost = 0;
    var routesTransit = 0;
    var routesCount = 0;
    var routesEfficiencySum = 0;
    var routesEfficiencyCount = 0;

    for (var id in self.routes) {
      var route = self.routes[id];
      routesCount += 1;
      routesSend += route.sendCount;
      routesReceived += route.receivedCount;
      routesTransit += route.transitCount;

      if (!isNaN(route.efficiency)) {
        routesEfficiencySum += route.efficiency;
        routesEfficiencyCount += 1;
      }
    }

    // Convert to medium percent
    var routingEfficiencyPercent = 100 * routesEfficiencySum / routesEfficiencyCount;

    function withPercent(val, all) {
      if (isNaN(all) || isNaN(val)) {
        return '-';
      }
      var ret = '' + val;
      if (all !== 0) {
        ret += ' (' + (100 * val / all).toFixed(1) + '%)';
      }
      return ret;
    };

    $$('sim_steps_total').nodeValue = self.simStep;
    $$('sim_duration').nodeValue = self.simDuration + ' ms';

    $$('packets_broadcast').nodeValue = withPercent(packets_broadcast, packets_unicast + packets_broadcast);
    $$('packets_unicast').nodeValue = withPercent(packets_unicast, packets_unicast + packets_broadcast);
    $$('packets_per_node').nodeValue = intNodes.length ? ((packets_unicast + packets_broadcast) / intNodes.length).toFixed(2) : '-';

    $$('routes_count').nodeValue = routesCount;
    $$('routes_packets_send').nodeValue = routesSend;
    $$('routes_packets_received').nodeValue = withPercent(routesReceived, routesSend);
    $$('routes_packets_transit').nodeValue = withPercent(routesTransit, routesSend);
    $$('routes_packets_lost').nodeValue = withPercent(routesSend - routesReceived - routesTransit, routesSend);
    $$('routing_efficiency').nodeValue = num(routingEfficiencyPercent, '%');

    updateRoutesTable();
  }

  function updateRoutesTable() {
    var tbody = $('sim_routes');

    // Remove all elements
    clearChildren(tbody);

    for (key in self.routes) {
      var route = self.routes[key];
      var tr = append(tbody, 'tr');
      var source_td = append(tr, 'td', route.sourceAddress.slice(-5));
      var target_td = append(tr, 'td', route.destinationAddress.slice(-5));
      append(tr, 'td', route.deployRate);
      append(tr, 'td', route.sendCount);
      append(tr, 'td', num(100 * route.receivedCount / route.sendCount, '%'));
      append(tr, 'td', num(100 * route.transitCount / route.sendCount, '%'));
      append(tr, 'td', num(100 * (route.sendCount - route.receivedCount - route.transitCount) / route.sendCount, '%'));
      append(tr, 'td', num(100 * route.efficiency, '%'));

      // Hover text
      source_td.title = route.sourceAddress
      target_td.title = route.destinationAddress;
    }

    var display = (tbody.children.length === 0);
    displayElement($('sim_no_routes'), display);
  }

  self.delRoutes = function delRoutes() {
    function delRoute(sourceNode, targetNode) {
      var id = sourceNode.mac + '=>' + targetNode.mac;
      delete routes[id];
      updateSimStatistics();
    }

    var intNodes = graph.getSelectedIntNodes();
    if (intNodes.length == 0) {
      alert('Select one source and at least one target node.');
      return;
    }

    var sourceNode = intNodes[0].o;
    for (var i = 1; i < intNodes.length; i += 1) {
      var targetNode = intNodes[i].o;
      delRoute(sourceNode, targetNode);
    }

    updateSimStatistics();
  }

  self.addRoutes = function addRoutes() {
    function addRoute(sourceAddress, destinationAddress) {
      var id = sourceAddress + '=>' + destinationAddress;
      if (!(id in self.routes)) {
        self.routes[id] = new Route(sourceAddress, destinationAddress, 1);
        updateSimStatistics();
      }
    }

    var intNodes = graph.getSelectedIntNodes();
    if (intNodes.length == 0) {
      alert('Select one source and at least one target node.');
      return;
    }

    var sourceAddress = intNodes[0].o.mac;
    for (var i = 1; i < intNodes.length; i += 1) {
      var destinationAddress = intNodes[i].o.mac;
      addRoute(sourceAddress, destinationAddress);
    }

    updateSimStatistics();
  }

  function deployPackets_() {
    var nodes = graph.getIntNodes();
    var nodeMap = {};

    nodes.forEach(function(e) {
      nodeMap[e.o.mac] = e.o;
    });

    for (var id in self.routes) {
      var route = self.routes[id];
      if (randomBoolean(route.deployRate)) {
        var src = route.sourceAddress;
        var dst = route.destinationAddress;
        if (src in nodeMap && dst in nodeMap) {
          var packet = new Packet(src, src, src, dst);
          // For route efficiency calculation
          packet.deployedAtStep = self.simStep;
          nodeMap[src].incoming.push(packet);
          route.sendCount += 1;
        } else {
          console.log('Route does not exists: ' + src  + ' => ' + dst);
        }
      }
    }
  }

  self.deployPackets = function deployPackets() {
    if (isEmpty(self.routes)) {
      alert('No routes set on which to deploy packets.');
      return;
    }

    deployPackets_();
    updateSimStatistics();

    graph.redraw();
  }

  function updateRoutesEfficiency() {
    var intNodes = graph.getIntNodes();
    var intLinks = graph.getIntLinks();
    var dijkstra = createDijkstra(intNodes, intLinks);

    for (var id in self.routes) {
      var route = self.routes[id];
      var sourceIntNode = intNodes.find(function(e) { return e.o.mac === route.sourceAddress; });
      var targetIntNode = intNodes.find(function(e) { return e.o.mac === route.destinationAddress; });
      var shortestDistance = dijkstra.getShortestDistance(sourceIntNode, targetIntNode);
      /*
      * Efficiency as rate of optimal step count in relation to the shortest path of reveived packets.
      */
      route.efficiency = (shortestDistance * route.receivedCount / route.receivedStepCount);
    }
  }

  function packetOnRouteReceived(packet) {
    var id = packet.sourceAddress + '=>' + packet.destinationAddress;
    if (id in self.routes) {
      var route = self.routes[id];
      route.receivedCount += 1;
      route.receivedStepCount += (self.simStep - packet.deployedAtStep);
    }
  }

  self.clearRouteCounter = function clearRouteCounter() {
    for (var id in self.routes) {
      self.routes[id].reset();
    }

    updateSimStatistics();
  }

  self.start = function start(steps, delay, deployPacketsEnabled) {
    function trigger(steps, delay, deployPacketsEnabled) {
      if (steps > 0) {
        self.run(1, deployPacketsEnabled);
        self.timerId = setTimeout(trigger, delay, steps - 1, delay, deployPacketsEnabled);
      } else {
        self.timerId = null;
      }
    }

    if (self.timerId) {
      clearTimeout(self.timerId);
      self.timerId = null;
      return;
    }

    if (!this.running) {
      if (delay > 0) {
        trigger(steps, delay, deployPacketsEnabled);
      } else {
        self.run(steps, deployPacketsEnabled);
      }
    } else {
      alert('Simulation is still running.');
    }
  }

  self.run = function run(steps, deployPacketsEnabled) {
    this.running = true;

    // All links are bidirectional
    function getOtherIntNode(intLink, mac) {
      return (mac === intLink.target.o.mac) ? intLink.source : intLink.target;
    }

    function clonePacket(packet) {
      return JSON.parse(JSON.stringify(packet));
    }

    var intNodes = graph.getIntNodes();
    var intLinks = graph.getIntLinks();

    // Map of internal node index to array of d3 link objects
    var connections = {};

    for (var i = 0; i < intNodes.length; i += 1) {
      var n = intNodes[i];
      connections[n.index] = [];
    }

    for (var i = 0; i < intLinks.length; i += 1) {
      var l = intLinks[i];
      connections[l.source.index].push(l);
      connections[l.target.index].push(l);
    }

    var simStartTime = (new Date()).getTime();

    // Cumulated packet count for each link
    var packetCount = {};

    // Simulation steps
    for (var step = 0; step < steps; step += 1) {
      self.simStep += 1;

      // Initialize packet count
      for (var i = 0; i < intLinks.length; i++) {
        var intLink = intLinks[i];
        packetCount[intLink.index] = 0;
      }

      // Randomize order
      shuffleArray(intNodes);

      // Step nodes
      for (var i = 0; i < intNodes.length; i++) {
        var node = intNodes[i].o;
        node.step();
        node.incoming = [];
      }

      // Propagate packets
      for (var i = 0; i < intNodes.length; i++) {
        var intNode = intNodes[i];
        var intLinks = connections[intNode.index];

        // Randomize order
        shuffleArray(intLinks);

        for (var p = 0; p < intNode.o.outgoing.length; p++) {
          var packet = intNode.o.outgoing[p];
          var isBroadcast = (packet.receiverAddress === BROADCAST_MAC);

          for (var j = 0; j < intLinks.length; j++) {
            var intLink = intLinks[j];
            var otherIntNode = getOtherIntNode(intLink, intNode.o.mac);

            if (isBroadcast || (packet.receiverAddress === otherIntNode.o.mac)) {
              if (isBroadcast) {
                // Necessary for broadcast
                packet = clonePacket(packet);
              }

              // Update count per node and channel to
              // simulate a shared transmission medium
              if (intLink.o.channel > 0) {
                intLinks.forEach(function(l) {
                  if (l.o.channel === intLink.o.channel) {
                    packetCount[l.index] += 1;
                  }
                });
              } else {
                packetCount[intLink.index] += 1;
              }

              if (intLink.o.transmit(packet, packetCount[intLink.index])) {
                // Packet transmitted
                otherIntNode.o.incoming.push(packet);
                if ('deployedAtStep' in packet) {
                  if (packet.destinationAddress === otherIntNode.o.mac) {
                    packetOnRouteReceived(packet);
                  }
                }
              }

              if (!isBroadcast) {
                break;
              }
            }
          }
        }

        // All packets should have been handled
        intNode.o.outgoing = [];
      }

      if (deployPacketsEnabled) {
        deployPackets_();
      }
    }

    self.simDuration = (new Date()).getTime() - simStartTime;

    updateRoutesEfficiency();
    updateSimStatistics();

    graph.redraw();

    this.running = false;
  }

  return self;
}
<commit_msg>reduce updateSimStatistics calls<commit_after>
function Route(sourceAddress, destinationAddress, deployRate = 1) {
  this.sourceAddress = sourceAddress;
  this.destinationAddress = destinationAddress;
  this.deployRate = deployRate;

  this.sendCount = 0;
  this.receivedCount = 0;
  this.receivedStepCount = 0;
  this.transitCount = 0;
  this.efficiency = NaN;

  this.reset = function reset() {
    this.sendCount = 0;
    this.receivedCount = 0;
    this.receivedStepCount = 0;
    this.transitCount = 0;
    this.efficiency = NaN;
  }
}

function createSim(graph) {
  var self = {};

  // Number of steps the simulation has run
  self.simStep = 0;

  // Duration of the last simulation run
  self.simDuration = 0;

  // Routes to deploy packet on
  self.routes = {};

  // Keep track of setTimeout id
  self.timerId = null;

  // Simulation state
  self.running = false;

  function shuffleArray(a) {
    for (let i = a.length; i; i--) {
      let j = Math.floor(Math.random() * i);
      [a[i - 1], a[j]] = [a[j], a[i - 1]];
    }
  }

  self.reset = function reset() {
    var intNodes = graph.getIntNodes();
    var intLinks = graph.getIntLinks();

    for (var i = 0; i < intLinks.length; i++) {
      intLinks[i].o.reset();
    }

    for (var i = 0; i < intNodes.length; i++) {
      intNodes[i].o.reset();
    }

    self.simStep = 0;
    self.simDuration = 0;

    self.clearRouteCounter();

    graph.redraw();
  }

  function num(v, suffix = '') {
    if (isNaN(v)) return '-';
    if (v === 100) return "100%";
    return (v.toFixed(1) + suffix);
  }

  function updateSimStatistics() {
    var packets_unicast = 0;
    var packets_broadcast = 0;

    for (var id in self.routes) {
      self.routes[id].transitCount = 0;
    }

    function countPackets(node, packets) {
      for (var j = 0; j < packets.length; j += 1) {
        var packet = packets[j];
        if ('deployedAtStep' in packet) {
          var id = packet.sourceAddress + '=>' + packet.destinationAddress;
          if (id in self.routes && packet.destinationAddress !== node.mac) {
            self.routes[id].transitCount += 1;
          }
        } else {
          var isBroadcast = (packet.receiverAddress === BROADCAST_MAC);
          packets_unicast += !isBroadcast;
          packets_broadcast += isBroadcast;
        }
      }
    }

    var intNodes = graph.getIntNodes();
    for (var i = 0; i < intNodes.length; i += 1) {
      var node = intNodes[i].o;
      countPackets(node, node.incoming);
      countPackets(node, node.outgoing);
    }

    var routesSend = 0;
    var routesReceived = 0;
    var routesLost = 0;
    var routesTransit = 0;
    var routesCount = 0;
    var routesEfficiencySum = 0;
    var routesEfficiencyCount = 0;

    for (var id in self.routes) {
      var route = self.routes[id];
      routesCount += 1;
      routesSend += route.sendCount;
      routesReceived += route.receivedCount;
      routesTransit += route.transitCount;

      if (!isNaN(route.efficiency)) {
        routesEfficiencySum += route.efficiency;
        routesEfficiencyCount += 1;
      }
    }

    // Convert to medium percent
    var routingEfficiencyPercent = 100 * routesEfficiencySum / routesEfficiencyCount;

    function withPercent(val, all) {
      if (isNaN(all) || isNaN(val)) {
        return '-';
      }
      var ret = '' + val;
      if (all !== 0) {
        ret += ' (' + (100 * val / all).toFixed(1) + '%)';
      }
      return ret;
    };

    $$('sim_steps_total').nodeValue = self.simStep;
    $$('sim_duration').nodeValue = self.simDuration + ' ms';

    $$('packets_broadcast').nodeValue = withPercent(packets_broadcast, packets_unicast + packets_broadcast);
    $$('packets_unicast').nodeValue = withPercent(packets_unicast, packets_unicast + packets_broadcast);
    $$('packets_per_node').nodeValue = intNodes.length ? ((packets_unicast + packets_broadcast) / intNodes.length).toFixed(2) : '-';

    $$('routes_count').nodeValue = routesCount;
    $$('routes_packets_send').nodeValue = routesSend;
    $$('routes_packets_received').nodeValue = withPercent(routesReceived, routesSend);
    $$('routes_packets_transit').nodeValue = withPercent(routesTransit, routesSend);
    $$('routes_packets_lost').nodeValue = withPercent(routesSend - routesReceived - routesTransit, routesSend);
    $$('routing_efficiency').nodeValue = num(routingEfficiencyPercent, '%');

    updateRoutesTable();
  }

  function updateRoutesTable() {
    var tbody = $('sim_routes');

    // Remove all elements
    clearChildren(tbody);

    for (key in self.routes) {
      var route = self.routes[key];
      var tr = append(tbody, 'tr');
      var source_td = append(tr, 'td', route.sourceAddress.slice(-5));
      var target_td = append(tr, 'td', route.destinationAddress.slice(-5));
      append(tr, 'td', route.deployRate);
      append(tr, 'td', route.sendCount);
      append(tr, 'td', num(100 * route.receivedCount / route.sendCount, '%'));
      append(tr, 'td', num(100 * route.transitCount / route.sendCount, '%'));
      append(tr, 'td', num(100 * (route.sendCount - route.receivedCount - route.transitCount) / route.sendCount, '%'));
      append(tr, 'td', num(100 * route.efficiency, '%'));

      // Hover text
      source_td.title = route.sourceAddress
      target_td.title = route.destinationAddress;
    }

    var display = (tbody.children.length === 0);
    displayElement($('sim_no_routes'), display);
  }

  self.delRoutes = function delRoutes() {
    function delRoute(sourceNode, targetNode) {
      var id = sourceNode.mac + '=>' + targetNode.mac;
      delete routes[id];
    }

    var intNodes = graph.getSelectedIntNodes();
    if (intNodes.length == 0) {
      alert('Select one source and at least one target node.');
      return;
    }

    var sourceNode = intNodes[0].o;
    for (var i = 1; i < intNodes.length; i += 1) {
      var targetNode = intNodes[i].o;
      delRoute(sourceNode, targetNode);
    }

    updateSimStatistics();
  }

  self.addRoutes = function addRoutes() {
    function addRoute(sourceAddress, destinationAddress) {
      var id = sourceAddress + '=>' + destinationAddress;
      if (!(id in self.routes)) {
        self.routes[id] = new Route(sourceAddress, destinationAddress, 1);
      }
    }

    var intNodes = graph.getSelectedIntNodes();
    if (intNodes.length == 0) {
      alert('Select one source and at least one target node.');
      return;
    }

    var sourceAddress = intNodes[0].o.mac;
    for (var i = 1; i < intNodes.length; i += 1) {
      var destinationAddress = intNodes[i].o.mac;
      addRoute(sourceAddress, destinationAddress);
    }

    updateSimStatistics();
  }

  function deployPackets_() {
    var nodes = graph.getIntNodes();
    var nodeMap = {};

    nodes.forEach(function(e) {
      nodeMap[e.o.mac] = e.o;
    });

    for (var id in self.routes) {
      var route = self.routes[id];
      if (randomBoolean(route.deployRate)) {
        var src = route.sourceAddress;
        var dst = route.destinationAddress;
        if (src in nodeMap && dst in nodeMap) {
          var packet = new Packet(src, src, src, dst);
          // For route efficiency calculation
          packet.deployedAtStep = self.simStep;
          nodeMap[src].incoming.push(packet);
          route.sendCount += 1;
        } else {
          console.log('Route does not exists: ' + src  + ' => ' + dst);
        }
      }
    }
  }

  self.deployPackets = function deployPackets() {
    if (isEmpty(self.routes)) {
      alert('No routes set on which to deploy packets.');
      return;
    }

    deployPackets_();
    updateSimStatistics();

    graph.redraw();
  }

  function updateRoutesEfficiency() {
    var intNodes = graph.getIntNodes();
    var intLinks = graph.getIntLinks();
    var dijkstra = createDijkstra(intNodes, intLinks);

    for (var id in self.routes) {
      var route = self.routes[id];
      var sourceIntNode = intNodes.find(function(e) { return e.o.mac === route.sourceAddress; });
      var targetIntNode = intNodes.find(function(e) { return e.o.mac === route.destinationAddress; });
      var shortestDistance = dijkstra.getShortestDistance(sourceIntNode, targetIntNode);
      /*
      * Efficiency as rate of optimal step count in relation to the shortest path of reveived packets.
      */
      route.efficiency = (shortestDistance * route.receivedCount / route.receivedStepCount);
    }
  }

  function packetOnRouteReceived(packet) {
    var id = packet.sourceAddress + '=>' + packet.destinationAddress;
    if (id in self.routes) {
      var route = self.routes[id];
      route.receivedCount += 1;
      route.receivedStepCount += (self.simStep - packet.deployedAtStep);
    }
  }

  self.clearRouteCounter = function clearRouteCounter() {
    for (var id in self.routes) {
      self.routes[id].reset();
    }

    updateSimStatistics();
  }

  self.start = function start(steps, delay, deployPacketsEnabled) {
    function trigger(steps, delay, deployPacketsEnabled) {
      if (steps > 0) {
        self.run(1, deployPacketsEnabled);
        self.timerId = setTimeout(trigger, delay, steps - 1, delay, deployPacketsEnabled);
      } else {
        self.timerId = null;
      }
    }

    if (self.timerId) {
      clearTimeout(self.timerId);
      self.timerId = null;
      return;
    }

    if (!this.running) {
      if (delay > 0) {
        trigger(steps, delay, deployPacketsEnabled);
      } else {
        self.run(steps, deployPacketsEnabled);
      }
    } else {
      alert('Simulation is still running.');
    }
  }

  self.run = function run(steps, deployPacketsEnabled) {
    this.running = true;

    // All links are bidirectional
    function getOtherIntNode(intLink, mac) {
      return (mac === intLink.target.o.mac) ? intLink.source : intLink.target;
    }

    function clonePacket(packet) {
      return JSON.parse(JSON.stringify(packet));
    }

    var intNodes = graph.getIntNodes();
    var intLinks = graph.getIntLinks();

    // Map of internal node index to array of d3 link objects
    var connections = {};

    for (var i = 0; i < intNodes.length; i += 1) {
      var n = intNodes[i];
      connections[n.index] = [];
    }

    for (var i = 0; i < intLinks.length; i += 1) {
      var l = intLinks[i];
      connections[l.source.index].push(l);
      connections[l.target.index].push(l);
    }

    var simStartTime = (new Date()).getTime();

    // Cumulated packet count for each link
    var packetCount = {};

    // Simulation steps
    for (var step = 0; step < steps; step += 1) {
      self.simStep += 1;

      // Initialize packet count
      for (var i = 0; i < intLinks.length; i++) {
        var intLink = intLinks[i];
        packetCount[intLink.index] = 0;
      }

      // Randomize order
      shuffleArray(intNodes);

      // Step nodes
      for (var i = 0; i < intNodes.length; i++) {
        var node = intNodes[i].o;
        node.step();
        node.incoming = [];
      }

      // Propagate packets
      for (var i = 0; i < intNodes.length; i++) {
        var intNode = intNodes[i];
        var intLinks = connections[intNode.index];

        // Randomize order
        shuffleArray(intLinks);

        for (var p = 0; p < intNode.o.outgoing.length; p++) {
          var packet = intNode.o.outgoing[p];
          var isBroadcast = (packet.receiverAddress === BROADCAST_MAC);

          for (var j = 0; j < intLinks.length; j++) {
            var intLink = intLinks[j];
            var otherIntNode = getOtherIntNode(intLink, intNode.o.mac);

            if (isBroadcast || (packet.receiverAddress === otherIntNode.o.mac)) {
              if (isBroadcast) {
                // Necessary for broadcast
                packet = clonePacket(packet);
              }

              // Update count per node and channel to
              // simulate a shared transmission medium
              if (intLink.o.channel > 0) {
                intLinks.forEach(function(l) {
                  if (l.o.channel === intLink.o.channel) {
                    packetCount[l.index] += 1;
                  }
                });
              } else {
                packetCount[intLink.index] += 1;
              }

              if (intLink.o.transmit(packet, packetCount[intLink.index])) {
                // Packet transmitted
                otherIntNode.o.incoming.push(packet);
                if ('deployedAtStep' in packet) {
                  if (packet.destinationAddress === otherIntNode.o.mac) {
                    packetOnRouteReceived(packet);
                  }
                }
              }

              if (!isBroadcast) {
                break;
              }
            }
          }
        }

        // All packets should have been handled
        intNode.o.outgoing = [];
      }

      if (deployPacketsEnabled) {
        deployPackets_();
      }
    }

    self.simDuration = (new Date()).getTime() - simStartTime;

    updateRoutesEfficiency();
    updateSimStatistics();

    graph.redraw();

    this.running = false;
  }

  return self;
}
<|endoftext|>


<commit_before>'use strict';

describe('ApiController', function(){
  var scope, ctrl, location, $httpBackend;
  var link = {
    href: "/api/sign_in",
    rel: "authentication",
    name: "email",
    prompt: "Sign in with email"
  };
  var image = {
    href: "/assets/cat.gif",
    rel: "My cat is cooler",
    render: "image"
  };
  var responseData = {
    collection: {
      version: "1.0",
      href: "/api",
      links: [ link, image ],
      template: {
        data: [
          {name: ''},
          {age: 2}
        ]
      }
    }
  }

  beforeEach(inject(function(_$httpBackend_, $location, $rootScope, $controller) {
    $httpBackend = _$httpBackend_;
    var root = "http://localhost:3000"
    $httpBackend.when('GET', '/api').respond(responseData);
    $httpBackend.when('GET', '/api/posts').respond(responseData);
    $httpBackend.when('POST', '/api').respond(responseData);
    $httpBackend.when('PUT', '/api/posts/1').respond(responseData);
    $httpBackend.when('DELETE', '/api/posts/1').respond(responseData);

    scope = $rootScope.$new();
    location = $location;
    ctrl = $controller(ApiController, {$scope: scope});
  }));

  it('sets collection model from the collection', function() {
    expect(scope.collection).toBeUndefined();
    $httpBackend.flush();

    expect(scope.collection).toEqual(responseData.collection);
  });

  it('sets the raw model from response data', function() {
    expect(scope.raw).toBeUndefined();
    $httpBackend.flush();

    expect(JSON.parse(scope.raw)).toEqual(responseData);
  });

  it('sets location path', function(){
    expect(location.path()).toEqual('');
    $httpBackend.flush();

    expect(location.path()).toEqual('/api')
  })

  describe('#goTo', function(){
    beforeEach(function(){
      spyOn(location, 'url')
    })

    it('stores fromRel', function(){
      scope.goTo('/api/posts', 'some-rel')
      expect(scope.fromRel).toEqual('some-rel')
    })

    it('sets location path', function(){
      scope.goTo('/api/posts', 'some-rel')
      $httpBackend.flush();
      expect(location.url).toHaveBeenCalledWith('/api/posts')
    })
  })

  describe('#submit', function(){
    beforeEach(function(){
      scope.collection = responseData.collection
    })

    it('post the form when fromRel is not edit-form', function(){
      $httpBackend.expectPOST('/api')

      scope.fromRel = 'some-stuff'
      scope.submit()

      $httpBackend.flush()
    })

    it('put the form when fromRel is edit-form', function(){
      $httpBackend.expectPUT('/api/posts/1')
      spyOn(location, 'url').andReturn('/api/posts/1')

      scope.fromRel = 'edit-form'
      scope.submit()

      $httpBackend.flush()
    })
  })

  describe('#destroy', function(){
    it('deletes the resource', function(){
      $httpBackend.expectDELETE('/api/posts/1')

      scope.destroy('/api/posts/1')

      $httpBackend.flush()
    })
  })
});
<commit_msg>minor test change<commit_after>'use strict';

describe('ApiController', function(){
  var scope, ctrl, location, $httpBackend;
  var link = {
    href: "/api/sign_in",
    rel: "authentication",
    name: "email",
    prompt: "Sign in with email"
  };
  var image = {
    href: "/assets/cat.gif",
    rel: "My cat is cooler",
    render: "image"
  };
  var responseData = {
    collection: {
      version: "1.0",
      href: "/api",
      links: [ link, image ],
      template: {
        data: [
          {name: ''},
          {age: 2}
        ]
      }
    }
  }

  beforeEach(inject(function(_$httpBackend_, $location, $rootScope, $controller) {
    $httpBackend = _$httpBackend_;
    var root = "http://localhost:3000"
    $httpBackend.when('GET', '/api').respond(responseData);
    $httpBackend.when('GET', '/api/posts').respond(responseData);
    $httpBackend.when('POST', '/api').respond(responseData);
    $httpBackend.when('PUT', '/api/posts/1').respond(responseData);
    $httpBackend.when('DELETE', '/api/posts/1').respond(responseData);

    scope = $rootScope.$new();
    location = $location;
    ctrl = $controller(ApiController, {$scope: scope});
  }));

  it('sets collection model from the collection', function() {
    expect(scope.collection).toBeUndefined();
    $httpBackend.flush();

    expect(scope.collection).toEqual(responseData.collection);
  });

  it('sets the raw model from response data', function() {
    expect(scope.raw).toBeUndefined();
    $httpBackend.flush();

    expect(JSON.parse(scope.raw)).toEqual(responseData);
  });

  it('sets location path', function(){
    expect(location.path()).toEqual('');
    $httpBackend.flush();

    expect(location.path()).toEqual('/api')
  })

  describe('#goTo', function(){
    beforeEach(function(){
      spyOn(location, 'url')
    })

    it('stores fromRel', function(){
      scope.goTo('/api/posts', 'some-rel')
      expect(scope.fromRel).toEqual('some-rel')
    })

    it('sets location path', function(){
      scope.goTo('/api/posts', 'some-rel')
      $httpBackend.flush();
      expect(location.url).toHaveBeenCalledWith('/api/posts')
    })
  })

  describe('#submit', function(){
    beforeEach(function(){
      scope.collection = responseData.collection
    })

    it('post the form when fromRel is not edit-form', function(){
      $httpBackend.expectPOST('/api')

      scope.fromRel = 'some-stuff'
      scope.submit()

      $httpBackend.flush()
    })

    it('put the form when fromRel is edit-form', function(){
      spyOn(location, 'url').andReturn('/api/posts/1')
      $httpBackend.expectPUT('/api/posts/1')

      scope.fromRel = 'edit-form'
      scope.submit()

      $httpBackend.flush()
    })
  })

  describe('#destroy', function(){
    it('deletes the resource', function(){
      $httpBackend.expectDELETE('/api/posts/1')

      scope.destroy('/api/posts/1')

      $httpBackend.flush()
    })
  })
});
<|endoftext|>


<commit_before>var http = require("http");
var fs  = require("fs");
var path = require("path");

http.createServer(function(req, res){
    console.log(`${req.method} request for ${req.url}`);

}).listen(4000); <commit_msg>Mapping root path for file server / Serve HTML ContentType<commit_after>var http = require("http");
var fs  = require("fs");
var path = require("path");

http.createServer(function(req, res){
    console.log(`${req.method} request for ${req.url}`);

    if (req.url === "/"){
        fs.readFile("./public/index.html", "UTF-8", function(err, html){
        res.writeHead(200 ,{"Content-Type":"text/html"} );
        res.end(html);

        });

}).listen(4000); <|endoftext|>