<commit_before>/**
 * Copyright reelyActive 2015
 * We believe in an open Internet of Things
 */

var MESSAGE_OK = "ok";
var MESSAGE_CREATED = "created";
var MESSAGE_NOTFOUND = "notFound";
var MESSAGE_BADREQUEST = "badRequest";
var MESSAGE_NOTIMPLEMENTED = "notImplemented";
var CODE_OK = 200;
var CODE_CREATED = 201;
var CODE_NOTFOUND = 404;
var CODE_BADREQUEST = 400;
var CODE_NOTIMPLEMENTED = 501;

/**
 * Prepares the JSON for an API query response which is successful
 * @param {Number} status Integer status code
 */
function prepareResponse(status) {
  var response = {};
  prepareMeta(response, status);
  return response;
};

/**
 * Prepares and adds the _meta to the given API query response
 * @param {Object} response JSON representation of the response
 * @param {String} status String representing the status message
 */
function prepareMeta(response, status) {
  switch(status) {
    case CODE_OK:
      response._meta = { "message": MESSAGE_OK,
                         "statusCode": CODE_OK };
      break;
    case CODE_CREATED:
      response._meta = { "message": MESSAGE_CREATED,
                         "statusCode": CODE_CREATED };
      break;
    case CODE_NOTFOUND:
      response._meta = { "message": MESSAGE_NOTFOUND,
                         "statusCode": CODE_NOTFOUND };
      break;
    case CODE_NOTIMPLEMENTED:
      response._meta = { "message": MESSAGE_NOTIMPLEMENTED,
                         "statusCode": CODE_NOTIMPLEMENTED };
      break;
    default:
      response._meta = { "message": MESSAGE_BADREQUEST,
                         "statusCode": CODE_BADREQUEST };
  }
};

module.exports.OK = CODE_OK;
module.exports.CREATED = CODE_CREATED;
module.exports.NOTFOUND = CODE_NOTFOUND;
module.exports.BADREQUEST = CODE_BADREQUEST;
module.exports.NOTIMPLEMENTED = CODE_NOTIMPLEMENTED;
module.exports.prepareResponse = prepareResponse;
<commit_msg>Added req parameter and prepareLinks function<commit_after>/**
 * Copyright reelyActive 2015
 * We believe in an open Internet of Things
 */

var MESSAGE_OK = "ok";
var MESSAGE_CREATED = "created";
var MESSAGE_NOTFOUND = "notFound";
var MESSAGE_BADREQUEST = "badRequest";
var MESSAGE_NOTIMPLEMENTED = "notImplemented";
var CODE_OK = 200;
var CODE_CREATED = 201;
var CODE_NOTFOUND = 404;
var CODE_BADREQUEST = 400;
var CODE_NOTIMPLEMENTED = 501;

/**
 * Prepares the JSON for an API query response which is successful
 * @param {Number} status Integer status code
 * @param {Object} req The request
 */
function prepareResponse(status, req) {
  var response = {};
  prepareMeta(response, status);
  if(req) {
    prepareLinks(response, req);
  }
  return response;
};

/**
 * Prepares and adds the _meta to the given API query response
 * @param {Object} response JSON representation of the response
 * @param {Number} status Integer status code
 */
function prepareMeta(response, status) {
  switch(status) {
    case CODE_OK:
      response._meta = { "message": MESSAGE_OK,
                         "statusCode": CODE_OK };
      break;
    case CODE_CREATED:
      response._meta = { "message": MESSAGE_CREATED,
                         "statusCode": CODE_CREATED };
      break;
    case CODE_NOTFOUND:
      response._meta = { "message": MESSAGE_NOTFOUND,
                         "statusCode": CODE_NOTFOUND };
      break;
    case CODE_NOTIMPLEMENTED:
      response._meta = { "message": MESSAGE_NOTIMPLEMENTED,
                         "statusCode": CODE_NOTIMPLEMENTED };
      break;
    default:
      response._meta = { "message": MESSAGE_BADREQUEST,
                         "statusCode": CODE_BADREQUEST };
  }
};

/**
 * Prepares and adds the _links to the given API query response
 * @param {Object} response JSON representation of the response
 * @param {Object} req The request
 */
function prepareLinks(response, req) {
  var rootUrl = req.protocol + '://' + req.get('host');
  var queryPath = req.originalUrl;
  var selfLink = { "href": rootUrl + queryPath };
  response._links = {};
  response._links["self"] = selfLink;
}

module.exports.OK = CODE_OK;
module.exports.CREATED = CODE_CREATED;
module.exports.NOTFOUND = CODE_NOTFOUND;
module.exports.BADREQUEST = CODE_BADREQUEST;
module.exports.NOTIMPLEMENTED = CODE_NOTIMPLEMENTED;
module.exports.prepareResponse = prepareResponse;
<|endoftext|>


<commit_before>/**
 * @fileoverview Enforce component methods order
 * @author Yannick Croissant
 */
'use strict';

var util = require('util');

var Components = require('../util/Components');

/**
 * Get the methods order from the default config and the user config
 * @param {Object} defaultConfig The default configuration.
 * @param {Object} userConfig The user configuration.
 * @returns {Array} Methods order
 */
function getMethodsOrder(defaultConfig, userConfig) {
  userConfig = userConfig || {};

  var groups = util._extend(defaultConfig.groups, userConfig.groups);
  var order = userConfig.order || defaultConfig.order;

  var config = [];
  var entry;
  for (var i = 0, j = order.length; i < j; i++) {
    entry = order[i];
    if (groups.hasOwnProperty(entry)) {
      config = config.concat(groups[entry]);
    } else {
      config.push(entry);
    }
  }

  return config;
}

// ------------------------------------------------------------------------------
// Rule Definition
// ------------------------------------------------------------------------------

module.exports = Components.detect(function(context, components) {

  var errors = {};

  var MISPOSITION_MESSAGE = '{{propA}} should be placed {{position}} {{propB}}';

  var methodsOrder = getMethodsOrder({
    order: [
      'static-methods',
      'lifecycle',
      'everything-else',
      'render'
    ],
    groups: {
      lifecycle: [
        'displayName',
        'propTypes',
        'contextTypes',
        'childContextTypes',
        'mixins',
        'statics',
        'defaultProps',
        'constructor',
        'getDefaultProps',
        'state',
        'getInitialState',
        'getChildContext',
        'componentWillMount',
        'componentDidMount',
        'componentWillReceiveProps',
        'shouldComponentUpdate',
        'componentWillUpdate',
        'componentDidUpdate',
        'componentWillUnmount'
      ]
    }
  }, context.options[0]);

  // --------------------------------------------------------------------------
  // Public
  // --------------------------------------------------------------------------

  var regExpRegExp = /\/(.*)\/([g|y|i|m]*)/;

  /**
   * Get indexes of the matching patterns in methods order configuration
   * @param {Object} method - Method metadata.
   * @returns {Array} The matching patterns indexes. Return [Infinity] if there is no match.
   */
  function getRefPropIndexes(method) {
    var isRegExp;
    var matching;
    var i;
    var j;
    var indexes = [];

    if (method.static) {
      for (i = 0, j = methodsOrder.length; i < j; i++) {
        if (methodsOrder[i] === 'static-methods') {
          indexes.push(i);
        }
      }
    }

    // Either this is not a static method or static methods are not specified
    // in the methodsOrder.
    if (indexes.length === 0) {
      for (i = 0, j = methodsOrder.length; i < j; i++) {
        isRegExp = methodsOrder[i].match(regExpRegExp);
        if (isRegExp) {
          matching = new RegExp(isRegExp[1], isRegExp[2]).test(method.name);
        } else {
          matching = methodsOrder[i] === method.name;
        }
        if (matching) {
          indexes.push(i);
        }
      }
    }

    // No matching pattern, return 'everything-else' index
    if (indexes.length === 0) {
      for (i = 0, j = methodsOrder.length; i < j; i++) {
        if (methodsOrder[i] === 'everything-else') {
          indexes.push(i);
        }
      }
    }

    // No matching pattern and no 'everything-else' group
    if (indexes.length === 0) {
      indexes.push(Infinity);
    }

    return indexes;
  }

  /**
   * Get properties name
   * @param {Object} node - Property.
   * @returns {String} Property name.
   */
  function getPropertyName(node) {
    // Special case for class properties
    // (babel-eslint does not expose property name so we have to rely on tokens)
    if (node.type === 'ClassProperty') {
      var tokens = context.getFirstTokens(node, 2);
      return tokens[1] && tokens[1].type === 'Identifier' ? tokens[1].value : tokens[0].value;
    }

    return node.key.name;
  }

  /**
   * Store a new error in the error list
   * @param {Object} propA - Mispositioned property.
   * @param {Object} propB - Reference property.
   */
  function storeError(propA, propB) {
    // Initialize the error object if needed
    if (!errors[propA.index]) {
      errors[propA.index] = {
        node: propA.node,
        score: 0,
        closest: {
          distance: Infinity,
          ref: {
            node: null,
            index: 0
          }
        }
      };
    }
    // Increment the prop score
    errors[propA.index].score++;
    // Stop here if we already have a closer reference
    if (Math.abs(propA.index - propB.index) > errors[propA.index].closest.distance) {
      return;
    }
    // Update the closest reference
    errors[propA.index].closest.distance = Math.abs(propA.index - propB.index);
    errors[propA.index].closest.ref.node = propB.node;
    errors[propA.index].closest.ref.index = propB.index;
  }

  /**
   * Dedupe errors, only keep the ones with the highest score and delete the others
   */
  function dedupeErrors() {
    for (var i in errors) {
      if (!errors.hasOwnProperty(i)) {
        continue;
      }
      var index = errors[i].closest.ref.index;
      if (!errors[index]) {
        continue;
      }
      if (errors[i].score > errors[index].score) {
        delete errors[index];
      } else {
        delete errors[i];
      }
    }
  }

  /**
   * Report errors
   */
  function reportErrors() {
    dedupeErrors();

    var nodeA;
    var nodeB;
    var indexA;
    var indexB;
    for (var i in errors) {
      if (!errors.hasOwnProperty(i)) {
        continue;
      }

      nodeA = errors[i].node;
      nodeB = errors[i].closest.ref.node;
      indexA = i;
      indexB = errors[i].closest.ref.index;

      context.report(nodeA, MISPOSITION_MESSAGE, {
        propA: getPropertyName(nodeA),
        propB: getPropertyName(nodeB),
        position: indexA < indexB ? 'before' : 'after'
      });
    }
  }

  /**
   * Get properties for a given AST node
   * @param {ASTNode} node The AST node being checked.
   * @returns {Array} Properties array.
   */
  function getComponentProperties(node) {
    switch (node.type) {
      case 'ClassDeclaration':
        return node.body.body;
      case 'ObjectExpression':
        return node.properties;
      default:
        return [];
    }
  }

  /**
   * Compare two properties and find out if they are in the right order
   * @param {Array} propertiesInfos Array containing all the properties metadata.
   * @param {Object} propA First property name and metadata
   * @param {Object} propB Second property name.
   * @returns {Object} Object containing a correct true/false flag and the correct indexes for the two properties.
   */
  function comparePropsOrder(propertiesInfos, propA, propB) {
    var i;
    var j;
    var k;
    var l;
    var refIndexA;
    var refIndexB;

    // Get references indexes (the correct position) for given properties
    var refIndexesA = getRefPropIndexes(propA);
    var refIndexesB = getRefPropIndexes(propB);

    // Get current indexes for given properties
    var classIndexA = propertiesInfos.indexOf(propA);
    var classIndexB = propertiesInfos.indexOf(propB);

    // Loop around the references indexes for the 1st property
    for (i = 0, j = refIndexesA.length; i < j; i++) {
      refIndexA = refIndexesA[i];

      // Loop around the properties for the 2nd property (for comparison)
      for (k = 0, l = refIndexesB.length; k < l; k++) {
        refIndexB = refIndexesB[k];

        if (
          // Comparing the same properties
          refIndexA === refIndexB ||
          // 1st property is placed before the 2nd one in reference and in current component
          refIndexA < refIndexB && classIndexA < classIndexB ||
          // 1st property is placed after the 2nd one in reference and in current component
          refIndexA > refIndexB && classIndexA > classIndexB
        ) {
          return {
            correct: true,
            indexA: classIndexA,
            indexB: classIndexB
          };
        }

      }
    }

    // We did not find any correct match between reference and current component
    return {
      correct: false,
      indexA: refIndexA,
      indexB: refIndexB
    };
  }

  /**
   * Check properties order from a properties list and store the eventual errors
   * @param {Array} properties Array containing all the properties.
   */
  function checkPropsOrder(properties) {
    var propertiesInfos = properties.map(function(node) {
      return {
        name: getPropertyName(node),
        static: node.static
      };
    });

    var i;
    var j;
    var k;
    var l;
    var propA;
    var propB;
    var order;

    // Loop around the properties
    for (i = 0, j = propertiesInfos.length; i < j; i++) {
      propA = propertiesInfos[i];

      // Loop around the properties a second time (for comparison)
      for (k = 0, l = propertiesInfos.length; k < l; k++) {
        propB = propertiesInfos[k];

        // Compare the properties order
        order = comparePropsOrder(propertiesInfos, propA, propB);

        // Continue to next comparison is order is correct
        if (order.correct === true) {
          continue;
        }

        // Store an error if the order is incorrect
        storeError({
          node: properties[i],
          index: order.indexA
        }, {
          node: properties[k],
          index: order.indexB
        });
      }
    }

  }

  return {
    'Program:exit': function() {
      var list = components.list();
      for (var component in list) {
        if (!list.hasOwnProperty(component)) {
          continue;
        }
        var properties = getComponentProperties(list[component].node);
        checkPropsOrder(properties);
      }

      reportErrors();
    }
  };

});

