<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.efficiency = NaN;

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

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

  self.sim_steps_total = 0;
  self.sim_duration = 0;
  self.routes = {};

  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();
    for (var i = 0; i < intNodes.length; i++) {
      intNodes[i].o.reset();
    }

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

    self.sim_steps_total = 0;
    self.sim_duration = 0;

    updateSimStatistics();
  }

  function num(v, suffix = '') {
    return isNaN(v) ? '-' : (v.toString() + suffix);
  }

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

    var packetsBroadcastCount = 0;
    var packetsUnicastCount = 0;
    var routesSendCount = 0;
    var routesReceivedCount = 0;
    var routesTransitCount = 0;
    var routesCount = 0;

    var routingEfficiencySum = 0;
    var routingEfficiencyCount = 0;

    function countPackets(packet) {
      if (packet.destinationMAC === BROADCAST_MAC) {
        packetsBroadcastCount += 1;
      } else {
        packetsUnicastCount += 1;

        var id = packet.sourceAddress + '=>' + packet.destinationMAC;
        routesTransitCount += (id in self.routes);
      }
    }

    for (var i in intNodes) {
      var node = intNodes[i].o;
      node.incoming.forEach(countPackets);
      node.outgoing.forEach(countPackets);
    }

    for (var id in self.routes) {
      var route = self.routes[id];
      routesCount += 1;
      routesSendCount += route.sendCount;
      routesReceivedCount += route.receivedCount;

      if (!isNaN(route.efficiency)) {
        routingEfficiencySum += route.efficiency;
        routingEfficiencyCount += 1;
      }
    }

    // Convert to medium percent
    var routingEfficiency = 100 * routingEfficiencySum / routingEfficiencyCount;

    $$('sim_steps_total').nodeValue = self.sim_steps_total;
    $$('sim_duration').nodeValue = (self.sim_duration / 1000);

    $$('packets_unicast_count').nodeValue = packetsUnicastCount
    $$('packets_broadcast_count').nodeValue = packetsBroadcastCount;

    function percent(value) {
      var p = (100 * value / routesSendCount);
      return isNaN(p) ? '' : (' (' + p + '%)');
    };

    $$('routes_count').nodeValue = routesCount;
    $$('routes_packets_send').nodeValue = routesSendCount;
    $$('routes_packets_received').nodeValue = routesReceivedCount + percent(routesReceivedCount);
    $$('routes_packets_transit').nodeValue = routesTransitCount + percent(routesTransitCount);
    var routesLostCount = (routesSendCount - routesReceivedCount - routesTransitCount);
    $$('routes_packets_lost').nodeValue = routesLostCount + percent(routesLostCount);

    $$('routing_efficiency').nodeValue = num(routingEfficiency);
  }

  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', route.receivedCount);
      append(tr, 'td', num(route.efficiency, '%'));

      source_td.title = route.sourceAddress
      target_td.title = route.destinationAddress;
    }
  }

  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);
    }
    updateRoutesTable();
  }

  self.addRoutes = function addRoutes() {
    function addRoute(sourceNode, targetNode) {
      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);
    }
    updateRoutesTable();
  }

  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;
        nodeMap[src].incoming.push(
          new Packet(src, src, src, dst, self.sim_steps_total)
        );
        route.sendCount += 1
      }
    }

    updateRoutesTable();
    updateSimStatistics();
  }

  self.deployPackets = function deployPackets() {
    deployPackets_();
    graph.redraw();
  }

  function updateRouteEfficiency() {
    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 weighted by rate of received packets.
      * This means packets in transit are counted as lost packets.
      */
      route.efficiency = (route.receivedStepCount / (route.receivedCount * shortestDistance)) * (route.receivedCount / route.sendCount);
    }
  }

  self.step = function step(steps_id, deploy_id) {
    var steps = getInteger(steps_id);
    var deployPacketsEnabled = getBoolean(deploy_id);

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

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

    intNodes.forEach(function(n) {
      connections[n.index] = [];
    });

    intLinks.forEach(function(l) {
      connections[l.source.index].push(l);
      connections[l.target.index].push(l);
    });

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

    // Get other part of the link
    function getNeighbor(intLink, intNode) {
      return (intLink.source.index !== intNode.index) ? intLink.source : intLink.target;
    }

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

    // _very_ simple simulation
    var len = intNodes.length;
    for (var step = 0; step < steps; step += 1) {
      self.sim_steps_total += 1;

      if (deployPacketsEnabled) {
        deployPackets_();
      }

      shuffleArray(intNodes);

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

      // Move packets over links
      for (var i = 0; i < len; i++) {
        var intNode = intNodes[i];
        var intLinks = connections[intNode.index];
        // Send outgoing packets over links
        for (var p = 0; p < intNode.o.outgoing.length; p++) {
          var packet = intNode.o.outgoing[p];
          if (packet.receiverAddress === BROADCAST_MAC) {
            // Handle broadcast packet
            // Send cloned apcket to all neighbors
            for (var k = 0; k < intLinks.length; k++) {
              var intLink = intLinks[k];
              var intNeigh = getNeighbor(intLink, intNode);
              packet = clonePacket(packet);

              if (randomBoolean(intLink.quality / 100)) {
                intNeigh.o.incoming.push(packet);
              } else {
                // Packet is lost
              }
            }
          } else {
            // Handle unicast packet
            var targetFound = false;
            // Send to one neighbor
            for (var k = 0; k < intLinks.length; k++) {
              var intLink = intLinks[k];
              var intNeigh = getNeighbor(intLink, intNode);
              if (packet.receiverAddress === intNeigh.o.mac) {
                targetFound = true;
                if (randomBoolean(intLink.quality / 100)) {
                  intNeigh.o.incoming.push(packet);
                  // Update route stats if packet reached final destination
                  if (packet.destinationMAC === intNeigh.o.mac) {
                    var id = packet.sourceAddress + '=>' + packet.destinationMAC;
                    if (id in self.routes) {
                      var route = self.routes[id];
                      route.receivedCount += 1;
                      route.receivedStepCount += (self.sim_steps_total - packet.step);
                    }
                  }
                }
                break;
              }

              if (!targetFound) {
                console.log('packet lost because next target not found: ' + packet.receiverAddress);
              }
            }
          }
        }

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

    self.sim_duration = date.getTime() - simStartTime;

    updateRouteEfficiency();
    updateSimStatistics();

    graph.redraw();
  }

  return self;
}<commit_msg>refactor sim code<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.efficiency = NaN;

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

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

  self.sim_steps_total = 0;
  self.sim_duration = 0;
  self.routes = {};

  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();
    for (var i = 0; i < intNodes.length; i++) {
      intNodes[i].o.reset();
    }

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

    self.sim_steps_total = 0;
    self.sim_duration = 0;

    updateSimStatistics();
  }

  function num(v, suffix = '') {
    return isNaN(v) ? '-' : (v.toString() + suffix);
  }

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

    var packetsBroadcastCount = 0;
    var packetsUnicastCount = 0;
    var routesSendCount = 0;
    var routesReceivedCount = 0;
    var routesTransitCount = 0;
    var routesCount = 0;

    var routingEfficiencySum = 0;
    var routingEfficiencyCount = 0;

    function countPackets(packet) {
      if (packet.destinationMAC === BROADCAST_MAC) {
        packetsBroadcastCount += 1;
      } else {
        packetsUnicastCount += 1;

        var id = packet.sourceAddress + '=>' + packet.destinationMAC;
        routesTransitCount += (id in self.routes);
      }
    }

    for (var i in intNodes) {
      var node = intNodes[i].o;
      node.incoming.forEach(countPackets);
      node.outgoing.forEach(countPackets);
    }

    for (var id in self.routes) {
      var route = self.routes[id];
      routesCount += 1;
      routesSendCount += route.sendCount;
      routesReceivedCount += route.receivedCount;

      if (!isNaN(route.efficiency)) {
        routingEfficiencySum += route.efficiency;
        routingEfficiencyCount += 1;
      }
    }

    // Convert to medium percent
    var routingEfficiency = 100 * routingEfficiencySum / routingEfficiencyCount;

    $$('sim_steps_total').nodeValue = self.sim_steps_total;
    $$('sim_duration').nodeValue = (self.sim_duration / 1000);

    $$('packets_unicast_count').nodeValue = packetsUnicastCount
    $$('packets_broadcast_count').nodeValue = packetsBroadcastCount;

    function percent(value) {
      var p = (100 * value / routesSendCount);
      return isNaN(p) ? '' : (' (' + p + '%)');
    };

    $$('routes_count').nodeValue = routesCount;
    $$('routes_packets_send').nodeValue = routesSendCount;
    $$('routes_packets_received').nodeValue = routesReceivedCount + percent(routesReceivedCount);
    $$('routes_packets_transit').nodeValue = routesTransitCount + percent(routesTransitCount);
    var routesLostCount = (routesSendCount - routesReceivedCount - routesTransitCount);
    $$('routes_packets_lost').nodeValue = routesLostCount + percent(routesLostCount);

    $$('routing_efficiency').nodeValue = num(routingEfficiency);
  }

  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', route.receivedCount);
      append(tr, 'td', num(route.efficiency, '%'));

      source_td.title = route.sourceAddress
      target_td.title = route.destinationAddress;
    }
  }

  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);
    }
    updateRoutesTable();
  }

  self.addRoutes = function addRoutes() {
    function addRoute(sourceNode, targetNode) {
      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);
    }
    updateRoutesTable();
  }

  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;
        nodeMap[src].incoming.push(
          new Packet(src, src, src, dst, self.sim_steps_total)
        );
        route.sendCount += 1
      }
    }

    updateRoutesTable();
    updateSimStatistics();
  }

  self.deployPackets = function deployPackets() {
    deployPackets_();
    graph.redraw();
  }

  function updateRouteEfficiency() {
    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 weighted by rate of received packets.
      * This means packets in transit are counted as lost packets.
      */
      route.efficiency = (route.receivedStepCount / (route.receivedCount * shortestDistance)) * (route.receivedCount / route.sendCount);
    }
  }

  function propagateBroadcastPacket(packet, intNode, intLinks) {
    // Send cloned packet to all neighbors
    for (var k = 0; k < intLinks.length; k++) {
      var intLink = intLinks[k];
      var intNeigh = getNeighbor(intLink, intNode);
      packet = clonePacket(packet);

      if (randomBoolean(intLink.quality / 100)) {
        intNeigh.o.incoming.push(packet);
      } else {
        // Packet is lost
      }
    }
  }

  function propagateUnicastPacket(packet, intNode, intLinks) {
    function updateRouteStats(route, packet) {
      var id = packet.sourceAddress + '=>' + packet.destinationMAC;
      if (id in self.routes) {
        var route = self.routes[id];
        route.receivedCount += 1;
        route.receivedStepCount += (self.sim_steps_total - packet.step);
      }
    }

    // Send to one neighbor
    for (var k = 0; k < intLinks.length; k++) {
      var intLink = intLinks[k];
      var intNeigh = getNeighbor(intLink, intNode);
      if (packet.receiverAddress === intNeigh.o.mac) {
        if (randomBoolean(intLink.quality / 100)) {
          intNeigh.o.incoming.push(packet);
          // Final destination reached
          if (packet.destinationMAC === intNeigh.o.mac) {
            updateRouteStats(route, packet);
          }
        }
        break;
      }
    }
  }

  self.step = function step(steps_id, deploy_id) {
    var steps = getInteger(steps_id);
    var deployPacketsEnabled = getBoolean(deploy_id);

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

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

    intNodes.forEach(function(n) {
      connections[n.index] = [];
    });

    intLinks.forEach(function(l) {
      connections[l.source.index].push(l);
      connections[l.target.index].push(l);
    });

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

    // Get other part of the link
    function getNeighbor(intLink, intNode) {
      return (intLink.source.index !== intNode.index) ? intLink.source : intLink.target;
    }

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

    // _very_ simple simulation
    var len = intNodes.length;
    for (var step = 0; step < steps; step += 1) {
      self.sim_steps_total += 1;

      if (deployPacketsEnabled) {
        deployPackets_();
      }

      shuffleArray(intNodes);

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

      // Propagate packets
      for (var i = 0; i < len; i++) {
        var intNode = intNodes[i];
        var intLinks = connections[intNode.index];
        for (var p = 0; p < intNode.o.outgoing.length; p++) {
          var packet = intNode.o.outgoing[p];
          if (packet.receiverAddress === BROADCAST_MAC) {
            propagateBroadcastPacket(packet, intNode, intLinks);
          } else {
            propagateUnicastPacket(packet, intNode, intLinks);
          }
        }

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

    self.sim_duration = date.getTime() - simStartTime;

    updateRouteEfficiency();
    updateSimStatistics();

    graph.redraw();
  }

  return self;
}<|endoftext|>


<commit_before>/**
 * knockout binding for Froala Editor
 */

(function() { 'use strict';

  // locals
  var unwrap = ko.utils.unwrapObservable;

  /**
   * initiate froala editor, listen to its changes
   * and updates the underlying observable model
   *
   * @param {element} element
   * @param {object} value
   * @param {object} bindings
   * @api public
   */

  function init( element, value, bindings ) {
    var $el = $( element );
    var model = value();
    var allBindings = unwrap( bindings() );
    var options = ko.toJS( allBindings.froalaOptions );

    // initialize the editor
    $el.froalaEditor( options || {} );

    // provide froala editor instance for flexibility
    if( allBindings.froalaInstance && ko.isWriteableObservable( allBindings.froalaInstance ) ) {
      allBindings.froalaInstance( $el.data( 'froala.editor' ) );
    }

    // update underlying model whenever editor content changed
    $el.on('froalaEditor.contentChanged', function (e, editor) {
      if( ko.isWriteableObservable( model ) ) {
        var editorValue = editor.html.get();
        var current = model();
        if(current !== editorValue) {
          model( editorValue );
        }
      }
    });

    // cleanup editor, when dom node is removed
    ko.utils.domNodeDisposal.addDisposeCallback( element, destroy( element ) );

    // do not handle child nodes
    return { controlsDescendantBindings: true };
  }

  /**
   * update froala editor whenever underlying observable model
   * is updated
   *
   * @param {element} element
   * @param {object} value
   * @api public
   */

  function update( element, value ) {
    var $el = $( element );
    var modelValue = unwrap( value() );
    var editorInstance = $el.data( 'froala.editor' );

    if( editorInstance == null ) {
      return;
    }

    var editorValue = editorInstance.html.get();

    // avoid any un-necessary updates
    if( editorValue !== modelValue && (typeof modelValue === 'string'  || modelValue === null)) {
      editorInstance.html.set( modelValue );
    }
  }

  /**
   * destroy froala editor instance
   *
   * @param {dom} element
   * @return {function} handler
   * @api private
   */

  function destroy( element ) {
    var $el = $( element );
    return function() {
      if( $el.data( 'froala.editor' ) ) {
        $el.froalaEditor( 'destroy' );
      }
    }
  }

  /**
   * expose `froala` binding handler methods
   */

  ko.bindingHandlers.froala = {
    init: init,
    update: update
  }

})();
<commit_msg>Updates model on paste-event in editor. Otherwise one-line-paste-operations are not updated.<commit_after>/**
 * knockout binding for Froala Editor
 */

(function() { 'use strict';

  // locals
  var unwrap = ko.utils.unwrapObservable;

  /**
   * initiate froala editor, listen to its changes
   * and updates the underlying observable model
   *
   * @param {element} element
   * @param {object} value
   * @param {object} bindings
   * @api public
   */

  function init( element, value, bindings ) {
    var $el = $( element );
    var model = value();
    var allBindings = unwrap( bindings() );
    var options = ko.toJS( allBindings.froalaOptions );

    // initialize the editor
    $el.froalaEditor( options || {} );

    // provide froala editor instance for flexibility
    if( allBindings.froalaInstance && ko.isWriteableObservable( allBindings.froalaInstance ) ) {
      allBindings.froalaInstance( $el.data( 'froala.editor' ) );
    }

    // update underlying model whenever editor content changed
    var processUpdateEvent = function (e, editor) {
        if (ko.isWriteableObservable(model)) {
            var editorValue = editor.html.get();
            var current = model();
            if (current !== editorValue) {
                model(editorValue);
            }
        }
    }

    $el.on('froalaEditor.contentChanged', processUpdateEvent);
    $el.on('froalaEditor.paste.after', processUpdateEvent);

    // cleanup editor, when dom node is removed
    ko.utils.domNodeDisposal.addDisposeCallback( element, destroy( element ) );

    // do not handle child nodes
    return { controlsDescendantBindings: true };
  }

  /**
   * update froala editor whenever underlying observable model
   * is updated
   *
   * @param {element} element
   * @param {object} value
   * @api public
   */

  function update( element, value ) {
    var $el = $( element );
    var modelValue = unwrap( value() );
    var editorInstance = $el.data( 'froala.editor' );

    if( editorInstance == null ) {
      return;
    }

    var editorValue = editorInstance.html.get();

    // avoid any un-necessary updates
    if( editorValue !== modelValue && (typeof modelValue === 'string'  || modelValue === null)) {
      editorInstance.html.set( modelValue );
    }
  }

  /**
   * destroy froala editor instance
   *
   * @param {dom} element
   * @return {function} handler
   * @api private
   */

  function destroy( element ) {
    var $el = $( element );
    return function() {
      if( $el.data( 'froala.editor' ) ) {
        $el.froalaEditor( 'destroy' );
      }
    }
  }

  /**
   * expose `froala` binding handler methods
   */

  ko.bindingHandlers.froala = {
    init: init,
    update: update
  }

})();
<|endoftext|>


<commit_before>/*
Language: Swift
Author: Chris Eidhof <chris@eidhof.nl>
*/

function(hljs) {
  var SWIFT_KEYWORDS =
    'class deinit enum extension func import init let protocol static ' +
    'struct subscript typealias var break case continue default do ' +
    'else fallthrough if in for return switch where while as dynamicType ' +
    'is new super self Self Type __COLUMN__ __FILE__ __FUNCTION__ ' +
    '__LINE__ associativity didSet get infix inout left mutating none ' +
    'nonmutating operator override postfix precedence prefix right set '+
    'unowned unowned safe unsafe weak willSet';
  var TYPE = {
    className: 'type',
    begin: '\\b[A-Z][\\w\']*',
    relevance: 0
  };
  var BLOCK_COMMENT = {
    className: 'comment',
    begin: '/\\*', end: '\\*/',
    contains: [hljs.PHRASAL_WORDS_MODE, 'self']
  };
  var SUBST = {
    className: 'subst',
    begin: /\\\(/, end: '\\)',
    keywords: SWIFT_KEYWORDS,
    contains: [hljs.C_NUMBER_MODE, hljs.TITLE_MODE, hljs.QUOTE_STRING_MODE]
  };
  var QUOTE_STRING_MODE = hljs.inherit(hljs.QUOTE_STRING_MODE, {
    contains: [SUBST, hljs.BACKSLASH_ESCAPE]
  });

  return {
    aliases: ['swift'],
    keywords: {
      keyword: SWIFT_KEYWORDS,
      literal:
        'true false nil'
    },
    contains: [
      QUOTE_STRING_MODE,
      hljs.C_LINE_COMMENT_MODE,
      BLOCK_COMMENT,
      TYPE,
      hljs.C_NUMBER_MODE,
      {
        className: 'func',
        beginKeywords: 'func', end: /(\{)|(\->)/, excludeEnd: true,
        contains: [
          hljs.inherit(hljs.TITLE_MODE, {begin: /[A-Za-z$_][0-9A-Za-z$_]*/}),
          {
            className: 'generics',
            begin: /\</, end: /\>/,
            illegal: /\>/
          },
          {
            className: 'params',
            begin: /\(/, end: /\)/,
            contains: [
              hljs.C_LINE_COMMENT_MODE,
              hljs.C_BLOCK_COMMENT_MODE
            ],
            illegal: /["'\(]/
          }
        ],
        illegal: /\[|%/
      },
    ]
  };
}
<commit_msg>Normalized whitespace.<commit_after>/*
Language: Swift
Author: Chris Eidhof <chris@eidhof.nl>
*/

function(hljs) {
  var SWIFT_KEYWORDS =
    'class deinit enum extension func import init let protocol static ' +
    'struct subscript typealias var break case continue default do ' +
    'else fallthrough if in for return switch where while as dynamicType ' +
    'is new super self Self Type __COLUMN__ __FILE__ __FUNCTION__ ' +
    '__LINE__ associativity didSet get infix inout left mutating none ' +
    'nonmutating operator override postfix precedence prefix right set '+
    'unowned unowned safe unsafe weak willSet';
  var TYPE = {
    className: 'type',
    begin: '\\b[A-Z][\\w\']*',
    relevance: 0
  };
  var BLOCK_COMMENT = {
    className: 'comment',
    begin: '/\\*', end: '\\*/',
    contains: [hljs.PHRASAL_WORDS_MODE, 'self']
  };
  var SUBST = {
    className: 'subst',
    begin: /\\\(/, end: '\\)',
    keywords: SWIFT_KEYWORDS,
    contains: [hljs.C_NUMBER_MODE, hljs.TITLE_MODE, hljs.QUOTE_STRING_MODE]
  };
  var QUOTE_STRING_MODE = hljs.inherit(hljs.QUOTE_STRING_MODE, {
    contains: [SUBST, hljs.BACKSLASH_ESCAPE]
  });

  return {
    aliases: ['swift'],
    keywords: {
      keyword: SWIFT_KEYWORDS,
      literal:
        'true false nil'
    },
    contains: [
      QUOTE_STRING_MODE,
      hljs.C_LINE_COMMENT_MODE,
      BLOCK_COMMENT,
      TYPE,
      hljs.C_NUMBER_MODE,
      {
        className: 'func',
        beginKeywords: 'func', end: /(\{)|(\->)/, excludeEnd: true,
        contains: [
          hljs.inherit(hljs.TITLE_MODE, {begin: /[A-Za-z$_][0-9A-Za-z$_]*/}),
          {
            className: 'generics',
            begin: /\</, end: /\>/,
            illegal: /\>/
          },
          {
            className: 'params',
            begin: /\(/, end: /\)/,
            contains: [
              hljs.C_LINE_COMMENT_MODE,
              hljs.C_BLOCK_COMMENT_MODE
            ],
            illegal: /["'\(]/
          }
        ],
        illegal: /\[|%/
      },
    ]
  };
}
<|endoftext|>


<commit_before>/*
 * The algorithms here are based on Brandes and Köpf, "Fast and Simple
 * Horizontal Coordinate Assignment".
 */
dagre.layout.position = function() {
  // External configuration
  var config = {
    nodeSep: 50,
    edgeSep: 10,
    rankSep: 30,
    rankDir: "TB",
    debugAlignment: null,
    debugLevel: 0
  };

  var timer = createTimer();

  var self = {};

  self.nodeSep = propertyAccessor(self, config, "nodeSep");
  self.edgeSep = propertyAccessor(self, config, "edgeSep");
  self.rankSep = propertyAccessor(self, config, "rankSep");
  self.rankDir = propertyAccessor(self, config, "rankDir");
  self.debugAlignment = propertyAccessor(self, config, "debugAlignment");
  self.debugLevel = propertyAccessor(self, config, "debugLevel", function(x) {
    timer.enabled(x);
  });

  self.run = timer.wrap("Position Phase", run);

  return self;

  function run(g) {
    var layering = [];
    g.eachNode(function(u, node) {
      var layer = layering[node.rank] || (layering[node.rank] = []);
      layer[node.order] = u;
    });

    var conflicts = findConflicts(g, layering);

    var xss = {};
    ["up", "down"].forEach(function(vertDir) {
      if (vertDir === "down") { layering.reverse(); }

      ["left", "right"].forEach(function(horizDir) {
        if (horizDir === "right") { reverseInnerOrder(layering); }

        var dir = vertDir + "-" + horizDir;
        if (!config.debugAlignment || config.debugAlignment === dir) {
          var align = verticalAlignment(g, layering, conflicts, vertDir === "up" ? "predecessors" : "successors");
          xss[dir]= horizontalCompaction(g, layering, align.pos, align.root, align.align);
          if (horizDir === "right") { flipHorizontally(layering, xss[dir]); }
        }

        if (horizDir === "right") { reverseInnerOrder(layering); }
      });

      if (vertDir === "down") { layering.reverse(); }
    });

    if (config.debugAlignment) {
      // In debug mode we allow forcing layout to a particular alignment.
      g.eachNode(function(u, node) {
        x(g, u, xss[config.debugAlignment][u]);
      });
    } else {
      alignToSmallest(g, layering, xss);

      // Find average of medians for xss array
      g.eachNode(function(u) {
        var xs = values(xss).map(function(xs) { return xs[u]; }).sort(function(x, y) { return x - y; });
        x(g, u, (xs[1] + xs[2]) / 2);
      });
    }

    // Align min center point with 0
    var minX = min(g.nodes().map(function(u) { return x(g, u) - width(g, u) / 2; }));
    g.eachNode(function(u) { x(g, u, x(g, u) - minX); });

    // Align y coordinates with ranks
    var posY = 0;
    layering.forEach(function(layer) {
      var maxHeight = max(layer.map(function(u) { return height(g, u); }));
      posY += maxHeight / 2;
      layer.forEach(function(u) { y(g, u, posY); });
      posY += maxHeight / 2 + config.rankSep;
    });
  };

  /*
   * Generate an ID that can be used to represent any undirected edge that is
   * incident on `u` and `v`.
   */
  function undirEdgeId(u, v) {
    return u < v
      ? u.toString().length + ":" + u + "-" + v
      : v.toString().length + ":" + v + "-" + u;
  }

  function findConflicts(g, layering) {
    if (layering.length <= 2) return conflicts;

    var conflicts = {}, // Set of conflicting edge ids
        pos = {};       // Position of node in its layer

    layering[1].forEach(function(u, i) { pos[u] = i; });
    for (var i = 1; i < layering.length - 1; ++i) {
      prevLayer = layering[i];
      currLayer = layering[i+1];
      var k0 = 0; // Position of the last inner segment in the previous layer
      var l = 0;  // Current position in the current layer (for iteration up to `l1`)

      // Scan current layer for next node that is incident to an inner segement
      // between layering[i+1] and layering[i].
      for (var l1 = 0; l1 < currLayer.length; ++l1) {
        var u = currLayer[l1]; // Next inner segment in the current layer or
                               // last node in the current layer
        pos[u] = l1;

        var k1 = undefined; // Position of the next inner segment in the previous layer or
                            // the position of the last element in the previous layer
        if (g.node(u).dummy) {
          var uPred = g.predecessors(u)[0];
          if (g.node(uPred).dummy)
            k1 = pos[uPred];
        }
        if (k1 === undefined && l1 === currLayer.length - 1)
          k1 = prevLayer.length - 1;

        if (k1 !== undefined) {
          for (; l <= l1; ++l) {
            g.predecessors(currLayer[l]).forEach(function(v) {
              var k = pos[v];
              if (k < k0 || k > k1)
                conflicts[undirEdgeId(currLayer[l], v)] = true;
            });
          }
          k0 = k1;
        }
      }
    }

    return conflicts;
  }

  function verticalAlignment(g, layering, conflicts, relationship) {
    var pos = {},   // Position for a node in its layer
        root = {},  // Root of the block that the node participates in
        align = {}; // Points to the next node in the block or, if the last
                    // element in the block, points to the first block's root

    layering.forEach(function(layer) {
      layer.forEach(function(u, i) {
        root[u] = u;
        align[u] = u;
        pos[u] = i;
      });
    });

    layering.forEach(function(layer) {
      var prevIdx = -1;
      layer.forEach(function(v) {
        var related = g[relationship](v), // Adjacent nodes from the previous layer
            m;                            // The mid point in the related array

        if (related.length > 0) {
          related.sort(function(x, y) { return pos[x] - pos[y]; });
          mid = (related.length - 1) / 2;
          related.slice(Math.floor(mid), Math.ceil(mid) + 1).forEach(function(u) {
            if (align[v] === v) {
              if (!conflicts[undirEdgeId(u, v)] && prevIdx < pos[u]) {
                align[u] = v;
                align[v] = root[v] = root[u];
                prevIdx = pos[u];
              }
            }
          });
        }
      });
    });

    return { pos: pos, root: root, align: align };
  }

  /*
   * Determines how much spacing u needs from its origin (center) to satisfy
   * width and node separation.
   */
  function deltaX(g, u) {
    var sep = g.node(u).dummy ? config.edgeSep : config.nodeSep;
    return width(g, u) / 2 + sep / 2;
  }

  function horizontalCompaction(g, layering, pos, root, align) {
    // Mapping of node id -> sink node id for class
    var sink = {};

    // Mapping of sink node id -> x delta
    var shift = {};

    // Mapping of node id -> predecessor node (or null)
    var pred = {};

    // Calculated X positions
    var xs = {};

    layering.forEach(function(layer) {
      layer.forEach(function(u, i) {
        sink[u] = u;
        pred[u] = i > 0 ? layer[i - 1] : null;
      });
    });

    function placeBlock(v) {
      if (!(v in xs)) {
        xs[v] = 0;
        var w = v;
        do {
          if (pos[w] > 0) {
            var u = root[pred[w]];
            placeBlock(u);
            if (sink[v] === v) {
              sink[v] = sink[u];
            }
            var delta = deltaX(g, pred[w]) + deltaX(g, w);
            if (sink[v] !== sink[u]) {
              shift[sink[u]] = Math.min(shift[sink[u]] || Number.POSITIVE_INFINITY, xs[v] - xs[u] - delta);
            } else {
              xs[v] = Math.max(xs[v], xs[u] + delta);
            }
          }
          w = align[w];
        } while (w !== v);
      }
    }

    // Root coordinates relative to sink
    values(root).forEach(function(v) {
      placeBlock(v);
    });

    var prevShift = 0;
    layering.forEach(function(layer) {
      var s = shift[layer[0]];
      if (s === undefined) {
        s = 0;
      }
      prevShift = shift[layer[0]] = s + prevShift;
    });

    // Absolute coordinates
    layering.forEach(function(layer) {
      layer.forEach(function(v) {
        xs[v] = xs[root[v]];
        if (root[v] === v) {
          var xDelta = shift[sink[v]];
          if (xDelta < Number.POSITIVE_INFINITY) {
            xs[v] += xDelta;
          }
        }
      });
    });

    return xs;
  }

  function findMinCoord(g, layering, xs) {
    return min(layering.map(function(layer) {
      var u = layer[0];
      return xs[u] - width(g, u) / 2;
    }));
  }

  function findMaxCoord(g, layering, xs) {
    return max(layering.map(function(layer) {
      var u = layer[layer.length - 1];
      return xs[u] - width(g, u) / 2;
    }));
  }

  function shiftX(delta, xs) {
    Object.keys(xs).forEach(function(x) {
      xs[x] += delta;
    });
  }

  function alignToSmallest(g, layering, xss) {
    // First find the smallest width
    var smallestWidthMinCoord;
    var smallestWidthMaxCoord;
    var smallestWidth = Number.POSITIVE_INFINITY;
    values(xss).forEach(function(xs) {
      var minCoord = findMinCoord(g, layering, xs);
      var maxCoord = findMaxCoord(g, layering, xs);
      var width = maxCoord - minCoord;
      if (width < smallestWidth) {
        smallestWidthMinCoord = minCoord;
        smallestWidthMaxCoord = maxCoord;
        smallestWidth = width;
      }
    });

    // Realign coordinates with smallest width
    ["up", "down"].forEach(function(vertDir) {
      var xs = xss[vertDir + "-left"];
      var delta = smallestWidthMinCoord - findMinCoord(g, layering, xs);
      if (delta) {
        shiftX(delta, xs);
      }
    });

    ["up", "down"].forEach(function(vertDir) {
      var xs = xss[vertDir + "-right"];
      var delta = smallestWidthMaxCoord - findMaxCoord(g, layering, xs);
      if (delta) {
        shiftX(delta, xs);
      }
    });
  }

  function flipHorizontally(layering, xs) {
    var maxCenter = max(values(xs));
    Object.keys(xs).forEach(function(u) {
      xs[u] = maxCenter - xs[u];
    });
  }

  function reverseInnerOrder(layering) {
    layering.forEach(function(layer) {
      layer.reverse();
    });
  }

  function width(g, u) {
    switch (config.rankDir) {
      case "LR": return g.node(u).height;
      default:   return g.node(u).width;
    }
  }

  function height(g, u) {
    switch(config.rankDir) {
      case "LR": return g.node(u).width;
      default:   return g.node(u).height;
    }
  }

  function x(g, u, x) {
    switch (config.rankDir) {
      case "LR":
        if (arguments.length < 3) {
          return g.node(u).y;
        } else {
          g.node(u).y = x;
        }
        break;
      default:
        if (arguments.length < 3) {
          return g.node(u).x;
        } else {
          g.node(u).x = x;
        }
    }
  }

  function y(g, u, y) {
    switch (config.rankDir) {
      case "LR":
        if (arguments.length < 3) {
          return g.node(u).x;
        } else {
          g.node(u).x = y;
        }
        break;
      default:
        if (arguments.length < 3) {
          return g.node(u).y;
        } else {
          g.node(u).y = y;
        }
    }
  }
}
<commit_msg>Cleanup to horizontal compaction and balance steps in position phase<commit_after>/*
 * The algorithms here are based on Brandes and Köpf, "Fast and Simple
 * Horizontal Coordinate Assignment".
 */
dagre.layout.position = function() {
  // External configuration
  var config = {
    nodeSep: 50,
    edgeSep: 10,
    rankSep: 30,
    rankDir: "TB",
    debugAlignment: null,
    debugLevel: 0
  };

  var timer = createTimer();

  var self = {};

  self.nodeSep = propertyAccessor(self, config, "nodeSep");
  self.edgeSep = propertyAccessor(self, config, "edgeSep");
  self.rankSep = propertyAccessor(self, config, "rankSep");
  self.rankDir = propertyAccessor(self, config, "rankDir");
  self.debugAlignment = propertyAccessor(self, config, "debugAlignment");
  self.debugLevel = propertyAccessor(self, config, "debugLevel", function(x) {
    timer.enabled(x);
  });

  self.run = timer.wrap("Position Phase", run);

  return self;

  function run(g) {
    var layering = [];
    g.eachNode(function(u, node) {
      var layer = layering[node.rank] || (layering[node.rank] = []);
      layer[node.order] = u;
    });

    var conflicts = findConflicts(g, layering);

    var xss = {};
    ["up", "down"].forEach(function(vertDir) {
      if (vertDir === "down") layering.reverse();

      ["left", "right"].forEach(function(horizDir) {
        if (horizDir === "right") reverseInnerOrder(layering);

        var dir = vertDir + "-" + horizDir;
        if (!config.debugAlignment || config.debugAlignment === dir) {
          var align = verticalAlignment(g, layering, conflicts, vertDir === "up" ? "predecessors" : "successors");
          xss[dir]= horizontalCompaction(g, layering, align.pos, align.root, align.align);
          if (horizDir === "right") flipHorizontally(layering, xss[dir]);
        }

        if (horizDir === "right") reverseInnerOrder(layering);
      });

      if (vertDir === "down") layering.reverse();
    });

    if (config.debugAlignment) {
      // In debug mode we allow forcing layout to a particular alignment.
      g.eachNode(function(u, node) { x(g, u, xss[config.debugAlignment][u]); });
    } else {
      balance(g, layering, xss);
    }

    // Translate layout so left edge of bounding rectangle has coordinate 0
    var minX = min(g.nodes().map(function(u) { return x(g, u) - width(g, u) / 2; }));
    g.eachNode(function(u) { x(g, u, x(g, u) - minX); });

    // Align y coordinates with ranks
    var posY = 0;
    layering.forEach(function(layer) {
      var maxHeight = max(layer.map(function(u) { return height(g, u); }));
      posY += maxHeight / 2;
      layer.forEach(function(u) { y(g, u, posY); });
      posY += maxHeight / 2 + config.rankSep;
    });
  };

  /*
   * Generate an ID that can be used to represent any undirected edge that is
   * incident on `u` and `v`.
   */
  function undirEdgeId(u, v) {
    return u < v
      ? u.toString().length + ":" + u + "-" + v
      : v.toString().length + ":" + v + "-" + u;
  }

  function findConflicts(g, layering) {
    if (layering.length <= 2) return conflicts;

    var conflicts = {}, // Set of conflicting edge ids
        pos = {};       // Position of node in its layer

    layering[1].forEach(function(u, i) { pos[u] = i; });
    for (var i = 1; i < layering.length - 1; ++i) {
      prevLayer = layering[i];
      currLayer = layering[i+1];
      var k0 = 0; // Position of the last inner segment in the previous layer
      var l = 0;  // Current position in the current layer (for iteration up to `l1`)

      // Scan current layer for next node that is incident to an inner segement
      // between layering[i+1] and layering[i].
      for (var l1 = 0; l1 < currLayer.length; ++l1) {
        var u = currLayer[l1]; // Next inner segment in the current layer or
                               // last node in the current layer
        pos[u] = l1;

        var k1 = undefined; // Position of the next inner segment in the previous layer or
                            // the position of the last element in the previous layer
        if (g.node(u).dummy) {
          var uPred = g.predecessors(u)[0];
          if (g.node(uPred).dummy)
            k1 = pos[uPred];
        }
        if (k1 === undefined && l1 === currLayer.length - 1)
          k1 = prevLayer.length - 1;

        if (k1 !== undefined) {
          for (; l <= l1; ++l) {
            g.predecessors(currLayer[l]).forEach(function(v) {
              var k = pos[v];
              if (k < k0 || k > k1)
                conflicts[undirEdgeId(currLayer[l], v)] = true;
            });
          }
          k0 = k1;
        }
      }
    }

    return conflicts;
  }

  function verticalAlignment(g, layering, conflicts, relationship) {
    var pos = {},   // Position for a node in its layer
        root = {},  // Root of the block that the node participates in
        align = {}; // Points to the next node in the block or, if the last
                    // element in the block, points to the first block's root

    layering.forEach(function(layer) {
      layer.forEach(function(u, i) {
        root[u] = u;
        align[u] = u;
        pos[u] = i;
      });
    });

    layering.forEach(function(layer) {
      var prevIdx = -1;
      layer.forEach(function(v) {
        var related = g[relationship](v), // Adjacent nodes from the previous layer
            m;                            // The mid point in the related array

        if (related.length > 0) {
          related.sort(function(x, y) { return pos[x] - pos[y]; });
          mid = (related.length - 1) / 2;
          related.slice(Math.floor(mid), Math.ceil(mid) + 1).forEach(function(u) {
            if (align[v] === v) {
              if (!conflicts[undirEdgeId(u, v)] && prevIdx < pos[u]) {
                align[u] = v;
                align[v] = root[v] = root[u];
                prevIdx = pos[u];
              }
            }
          });
        }
      });
    });

    return { pos: pos, root: root, align: align };
  }

  /*
   * Determines how much spacing u needs from its origin (center) to satisfy
   * width and node separation.
   */
  function deltaX(g, u) {
    var sep = g.node(u).dummy ? config.edgeSep : config.nodeSep;
    return width(g, u) / 2 + sep / 2;
  }

  // This function deviates from the standard BK algorithm in two ways. First
  // it takes into account the size of the nodes. Second it includes a fix to
  // the original algorithm that is described in Carstens, "Node and Label
  // Placement in a Layered Layout Algorithm".
  function horizontalCompaction(g, layering, pos, root, align) {
    var sink = {},  // Mapping of node id -> sink node id for class
        shift = {}, // Mapping of sink node id -> x delta
        pred = {},  // Mapping of node id -> predecessor node (or null)
        xs = {};    // Calculated X positions

    layering.forEach(function(layer) {
      layer.forEach(function(u, i) {
        sink[u] = u;
        if (i > 0)
          pred[u] = layer[i - 1];
      });
    });

    function placeBlock(v) {
      if (!(v in xs)) {
        xs[v] = 0;
        var w = v;
        do {
          if (pos[w] > 0) {
            var u = root[pred[w]];
            placeBlock(u);
            if (sink[v] === v) {
              sink[v] = sink[u];
            }
            var delta = deltaX(g, pred[w]) + deltaX(g, w);
            if (sink[v] !== sink[u]) {
              shift[sink[u]] = Math.min(shift[sink[u]] || Number.POSITIVE_INFINITY, xs[v] - xs[u] - delta);
            } else {
              xs[v] = Math.max(xs[v], xs[u] + delta);
            }
          }
          w = align[w];
        } while (w !== v);
      }
    }

    // Root coordinates relative to sink
    values(root).forEach(function(v) {
      placeBlock(v);
    });

    // Absolute coordinates
    layering.forEach(function(layer) {
      layer.forEach(function(v) {
        xs[v] = xs[root[v]];
        var xDelta = shift[sink[v]];
        if (root[v] === v && xDelta < Number.POSITIVE_INFINITY)
          xs[v] += xDelta;
      });
    });

    return xs;
  }

  function findMinCoord(g, layering, xs) {
    return min(layering.map(function(layer) {
      var u = layer[0];
      return xs[u] - width(g, u) / 2;
    }));
  }

  function findMaxCoord(g, layering, xs) {
    return max(layering.map(function(layer) {
      var u = layer[layer.length - 1];
      return xs[u] - width(g, u) / 2;
    }));
  }

  function balance(g, layering, xss) {
    var min = {},                            // Min coordinate for the alignment
        max = {},                            // Max coordinate for the alginment
        smallestAlignment,
        shift = {};                          // Amount to shift a given alignment

    var smallest = Number.POSITIVE_INFINITY;
    for (var alignment in xss) {
      var xs = xss[alignment];
      min[alignment] = findMinCoord(g, layering, xs);
      max[alignment] = findMaxCoord(g, layering, xs);
      var w = max[alignment] - min[alignment];
      if (w < smallest) {
        smallest = w;
        smallestAlignment = alignment;
      }
    }

    // Determine how much to adjust positioning for each alignment
    ["up", "down"].forEach(function(vertDir) {
      ["left", "right"].forEach(function(horizDir) {
        var alignment = vertDir + "-" + horizDir;
        shift[alignment] = horizDir === "left"
            ? min[smallestAlignment] - min[alignment]
            : max[smallestAlignment] - max[alignment];
      });
    });

    // Find average of medians for xss array
    g.eachNode(function(v) {
      var xs = [];
      for (alignment in xss)
        xs.push(xss[alignment][v] + shift[alignment]);
      xs.sort(function(x, y) { return x - y; });
      x(g, v, (xs[1] + xs[2]) / 2);
    });
  }

  function flipHorizontally(layering, xs) {
    var maxCenter = max(values(xs));
    Object.keys(xs).forEach(function(u) {
      xs[u] = maxCenter - xs[u];
    });
  }

  function reverseInnerOrder(layering) {
    layering.forEach(function(layer) {
      layer.reverse();
    });
  }

  function width(g, u) {
    switch (config.rankDir) {
      case "LR": return g.node(u).height;
      default:   return g.node(u).width;
    }
  }

  function height(g, u) {
    switch(config.rankDir) {
      case "LR": return g.node(u).width;
      default:   return g.node(u).height;
    }
  }

  function x(g, u, x) {
    switch (config.rankDir) {
      case "LR":
        if (arguments.length < 3) {
          return g.node(u).y;
        } else {
          g.node(u).y = x;
        }
        break;
      default:
        if (arguments.length < 3) {
          return g.node(u).x;
        } else {
          g.node(u).x = x;
        }
    }
  }

  function y(g, u, y) {
    switch (config.rankDir) {
      case "LR":
        if (arguments.length < 3) {
          return g.node(u).x;
        } else {
          g.node(u).x = y;
        }
        break;
      default:
        if (arguments.length < 3) {
          return g.node(u).y;
        } else {
          g.node(u).y = y;
        }
    }
  }
}
<|endoftext|>


<commit_before>'use strict';

// @flow

import path from 'path';
import Table from 'terminal-table';

import type {FlowCoverageSummaryData} from './flow';
import type {FlowCoverageReportOptions} from './index';

function renderTextReport(
  coverageData: FlowCoverageSummaryData,
  opts: FlowCoverageReportOptions
): Promise<void> {
  const print = opts.log || console.log.bind(console);

  const filesTable = new Table({
    leftPadding: 1,
    rightPadding: 1,
    borderStyle: 2
  });
  filesTable.push(['filename', 'percent', 'total', 'covered', 'uncovered']);

  let row = 0;
  for (const filename of Object.keys(coverageData.files).sort()) {
    row += 1;
    const data = coverageData.files[filename];

    const covered = data.expressions.covered_count;
    const uncovered = data.expressions.uncovered_count;
    let percent = data.percent || NaN;

    filesTable.push([
      filename,
      data.isError ? '\u26A0 Error' : percent + ' %',
      covered + uncovered, covered, uncovered
    ]);

    let rowColor;
    if (percent >= (opts.threshold || 80)) {
      rowColor = 'green';
    } else {
      rowColor = 'red';
    }

    if (data.isError) {
      rowColor = 'red';
    }

    filesTable.attrRange({row: [row]}, {
      color: rowColor
    });
  }

  filesTable.attrRange({column: [1, 5]}, {
    align: 'right'
  });

  const summaryTablePre = new Table({
    leftPadding: 1,
    rightPadding: 1,
    borderStyle: 2
  });

  summaryTablePre.push([
    'included glob patterns:',
    coverageData.globIncludePatterns.join(', ')
  ]);
  summaryTablePre.push([
    'excluded glob patterns:',
    (coverageData.globExcludePatterns || []).join(', ')
  ]);
  summaryTablePre.push([
    'threshold:',
    coverageData.threshold
  ]);
  summaryTablePre.push([
    'concurrent files:',
    coverageData.concurrentFiles
  ]);

  summaryTablePre.push(['generated at:', coverageData.generatedAt]);
  summaryTablePre.push(['flow version:', coverageData.flowStatus.flowVersion]);
  summaryTablePre.push([
    'flow check passed:',
    (coverageData.flowStatus.passed ? 'yes' : 'no') + ' (' +
    (coverageData.flowStatus.errors.length >= 50 ?
      ' >= 50' : coverageData.flowStatus.errors.length) +
    ' errors)'
  ]);
  summaryTablePre.attrRange({row: [6]}, {
    color: coverageData.flowStatus.passed ? 'green' : 'red'
  });

  summaryTablePre.attrRange({column: [0, 1]}, {
    align: 'right'
  });

  const summaryTable = new Table({
    leftPadding: 1,
    rightPadding: 1,
    borderStyle: 2
  });

  summaryTable.push(['project', 'percent', 'total', 'covered', 'uncovered']);
  const summaryTotal = coverageData.covered_count + coverageData.uncovered_count;
  let summaryPercent = coverageData.percent || 0;

  let summaryColor;
  if (summaryPercent >= (opts.threshold || 80)) {
    summaryColor = 'green';
  } else {
    summaryColor = 'red';
  }

  summaryTable.push([
    path.basename(opts.projectDir),
    summaryPercent + ' %',
    summaryTotal,
    coverageData.covered_count,
    coverageData.uncovered_count
  ]);

  summaryTable.attrRange({row: [1]}, {color: summaryColor});

  summaryTable.attrRange({column: [1, 5]}, {
    align: 'right'
  });

  const waitForDrain = new Promise(resolve => {
    if (opts.log) {
      resolve();
    } else {
      process.stdout.on('drain', resolve);
    }
  });

  print(String(filesTable));
  print(String(summaryTablePre));
  print(String(summaryTable));

  return waitForDrain;
}

function generateFlowCoverageReportText(
  coverageData: FlowCoverageSummaryData,
  opts: Object
): Promise<void> {
  return renderTextReport(coverageData, opts);
}

module.exports = {
  render: renderTextReport,
  generate: generateFlowCoverageReportText
};
<commit_msg>fix: Fixed missing error exit code with text reporter (#22)<commit_after>'use strict';

// @flow

import path from 'path';
import Table from 'terminal-table';

import type {FlowCoverageSummaryData} from './flow';
import type {FlowCoverageReportOptions} from './index';

function renderTextReport(
  coverageData: FlowCoverageSummaryData,
  opts: FlowCoverageReportOptions
): Promise<void> {
  const print = opts.log || console.log.bind(console);

  const filesTable = new Table({
    leftPadding: 1,
    rightPadding: 1,
    borderStyle: 2
  });
  filesTable.push(['filename', 'percent', 'total', 'covered', 'uncovered']);

  let row = 0;
  for (const filename of Object.keys(coverageData.files).sort()) {
    row += 1;
    const data = coverageData.files[filename];

    const covered = data.expressions.covered_count;
    const uncovered = data.expressions.uncovered_count;
    let percent = data.percent || NaN;

    filesTable.push([
      filename,
      data.isError ? '\u26A0 Error' : percent + ' %',
      covered + uncovered, covered, uncovered
    ]);

    let rowColor;
    if (percent >= (opts.threshold || 80)) {
      rowColor = 'green';
    } else {
      rowColor = 'red';
    }

    if (data.isError) {
      rowColor = 'red';
    }

    filesTable.attrRange({row: [row]}, {
      color: rowColor
    });
  }

  filesTable.attrRange({column: [1, 5]}, {
    align: 'right'
  });

  const summaryTablePre = new Table({
    leftPadding: 1,
    rightPadding: 1,
    borderStyle: 2
  });

  summaryTablePre.push([
    'included glob patterns:',
    coverageData.globIncludePatterns.join(', ')
  ]);
  summaryTablePre.push([
    'excluded glob patterns:',
    (coverageData.globExcludePatterns || []).join(', ')
  ]);
  summaryTablePre.push([
    'threshold:',
    coverageData.threshold
  ]);
  summaryTablePre.push([
    'concurrent files:',
    coverageData.concurrentFiles
  ]);

  summaryTablePre.push(['generated at:', coverageData.generatedAt]);
  summaryTablePre.push(['flow version:', coverageData.flowStatus.flowVersion]);
  summaryTablePre.push([
    'flow check passed:',
    (coverageData.flowStatus.passed ? 'yes' : 'no') + ' (' +
    (coverageData.flowStatus.errors.length >= 50 ?
      ' >= 50' : coverageData.flowStatus.errors.length) +
    ' errors)'
  ]);
  summaryTablePre.attrRange({row: [6]}, {
    color: coverageData.flowStatus.passed ? 'green' : 'red'
  });

  summaryTablePre.attrRange({column: [0, 1]}, {
    align: 'right'
  });

  const summaryTable = new Table({
    leftPadding: 1,
    rightPadding: 1,
    borderStyle: 2
  });

  summaryTable.push(['project', 'percent', 'total', 'covered', 'uncovered']);
  const summaryTotal = coverageData.covered_count + coverageData.uncovered_count;
  let summaryPercent = coverageData.percent || 0;

  let summaryColor;
  if (summaryPercent >= (opts.threshold || 80)) {
    summaryColor = 'green';
  } else {
    summaryColor = 'red';
  }

  summaryTable.push([
    path.basename(opts.projectDir),
    summaryPercent + ' %',
    summaryTotal,
    coverageData.covered_count,
    coverageData.uncovered_count
  ]);

  summaryTable.attrRange({row: [1]}, {color: summaryColor});

  summaryTable.attrRange({column: [1, 5]}, {
    align: 'right'
  });

  const waitForDrain = new Promise(resolve => {
    process.stdout.once('drain', resolve);

    const flushed = process.stdout.write('');

    if (flushed || opts.log) {
      resolve();
    }
  });

  print(String(filesTable));
  print(String(summaryTablePre));
  print(String(summaryTable));

  return waitForDrain;
}

function generateFlowCoverageReportText(
  coverageData: FlowCoverageSummaryData,
  opts: Object
): Promise<void> {
  return renderTextReport(coverageData, opts);
}

module.exports = {
  render: renderTextReport,
  generate: generateFlowCoverageReportText
};
<|endoftext|>


<commit_before>/**
 * Constants and functions shared across LIFF, webhook and other parts
 */

import { t } from 'ttag';
import dateFnsFormat from 'date-fns/format';
import dateFnsFormatDistanceToNow from 'date-fns/formatDistanceToNow';

const SITE_URL = process.env.SITE_URL || 'https://cofacts.g0v.tw';

/**
 * prefilled text for LIFF sendMessage()
 */
export const REASON_PREFIX = `💁 ${t`My reason is`}:\n`;
export const SOURCE_PREFIX = `ℹ ${t`I got the message from`}:\n`;
export const UPVOTE_PREFIX = `👍 ${t`I think the reply is useful and I want to add`}:\n`;
export const DOWNVOTE_PREFIX = `💡 ${t`I think the reply is not useful and I suggest`}:\n`;
export const VIEW_ARTICLE_PREFIX = `📃 ${t`See replies of`}:\n`;

/**
 * @param {string} articleId
 * @returns {string} The article's full URL
 */
export function getArticleURL(articleId) {
  return `${SITE_URL}/article/${articleId}`;
}

/**
 * label: Display text in LIFF & the value sent via sendMessage
 * value: Normalized value to store in database / Google Analytics
 * valid: If we should proceed submission
 */
export const ARTICLE_SOURCE_OPTIONS = [
  {
    label: t`A LINE group`,
    value: 'group message',
    valid: true,
  },
  {
    label: t`A LINE official account`,
    value: 'official account',
    valid: true,
  },
  {
    label: t`Someone sent me on LINE in private`,
    value: 'private message',
    valid: true,
  },
  {
    label: t`Somewhere outside LINE`,
    value: 'outside LINE',
    valid: false,
  },
  {
    label: t`I typed it out myself`,
    value: 'manual input',
    valid: false,
  },
];

let locale = require(`date-fns/locale/${(process.env.LOCALE || 'en_US').replace(
  '_',
  '-'
)}`);

// Svelte fix: babel interprets default automatically, but svelte doesn't.
locale = locale.default ? locale.default : locale;

export function format(date, format = 'Pp', config = {}) {
  return dateFnsFormat(date, format, { ...config, locale });
}

export function formatDistanceToNow(date, config = {}) {
  return dateFnsFormatDistanceToNow(date, { ...config, locale });
}
<commit_msg>Ignore coverage of svelte fix<commit_after>/**
 * Constants and functions shared across LIFF, webhook and other parts
 */

import { t } from 'ttag';
import dateFnsFormat from 'date-fns/format';
import dateFnsFormatDistanceToNow from 'date-fns/formatDistanceToNow';

const SITE_URL = process.env.SITE_URL || 'https://cofacts.g0v.tw';

/**
 * prefilled text for LIFF sendMessage()
 */
export const REASON_PREFIX = `💁 ${t`My reason is`}:\n`;
export const SOURCE_PREFIX = `ℹ ${t`I got the message from`}:\n`;
export const UPVOTE_PREFIX = `👍 ${t`I think the reply is useful and I want to add`}:\n`;
export const DOWNVOTE_PREFIX = `💡 ${t`I think the reply is not useful and I suggest`}:\n`;
export const VIEW_ARTICLE_PREFIX = `📃 ${t`See replies of`}:\n`;

/**
 * @param {string} articleId
 * @returns {string} The article's full URL
 */
export function getArticleURL(articleId) {
  return `${SITE_URL}/article/${articleId}`;
}

/**
 * label: Display text in LIFF & the value sent via sendMessage
 * value: Normalized value to store in database / Google Analytics
 * valid: If we should proceed submission
 */
export const ARTICLE_SOURCE_OPTIONS = [
  {
    label: t`A LINE group`,
    value: 'group message',
    valid: true,
  },
  {
    label: t`A LINE official account`,
    value: 'official account',
    valid: true,
  },
  {
    label: t`Someone sent me on LINE in private`,
    value: 'private message',
    valid: true,
  },
  {
    label: t`Somewhere outside LINE`,
    value: 'outside LINE',
    valid: false,
  },
  {
    label: t`I typed it out myself`,
    value: 'manual input',
    valid: false,
  },
];

let locale = require(`date-fns/locale/${(process.env.LOCALE || 'en_US').replace(
  '_',
  '-'
)}`);

// Svelte fix: babel interprets default automatically, but svelte doesn't.
/* istanbul ignore next */
locale = locale.default ? locale.default : locale;

export function format(date, format = 'Pp', config = {}) {
  return dateFnsFormat(date, format, { ...config, locale });
}

export function formatDistanceToNow(date, config = {}) {
  return dateFnsFormatDistanceToNow(date, { ...config, locale });
}
<|endoftext|>


<commit_before>/**
 * Created by 清月_荷雾 on 14-2-8.
 * author 清月_荷雾(441984145@qq.com)
 *        赤菁风铃(liuxuanzy@qq.com)
 * UI组件基础类库
 */
var util = require("../util/util");
var dom = require("../util/dom");
var EventEmitter = require("../util/EventEmitter").EventEmitter;

/**
 * UI对象基类
 * @constructor
 */
function UIObject(dom) {
    EventEmitter.call(this);
    this._dom = dom || null;
    this._parent = null;
    this._children = [];
    this._eventCache = [];
}

util.inherits(UIObject, EventEmitter);

/**
 * 挂接对象到dom树
 * @param parentObject  父对象
 */
UIObject.prototype.attach = function (parentObject) {
    var me = this;
    if (parentObject instanceof UIObject) {
        parentObject.append(me);
        this._parent = parentObject;
    }
    var parent = dom.find(parentObject);
    for (var i = 0; i < parent.length; i++) {
        parent[i].appendChild(me._dom);
    }
};

/**
 * 将对象从dom树上移除
 */
UIObject.prototype.detach = function () {
    var me = this;
    if (me._parent) {
        me._parent.remove(me);
        me._parent = null;
        return;
    }
    if (this._dom && this._dom.parentNode) {
        this._dom.parentNode.removeChild(this._dom);
    }
};

/**
 * 在本对象中UIObject追加元素
 * @param ele 要追加的元素
 */
UIObject.prototype.append = function (ele) {
    if (!(ele instanceof UIObject)) {
        throw  new Error("只能添加UIObject元素");
    }
    this._children.push(ele);
    if (!this._dom) {
        this._dom = ele._dom;
        return;
    }
    this._dom.appendChild(ele._dom);
};

/**
 * 在本对象删除UIObject元素
 * @param ele 要删除的元素
 */
UIObject.prototype.remove = function (ele) {
    if (!(ele instanceof UIObject)) {
        throw  new Error("只能移除UIObject元素");
    }
    var index = -1;
    for (var i = 0; i < this._children.length; i++) {
        if (this._children[i] === ele) {
            index = i;
        }
    }
    if (index >= 0) {
        this._children.splice(index, 1);
    }
    ele.detach();
    if (this._dom === ele._dom) {
        this._dom = null;
    }
};

/**
 * 查询元素
 * @param selector  需要查询的元素
 * @returns {*}
 */
UIObject.prototype.find = function (selector) {
    return dom.find(selector, this._dom);
};

/**
 * 绑定事件
 * @param target    目标对象Dom或者Dom数组
 * @param type      事件类型
 * @param listener  事件方法
 */
UIObject.prototype.bind = function (target, type, listener) {
    var me = this;
    var item;
    if (!util.isArray(target)) {
        target = me.find(target);
    }
    for (var i = 0; i < target.length; i++) {
        item = target[i];
        item.addEventListener(type, listener, false);
        me._eventCache.push({
            target: item,
            type: type,
            listener: listener
        });
    }
};

/**
 * 解绑定事件
 * @param target    目标对象Dom或者Dom数组
 * @param type      事件类型
 * @param listener  事件方法
 */
UIObject.prototype.unbind = function (target, type, listener) {
    var me = this;
    var item, cache;
    if (!util.isArray(target)) {
        target = me.find(target);
    }
    for (var i = 0; i < target.length; i++) {
        item = target[i];
        for (var j = 0; j < this._eventCache.length; j++) {
            cache = this._eventCache[j];
            if (type === cache.type && item === cache.target && listener === cache.listener) {
                item.removeEventListener(type, listener, false);
            }
        }
    }
};

/**
 * 销毁方法（销毁对象上绑定的所有事件）
 * @param isSelf
 */
UIObject.prototype.destroy = function (isSelf) {
    this.emit('destroy');
    for (var i = 0; i < this._children.length; i++) {
        this._children[i].destroy(false);
    }
    this._children = null;
    this.removeAllListeners();
    var item;
    for (i = 0; i < this._eventCache.length; i++) {
        item = this._eventCache[i];
        item.target.removeEventListener(item.target, item.listener, false);
    }
    this._eventCache = null;
    if (isSelf && this._parent) {
        this._parent.remove(this);
    }
    this._parent = null;
    if (isSelf && this._dom) {
        var garbageBin = document.getElementById('IELeakGarbageBin');
        if (!garbageBin) {
            garbageBin = document.createElement('div');
            garbageBin.id = 'IELeakGarbageBin';
            garbageBin.style.display = 'none';
            document.body.appendChild(garbageBin);
        }
        garbageBin.appendChild(this._dom);
        garbageBin.innerHTML = '';
        this._dom = null;
    }
};

exports.UIObject = UIObject;<commit_msg>add some function on UIObject, so that we can destroy any UIObject freedom<commit_after>/**
 * Created by 清月_荷雾 on 14-2-8.
 * author 清月_荷雾(441984145@qq.com)
 *        赤菁风铃(liuxuanzy@qq.com)
 * UI组件基础类库
 */
var util = require("../util/util");
var dom = require("../util/dom");
var EventEmitter = require("../util/EventEmitter").EventEmitter;

/**
 * UI对象基类
 * @constructor
 */
function UIObject(dom) {
    EventEmitter.call(this);
    this._dom = dom || null;
    this._parent = null;
    this._children = [];
    this._eventCache = [];
}

util.inherits(UIObject, EventEmitter);

/**
 * 挂接对象到dom树
 * @param parentObject  父对象
 */
UIObject.prototype.attach = function (parentObject) {
    var me = this;
    if (parentObject instanceof UIObject) {
        parentObject.append(me);
    }
    var parent = dom.find(parentObject);
    for (var i = 0; i < parent.length; i++) {
        parent[i].appendChild(me._dom);
    }
};

/**
 * 将对象从dom树上移除
 */
UIObject.prototype.detach = function () {
    var me = this;
    if (me._parent) {
        me._parent.remove(me);
        return;
    }
    if (this._dom && this._dom.parentNode) {
        this._dom.parentNode.removeChild(this._dom);
    }
};

/**
 * 在本对象中追加元素（可以对DOM元素进行追加，但是无法使用remove函数对追加的元素进行移除）
 * @param ele 要追加的元素
 */
UIObject.prototype.append = function (ele) {
    if (!(ele instanceof UIObject)) {
        if (this._dom) {
            this._dom.appendChild(ele);
        } else {
            this._dom = ele;
        }
        return;
    }
    this._children.push(ele);
    ele._parent = this;
    if (!this._dom) {
        this._dom = ele._dom;
        return;
    }
    this._dom.appendChild(ele._dom);
};

/**
 * 在本对象删除UIObject元素（请注意如果当前元素在添加时没有_dom，则可能不会把元素从DOM树上移除，请不要把一个元素挂接到多处）
 * @param ele 要删除的元素
 */
UIObject.prototype.remove = function (ele) {
    if (!(ele instanceof UIObject)) {
        throw  new Error("只能移除UIObject元素");
    }
    var index = -1;
    for (var i = 0; i < this._children.length; i++) {
        if (this._children[i] === ele) {
            index = i;
        }
    }
    if (index >= 0) {
        this._children.splice(index, 1);
    }
    ele._parent = null;
    if (this._dom === ele._dom) {
        this._dom = null;
    } else {
        ele._dom.parent.removeChild(ele._dom);
    }
};

/**
 * 查询元素
 * @param selector  需要查询的元素
 * @returns {*}
 */
UIObject.prototype.find = function (selector) {
    return dom.find(selector, this._dom);
};

/**
 * 绑定事件
 * @param target    目标对象Dom或者Dom数组
 * @param type      事件类型
 * @param listener  事件方法
 */
UIObject.prototype.bind = function (target, type, listener) {
    var me = this;
    var item;
    if (!util.isArray(target)) {
        target = me.find(target);
    }
    for (var i = 0; i < target.length; i++) {
        item = target[i];
        item.addEventListener(type, listener, false);
        me._eventCache.push({
            target: item,
            type: type,
            listener: listener
        });
    }
};

/**
 * 解绑定事件
 * @param target    目标对象Dom或者Dom数组
 * @param type      事件类型
 * @param listener  事件方法
 */
UIObject.prototype.unbind = function (target, type, listener) {
    var me = this;
    var item, cache;
    if (!util.isArray(target)) {
        target = me.find(target);
    }
    for (var i = 0; i < target.length; i++) {
        item = target[i];
        for (var j = 0; j < this._eventCache.length; j++) {
            cache = this._eventCache[j];
            if (type === cache.type && item === cache.target && listener === cache.listener) {
                item.removeEventListener(type, listener, false);
            }
        }
    }
};

/**
 * 销毁方法（销毁对象上绑定的所有事件）
 * @param isSelf
 */
UIObject.prototype.destroy = function (isSelf) {
    this.emit('destroy');
    for (var i = 0; i < this._children.length; i++) {
        this._children[i].destroy(false);
    }
    this._children = null;
    this.removeAllListeners();
    var item;
    for (i = 0; i < this._eventCache.length; i++) {
        item = this._eventCache[i];
        item.target.removeEventListener(item.target, item.listener, false);
    }
    this._eventCache = null;
    if (isSelf && this._parent) {
        this._parent.remove(this);
    }
    this._parent = null;
    if (isSelf && this._dom) {
        var garbageBin = document.getElementById('IELeakGarbageBin');
        if (!garbageBin) {
            garbageBin = document.createElement('div');
            garbageBin.id = 'IELeakGarbageBin';
            garbageBin.style.display = 'none';
            document.body.appendChild(garbageBin);
        }
        garbageBin.appendChild(this._dom);
        garbageBin.innerHTML = '';
        this._dom = null;
    }
};

exports.UIObject = UIObject;<|endoftext|>


<commit_before>var d = require ('dequeue');
var redis = require("redis");
var rClient = redis.createClient();
var dgram = require("dgram");

var FIFO = new d();
var index =0;

fetcher();

var udpserver = dgram.createSocket("udp4");
udpserver.on('error', function (err) {
    console.log("Error Server", err);
})
udpserver.on("message",
    function (msg, rinfo) {
        index++;
        FIFO.push(msg.toString());

    }
);

udpserver.bind(4444);

function fetcher () {
    while (FIFO.length > 0)
    {
        var msg = FIFO.shift();

        rClient.hset("raw", "data"+index, msg);

    }
}<commit_msg>Update UDPefef<commit_after>var d = require ('dequeue');
var redis = require("redis");
var rClient = redis.createClient();
var dgram = require("dgram");

var FIFO = new d();

fetcher();

var udpserver = dgram.createSocket("udp4");

udpserver.on("message",
    function (msg, rinfo) {
        FIFO.push(msg.toString());
    }
);

udpserver.bind(4444);

var index =0;

function fetcher () {
    while (FIFO.length > 0)
    {
        index++;
        var msg = FIFO.shift();
        console.log("Success: ", index);
        rClient.hset("raw", "data"+index, msg);
    }
}

///<|endoftext|>


<commit_before>$(document).ready(function() {

    current_station_id = 38 // FIX ME: Hard coded what station to look at for now!
    current_hour_selected = 17 // FIX ME: This should be based on user input too. How to handle aggregate?

    stations = _(stations).filter(function(station) { return !station.temporary });

    // Center coords, and zoomlevel 13
    var map = L.map('map', {
        scrollWheelZoom: false
    });

    var layer = new L.MAPCTileLayer("basemap");
    map.addLayer(layer);

    var stations_by_id = {};
    _(stations).each(function(station) {
        //station.flow = { in: 0, out: 0 };
        stations_by_id[station.id] = station;
    });

    // May as well tack a pointer to the station on each datum
    _(hourly_data).each(function(d) {
        d.station = stations_by_id[d.station_id];
    });

    var min_lat = _.chain(stations).map(function(s) { return s.lat }).min().value();
    var max_lat = _.chain(stations).map(function(s) { return s.lat }).max().value();
    var min_lng = _.chain(stations).map(function(s) { return s.lng }).min().value();
    var max_lng = _.chain(stations).map(function(s) { return s.lng }).max().value();

    map.fitBounds(L.latLngBounds([min_lat, min_lng], [max_lat, max_lng]));

    var current_hour_data = _.chain(hourly_data)
        .filter(function(item) { return item.hour == current_hour_selected; })
        .sortBy(function(d) { return d.accumulation; })
        .value();

    var one_station_data_arrivals = _.chain(hourly_data)
	.filter(function(d) { return d.station_id == current_station_id;})
	.map(function(d) { return d.arrivals;})
    .sortBy(function(d) { return d.hour })
	.value();

    var one_station_data_departures = _.chain(hourly_data)
	.filter(function(d) { return d.station_id == current_station_id;})
	.map(function(d) { return d.departures;})
    .sortBy(function(d) { return d.hour })
	.value();

    var one_station_max = Math.max(_.max(one_station_data_departures),
				   _.max(one_station_data_arrivals))
    var circle_scale = 20;
    function getStationCoords(id){
	    return [stations_by_id[id].lat, stations_by_id[id].lng];
    }
    _(current_hour_data).each(function(d) {
        d.circle = L.circle(getStationCoords(d.station_id),
			    // TBD how to handle size and color to indicate total traffic
			    // and net imbance
                            circle_scale * Math.sqrt(Math.abs(d.arrivals+d.departures)),
                            {color: (d.arrivals > d.departures?'steelBlue':'brown'),
			     weight: 2, opacity: 1.0, fillOpacity: 0.5})
            .addTo(map)
            .bindPopup(stations_by_id[d.station_id].name);
    });

    var width = $('#aggregates').width()
    height = $('#aggregates').height();

    var min_acc = _.min(_(current_hour_data).map(function(d) { return d.accumulation;} ))
    var max_acc = _.max(_(current_hour_data).map(function(d) { return d.accumulation;} ))
    var y_max = Math.max(-min_acc, max_acc); // So the zero line always goes down the middle

    var y = d3.scale.linear()
	.domain([-y_max, y_max])
	.range([0, height])
	.nice();

    var x = d3.scale.ordinal()
	.domain(_(current_hour_data).map(function(d) { return d.station_id; }))
	.rangeRoundBands([0, width]);

    var yAxis = d3.svg.axis()
	.scale(y)
	.orient("top");

    var svg = d3.select("#aggregates").append("svg")
	.attr("width", width)
	.attr("height", height)

    svg.selectAll(".bar")
	.data(current_hour_data)
	.enter().append("rect")
	.attr("class", function(d) { return d.accumulation > 0 ? "bar negative" : "bar positive"; })
	.attr("data-station", function(d) { return d.station_id; })
	.attr("data-accum", function(d) { return d.accumulation; })
	.attr("x", function(d, i) { return x(d.station_id); })
	.attr("y", function(d) { return y(Math.min(0, d.accumulation)) })
	.attr("width", x.rangeBand())
	.attr("height", function(d) { return Math.abs(y(d.accumulation) - y(0)); });

    svg.selectAll("text")
	.data(current_hour_data)
	.enter()
    .append("g").attr("transform", function(d) {
        return "translate(" + ( x(d.station_id) + x.rangeBand()*2/3 )+ ", " + y(0) + "),"
        + "rotate(270)";
    })
	.append("text")
	.attr("class", function(d) { return d.accumulation > 0 ? "bar-label negative" : "bar-label positive"; })
    .attr("dx", function(d) { return d.accumulation > 0 ? "0.6em" : "-0.6em" })
	.text(function(d) { return d.station.name });

    svg.selectAll(".activator")
        .data(current_hour_data)
	    .enter().append("rect")
	    .attr("class", "activator")
	    .attr("data-station", function(d) { return d.station_id; })
	    .attr("data-accum", function(d) { return d.accumulation; })
	    .attr("x", function(d, i) { return x(d.station_id); })
	    .attr("y", 0)
	    .attr("width", x.rangeBand())
	    .attr("height", height);

    // FIXME: This is triggering with the right stuff, but the addClass / removeClass seemingly do nothing
    $('#aggregates').on("mouseover mouseout", ".activator", function(event) {
        console.log(event.type, " on ", $(this));
        if (event.type == "mouseover") {
            $(this).addClass("active");
            console.log("Changed to", $(this))
        } else if (event.type == "mouseout") {
            $(this).removeClass("active");
        }
    })

    // Begin Graphical Elements for Station Chart
    // sc for station_chart
    // Margin convention from here: http://bl.ocks.org/3019563
    var width = $('#line-chart').width();    // TODO how to make width '100%' again dynamically?, use width of parent?
    var height = $('#line-chart').height();
    var margin_bottom = 30; // This has to be within the SVG, to make room for x axis labels, but nothing else does
    console.log(width, height);

    var chart_svg = d3.select('#line-chart').append('svg')
        //.attr('style', 'border: 1px solid red') // For debugging
        .attr('width', width)
        .attr('height', height);
    $('#title-hourly')
	.text('Hourly Traffic for '+
	      stations_by_id[current_station_id].name);

    chart_svg.selectAll();

    var x_scale = d3.scale.ordinal()
        .domain(_.range(0, 23))
        .rangeRoundBands([0, width]);

    var y_scale = d3.scale.linear()
        .domain([0, one_station_max])
        .range([height-margin_bottom, 0]);

    var line = d3.svg.line()
        .x(function(d, i) { return x_scale(i); })
        .y(function(d) { return y_scale(d); });

    chart_svg.append("path")
        .datum(one_station_data_arrivals)
        .attr("class", "line arrivals")
        .attr("d", line);
    chart_svg.append("path")
        .datum(one_station_data_departures)
        .attr("class", "line departures")
        .attr("d", line);

    sc_x_axis = d3.svg.axis()
	.scale(x_scale)
	.orient("bottom")
	//.ticks(d3.time.hours,2);
	.tickValues([0,4,8,12,16, 20]); // TODO, should wrap data so that you can see continuity over midnight - 2am?

    sc_y_axis = d3.svg.axis()
	.scale(y_scale)
	.orient("right")
	.ticks(5);

    chart_svg.append("g")
	.attr("class", "axis")
	.attr("transform", "translate(0,"+ (height - margin_bottom) + ")")
	.call(sc_x_axis);

    chart_svg.append("g")
	.attr("class", "y axis")
	.call(sc_y_axis)
	.append("text")
	.attr("transform", "rotate(-90)")
          .attr("y", 30) // Does this make sense?
      .attr("dy", ".71em")
      .style("text-anchor", "end")
      .text("Average Weekday Trips");
});
<commit_msg>Refactor accumulation stuff out of the way so it can be rebound to new data (hopefully)<commit_after>
/* The station accumulation bars */
function set_up_station_accumulations() {
    var width = $('#aggregates').width();
    var height = $('#aggregates').height();

    var svg = d3.select("#aggregates").append("svg")
	  .attr("width", width)
	  .attr("height", height);

    return svg;
}

function bind_station_accumulation_data(svg, data) {
    var width = $('#aggregates').width();
    var height = $('#aggregates').height();

    var min_acc = _.min(_(data).map(function(d) { return d.accumulation;} ))
    var max_acc = _.max(_(data).map(function(d) { return d.accumulation;} ))
    var y_max = Math.max(-min_acc, max_acc);

    // Y scale keep 0 at exactly the midpoint of the SVG canvas
    var y = d3.scale.linear()
	    .domain([-y_max, y_max])
	    .range([0, height])
	    .nice();

    // X scale allocates fixed-size rectangles for stations in order.
    // FIXME: I think it should be by index, not station id, since it stays sorted. We'll see when things get dynamic
    var x = d3.scale.ordinal()
	    .domain(_(data).map(function(d) { return d.station_id; }))
	    .rangeRoundBands([0, width]);

    // FIXME: I don't see this appearing
    var yAxis = d3.svg.axis()
	    .scale(y)
	    .orient("top");

    // Actually bind the data
    var accumulation_enter = svg.selectAll(".station-accumulation").data(data).enter()
        .append("g").attr("class", "station-accumulation");

    /* The visible bar */
    accumulation_enter.append("rect")
	    .attr("class", function(d) { return d.accumulation > 0 ? "bar negative" : "bar positive"; })
	    .attr("data-station", function(d) { return d.station_id; })
	    .attr("data-accum", function(d) { return d.accumulation; })
	    .attr("x", function(d, i) { return x(d.station_id); })
	    .attr("y", function(d) { return y(Math.min(0, d.accumulation)) })
	    .attr("width", x.rangeBand())
	    .attr("height", function(d) { return Math.abs(y(d.accumulation) - y(0)); });

    /* The station name*/
    accumulation_enter.append("g").attr("transform", function(d) { return "translate(" + ( x(d.station_id) + x.rangeBand()*2/3 )+ ", " + y(0) + ")," + "rotate(270)" })
	    .append("text")
          .attr("class", function(d) { return d.accumulation > 0 ? "bar-label negative" : "bar-label positive"; })
          .attr("dx", function(d) { return d.accumulation > 0 ? "0.6em" : "-0.6em" })
	      .text(function(d) { return d.station.short_name });

    /* An invisible rectangle over everything to receive selection */
    accumulation_enter.append("rect")
	    .attr("class", "activator")
	    .attr("data-station", function(d) { return d.station_id; })
	    .attr("data-accum", function(d) { return d.accumulation; })
	    .attr("x", function(d, i) { return x(d.station_id); })
	    .attr("y", 0)
	    .attr("width", x.rangeBand())
	    .attr("height", height);

    // FIXME: This is triggering with the right stuff, but the addClass / removeClass seemingly do nothing
    $('#aggregates').on("mouseover mouseout", ".activator", function(event) {
        console.log(event.type, " on ", $(this));
        if (event.type == "mouseover") {
            $(this).addClass("active");
            console.log("Changed to", $(this))
        } else if (event.type == "mouseout") {
            $(this).removeClass("active");
        }
    });
}

$(document).ready(function() {

    current_station_id = 38 // FIX ME: Hard coded what station to look at for now!
    current_hour_selected = 17 // FIX ME: This should be based on user input too. How to handle aggregate?

    stations = _(stations).filter(function(station) { return !station.temporary });

    // Center coords, and zoomlevel 13
    var map = L.map('map', {
        scrollWheelZoom: false
    });

    var layer = new L.MAPCTileLayer("basemap");
    map.addLayer(layer);

    var stations_by_id = {};
    _(stations).each(function(station) {
        //station.flow = { in: 0, out: 0 };
        stations_by_id[station.id] = station;
    });

    // May as well tack a pointer to the station on each datum
    _(hourly_data).each(function(d) {
        d.station = stations_by_id[d.station_id];
    });

    var min_lat = _.chain(stations).map(function(s) { return s.lat }).min().value();
    var max_lat = _.chain(stations).map(function(s) { return s.lat }).max().value();
    var min_lng = _.chain(stations).map(function(s) { return s.lng }).min().value();
    var max_lng = _.chain(stations).map(function(s) { return s.lng }).max().value();

    map.fitBounds(L.latLngBounds([min_lat, min_lng], [max_lat, max_lng]));

    var current_hour_data = _.chain(hourly_data)
        .filter(function(item) { return item.hour == current_hour_selected; })
        .sortBy(function(d) { return d.accumulation; })
        .value();

    var one_station_data_arrivals = _.chain(hourly_data)
	.filter(function(d) { return d.station_id == current_station_id;})
	.map(function(d) { return d.arrivals;})
    .sortBy(function(d) { return d.hour })
	.value();

    var one_station_data_departures = _.chain(hourly_data)
	.filter(function(d) { return d.station_id == current_station_id;})
	.map(function(d) { return d.departures;})
    .sortBy(function(d) { return d.hour })
	.value();

    var one_station_max = Math.max(_.max(one_station_data_departures),
				   _.max(one_station_data_arrivals))
    var circle_scale = 20;
    function getStationCoords(id){
	    return [stations_by_id[id].lat, stations_by_id[id].lng];
    }
    _(current_hour_data).each(function(d) {
        d.circle = L.circle(getStationCoords(d.station_id),
			    // TBD how to handle size and color to indicate total traffic
			    // and net imbance
                            circle_scale * Math.sqrt(Math.abs(d.arrivals+d.departures)),
                            {color: (d.arrivals > d.departures?'steelBlue':'brown'),
			     weight: 2, opacity: 1.0, fillOpacity: 0.5})
            .addTo(map)
            .bindPopup(stations_by_id[d.station_id].name);
    });

    // Set up the station accumulation chart with some initial data
    var accumulations_svg = set_up_station_accumulations();
    bind_station_accumulation_data(accumulations_svg, current_hour_data); // FIXME: the default should be a sum, not the current hour

    // Begin Graphical Elements for Station Chart
    // sc for station_chart
    // Margin convention from here: http://bl.ocks.org/3019563
    var width = $('#line-chart').width();    // TODO how to make width '100%' again dynamically?, use width of parent?
    var height = $('#line-chart').height();
    var margin_bottom = 30; // This has to be within the SVG, to make room for x axis labels, but nothing else does
    console.log(width, height);

    var chart_svg = d3.select('#line-chart').append('svg')
        //.attr('style', 'border: 1px solid red') // For debugging
        .attr('width', width)
        .attr('height', height);
    $('#title-hourly')
	.text('Hourly Traffic for '+
	      stations_by_id[current_station_id].name);

    chart_svg.selectAll();

    var x_scale = d3.scale.ordinal()
        .domain(_.range(0, 23))
        .rangeRoundBands([0, width]);

    var y_scale = d3.scale.linear()
        .domain([0, one_station_max])
        .range([height-margin_bottom, 0]);

    var line = d3.svg.line()
        .x(function(d, i) { return x_scale(i); })
        .y(function(d) { return y_scale(d); });

    chart_svg.append("path")
        .datum(one_station_data_arrivals)
        .attr("class", "line arrivals")
        .attr("d", line);
    chart_svg.append("path")
        .datum(one_station_data_departures)
        .attr("class", "line departures")
        .attr("d", line);

    sc_x_axis = d3.svg.axis()
	.scale(x_scale)
	.orient("bottom")
	//.ticks(d3.time.hours,2);
	.tickValues([0,4,8,12,16, 20]); // TODO, should wrap data so that you can see continuity over midnight - 2am?

    sc_y_axis = d3.svg.axis()
	.scale(y_scale)
	.orient("right")
	.ticks(5);

    chart_svg.append("g")
	.attr("class", "axis")
	.attr("transform", "translate(0,"+ (height - margin_bottom) + ")")
	.call(sc_x_axis);

    chart_svg.append("g")
	.attr("class", "y axis")
	.call(sc_y_axis)
	.append("text")
	.attr("transform", "rotate(-90)")
          .attr("y", 30) // Does this make sense?
      .attr("dy", ".71em")
      .style("text-anchor", "end")
      .text("Average Weekday Trips");
});
<|endoftext|>


<commit_before>/**
 * @class jQuery
 */

/**
 * Browser information. This object has 3 sub-properties: name, version and OS.

 * Overrides jQuery.browser.
 * You also get three 'is' prefixed boolean properties attached to ion:

 * 1. for OS (e.g ion.isWindows), 2. for browser vendor (e.g ion.isIE), and 3. vendor+version (e.g ion.isIE8).
 * @property browser
 * @type Object
 * @final
 * @credits Modified code from from http://www.quirksmode.org/js/detect.html.
 */
(function (Li) {
    var agent = navigator.userAgent.toLowerCase(),
        it, match,
        name, version, OS, OSVersion,
        browserList = [
            [/iemobile.([\d\.]+)/, 'IEMobile'],
            [/msie.([\d\.]+)/, 'IE'],
            [/chrome.([\d\.]+)/, 'Chrome'],
            [/firefox.([\d\.]+)/, 'Firefox'],
            [/version\/([\d\.]+).+?safari/, 'Safari'],
            [/opera.+?version\/([\d\.]+)/, 'Opera'],
            [/rv.([\d\.]+).+?gecko/, 'Gecko'],
            [/applewebkit.([\d\.]+)/, 'WebKit']
        ],
        OSList = [
            [/windows phone os ([\d\.]+)/, "WindowsPhone"],
            [/windows (nt [\d\.]+)/, "Windows"],
            [/mac os x ([\d_]+)/, "Mac"],
            [/linux/, "Linux"],
            [/android ([\d\.]+)/, "Android"],
            [/iphone os ([\d_]+)/, "IPhone"],
            [/ipad.+?os ([\d_]+)/, "IPad"],
            [/blackberry (\d+)/, "BlackBerry"],
            [/symbianos.([\d\.]+)/, "Symbian"]
        ];

    while ((it = browserList.shift())) {
        match = agent.match(it[0]);
        if (match) {
            name = it[1];
            version = match[1];
            break;
        }
    }

    if (name) {
        Li["is" + name] = true;
        if (version) {
            Li["is" + name + parseInt(version, 10)] = true;
        }
    }

    Li.isWebKit = Li.isChrome || Li.isSafari || Li.isWebKit;
    Li.isGecko = Li.isFirefox || Li.isGecko;
    Li.isIOS = Li.isIPhone || Li.isIPad;
    Li.isMobile = Li.isAndroid || Li.isIPhone || Li.isBlackBerry || Li.isWindowsPhone || Li.isSymbian;

    //TODO: Detect IE compatibility mode

    while ((it = OSList.shift())) {
        match = agent.match(it[0]);
        if (match) {
            OS = it[1];
            if ((OSVersion = match[1])) {
                OSVersion = OSVersion.replace('_', '.');
            }
            break;
        }
    }
    if (OS) {
        Li["is" + OS] = true;
    }

    //Bug that WebKit is being detected as Safari on non-iOS phones/tablets
    if (Li.isSafari && (Li.isAndroid || Li.isBlackBerry || Li.isSymbian)) {
        delete Li.isSafari;
        delete Li["is" + name + parseInt(version, 10)];
        name = 'WebKit';
        version = agent.match(/applewebkit.([\d\.]+)/)[1];
        Li["is" + name + parseInt(version, 10)] = true;
    }

    Li.browser = {
        name: name,
        version: version,
        OS: OS,
        OSVersion: OSVersion
    };
}(window.Li));
<commit_msg>Fixed issue where android was being detected as linux.<commit_after>/**
 * @class jQuery
 */

/**
 * Browser information. This object has 3 sub-properties: name, version and OS.

 * Overrides jQuery.browser.
 * You also get three 'is' prefixed boolean properties attached to ion:

 * 1. for OS (e.g ion.isWindows), 2. for browser vendor (e.g ion.isIE), and 3. vendor+version (e.g ion.isIE8).
 * @property browser
 * @type Object
 * @final
 * @credits Modified code from from http://www.quirksmode.org/js/detect.html.
 */
(function (Li) {
    var agent = navigator.userAgent.toLowerCase(),
        it, match,
        name, version, OS, OSVersion,
        browserList = [
            [/iemobile.([\d\.]+)/, 'IEMobile'],
            [/msie.([\d\.]+)/, 'IE'],
            [/chrome.([\d\.]+)/, 'Chrome'],
            [/firefox.([\d\.]+)/, 'Firefox'],
            [/version\/([\d\.]+).+?safari/, 'Safari'],
            [/opera.+?version\/([\d\.]+)/, 'Opera'],
            [/rv.([\d\.]+).+?gecko/, 'Gecko'],
            [/applewebkit.([\d\.]+)/, 'WebKit']
        ],
        OSList = [
            [/windows phone os ([\d\.]+)/, "WindowsPhone"],
            [/windows (nt [\d\.]+)/, "Windows"],
            [/mac os x ([\d_]+)/, "Mac"],
            [/android ([\d\.]+)/, "Android"],
            [/linux/, "Linux"],
            [/iphone os ([\d_]+)/, "IPhone"],
            [/ipad.+?os ([\d_]+)/, "IPad"],
            [/blackberry (\d+)/, "BlackBerry"],
            [/symbianos.([\d\.]+)/, "Symbian"]
        ];

    while ((it = browserList.shift())) {
        match = agent.match(it[0]);
        if (match) {
            name = it[1];
            version = match[1];
            break;
        }
    }

    if (name) {
        Li["is" + name] = true;
        if (version) {
            Li["is" + name + parseInt(version, 10)] = true;
        }
    }

    Li.isWebKit = Li.isChrome || Li.isSafari || Li.isWebKit;
    Li.isGecko = Li.isFirefox || Li.isGecko;
    Li.isIOS = Li.isIPhone || Li.isIPad;
    Li.isMobile = Li.isAndroid || Li.isIPhone || Li.isBlackBerry || Li.isWindowsPhone || Li.isSymbian;

    //TODO: Detect IE compatibility mode

    while ((it = OSList.shift())) {
        match = agent.match(it[0]);
        if (match) {
            OS = it[1];
            if ((OSVersion = match[1])) {
                OSVersion = OSVersion.replace('_', '.');
            }
            break;
        }
    }
    if (OS) {
        Li["is" + OS] = true;
    }

    //Bug that WebKit is being detected as Safari on non-iOS phones/tablets
    if (Li.isSafari && (Li.isAndroid || Li.isBlackBerry || Li.isSymbian)) {
        delete Li.isSafari;
        delete Li["is" + name + parseInt(version, 10)];
        name = 'WebKit';
        version = agent.match(/applewebkit.([\d\.]+)/)[1];
        Li["is" + name + parseInt(version, 10)] = true;
    }

    Li.browser = {
        name: name,
        version: version,
        OS: OS,
        OSVersion: OSVersion
    };
}(window.Li));
<|endoftext|>


<commit_before>'use strict';

/**
 * app所用到的模块初始化
 * @author Architect.bian
 */

const Router = require('koa-router');
const fs = require('fs');
const path = require('path');

module.exports = class {

    /*
    初始化router
     */
    static initRouters (app){
        console.log(`......begin to initialize routers:`);
        const router = new Router();
        const dir = path.resolve(__dirname, './routers');
        var files = fs.readdirSync(dir).filter((file) => ((file.endsWith("Router.js"))));
        for(var file of files) {
            const fun = require(path.resolve(dir, file));
            fun(router);
            console.log(`initialized router: ${file}`);
        }
        app.use(router.routes()).use(router.allowedMethods());
    }

    /*
     初始化拦截器
     */
    static initInterceptors(app) {
        console.log(`......begin to initialize interceptor:`);
        const dir = path.resolve(__dirname, './interceptors');
        var files = fs.readdirSync(dir).filter((file) => ((file.endsWith("Interceptor.js"))));
        for(var file of files) {
            const interceptor = require(path.resolve(dir, file));
            interceptor.handle(app);
            console.log(`initialized interceptor: ${file}`);
        }
    }
}<commit_msg>Comment:重构appmodule.js的initRouters方法，router可放到文件夹下，更清晰<commit_after>'use strict';

/**
 * app所用到的模块初始化
 * @author Architect.bian
 */

const Router = require('koa-router');
const fs = require('fs');
const path = require('path');

module.exports = class {

    /*
    初始化router
     */
    static initRouters (app){
        console.log(`......begin to initialize routers:`);
        const router = new Router();
        const dir = path.resolve(__dirname, './routers');
        this.requireRouterJs(router, dir);
        app.use(router.routes()).use(router.allowedMethods());
    }

    /**
     * require dirName下所有*Router.js文件，包括子目录
     * @param router
     * @param dir 文件夹完整的路径
     */
    static requireRouterJs(router, dir) {
        var files = fs.readdirSync(dir);
        for(var file of files) {
            const filePath = path.resolve(dir, file);
            const stats = fs.statSync(filePath);
            if(stats.isDirectory()) {
                this.requireRouterJs(router, filePath); //子目录
            } else if(stats.isFile() && file.endsWith("Router.js")) {
                const fun = require(filePath);
                fun(router);
                console.log(`initialized router: ${file}`);
            }
        }
    }

    /*
     初始化拦截器
     */
    static initInterceptors(app) {
        console.log(`......begin to initialize interceptor:`);
        const dir = path.resolve(__dirname, './interceptors');
        var files = fs.readdirSync(dir).filter((file) => ((file.endsWith("Interceptor.js"))));
        for(var file of files) {
            const interceptor = require(path.resolve(dir, file));
            interceptor.handle(app);
            console.log(`initialized interceptor: ${file}`);
        }
    }
}<|endoftext|>