module.exports.schema = [{
  type: 'object',
  properties: {
    order: {
      type: 'array',
      items: {
        type: 'string'
      }
    },
    groups: {
      type: 'object',
      patternProperties: {
        '^.*$': {
          type: 'array',
          items: {
            type: 'string'
          }
        }
      }
    }
  },
  additionalProperties: false
}];
<commit_msg>Break out of some loops in sort-comp<commit_after>/**
 * @fileoverview Enforce component methods order
 * @author Yannick Croissant
 */
'use strict';

var util = require('util');

var Components = require('../util/Components');

/**
 * Get the methods order from the default config and the user config
 * @param {Object} defaultConfig The default configuration.
 * @param {Object} userConfig The user configuration.
 * @returns {Array} Methods order
 */
function getMethodsOrder(defaultConfig, userConfig) {
  userConfig = userConfig || {};

  var groups = util._extend(defaultConfig.groups, userConfig.groups);
  var order = userConfig.order || defaultConfig.order;

  var config = [];
  var entry;
  for (var i = 0, j = order.length; i < j; i++) {
    entry = order[i];
    if (groups.hasOwnProperty(entry)) {
      config = config.concat(groups[entry]);
    } else {
      config.push(entry);
    }
  }

  return config;
}

// ------------------------------------------------------------------------------
// Rule Definition
// ------------------------------------------------------------------------------

module.exports = Components.detect(function(context, components) {

  var errors = {};

  var MISPOSITION_MESSAGE = '{{propA}} should be placed {{position}} {{propB}}';

  var methodsOrder = getMethodsOrder({
    order: [
      'static-methods',
      'lifecycle',
      'everything-else',
      'render'
    ],
    groups: {
      lifecycle: [
        'displayName',
        'propTypes',
        'contextTypes',
        'childContextTypes',
        'mixins',
        'statics',
        'defaultProps',
        'constructor',
        'getDefaultProps',
        'state',
        'getInitialState',
        'getChildContext',
        'componentWillMount',
        'componentDidMount',
        'componentWillReceiveProps',
        'shouldComponentUpdate',
        'componentWillUpdate',
        'componentDidUpdate',
        'componentWillUnmount'
      ]
    }
  }, context.options[0]);

  // --------------------------------------------------------------------------
  // Public
  // --------------------------------------------------------------------------

  var regExpRegExp = /\/(.*)\/([g|y|i|m]*)/;

  /**
   * Get indexes of the matching patterns in methods order configuration
   * @param {Object} method - Method metadata.
   * @returns {Array} The matching patterns indexes. Return [Infinity] if there is no match.
   */
  function getRefPropIndexes(method) {
    var isRegExp;
    var matching;
    var i;
    var j;
    var indexes = [];

    if (method.static) {
      for (i = 0, j = methodsOrder.length; i < j; i++) {
        if (methodsOrder[i] === 'static-methods') {
          indexes.push(i);
          break;
        }
      }
    }

    // Either this is not a static method or static methods are not specified
    // in the methodsOrder.
    if (indexes.length === 0) {
      for (i = 0, j = methodsOrder.length; i < j; i++) {
        isRegExp = methodsOrder[i].match(regExpRegExp);
        if (isRegExp) {
          matching = new RegExp(isRegExp[1], isRegExp[2]).test(method.name);
        } else {
          matching = methodsOrder[i] === method.name;
        }
        if (matching) {
          indexes.push(i);
        }
      }
    }

    // No matching pattern, return 'everything-else' index
    if (indexes.length === 0) {
      for (i = 0, j = methodsOrder.length; i < j; i++) {
        if (methodsOrder[i] === 'everything-else') {
          indexes.push(i);
          break;
        }
      }
    }

    // No matching pattern and no 'everything-else' group
    if (indexes.length === 0) {
      indexes.push(Infinity);
    }

    return indexes;
  }

  /**
   * Get properties name
   * @param {Object} node - Property.
   * @returns {String} Property name.
   */
  function getPropertyName(node) {
    // Special case for class properties
    // (babel-eslint does not expose property name so we have to rely on tokens)
    if (node.type === 'ClassProperty') {
      var tokens = context.getFirstTokens(node, 2);
      return tokens[1] && tokens[1].type === 'Identifier' ? tokens[1].value : tokens[0].value;
    }

    return node.key.name;
  }

  /**
   * Store a new error in the error list
   * @param {Object} propA - Mispositioned property.
   * @param {Object} propB - Reference property.
   */
  function storeError(propA, propB) {
    // Initialize the error object if needed
    if (!errors[propA.index]) {
      errors[propA.index] = {
        node: propA.node,
        score: 0,
        closest: {
          distance: Infinity,
          ref: {
            node: null,
            index: 0
          }
        }
      };
    }
    // Increment the prop score
    errors[propA.index].score++;
    // Stop here if we already have a closer reference
    if (Math.abs(propA.index - propB.index) > errors[propA.index].closest.distance) {
      return;
    }
    // Update the closest reference
    errors[propA.index].closest.distance = Math.abs(propA.index - propB.index);
    errors[propA.index].closest.ref.node = propB.node;
    errors[propA.index].closest.ref.index = propB.index;
  }

  /**
   * Dedupe errors, only keep the ones with the highest score and delete the others
   */
  function dedupeErrors() {
    for (var i in errors) {
      if (!errors.hasOwnProperty(i)) {
        continue;
      }
      var index = errors[i].closest.ref.index;
      if (!errors[index]) {
        continue;
      }
      if (errors[i].score > errors[index].score) {
        delete errors[index];
      } else {
        delete errors[i];
      }
    }
  }

  /**
   * Report errors
   */
  function reportErrors() {
    dedupeErrors();

    var nodeA;
    var nodeB;
    var indexA;
    var indexB;
    for (var i in errors) {
      if (!errors.hasOwnProperty(i)) {
        continue;
      }

      nodeA = errors[i].node;
      nodeB = errors[i].closest.ref.node;
      indexA = i;
      indexB = errors[i].closest.ref.index;

      context.report(nodeA, MISPOSITION_MESSAGE, {
        propA: getPropertyName(nodeA),
        propB: getPropertyName(nodeB),
        position: indexA < indexB ? 'before' : 'after'
      });
    }
  }

  /**
   * Get properties for a given AST node
   * @param {ASTNode} node The AST node being checked.
   * @returns {Array} Properties array.
   */
  function getComponentProperties(node) {
    switch (node.type) {
      case 'ClassDeclaration':
        return node.body.body;
      case 'ObjectExpression':
        return node.properties;
      default:
        return [];
    }
  }

  /**
   * Compare two properties and find out if they are in the right order
   * @param {Array} propertiesInfos Array containing all the properties metadata.
   * @param {Object} propA First property name and metadata
   * @param {Object} propB Second property name.
   * @returns {Object} Object containing a correct true/false flag and the correct indexes for the two properties.
   */
  function comparePropsOrder(propertiesInfos, propA, propB) {
    var i;
    var j;
    var k;
    var l;
    var refIndexA;
    var refIndexB;

    // Get references indexes (the correct position) for given properties
    var refIndexesA = getRefPropIndexes(propA);
    var refIndexesB = getRefPropIndexes(propB);

    // Get current indexes for given properties
    var classIndexA = propertiesInfos.indexOf(propA);
    var classIndexB = propertiesInfos.indexOf(propB);

    // Loop around the references indexes for the 1st property
    for (i = 0, j = refIndexesA.length; i < j; i++) {
      refIndexA = refIndexesA[i];

      // Loop around the properties for the 2nd property (for comparison)
      for (k = 0, l = refIndexesB.length; k < l; k++) {
        refIndexB = refIndexesB[k];

        if (
          // Comparing the same properties
          refIndexA === refIndexB ||
          // 1st property is placed before the 2nd one in reference and in current component
          refIndexA < refIndexB && classIndexA < classIndexB ||
          // 1st property is placed after the 2nd one in reference and in current component
          refIndexA > refIndexB && classIndexA > classIndexB
        ) {
          return {
            correct: true,
            indexA: classIndexA,
            indexB: classIndexB
          };
        }

      }
    }

    // We did not find any correct match between reference and current component
    return {
      correct: false,
      indexA: refIndexA,
      indexB: refIndexB
    };
  }

  /**
   * Check properties order from a properties list and store the eventual errors
   * @param {Array} properties Array containing all the properties.
   */
  function checkPropsOrder(properties) {
    var propertiesInfos = properties.map(function(node) {
      return {
        name: getPropertyName(node),
        static: node.static
      };
    });

    var i;
    var j;
    var k;
    var l;
    var propA;
    var propB;
    var order;

    // Loop around the properties
    for (i = 0, j = propertiesInfos.length; i < j; i++) {
      propA = propertiesInfos[i];

      // Loop around the properties a second time (for comparison)
      for (k = 0, l = propertiesInfos.length; k < l; k++) {
        propB = propertiesInfos[k];

        // Compare the properties order
        order = comparePropsOrder(propertiesInfos, propA, propB);

        // Continue to next comparison is order is correct
        if (order.correct === true) {
          continue;
        }

        // Store an error if the order is incorrect
        storeError({
          node: properties[i],
          index: order.indexA
        }, {
          node: properties[k],
          index: order.indexB
        });
      }
    }

  }

  return {
    'Program:exit': function() {
      var list = components.list();
      for (var component in list) {
        if (!list.hasOwnProperty(component)) {
          continue;
        }
        var properties = getComponentProperties(list[component].node);
        checkPropsOrder(properties);
      }

      reportErrors();
    }
  };

});

module.exports.schema = [{
  type: 'object',
  properties: {
    order: {
      type: 'array',
      items: {
        type: 'string'
      }
    },
    groups: {
      type: 'object',
      patternProperties: {
        '^.*$': {
          type: 'array',
          items: {
            type: 'string'
          }
        }
      }
    }
  },
  additionalProperties: false
}];
<|endoftext|>


<commit_before>var WebSocket = require('ws');
var EventEmitter = require('events').EventEmitter;
var formatter = require('./formatter');
var Response = require('./response').Response;

var SCSocket = function (id, server, socket, authData) {
  var self = this;

  EventEmitter.call(this);

  this._localEvents = {
    'open': 1,
    'close': 1,
    'connect': 1,
    'disconnect': 1,
    'error': 1,
    'raw': 1
  };

  this._autoAckEvents = {
    'ready': 1,
    'publish': 1
  };

  this.id = id;
  this.server = server;
  this.socket = socket;

  this.request = this.socket.upgradeReq;
  this.connected = true;

  this.remoteAddress = this.request.remoteAddress;

  this._cid = 1;
  this._callbackMap = {};

  this._authToken = authData.token || null;

  this.socket.on('error', function (err) {
    EventEmitter.prototype.emit.call(self, 'error', err);
  });

  this.socket.on('close', function (code) {
    self._onSCClose(code);
  });

  this._pingIntervalTicker = setInterval(this._sendPing.bind(this), this.server.pingInterval);
  this._resetPingTimeout();

  // Receive incoming raw messages
  this.socket.on('message', function (message, flags) {
    self._resetPingTimeout();

    EventEmitter.prototype.emit.call(self, 'message', message);
    var json = formatter.parse(message);

    if (json == null) {
      var err = new Error('Received empty message');
      EventEmitter.prototype.emit.call(self, 'error', err);
    } else if (json.event) {
      if (self._localEvents[json.event] == null) {
        var response = new Response(self, json.cid);
        self.server.verifyEvent(self, json.event, json.data, function (err) {
          if (err) {
            response.error(err);
          } else {
            if (self._autoAckEvents[json.event]) {
              response.end();
              EventEmitter.prototype.emit.call(self, json.event, json.data);
            } else {
              EventEmitter.prototype.emit.call(self, json.event, json.data, response.callback.bind(response));
            }
          }
        });
      }
    } else if (json.cid == null) {
      // In case of pong, do nothing
      if (!json.pong) {
        // If incoming message has no cid, then we emit it as raw data
        EventEmitter.prototype.emit.call(self, 'raw', message);
      }
    } else {
      // If incoming message is a response to a previously sent message
      var ret = self._callbackMap[json.cid];
      if (ret) {
        clearTimeout(ret.timeout);
        delete self._callbackMap[json.cid];
        ret.callback(json.error, json.data);
      }
    }
  });

  // Emit initial status to client
  this.emit('ready', {
    id: this.id,
    isAuthenticated: !!this._authToken,
    authError: authData.error,
    pingTimeout: this.server.pingTimeout
  });
};

SCSocket.prototype = Object.create(EventEmitter.prototype);

SCSocket.CONNECTING = SCSocket.prototype.CONNECTING = WebSocket.prototype.CONNECTING;
SCSocket.OPEN = SCSocket.prototype.OPEN = WebSocket.prototype.OPEN;
SCSocket.CLOSING = SCSocket.prototype.CLOSING = WebSocket.prototype.CLOSING;
SCSocket.CLOSED = SCSocket.prototype.CLOSED = WebSocket.prototype.CLOSED;

SCSocket.ignoreStatuses = {
  1000: 'Socket closed normally',
  1001: 'Socket hung up'
};

SCSocket.errorStatuses = {
  1002: 'A WebSocket protocol error was encountered',
  1003: 'Server terminated socket because it received invalid data',
  1006: 'Socket hung up',
  1007: 'Message format was incorrect',
  1008: 'Encountered a policy violation',
  1009: 'Message was too big to process',
  1010: 'Client ended the connection because the server did not comply with extension requirements',
  1011: 'Server encountered an unexpected fatal condition',
  4000: 'Socket pong timed out'
};

SCSocket.prototype._sendPing = function () {
  this.emitRaw({ping: 1});
};

SCSocket.prototype._resetPingTimeout = function () {
  var self = this;

  clearTimeout(this._pingTimeoutTicker);
  this._pingTimeoutTicker = setTimeout(function() {
    self.socket.terminate();
  }, this.server.pingTimeout);
};

SCSocket.prototype._nextCallId = function () {
  return this._cid++;
};

SCSocket.prototype.getState = function () {
  return this.socket.readyState;
};

SCSocket.prototype.getBytesReceived = function () {
  return this.socket.bytesReceived;
};

SCSocket.prototype._onSCClose = function (code) {
  this.connected = false;

  clearInterval(this._pingIntervalTicker);
  EventEmitter.prototype.emit.call(this, 'disconnect', code);

  if (!SCSocket.ignoreStatuses[code]) {
    var err = new Error(SCSocket.errorStatuses[code] || 'Socket connection failed for unknown reasons');
    err.code = code;
    EventEmitter.prototype.emit.call(this, 'error', err);
  }
};

SCSocket.prototype.disconnect = function (code, data) {
  this.socket.close(code, data);
};

SCSocket.prototype.terminate = function () {
  this.socket.terminate();
};

SCSocket.prototype.send = function (data, options, callback) {
  var self = this;

  this.socket.send(data, options, function (err) {
    callback && callback(err);
    if (err) {
      EventEmitter.prototype.emit.call(self, 'error', err);
    }
  });
};

SCSocket.prototype.emitRaw = function (eventObject) {
  this.send(formatter.stringify(eventObject));
};

SCSocket.prototype.emit = function (event, data, callback) {
  var self = this;

  if (this._localEvents[event] == null) {
    var eventObject = {
      event: event
    };
    if (data !== undefined) {
      eventObject.data = data;
    }
    eventObject.cid = this._nextCallId();

    if (callback) {
      var timeout = setTimeout(function () {
        var error = new Error("Event response for '" + event + "' timed out");
        delete self._callbackMap[eventObject.cid];
        callback(error, eventObject);
      }, this.server.ackTimeout);

      this._callbackMap[eventObject.cid] = {callback: callback, timeout: timeout};
    }
    this.emitRaw(eventObject);
  } else {
    EventEmitter.prototype.emit.call(this, event, data);
  }
};

SCSocket.prototype.setAuthToken = function (data, options, callback) {
  this._authToken = data;
  var signedToken = this.server.auth.signToken(data, options);
  this.emit('setAuthToken', signedToken, callback);
};

SCSocket.prototype.getAuthToken = function () {
  return this._authToken;
};

SCSocket.prototype.removeAuthToken = function (callback) {
  this._authToken = null;
  this.emit('removeAuthToken', null, callback);
};

module.exports = SCSocket;<commit_msg>modified error message<commit_after>var WebSocket = require('ws');
var EventEmitter = require('events').EventEmitter;
var formatter = require('./formatter');
var Response = require('./response').Response;

var SCSocket = function (id, server, socket, authData) {
  var self = this;

  EventEmitter.call(this);

  this._localEvents = {
    'open': 1,
    'close': 1,
    'connect': 1,
    'disconnect': 1,
    'error': 1,
    'raw': 1
  };

  this._autoAckEvents = {
    'ready': 1,
    'publish': 1
  };

  this.id = id;
  this.server = server;
  this.socket = socket;

  this.request = this.socket.upgradeReq;
  this.connected = true;

  this.remoteAddress = this.request.remoteAddress;

  this._cid = 1;
  this._callbackMap = {};

  this._authToken = authData.token || null;

  this.socket.on('error', function (err) {
    EventEmitter.prototype.emit.call(self, 'error', err);
  });

  this.socket.on('close', function (code) {
    self._onSCClose(code);
  });

  this._pingIntervalTicker = setInterval(this._sendPing.bind(this), this.server.pingInterval);
  this._resetPingTimeout();

  // Receive incoming raw messages
  this.socket.on('message', function (message, flags) {
    self._resetPingTimeout();

    EventEmitter.prototype.emit.call(self, 'message', message);
    var json = formatter.parse(message);

    if (json == null) {
      var err = new Error('Received empty message');
      EventEmitter.prototype.emit.call(self, 'error', err);
    } else if (json.event) {
      if (self._localEvents[json.event] == null) {
        var response = new Response(self, json.cid);
        self.server.verifyEvent(self, json.event, json.data, function (err) {
          if (err) {
            response.error(err);
          } else {
            if (self._autoAckEvents[json.event]) {
              response.end();
              EventEmitter.prototype.emit.call(self, json.event, json.data);
            } else {
              EventEmitter.prototype.emit.call(self, json.event, json.data, response.callback.bind(response));
            }
          }
        });
      }
    } else if (json.cid == null) {
      // In case of pong, do nothing
      if (!json.pong) {
        // If incoming message has no cid, then we emit it as raw data
        EventEmitter.prototype.emit.call(self, 'raw', message);
      }
    } else {
      // If incoming message is a response to a previously sent message
      var ret = self._callbackMap[json.cid];
      if (ret) {
        clearTimeout(ret.timeout);
        delete self._callbackMap[json.cid];
        ret.callback(json.error, json.data);
      }
    }
  });

  // Emit initial status to client
  this.emit('ready', {
    id: this.id,
    isAuthenticated: !!this._authToken,
    authError: authData.error,
    pingTimeout: this.server.pingTimeout
  });
};

SCSocket.prototype = Object.create(EventEmitter.prototype);

SCSocket.CONNECTING = SCSocket.prototype.CONNECTING = WebSocket.prototype.CONNECTING;
SCSocket.OPEN = SCSocket.prototype.OPEN = WebSocket.prototype.OPEN;
SCSocket.CLOSING = SCSocket.prototype.CLOSING = WebSocket.prototype.CLOSING;
SCSocket.CLOSED = SCSocket.prototype.CLOSED = WebSocket.prototype.CLOSED;

SCSocket.ignoreStatuses = {
  1000: 'Socket closed normally',
  1001: 'Socket hung up'
};

SCSocket.errorStatuses = {
  1002: 'A WebSocket protocol error was encountered',
  1003: 'Server terminated socket because it received invalid data',
  1006: 'Socket hung up',
  1007: 'Message format was incorrect',
  1008: 'Encountered a policy violation',
  1009: 'Message was too big to process',
  1010: 'Client ended the connection because the server did not comply with extension requirements',
  1011: 'Server encountered an unexpected fatal condition',
  4001: 'Client pong timed out'
};

SCSocket.prototype._sendPing = function () {
  this.emitRaw({ping: 1});
};

SCSocket.prototype._resetPingTimeout = function () {
  var self = this;

  clearTimeout(this._pingTimeoutTicker);
  this._pingTimeoutTicker = setTimeout(function() {
    self.socket.terminate();
  }, this.server.pingTimeout);
};

SCSocket.prototype._nextCallId = function () {
  return this._cid++;
};

SCSocket.prototype.getState = function () {
  return this.socket.readyState;
};

SCSocket.prototype.getBytesReceived = function () {
  return this.socket.bytesReceived;
};

SCSocket.prototype._onSCClose = function (code) {
  this.connected = false;

  clearInterval(this._pingIntervalTicker);
  EventEmitter.prototype.emit.call(this, 'disconnect', code);

  if (!SCSocket.ignoreStatuses[code]) {
    var err = new Error(SCSocket.errorStatuses[code] || 'Socket connection failed for unknown reasons');
    err.code = code;
    EventEmitter.prototype.emit.call(this, 'error', err);
  }
};

SCSocket.prototype.disconnect = function (code, data) {
  this.socket.close(code, data);
};

SCSocket.prototype.terminate = function () {
  this.socket.terminate();
};

SCSocket.prototype.send = function (data, options, callback) {
  var self = this;

  this.socket.send(data, options, function (err) {
    callback && callback(err);
    if (err) {
      EventEmitter.prototype.emit.call(self, 'error', err);
    }
  });
};

SCSocket.prototype.emitRaw = function (eventObject) {
  this.send(formatter.stringify(eventObject));
};

SCSocket.prototype.emit = function (event, data, callback) {
  var self = this;

  if (this._localEvents[event] == null) {
    var eventObject = {
      event: event
    };
    if (data !== undefined) {
      eventObject.data = data;
    }
    eventObject.cid = this._nextCallId();

    if (callback) {
      var timeout = setTimeout(function () {
        var error = new Error("Event response for '" + event + "' timed out");
        delete self._callbackMap[eventObject.cid];
        callback(error, eventObject);
      }, this.server.ackTimeout);

      this._callbackMap[eventObject.cid] = {callback: callback, timeout: timeout};
    }
    this.emitRaw(eventObject);
  } else {
    EventEmitter.prototype.emit.call(this, event, data);
  }
};

SCSocket.prototype.setAuthToken = function (data, options, callback) {
  this._authToken = data;
  var signedToken = this.server.auth.signToken(data, options);
  this.emit('setAuthToken', signedToken, callback);
};

SCSocket.prototype.getAuthToken = function () {
  return this._authToken;
};

SCSocket.prototype.removeAuthToken = function (callback) {
  this._authToken = null;
  this.emit('removeAuthToken', null, callback);
};

module.exports = SCSocket;<|endoftext|>


<commit_before>
const expect = require('chai').expect;
const RG = require('../../../client/src/battles.js');
const Game = require('../../..//client/src/game.js');

const BattleFact = require('../../../client/src/factory.battle');
// const RGTest = require('../../roguetest');

const {Army} = require('../../../client/src/game.battle');

describe('Game.Army', () => {
    it('it has actors', () => {
        const army = new Army();
        const a1 = new RG.Actor.Rogue('soldier');
        const a2 = new RG.Actor.Rogue('pacifist');
        army.addActor(a1);
        expect(army.hasActor(a1)).to.equal(true);
        expect(army.hasActor(a2)).to.equal(false);
    });

    it('responds to ACTOR_KILLED events', () => {
        const MockBattle = {getName: () => 'MockBattle'};
        const army = new Army();
        const a1 = new RG.Actor.Rogue('soldier');
        const a2 = new RG.Actor.Rogue('pacifist');
        army.addActor(a1);
        army.setBattle(MockBattle);

        RG.POOL.emitEvent(RG.EVT_ACTOR_KILLED, {actor: a2});
        expect(army.isDefeated()).to.equal(false);

        RG.POOL.emitEvent(RG.EVT_ACTOR_KILLED, {actor: a1});
        expect(army.isDefeated()).to.equal(true);
    });
});

describe('Game.Battle', () => {
    it('It is fought until end condition', () => {
        const areaLevel = RG.FACT.createLevel('arena', 40, 40);
        const game = new Game.Main();
        game.addLevel(areaLevel);

        const conf = {
            cols: 20, rows: 10,
            armySize: 10, centerX: true, centerY: true
        };
        const battle = new BattleFact().createBattle(areaLevel, conf);
        battle.getLevel().setParent(areaLevel);
        game.addBattle(battle);

        const armies = battle.getArmies();
        armies.forEach(army => {
            army.setDefeatThreshold(0);
        });

        expect(areaLevel.getActors().length).to.equal(0);
        expect(battle.isOver()).to.equal(false);

        let count = 0;
        const turnLimit = 10000;
        while (!battle.isOver() && count < turnLimit) {
            game.simulateGame();
            ++count;
        }

        for (let i = 0; i < 10; i++) {
            game.simulateGame();
        }

        expect(battle.isOver()).to.equal(true);
        expect(battle.getLevel().getActors().length).to.be.equal(0);

        const survivors = areaLevel.getActors();
        expect(survivors.length).to.be.above(0);

        // Check that event listeners are properly cleaned up
        const func = () => {
            RG.POOL.emitEvent(RG.EVT_ACTOR_KILLED, {actor: survivors[0]});
        };
        expect(func).not.to.throw();

        survivors.forEach(actor => {
            expect(actor.has('BattleBadge')).to.equal(true);
            expect(actor.has('InBattle')).to.equal(false);
            expect(actor.has('BattleOver')).to.equal(false);
        });
    });

});
<commit_msg>Added arg true to addBattle().<commit_after>
const expect = require('chai').expect;
const RG = require('../../../client/src/battles.js');
const Game = require('../../..//client/src/game.js');

const BattleFact = require('../../../client/src/factory.battle');
// const RGTest = require('../../roguetest');

const {Army} = require('../../../client/src/game.battle');

describe('Game.Army', () => {
    it('it has actors', () => {
        const army = new Army();
        const a1 = new RG.Actor.Rogue('soldier');
        const a2 = new RG.Actor.Rogue('pacifist');
        army.addActor(a1);
        expect(army.hasActor(a1)).to.equal(true);
        expect(army.hasActor(a2)).to.equal(false);
    });

    it('responds to ACTOR_KILLED events', () => {
        const MockBattle = {getName: () => 'MockBattle'};
        const army = new Army();
        const a1 = new RG.Actor.Rogue('soldier');
        const a2 = new RG.Actor.Rogue('pacifist');
        army.addActor(a1);
        army.setBattle(MockBattle);

        RG.POOL.emitEvent(RG.EVT_ACTOR_KILLED, {actor: a2});
        expect(army.isDefeated()).to.equal(false);

        RG.POOL.emitEvent(RG.EVT_ACTOR_KILLED, {actor: a1});
        expect(army.isDefeated()).to.equal(true);
    });
});

describe('Game.Battle', () => {
    it('It is fought until end condition', () => {
        const areaLevel = RG.FACT.createLevel('arena', 40, 40);
        const game = new Game.Main();
        game.addLevel(areaLevel);

        const conf = {
            cols: 20, rows: 10,
            armySize: 10, centerX: true, centerY: true
        };
        const battle = new BattleFact().createBattle(areaLevel, conf);
        battle.getLevel().setParent(areaLevel);
        game.addBattle(battle, true);

        const armies = battle.getArmies();
        armies.forEach(army => {
            army.setDefeatThreshold(0);
        });

        expect(areaLevel.getActors().length).to.equal(0);
        expect(battle.isOver()).to.equal(false);

        let count = 0;
        const turnLimit = 10000;
        while (!battle.isOver() && count < turnLimit) {
            game.simulateGame();
            ++count;
        }

        for (let i = 0; i < 10; i++) {
            game.simulateGame();
        }

        expect(battle.isOver()).to.equal(true);
        expect(battle.getLevel().getActors().length).to.be.equal(0);

        const survivors = areaLevel.getActors();
        expect(survivors.length).to.be.above(0);

        // Check that event listeners are properly cleaned up
        const func = () => {
            RG.POOL.emitEvent(RG.EVT_ACTOR_KILLED, {actor: survivors[0]});
        };
        expect(func).not.to.throw();

        survivors.forEach(actor => {
            expect(actor.has('BattleBadge')).to.equal(true);
            expect(actor.has('InBattle')).to.equal(false);
            expect(actor.has('BattleOver')).to.equal(false);
        });
    });

});
<|endoftext|>


<commit_before>"use strict";

var pjson = require('./package.json');
var child_process = require('child_process');
var ArgumentParser = require('argparse').ArgumentParser;

var DEVICE_ID_REGEXP = /^[a-zA-Z0-9]{5,}/;
var ADB_PATH = 'adb';

var args = new ArgumentParser({
  version: pjson.version,
  addHelp: true,
  description: pjson.description,
  epilog: 'Example usage: node android-input-repeater.js"'
});

var params = args.parseArgs();
var devices = getDeviceIds();
console.log("Devices detected:", devices);

if (devices.length < 2) {
  console.error("There has to be more than one connected android device.");
} else {
  // TODO: continue here
}

/**
 * @return array of device ids of connected android devices,
 *  empty when no device is connected
 */
function getDeviceIds() {
  // get devices list from adb
  var devices = child_process.execSync(ADB_PATH + ' devices').toString();
  devices = devices.split(/[\r|\n]+/);

  // delete all metadata
  devices = devices.filter(function (line, index) {
    if (index === 0 || line === "") {
      return false;
    }

    if (line.match(DEVICE_ID_REGEXP) === null) {
      console.error(line.split(/\s+/)[0] + ' is no valid device id');
      return false;
    } else {
      return true;
    }
  });

  // we only need device ids
  return devices.map(function (line) {
    return line.match(DEVICE_ID_REGEXP)[0];
  });
}
<commit_msg>getting input events for first connected device<commit_after>"use strict";

var pjson = require('./package.json');
var child_process = require('child_process');
var ArgumentParser = require('argparse').ArgumentParser;

var NEWLINE_REGEXP = /[\r|\n]+/;
var DEVICE_ID_REGEXP = /^[a-zA-Z0-9]{5,}/;
var EVENT_STRING_REGEXP = /^\/dev\/input\/event(\d): (\d{4}) (\d{4}) (\d{8})$/mg;
var ADB_PATH = 'adb';

var args = new ArgumentParser({
  version: pjson.version,
  addHelp: true,
  description: pjson.description,
  epilog: 'Example usage: node android-input-repeater.js"'
});

var params = args.parseArgs();
var devices = getDeviceIds();
console.log("Devices detected:", devices);

if (devices.length < 2) {
  console.error("There has to be more than one connected android device.");
} else {
  getInputEvents(devices[0], function (event) {
      console.log(event);
  });
}

function getInputEvents(deviceId, onEvent) {
  var command = ADB_PATH + ' -s ' + deviceId + ' shell getevent';
  var child = child_process.exec(command);
  child.stdout.on('data', function (data) {
    var eventStr = data.toString();
    parseEventString(eventStr, onEvent);
  });
}

function parseEventString(eventStr, onEvent) {
  var match;
  var eventObj = {};
  while (match = EVENT_STRING_REGEXP.exec(eventStr)) {
    eventObj.type = match[1];
    eventObj.params = match.slice(2, 5).map(function (hex) { return parseInt(hex, 16) });
    onEvent(eventObj);
  }
}

/**
 * @return array of device ids of connected android devices,
 *  empty when no device is connected
 */
function getDeviceIds() {
  // get devices list from adb
  var devices = child_process.execSync(ADB_PATH + ' devices').toString();
  devices = devices.split(NEWLINE_REGEXP);

  // delete all metadata
  devices = devices.filter(function (line, index) {
    if (index === 0 || line === "") {
      return false;
    }

    if (line.match(DEVICE_ID_REGEXP) === null) {
      console.error(line.split(/\s+/)[0] + ' is no valid device id');
      return false;
    } else {
      return true;
    }
  });

  // we only need device ids
  return devices.map(function (line) {
    return line.match(DEVICE_ID_REGEXP)[0];
  });
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2008-2013 The Open Source Geospatial Foundation
 *
 * Published under the BSD license.
 * See https://github.com/geoext/geoext2/blob/master/license.txt for the full
 * text of the license.
 */
(function() {
    var major = 2,
        minor = 0,
        patch = 1,
        label = 'dev',
        environment = [],
        v = '';

    // Concatenate GeoExt version.
    v = 'v' + major + '.' + minor + '.' + patch + (label ? '.' + label : '');

    // Grab versions of libraries in the environment
    if ( Ext.versions.extjs.version ) {
        environment.push('ExtJS: ' + Ext.versions.extjs.version);
    }
    if ( OpenLayers ) {
        environment.push('OpenLayers: ' + OpenLayers.VERSION_NUMBER);
    }
    environment.push('GeoExt: ' + v);

    /**
     * A singleton class holding the properties #version with the current
     * GeoExt version and #environment with a string about the surrounding
     * libraries ExtJS and OpenLayers.
     */
    Ext.define('GeoExt.Version', {
        singleton: true,

        /**
         * The version number of GeoExt.
         *
         * @property {String} version
         */
        version: v,

        /**
         * Lists the versions of the currently loaded libraries and contains the
         * versions of `ExtJS`, `OpenLayers` and `GeoExt`.
         *
         * @property {String} environment
         */
        environment: (environment.join(', '))
    }, function() {
        /**
         * The GeoExt root object.
         *
         * @class GeoExt
         * @singleton
         */
        /**
         * @inheritdoc GeoExt.Version#version
         * @member GeoExt
         * @property version
         */
        GeoExt.version = GeoExt.Version.version;
    });
})();
<commit_msg>Be more cautious when testing for OpenLayers<commit_after>/*
 * Copyright (c) 2008-2013 The Open Source Geospatial Foundation
 *
 * Published under the BSD license.
 * See https://github.com/geoext/geoext2/blob/master/license.txt for the full
 * text of the license.
 */
(function() {
    var major = 2,
        minor = 0,
        patch = 1,
        label = 'dev',
        environment = [],
        v = '';

    // Concatenate GeoExt version.
    v = 'v' + major + '.' + minor + '.' + patch + (label ? '.' + label : '');

    // Grab versions of libraries in the environment
    if ( Ext.versions.extjs.version ) {
        environment.push('ExtJS: ' + Ext.versions.extjs.version);
    }
    if ( window.OpenLayers ) {
        environment.push('OpenLayers: ' + OpenLayers.VERSION_NUMBER);
    }
    environment.push('GeoExt: ' + v);

    /**
     * A singleton class holding the properties #version with the current
     * GeoExt version and #environment with a string about the surrounding
     * libraries ExtJS and OpenLayers.
     */
    Ext.define('GeoExt.Version', {
        singleton: true,

        /**
         * The version number of GeoExt.
         *
         * @property {String} version
         */
        version: v,

        /**
         * Lists the versions of the currently loaded libraries and contains the
         * versions of `ExtJS`, `OpenLayers` and `GeoExt`.
         *
         * @property {String} environment
         */
        environment: (environment.join(', '))
    }, function() {
        /**
         * The GeoExt root object.
         *
         * @class GeoExt
         * @singleton
         */
        /**
         * @inheritdoc GeoExt.Version#version
         * @member GeoExt
         * @property version
         */
        GeoExt.version = GeoExt.Version.version;
    });
})();
<|endoftext|>


<commit_before>/**
 * Created by paulparsons on 1/27/15.
 */

(function() {

	var angularChromosomeVis = angular.module('angularChromosomeVis', []);

	/**
	 * service that retrieves DAS model
	 */
	angularChromosomeVis.factory('dasLoader', function() {
		return {
			loadModel: function (segment, assembly) {
				return JSDAS.Simple.getClient("http://www.ensembl.org/das/Homo_sapiens.GRCh" + assembly + ".karyotype");
			}
		}
	});

	/**
	 * service that maintains an array of selectors. can be injected into any controller, directive, etc.
	 */
	angularChromosomeVis.factory('chrSelectors', ['$rootScope', function($rootScope) {
		"use strict";
		var selectors = []; //holds a list of all selectors that are input by the user

		return {
			getSelectors: function () {
				return selectors;
			},
			addSelector: function (selector) {
				selectors.push(selector);
				$rootScope.$broadcast('selectors:updated', selectors); //notify listeners and provide the new selectors array
			},
			deleteSelector: function (selector) {
				selectors = _.without(selectors, selector);
				$rootScope.$broadcast('selectors:updated', selectors); //notify listeners and provide the new selectors array
			},
			deleteChrSelectors: function (chr) {
				selectors = _.reject(selectors, function(sel) {
					return sel.chr == chr;
				});
				$rootScope.$broadcast('selectors:updated', selectors);
			},
			deleteAll: function () {
				selectors = [];
				$rootScope.$broadcast('selectors:updated', selectors); //notify listeners and provide the new selectors array
				$rootScope.$broadcast('selectors:deleted');
			}
		};
	}])

	angularChromosomeVis.directive('chromosome', ['dasLoader', 'chrSelectors', function(dasLoader, chrSelectors) {

		function link(scope, element, attr) {

			//set default scope values if not provided
			scope.relSize = angular.isDefined(scope.relSize) ? scope.relSize : true;
			scope.assembly = angular.isDefined(scope.assembly) ? scope.assembly : 37;
			scope.height = angular.isDefined(scope.height) ? scope.height : 20;
			scope.axis = angular.isDefined(scope.axis) ? scope.axis : true;
			scope.mode = angular.isDefined(scope.mode) ? scope.mode : "multi";

			var dasModel;
			scope.selectors = { list: [] }; //holds selector objects

			var CHR1_BP_END = 248956422,
				STALK_MAG_PC = 0.8,
				PADDING = 30,
				LABEL_PADDING = 24,
				AXIS_SPACING = 4,
				STALK_SPACING = 3;

			var target = d3.select(element[0]).append('svg');
			target.attr('id', scope.id + 'svg'); //take id from the scope
			target.attr({width: '100%'});

			if (scope.axis) {
				target.attr({height: scope.height + (2 * PADDING)});
			} else {
				target.attr({height: scope.height + PADDING});
			}
			dasLoader.loadModel(scope.chr, scope.assembly)
				.features({segment: scope.chr}, function (res) {
					//success response
					if (res.GFF.SEGMENT.length > 0) {
						dasModel = {
							id: res.GFF.SEGMENT[0].id,
							start: res.GFF.SEGMENT[0].start,
							stop: res.GFF.SEGMENT[0].stop,
							bands: res.GFF.SEGMENT[0].FEATURE
						};
					} else {
						console.log("JSDAS results empty for segment");
					}

					if (typeof dasModel.err === 'undefined') {

						var rangeTo;

						if (scope.width === 'inherit') {
							var svgWidth = target[0][0].width.baseVal.value;
							rangeTo = scope.relSize ? ((+dasModel.stop / CHR1_BP_END) * svgWidth) - PADDING : svgWidth - PADDING;
						}
						else {
							rangeTo = scope.relSize ? ((+dasModel.stop / CHR1_BP_END) * scope.width) - PADDING : scope.width - PADDING;
						}

						var xscale = d3.scale.linear()
							.domain([dasModel.start, dasModel.stop])
							.range([0, rangeTo]);

						var band = target.selectAll("chromosome" + " g")
							.data(dasModel.bands)
							.enter().append("g");

						band.append("title")
							.text(function(m) {return m.id; });

						band.append('rect')
							.attr('class', function (m) {
								return m.TYPE.id.replace(':', ' ');
							})
							.attr('height', function (m) {
								return (m.TYPE.id === "band:stalk") ? (scope.height * STALK_MAG_PC) : scope.height;
							})
							.attr('width', function (m) {
								return xscale(+m.END.textContent) - xscale(+m.START.textContent);
							})
							.attr('x', function (m) {
								return xscale(m.START.textContent);
							})
							.attr('y', function (m) {
								return (m.TYPE.id === "band:stalk") ? (PADDING + STALK_SPACING) : PADDING;
							});

						var label = target.append("text")
							.attr("class", "band-lbl")
							.attr("y", LABEL_PADDING);

						band.on("mouseover", function (m) {
							label.text(m.id)
								.attr('x', (xscale(m.START.textContent)));
						});

						band.on("mouseout", function (m) {
							label.text(''); //empty the label
						});

						band.on("click", function (m) {
							var start = +m.START.textContent,
								end = +m.END.textContent;

							if (scope.mode === 'multi' || (scope.mode === "single" && scope.selectors.list.length == 0)) {
								var newSel = newSelector(scope, xscale, start, end, (PADDING - AXIS_SPACING)).draw(); //create new selector and draw it
								addSelector(newSel);//add new selector to local scope
								chrSelectors.addSelector(newSel); //add new location to the service
							}
						});

						if (scope.axis) {
							var bpAxis = d3.svg.axis()
								.scale(xscale)
								.tickFormat(d3.format('s'))
								.orient("bottom");

							target.append('g')
								.attr('class', 'bp-axis')
								.attr('transform', 'translate(0,' + (scope.height + PADDING + AXIS_SPACING) + ")")
								.call(bpAxis);
						}
					}

				}, function (err) {
					target.append("text").attr("y", 30).text("Error retrieving data model for chromosome " + scope.chr + ". Message from server: " + err.id + ", " + err.msg);
				});

			function addSelector(sel) {
				"use strict";
				scope.$apply(function() {
					scope.selectors.list.push(sel);
				})
			}

			//when all selectors have been deleted from outside
			scope.$on('selectors:deleted', function(event) {
				angular.forEach (scope.selectors.list, function(sel) {
					"use strict";
					sel.delete();
				});
				scope.selectors.list = []; //delete locally
			});

			scope.delSelector = function(sel) {
				"use strict";
				sel.delete();
				scope.selectors.list = _.without(scope.selectors.list, sel) //delete locally
				chrSelectors.deleteSelector(sel); //delete from the service
			}

			function newSelector(scope, xscale, start, end, yshift) {
				return new Selector({
					scope: scope,
					xscale: xscale,
					y: yshift,
					target: '#' + scope.id + 'svg'
				}).init(start, end);
			};
		}

		/**
		 * selector object for chromosome. uses D3 brush
		 * @param opt - options for the selector
		 * @constructor
		 */
		function Selector (opt) {

			var self = this,
				_selector,
				_initialized;

			var AXIS_SPACING = 4;

			var options = (function () {
				return _.extend({}, {
					//DEFAULT OPTIONS
					height: 20,
					y:9
				}, opt || {});
			}());

			this.delete = function () {
				_selector.remove();
				_initialized = false;
			};

			function triggerSelectionChange () {
				var ext = self.brush.extent();
				self.start = Math.round(ext[0]);
				self.end = Math.round(ext[1]);
			}

			//initialize the selector
			this.init = function (start, end) {
				var brushStart;
				var brushEnd;

				self.brush = d3.svg.brush()
					.x(options.xscale)
					.extent([start, end]);

				self.start = Math.round(start);
				self.end = Math.round(end);
				self.chr = options.scope.chr;
				self.selected = false;

				self.brush.on("brush", function () {
					triggerSelectionChange();
					options.scope.$apply();
				});

				self.brush.on("brushstart", function () {
					var ext = self.brush.extent();
					brushStart = ext[0];
					brushEnd = ext[1];
				});

				//uncomment to use
				self.brush.on("brushend", function () {
					var ext = self.brush.extent();
					var newStart = ext[0];
					var newEnd = ext[1];

					//if there was no movement--i.e., just a click
					if (brushStart === newStart && brushEnd === newEnd) {
						self.selected = !self.selected;
						if (_selector.classed('selector')) {
							_selector.classed('selector', false);
							_selector.classed('selected', true);
						}
						else {
							_selector.classed('selector', true);
							_selector.classed('selected', false);
						}
						options.scope.$apply();

					}
				});

				_selector = d3.select(options.target).append("g")
					.classed('selector', true)
					.attr('transform',"translate(0,"+ options.y +")")
					.call(self.brush);

				_selector.selectAll('rect')
					.attr('height', options.height + (AXIS_SPACING * 2));

				_initialized = true;
				return self;
			};

			this.draw = function () {
				if (!_initialized) self.init();
				_selector.select('.background').remove();
				_selector.call(self.brush);
				return self;
			};

			this.move = function (to, from) {
				self.brush.extent([to, from]);
				var selector = d3.select(options.target + ' .selector');
				selector.call(self.brush);
				return self;
			};

		};

		return {
			link: link,
			restrict: 'AE',
			scope: {
				chr: '@',
				relSize: '=?',
				assembly: '=?',
				width: '@',
				height: '=?',
				axis: '=?',
				mode: '@',
				id: '@'
			}
		}
	}]);

})();<commit_msg>handle selected selectors<commit_after>/**
 * Created by paulparsons on 1/27/15.
 */

(function() {

	var angularChromosomeVis = angular.module('angularChromosomeVis', []);

	/**
	 * service that retrieves DAS model
	 */
	angularChromosomeVis.factory('dasLoader', function() {
		return {
			loadModel: function (segment, assembly) {
				return JSDAS.Simple.getClient("http://www.ensembl.org/das/Homo_sapiens.GRCh" + assembly + ".karyotype");
			}
		}
	});

	/**
	 * service that maintains an array of selectors. can be injected into any controller, directive, etc.
	 */
	angularChromosomeVis.factory('chrSelectors', ['$rootScope', function($rootScope) {
		"use strict";
		var selectors = []; //holds a list of all selectors that are input by the user

		return {
			getSelectors: function () {
				return selectors;
			},
			addSelector: function (selector) {
				selectors.push(selector);
				$rootScope.$broadcast('selectors:updated', selectors); //notify listeners and provide the new selectors array
			},
			deleteSelector: function (selector) {
				selectors = _.without(selectors, selector);
				$rootScope.$broadcast('selectors:updated', selectors); //notify listeners and provide the new selectors array
			},
			deleteChrSelectors: function (chr) {
				selectors = _.reject(selectors, function(sel) {
					return sel.chr == chr;
				});
				$rootScope.$broadcast('selectors:updated', selectors);
			},
			deleteAll: function () {
				selectors = [];
				$rootScope.$broadcast('selectors:updated', selectors); //notify listeners and provide the new selectors array
				$rootScope.$broadcast('selectors:deleted');
			}
		};
	}])

	angularChromosomeVis.directive('chromosome', ['dasLoader', 'chrSelectors', function(dasLoader, chrSelectors) {

		function link(scope, element, attr) {

			//set default scope values if not provided
			scope.relSize = angular.isDefined(scope.relSize) ? scope.relSize : true;
			scope.assembly = angular.isDefined(scope.assembly) ? scope.assembly : 37;
			scope.height = angular.isDefined(scope.height) ? scope.height : 20;
			scope.axis = angular.isDefined(scope.axis) ? scope.axis : true;
			scope.mode = angular.isDefined(scope.mode) ? scope.mode : "multi";

			var dasModel;
			scope.selectors = { list: [] }; //holds selector objects

			var CHR1_BP_END = 248956422,
				STALK_MAG_PC = 0.8,
				PADDING = 30,
				LABEL_PADDING = 24,
				AXIS_SPACING = 4,
				STALK_SPACING = 3;

			var target = d3.select(element[0]).append('svg');
			target.attr('id', scope.id + 'svg'); //take id from the scope
			target.attr({width: '100%'});

			if (scope.axis) {
				target.attr({height: scope.height + (2 * PADDING)});
			} else {
				target.attr({height: scope.height + PADDING});
			}
			dasLoader.loadModel(scope.chr, scope.assembly)
				.features({segment: scope.chr}, function (res) {
					//success response
					if (res.GFF.SEGMENT.length > 0) {
						dasModel = {
							id: res.GFF.SEGMENT[0].id,
							start: res.GFF.SEGMENT[0].start,
							stop: res.GFF.SEGMENT[0].stop,
							bands: res.GFF.SEGMENT[0].FEATURE
						};
					} else {
						console.log("JSDAS results empty for segment");
					}

					if (typeof dasModel.err === 'undefined') {

						var rangeTo;

						if (scope.width === 'inherit') {
							var svgWidth = target[0][0].width.baseVal.value;
							rangeTo = scope.relSize ? ((+dasModel.stop / CHR1_BP_END) * svgWidth) - PADDING : svgWidth - PADDING;
						}
						else {
							rangeTo = scope.relSize ? ((+dasModel.stop / CHR1_BP_END) * scope.width) - PADDING : scope.width - PADDING;
						}

						var xscale = d3.scale.linear()
							.domain([dasModel.start, dasModel.stop])
							.range([0, rangeTo]);

						var band = target.selectAll("chromosome" + " g")
							.data(dasModel.bands)
							.enter().append("g");

						band.append("title")
							.text(function(m) {return m.id; });

						band.append('rect')
							.attr('class', function (m) {
								return m.TYPE.id.replace(':', ' ');
							})
							.attr('height', function (m) {
								return (m.TYPE.id === "band:stalk") ? (scope.height * STALK_MAG_PC) : scope.height;
							})
							.attr('width', function (m) {
								return xscale(+m.END.textContent) - xscale(+m.START.textContent);
							})
							.attr('x', function (m) {
								return xscale(m.START.textContent);
							})
							.attr('y', function (m) {
								return (m.TYPE.id === "band:stalk") ? (PADDING + STALK_SPACING) : PADDING;
							});

						var label = target.append("text")
							.attr("class", "band-lbl")
							.attr("y", LABEL_PADDING);

						band.on("mouseover", function (m) {
							label.text(m.id)
								.attr('x', (xscale(m.START.textContent)));
						});

						band.on("mouseout", function (m) {
							label.text(''); //empty the label
						});

						band.on("click", function (m) {
							var start = +m.START.textContent,
								end = +m.END.textContent;

							if (scope.mode === 'multi' || (scope.mode === "single" && scope.selectors.list.length == 0)) {
								var newSel = newSelector(scope, xscale, start, end, (PADDING - AXIS_SPACING)).draw(); //create new selector and draw it
								addSelector(newSel);//add new selector to local scope
								chrSelectors.addSelector(newSel); //add new location to the service
							}
						});

						if (scope.axis) {
							var bpAxis = d3.svg.axis()
								.scale(xscale)
								.tickFormat(d3.format('s'))
								.orient("bottom");

							target.append('g')
								.attr('class', 'bp-axis')
								.attr('transform', 'translate(0,' + (scope.height + PADDING + AXIS_SPACING) + ")")
								.call(bpAxis);
						}
					}

				}, function (err) {
					target.append("text").attr("y", 30).text("Error retrieving data model for chromosome " + scope.chr + ". Message from server: " + err.id + ", " + err.msg);
				});

			function addSelector(sel) {
				"use strict";
				scope.$apply(function() {
					scope.selectors.list.push(sel);
				})
			}

			//when all selectors have been deleted from outside
			scope.$on('selectors:deleted', function(event) {
				angular.forEach (scope.selectors.list, function(sel) {
					"use strict";
					sel.delete();
				});
				scope.selectors.list = []; //delete locally
			});

			scope.delSelector = function(sel) {
				"use strict";
				sel.delete();
				scope.selectors.list = _.without(scope.selectors.list, sel) //delete locally
				chrSelectors.deleteSelector(sel); //delete from the service
			}

			function newSelector(scope, xscale, start, end, yshift) {
				return new Selector({
					scope: scope,
					xscale: xscale,
					y: yshift,
					target: '#' + scope.id + 'svg'
				}).init(start, end);
			};
		}

		/**
		 * selector object for chromosome. uses D3 brush
		 * @param opt - options for the selector
		 * @constructor
		 */
		function Selector (opt) {

			var self = this,
				_selector,
				_initialized;

			var AXIS_SPACING = 4;

			var options = (function () {
				return _.extend({}, {
					//DEFAULT OPTIONS
					height: 20,
					y:9
				}, opt || {});
			}());

			this.delete = function () {
				_selector.remove();
				_initialized = false;
			};

			function triggerSelectionChange () {
				var ext = self.brush.extent();
				self.start = Math.round(ext[0]);
				self.end = Math.round(ext[1]);
			}

			//initialize the selector
			this.init = function (start, end) {
				var brushStart;
				var brushEnd;

				self.brush = d3.svg.brush()
					.x(options.xscale)
					.extent([start, end]);

				self.start = Math.round(start);
				self.end = Math.round(end);
				self.chr = options.scope.chr;
				self.selected = false;

				self.brush.on("brush", function () {
					triggerSelectionChange();
					options.scope.$apply();
				});

				self.brush.on("brushstart", function () {
					var ext = self.brush.extent();
					brushStart = ext[0];
					brushEnd = ext[1];
				});

				//uncomment to use
				self.brush.on("brushend", function () {
					var ext = self.brush.extent();
					var newStart = ext[0];
					var newEnd = ext[1];

					//if there was no movement--i.e., just a click
					if (brushStart === newStart && brushEnd === newEnd) {
						self.selected = !self.selected;
						if (_selector.classed('selector')) {
							_selector.classed('selector', false);
							_selector.classed('selected', true);
						}
						else {
							_selector.classed('selector', true);
							_selector.classed('selected', false);
						}
						options.scope.$apply();

					}
				});

				_selector = d3.select(options.target).append("g")
					.classed('selector', true)
					.attr('transform',"translate(0,"+ options.y +")")
					.call(self.brush);

				_selector.selectAll('rect')
					.attr('height', options.height + (AXIS_SPACING * 2));

				_initialized = true;
				return self;
			};

			this.draw = function () {
				if (!_initialized) self.init();
				_selector.select('.background').remove();
				_selector.call(self.brush);
				return self;
			};

			this.move = function (to, from) {
				self.brush.extent([to, from]);
				var selector;
				if (self.selected === true) {
					selector = d3.select(options.target + ' .selected');
				}
				else {
					selector = d3.select(options.target + ' .selector');
				}
				selector.call(self.brush);
				return self;
			};

		};

		return {
			link: link,
			restrict: 'AE',
			scope: {
				chr: '@',
				relSize: '=?',
				assembly: '=?',
				width: '@',
				height: '=?',
				axis: '=?',
				mode: '@',
				id: '@'
			}
		}
	}]);

})();<|endoftext|>


<commit_before>/**
 * Visual Blocks Language
 *
 * Copyright 2012 Google Inc.
 * https://blockly.googlecode.com/
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * @fileoverview Generating Arduino for unit test blocks.
 * @author fraser@google.com (Neil Fraser)
 */
'use strict';

Blockly.Arduino['unittest_main'] = function(block) {
  // Container for unit tests.
  var resultsVar = Blockly.Arduino.variableDB_.getName('unittestResults',
      Blockly.Variables.NAME_TYPE);
  var functionName = Blockly.Arduino.provideFunction_(
      'unittest_report',
      [ 'function ' + Blockly.Arduino.FUNCTION_NAME_PLACEHOLDER_ + '() {',
        '  // Create test report.',
        '  var report = [];',
        '  var summary = [];',
        '  var fails = 0;',
        '  for (var x = 0; x < ' + resultsVar + '.length; x++) {',
        '    if (' + resultsVar + '[x][0]) {',
        '      summary.push(".");',
        '    } else {',
        '      summary.push("F");',
        '      fails++;',
        '      report.push("");',
        '      report.push("FAIL: " + ' + resultsVar + '[x][2]);',
        '      report.push(' + resultsVar + '[x][1]);',
        '    }',
        '  }',
        '  report.unshift(summary.join(""));',
        '  report.push("");',
        '  report.push("Number of tests run: " + ' + resultsVar +
              '.length);',
        '  report.push("");',
        '  if (fails) {',
        '    report.push("FAILED (failures=" + fails + ")");',
        '  } else {',
        '    report.push("OK");',
        '  }',
        '  return report.join("\\n");',
        '}']);
  // Setup global to hold test results.
  var code = resultsVar + ' = [];\n';
  // Run tests (unindented).
  code += Blockly.Arduino.statementToCode(block, 'DO')
      .replace(/^  /, '').replace(/\n  /g, '\n');
  var reportVar = Blockly.Arduino.variableDB_.getDistinctName(
      'report', Blockly.Variables.NAME_TYPE);
  code += 'var ' + reportVar + ' = ' + functionName + '();\n';
  // Destroy results.
  code += resultsVar + ' = null;\n';
  // Send the report to the console (that's where errors will go anyway).
  code += 'console.log(' + reportVar + ');\n';
  return code;
};

Blockly.Arduino['unittest_main'].defineAssert_ = function(block) {
  var resultsVar = Blockly.Arduino.variableDB_.getName('unittestResults',
      Blockly.Variables.NAME_TYPE);
  var functionName = Blockly.Arduino.provideFunction_(
      'assertEquals',
      [ 'function ' + Blockly.Arduino.FUNCTION_NAME_PLACEHOLDER_ +
          '(actual, expected, message) {',
        '  // Asserts that a value equals another value.',
        '  if (!' + resultsVar + ') {',
        '    throw "Orphaned assert: " + message;',
        '  }',
        '  function equals(a, b) {',
        '    if (a === b) {',
        '      return true;',
        '    } else if (a instanceof Array && b instanceof Array) {',
        '      if (a.length != b.length) {',
        '        return false;',
        '      }',
        '      for (var i = 0; i < a.length; i++) {',
        '        if (!equals(a[i], b[i])) {',
        '          return false;',
        '        }',
        '      }',
        '      return true;',
        '    }',
        '    return false;',
        '  }',
        '  if (equals(actual, expected)) {',
        '    ' + resultsVar + '.push([true, "OK", message]);',
        '  } else {',
        '    ' + resultsVar + '.push([false, ' +
          '"Expected: " + expected + "\\nActual: " + actual, message]);',
        '  }',
        '}']);
  return functionName;
};

Blockly.Arduino['unittest_assertequals'] = function(block) {
  // Asserts that a value equals another value.
  var message = Blockly.Arduino.quote_(block.getFieldValue('MESSAGE'));
  var actual = Blockly.Arduino.valueToCode(block, 'ACTUAL',
      Blockly.Arduino.ORDER_COMMA) || 'null';
  var expected = Blockly.Arduino.valueToCode(block, 'EXPECTED',
      Blockly.Arduino.ORDER_COMMA) || 'null';
  return Blockly.Arduino['unittest_main'].defineAssert_() +
      '(' + actual + ', ' + expected + ', ' + message + ');\n';
};

Blockly.Arduino['unittest_assertvalue'] = function(block) {
  // Asserts that a value is true, false, or null.
  var message = Blockly.Arduino.quote_(block.getFieldValue('MESSAGE'));
  var actual = Blockly.Arduino.valueToCode(block, 'ACTUAL',
      Blockly.Arduino.ORDER_COMMA) || 'null';
  var expected = block.getFieldValue('EXPECTED');
  if (expected == 'TRUE') {
    expected = 'true';
  } else if (expected == 'FALSE') {
    expected = 'false';
  } else if (expected == 'NULL') {
    expected = 'null';
  }
  return Blockly.Arduino['unittest_main'].defineAssert_() +
      '(' + actual + ', ' + expected + ', ' + message + ');\n';
};

Blockly.Arduino['unittest_fail'] = function(block) {
  // Always assert an error.
  var resultsVar = Blockly.Arduino.variableDB_.getName('unittestResults',
      Blockly.Variables.NAME_TYPE);
  var message = Blockly.Arduino.quote_(block.getFieldValue('MESSAGE'));
  var functionName = Blockly.Arduino.provideFunction_(
      'unittest_fail',
      [ 'function ' + Blockly.Arduino.FUNCTION_NAME_PLACEHOLDER_ +
          '(message) {',
        '  // Always assert an error.',
        '  if (!' + resultsVar + ') {',
        '    throw "Orphaned assert fail: " + message;',
        '  }',
        '  ' + resultsVar + '.push([false, "Fail.", message]);',
        '}']);
  return functionName + '(' + message + ');\n';
};
<commit_msg>Unit test for arduino code generates some of the code. Minor improvement from previous version. Paving the way through to start doing unit test on some of the blocks.<commit_after>/**
 * @license Licensed under the Apache License, Version 2.0 (the "License"):
 *          http://www.apache.org/licenses/LICENSE-2.0
 *
 * @fileoverview Generating Arduino code for unit test blocks.
 *               Based on the Dart unite test code.
 *
 * TODO: Everything. For now it at least parses the code into (invalid) code
 *       text.
 */
'use strict';

Blockly.Arduino['unittest_main'] = function(block) {
  // Container for unit tests.
  var resultsVar = Blockly.Arduino.variableDB_.getName('unittestResults',
      Blockly.Variables.NAME_TYPE);
  var functionName = Blockly.Arduino.provideFunction_(
      'unittest_report',
      [ 'String ' + Blockly.Arduino.FUNCTION_NAME_PLACEHOLDER_ + '() {',
        '  // Create test report.',
        '  List report = [];',
        '  String summary = new String();',
        '  int fails = 0;',
        '  for (int x = 0; x < ' + resultsVar + '.length; x++) {',
        '    if (' + resultsVar + '[x][0]) {',
        '      summary += ".";',
        '    } else {',
        '      summary += "F";',
        '      fails++;',
        '      report.add("");',
        '      report.add("FAIL: ${' + resultsVar + '[x][2]}");',
        '      report.add(' + resultsVar + '[x][1]);',
        '    }',
        '  }',
        '  report.insert(0, summary);',
        '  report.add("");',
        '  report.add("Ran ${' + resultsVar + '.length} tests.");',
        '  report.add("");',
        '  if (fails != 0) {',
        '    report.add("FAILED (failures=$fails)");',
        '  } else {',
        '    report.add("OK");',
        '  }',
        '  return report.join("\\n");',
        '}']);
  // Setup global to hold test results.
  var code = resultsVar + ' = [];\n';
  // Run tests (unindented).
  code += Blockly.Arduino.statementToCode(block, 'DO')
      .replace(/^  /, '').replace(/\n  /g, '\n');
  var reportVar = Blockly.Arduino.variableDB_.getDistinctName(
      'report', Blockly.Variables.NAME_TYPE);
  code += 'String ' + reportVar + ' = ' + functionName + '();\n';
  // Destroy results.
  code += resultsVar + ' = null;\n';
  // Print the report to the console (that's where errors will go anyway).
  code += 'print(' + reportVar + ');\n';
  return code;
};

Blockly.Arduino['unittest_main'].defineAssert_ = function() {
  var resultsVar = Blockly.Arduino.variableDB_.getName('unittestResults',
      Blockly.Variables.NAME_TYPE);
  var functionName = Blockly.Arduino.provideFunction_(
      'unittest_assertequals',
      [ 'void ' + Blockly.Arduino.FUNCTION_NAME_PLACEHOLDER_ +
          '(dynamic actual, dynamic expected, String message) {',
        '  // Asserts that a value equals another value.',
        '  if (' + resultsVar + ' == null) {',
        '    throw "Orphaned assert: ${message}";',
        '  }',
        '  bool equals(a, b) {',
        '    if (a == b) {',
        '      return true;',
        '    } else if (a is List && b is List) {',
        '      if (a.length != b.length) {',
        '        return false;',
        '      }',
        '      for (num i = 0; i < a.length; i++) {',
        '        if (!equals(a[i], b[i])) {',
        '          return false;',
        '        }',
        '      }',
        '      return true;',
        '    }',
        '    return false;',
        '  }',
        '  if (equals(actual, expected)) {',
        '    ' + resultsVar + '.add([true, "OK", message]);',
        '  } else {',
        '    ' + resultsVar + '.add([false, ' +
          '"Expected: $expected\\nActual: $actual", message]);',
        '  }',
        '}\n']);
  return functionName;
};

Blockly.Arduino['unittest_assertequals'] = function(block) {
  // Asserts that a value equals another value.
  var message = Blockly.Arduino.quote_(block.getFieldValue('MESSAGE'));
  var actual = Blockly.Arduino.valueToCode(block, 'ACTUAL',
      Blockly.Arduino.ORDER_NONE) || 'null';
  var expected = Blockly.Arduino.valueToCode(block, 'EXPECTED',
      Blockly.Arduino.ORDER_NONE) || 'null';
  return Blockly.Arduino['unittest_main'].defineAssert_() +
      '(' + actual + ', ' + expected + ', ' + message + ');\n';
};

Blockly.Arduino['unittest_assertvalue'] = function(block) {
  // Asserts that a value is true, false, or null.
  var message = Blockly.Arduino.quote_(block.getFieldValue('MESSAGE'));
  var actual = Blockly.Arduino.valueToCode(block, 'ACTUAL',
      Blockly.Arduino.ORDER_NONE) || 'null';
  var expected = block.getFieldValue('EXPECTED');
  if (expected == 'TRUE') {
    expected = 'true';
  } else if (expected == 'FALSE') {
    expected = 'false';
  } else if (expected == 'NULL') {
    expected = 'null';
  }
  return Blockly.Arduino['unittest_main'].defineAssert_() +
      '(' + actual + ', ' + expected + ', ' + message + ');\n';
};

Blockly.Arduino['unittest_fail'] = function(block) {
  // Always assert an error.
  var resultsVar = Blockly.Arduino.variableDB_.getName('unittestResults',
      Blockly.Variables.NAME_TYPE);
  var message = Blockly.Arduino.quote_(block.getFieldValue('MESSAGE'));
  var functionName = Blockly.Arduino.provideFunction_(
      'unittest_fail',
      [ 'void ' + Blockly.Arduino.FUNCTION_NAME_PLACEHOLDER_ +
          '(String message) {',
        '  // Always assert an error.',
        '  if (' + resultsVar + ' == null) {',
        '    throw "Orphaned assert fail: ${message}";',
        '  }',
        '  ' + resultsVar + '.add([false, "Fail.", message]);',
        '}\n']);
  return functionName + '(' + message + ');\n';
};
<|endoftext|>