<commit_before>var slug = require('slug'),
    async = require("async"),
    sys = require('sys'),
    exec = require('child_process').exec,
    Mustache = require("mustache"),
    _ = require("lodash"),
    S = require("string");

var pullRepositoryCodeClass = function(container) {

    this.seekServer = function(repository) {

        var arrMatches = null,
            ssh_rePattern = null;

        //SSH type
        if(S(repository).startsWith('ssh')) {
            ssh_rePattern = new RegExp(/^ssh\:\/\/[a-zA-Z0-9]+\@([0-9a-zA-Z\.]+)([:0-9]*).*$/);
            arrMatches = repository.match(ssh_rePattern);

            if (/^\:[0-9]+$/.test(arrMatches[2])) {
                //Port detected
                return {
                    host: arrMatches[1],
                    port: "-p "+arrMatches[2].replace(":", "") //prepares the string
                };
            } else {
                return {
                    host: arrMatches[1],
                    port: ""
                };
            }
        }

        //Git Type
        if(S(repository).startsWith("git@")) {
            rePattern = new RegExp(/^git@([a-zA-Z0-9\.]+):.*$/);
            arrMatches = repository.match(rePattern);

            return {
                host: arrMatches[1],
                port: ""
            };
        }

        //HTTP type
        if(S(repository).startsWith("http")) {
            rePattern = new RegExp(/^http(|s):\/\/([a-zA-Z0-9.]+)(|:)(|[0-9]+)\/.*$/);
            arrMatches = repository.match(rePattern);

            return {
                host: arrMatches[2],
                port: (!_.isEmpty(arrMatches[4])) ? "-p "+arrMatches[4] : ""
            };
        }

    };

    this.run = function(buildConfiguration, return_callback, extra_links, type) {

        if(_.isUndefined(type)) type = "default";

        var buildId = buildConfiguration.buildId;
        var repository = buildConfiguration.repository;
        var ssh_key = buildConfiguration.sshKey;
        var ssh_private_key = buildConfiguration.sshPrivateKey;
        var commit = buildConfiguration.commit;
        if(!_.has(buildConfiguration, "hosts")) buildConfiguration.hosts = [];
        var hosts = buildConfiguration.hosts;

        if(_.isUndefined(extra_links)) extra_links = "";

        //Use the image sailfish-git-puller to place all the code inside the workspace
        var containerName = slug(buildId);
        var self = this;

        async.series([
            function(callback) {
                /*
                 * Create the container on the background
                 */
                var dockerCommand = [];
                dockerCommand.push('docker run --rm');
                dockerCommand.push('--name="{{&containerName}}" ');
                dockerCommand.push('-e SAILFISH_SSH_KEY="{{&ssh_key}}"');
                dockerCommand.push('-e SAILFISH_PRIVATE_SSH_KEY="{{&ssh_private_key}}"');
                dockerCommand.push('-e SAILFISH_REPOSITORY="{{&repository}}"');
                dockerCommand.push('-e SAILFISH_REPOSITORY_SERVER="{{&repository_server}}"');
                dockerCommand.push('-e SAILFISH_REPOSITORY_SERVER_PORT="{{&repository_server_port}}"');
                dockerCommand.push('-e SAILFISH_COMMIT="{{&commit}}"');
                dockerCommand.push('-e SAILFISH_TYPE="{{&type}}"');
                dockerCommand.push('-e SAILFISH_HOSTS="{{&hosts}}"');
                dockerCommand.push(extra_links); //Adding extra links if defined
                dockerCommand.push('-v {{&workspace}}/{{containerName}}:/workspace:rw');
                dockerCommand.push("{{&puller_image}}");

                var dockerCommandJoin = dockerCommand.join(" ");

                var seekServerInfo = self.seekServer(repository);
                var dockerCommandFinal = Mustache.render(dockerCommandJoin, {
                    "puller_image": "urodoz/sailfish-git-puller:1.0",
                    "ssh_key": ssh_key,
                    "ssh_private_key": ssh_private_key,
                    "repository": repository,
                    "containerName": containerName,
                    "commit": commit,
                    "hosts": buildConfiguration.hosts.join(","),
                    "repository_server": seekServerInfo.host,
                    "repository_server_port": seekServerInfo.port,
                    "workspace": container.getParameter("workspace_root"),
                    "type": type
                });

                var child = exec(dockerCommandFinal, function(error, stdout, stderr) {
                    //TODO: Control error
                    //Check environment variable setted on the configuration on the running container
                    var chmod777Frame = exec("chmod 777 "+container.getParameter("workspace_root")+"/"+containerName+"/frame", function(error, stdout, stderr) {
                        callback(error, true); //TODO : Control error
                    });

                });
            }
        ], return_callback);

    };

};

module.exports = function(container) {
    return new pullRepositoryCodeClass(container);
};<commit_msg>Adding logging to pull repostiry code command<commit_after>var slug = require('slug'),
    async = require("async"),
    sys = require('sys'),
    exec = require('child_process').exec,
    Mustache = require("mustache"),
    _ = require("lodash"),
    S = require("string");

var pullRepositoryCodeClass = function(container) {

    this.seekServer = function(repository) {

        var arrMatches = null,
            result = null,
            ssh_rePattern = null;

        //SSH type
        if(S(repository).startsWith('ssh')) {
            ssh_rePattern = new RegExp(/^ssh\:\/\/[a-zA-Z0-9]+\@([0-9a-zA-Z\.]+)([:0-9]*).*$/);
            arrMatches = repository.match(ssh_rePattern);

            if (/^\:[0-9]+$/.test(arrMatches[2])) {
                result = {
                    host: arrMatches[1],
                    port: "-p "+arrMatches[2].replace(":", "") //prepares the string
                };
            } else {
                result = {
                    host: arrMatches[1],
                    port: ""
                };
            }

            container.get("logger").info("Found server from repository (SSH)", result);
            return result;
        }

        //Git Type
        if(S(repository).startsWith("git@")) {
            rePattern = new RegExp(/^git@([a-zA-Z0-9\.]+):.*$/);
            arrMatches = repository.match(rePattern);

            result = {
                host: arrMatches[1],
                port: ""
            };

            container.get("logger").info("Found server from repository (GIT)", result);

            return result;
        }

        //HTTP type
        if(S(repository).startsWith("http")) {
            rePattern = new RegExp(/^http(|s):\/\/([a-zA-Z0-9.]+)(|:)(|[0-9]+)\/.*$/);
            arrMatches = repository.match(rePattern);

            result = {
                host: arrMatches[2],
                port: (!_.isEmpty(arrMatches[4])) ? "-p "+arrMatches[4] : ""
            };

            container.get("logger").info("Found server from repository (HTTP)", result);

            return result;
        }

        container.get("logger").error("Error seeking server from git repository description", {
            repository: repository
        });

    };

    this.run = function(buildConfiguration, return_callback, extra_links, type) {

        if(_.isUndefined(type)) type = "default";

        var buildId = buildConfiguration.buildId;
        var repository = buildConfiguration.repository;
        var ssh_key = buildConfiguration.sshKey;
        var ssh_private_key = buildConfiguration.sshPrivateKey;
        var commit = buildConfiguration.commit;
        if(!_.has(buildConfiguration, "hosts")) buildConfiguration.hosts = [];
        var hosts = buildConfiguration.hosts;

        if(_.isUndefined(extra_links)) extra_links = "";

        //Use the image sailfish-git-puller to place all the code inside the workspace
        var containerName = slug(buildId);
        var self = this;

        async.series([
            function(callback) {
                /*
                 * Create the container on the background
                 */
                var dockerCommand = [];
                dockerCommand.push('docker run --rm');
                dockerCommand.push('--name="{{&containerName}}" ');
                dockerCommand.push('-e SAILFISH_SSH_KEY="{{&ssh_key}}"');
                dockerCommand.push('-e SAILFISH_PRIVATE_SSH_KEY="{{&ssh_private_key}}"');
                dockerCommand.push('-e SAILFISH_REPOSITORY="{{&repository}}"');
                dockerCommand.push('-e SAILFISH_REPOSITORY_SERVER="{{&repository_server}}"');
                dockerCommand.push('-e SAILFISH_REPOSITORY_SERVER_PORT="{{&repository_server_port}}"');
                dockerCommand.push('-e SAILFISH_COMMIT="{{&commit}}"');
                dockerCommand.push('-e SAILFISH_TYPE="{{&type}}"');
                dockerCommand.push('-e SAILFISH_HOSTS="{{&hosts}}"');
                dockerCommand.push(extra_links); //Adding extra links if defined
                dockerCommand.push('-v {{&workspace}}/{{containerName}}:/workspace:rw');
                dockerCommand.push("{{&puller_image}}");

                var dockerCommandJoin = dockerCommand.join(" ");

                var seekServerInfo = self.seekServer(repository);
                var dockerCommandFinal = Mustache.render(dockerCommandJoin, {
                    "puller_image": "urodoz/sailfish-git-puller:1.0",
                    "ssh_key": ssh_key,
                    "ssh_private_key": ssh_private_key,
                    "repository": repository,
                    "containerName": containerName,
                    "commit": commit,
                    "hosts": buildConfiguration.hosts.join(","),
                    "repository_server": seekServerInfo.host,
                    "repository_server_port": seekServerInfo.port,
                    "workspace": container.getParameter("workspace_root"),
                    "type": type
                });

                container.get("logger").info("Running container to pull the code", {command: dockerCommandFinal});

                var child = exec(dockerCommandFinal, function(error, stdout, stderr) {
                    //TODO: Control error
                    //Check environment variable setted on the configuration on the running container
                    var chmod777Frame = exec("chmod 777 "+container.getParameter("workspace_root")+"/"+containerName+"/frame", function(error, stdout, stderr) {
                        callback(error, true); //TODO : Control error
                    });

                });
            }
        ], return_callback);

    };

};

module.exports = function(container) {
    return new pullRepositoryCodeClass(container);
};<|endoftext|>


<commit_before>var EventEmitter = require('events').EventEmitter;
var util         = require('util');
var Part         = require('./part');

/* Tokens as defined by rfc 2616. Also lowercases them.
 *        token       = 1*<any CHAR except CTLs or separators>
 *     separators     = "(" | ")" | "<" | ">" | "@"
 *                    | "," | ";" | ":" | "\" | <">
 *                    | "/" | "[" | "]" | "?" | "="
 *                    | "{" | "}" | SP | HT
 *
 * From Ryan Dahl's http_parser.c
 */
var TOKENS = [
/*   0 nul    1 soh    2 stx    3 etx    4 eot    5 enq    6 ack    7 bel  */
        0,       0,       0,       0,       0,       0,       0,       0,
/*   8 bs     9 ht    10 nl    11 vt    12 np    13 cr    14 so    15 si   */
        0,       0,       0,       0,       0,       0,       0,       0,
/*  16 dle   17 dc1   18 dc2   19 dc3   20 dc4   21 nak   22 syn   23 etb */
        0,       0,       0,       0,       0,       0,       0,       0,
/*  24 can   25 em    26 sub   27 esc   28 fs    29 gs    30 rs    31 us  */
        0,       0,       0,       0,       0,       0,       0,       0,
/*  32 sp    33  !    34  "    35  #    36  $    37  %    38  &    39  '  */
       ' ',      '!',     '"',     '#',     '$',     '%',     '&',    '\'',
/*  40  (    41  )    42  *    43  +    44  ,    45  -    46  .    47  /  */
        0,       0,      '*',     '+',      0,      '-',     '.',     '/',
/*  48  0    49  1    50  2    51  3    52  4    53  5    54  6    55  7  */
       '0',     '1',     '2',     '3',     '4',     '5',     '6',     '7',
/*  56  8    57  9    58  :    59  ;    60  <    61  =    62  >    63  ?  */
       '8',     '9',      0,       0,       0,       0,       0,       0,
/*  64  @    65  A    66  B    67  C    68  D    69  E    70  F    71  G  */
        0,      'a',     'b',     'c',     'd',     'e',     'f',     'g',
/*  72  H    73  I    74  J    75  K    76  L    77  M    78  N    79  O  */
       'h',     'i',     'j',     'k',     'l',     'm',     'n',     'o',
/*  80  P    81  Q    82  R    83  S    84  T    85  U    86  V    87  W  */
       'p',     'q',     'r',     's',     't',     'u',     'v',     'w',
/*  88  X    89  Y    90  Z    91  [    92  \    93  ]    94  ^    95  _  */
       'x',     'y',     'z',      0,       0,       0,      '^',     '_',
/*  96  `    97  a    98  b    99  c   100  d   101  e   102  f   103  g  */
       '`',     'a',     'b',     'c',     'd',     'e',     'f',     'g',
/* 104  h   105  i   106  j   107  k   108  l   109  m   110  n   111  o  */
       'h',     'i',     'j',     'k',     'l',     'm',     'n',     'o',
/* 112  p   113  q   114  r   115  s   116  t   117  u   118  v   119  w  */
       'p',     'q',     'r',     's',     't',     'u',     'v',     'w',
/* 120  x   121  y   122  z   123  {   124  |   125  }   126  ~   127 del */
       'x',     'y',     'z',      0,      '|',     '}',     '~',       0 ];

var LF       = 10;
var CR       = 13;
var COLON    = 58;
var DASH     = 45;
var CRLF     = new Buffer('\r\n');
var DASH_END = new Buffer('--\r\n');

module.exports = MultipartParser;
util.inherits(MultipartParser, EventEmitter);
function MultipartParser() {
  EventEmitter.call(this);

  this.writable            = false;
  this._state              = 'NO_BOUNDARY';
  this._boundary           = null;
  this._preamble           = true;
  this._counter            = 0;
  this._marker             = 0;
  this._offset             = 0;
  this._part               = null;
  this._headerBufferLimit  = 4 * 1024;
  this._headerBufferLength = 0;
  this._headerField        = '';
  this._headerValue        = '';
  }

MultipartParser.create = function(boundary) {
  var instance = new this();
  instance.boundary(boundary);
  return instance;
};

MultipartParser.prototype.boundary = function(boundary) {
  // Last 3 bytes are used for lookbehind
  this._boundary = new Buffer('\r\n--' + boundary + '???');
  this._state    = 'PREAMBLE';
  this.writable = true;
};

MultipartParser.prototype.write = function(buffer) {
  if (!this.writable) {
    throw this._error('NotWritable', 'Bad state: ' + this._state);
  }

  var i    = 0;
  var byte = buffer[i];

  while (true) {
    switch (this._state) {
      case 'PREAMBLE':
        switch (byte) {
          case CR:
            this._state   = 'BOUNDARY';
            this._marker  = 0;
            break;
          case DASH:
            // As per RFC-1341 we have to accept this, but complying clients
            // should not generate this.
            this._state  = 'BOUNDARY';
            this._marker = 2;
            break;
        }
        break;
      case 'BOUNDARY':
        if (byte !== this._boundary[++this._marker]) {
          this._state  = 'BOUNDARY_MISMATCH';
          continue;
        }

        if (this._marker === this._boundary.length - 4) {
          this._state = 'BOUNDARY_END';
        }
        break;
      case 'BOUNDARY_END':
        this._counter                  = 0;
        this._boundary[++this._marker] = byte;

        switch (byte) {
          case CR:
            this._state = 'BOUNDARY_LINE_END';
            break;
          case DASH:
            if (this._preamble) {
              this._state = 'PREAMBLE';
              break;
            }

            this._state = 'BOUNDARY_DASH_END';
            break;
          default:
            this._state = 'BOUNDARY_MISMATCH';
            continue;
        }
        break;
      case 'BOUNDARY_LINE_END':
        switch (byte) {
          case LF:
            this._preamble = false;
            this._state    = 'HEADER_FIELD';
            this._counter  = 0;
            this._marker   = 0;
            this._part     = new Part();
            break
          default:
            this._state = 'BOUNDARY_MISMATCH';
            continue;
        }
        break;
      case 'BOUNDARY_DASH_END':
        if (byte !== DASH_END[++this._counter]) {
          this._state = 'BOUNDARY_MISMATCH';
          continue;
        }

        if (this._counter === DASH_END.length - 1) {
          this.emit('end');
          this._state = 'EPILOGUE';
          break;
        }

        this._boundary[++this._marker] = byte;
        break;
      case 'BOUNDARY_MISMATCH':
        if (this._preamble) {
          this._state = 'PREAMBLE';
          continue;
        }

        this._part.write(this._boundary, 0, this._marker);
        this._state = 'PART_BODY';
        continue;
      case 'HEADER_FIELD':
        switch (byte) {
          case COLON:
            this._state = 'HEADER_VALUE';
            break
          case CR:
            if (this._headerField) {
              this._emitError('InvalidHeaderFieldToken', byte);
              return;
            }

            this._state = 'HEADERS_END';
            break
          default:
            var character = TOKENS[byte];
            if (!character) {
              this._emitError('InvalidHeaderFieldToken', byte);
              return;
            }

            if (++this._headerBufferLength > this._headerBufferLimit) {
              this._state = 'HEADER_BUFFER_OVERFLOW';
              continue;
            }

            this._headerField += character;
            break;
        }
        break;
      case 'HEADERS_END':
        switch (byte) {
          case LF:
            this._marker = i;
            this._state  = 'PART_BODY';
            this.emit('part', this._part);
            break;
          default:
            this._emitError('InvalidHeaderFieldToken', byte);
            return;
            break;
        }
        break;
        break;
      case 'HEADER_VALUE':
        if (byte === CRLF[this._counter++]) {
          if (this._counter === CRLF.length) {
            this._part.addHeader(this._headerField, this._headerValue.trim());
            this._headerField = '';
            this._headerValue = '';
            this._counter     = 0;
            this._state       = 'HEADER_FIELD';
          }
          break;
        }

        this._counter = 0;

        if (++this._headerBufferLength > this._headerBufferLimit) {
          this._state = 'HEADER_BUFFER_OVERFLOW';
          continue;
        }

        this._headerValue += String.fromCharCode(byte);
        break;
      case 'PART_BODY':
        this._marker = i;

        while(true) {
          if (byte === CR) {
            this._part.write(buffer, this._marker, i);
            this._marker = 0;
            this._state  = 'BOUNDARY';
            break;
          }

          this._offset++;

          if ((byte = buffer[++i]) === undefined) {
            this._part.write(buffer, this._marker, i);
            break;
          }
        }
        break;
      case 'EPILOGUE':
        return;
      case 'HEADER_BUFFER_OVERFLOW':
        this._emitError(
          'HeaderBufferOverflow',
          'Max buffer size: ' + this._headerBufferLimit + 'bytes'
        );
        return;
      case 'NO_BOUNDARY':
        this._emitError('NoBoundary', 'No boundary configured for parser.');
        return;
      default:
        this._emitError('InvalidParserState', 'Unknown state: ' + this._state);
        return;
    }

    if ((byte = buffer[++i]) === undefined) break;
    this._offset++;
  }
};

MultipartParser.prototype._error = function(type, reason) {
  if (typeof reason === 'number') {
    var byte      = reason;
    var character = String.fromCharCode(byte);

    reason =
      'Got byte: ' + byte + ' / ' + JSON.stringify(character) + ' ' +
      'at offset: ' + this._offset;
  }

  return new Error('MultipartParser.' + type + ': ' + reason);
};

MultipartParser.prototype._emitError = function(type, reason) {
  var err       = this._error(type, reason);
  this.writable = false;
  this.emit('error', err);
};

MultipartParser.prototype.end = function() {
  this.emit('end');
};
<commit_msg>Boyer-Moore Optimization<commit_after>var EventEmitter = require('events').EventEmitter;
var util         = require('util');
var Part         = require('./part');

/* Tokens as defined by rfc 2616. Also lowercases them.
 *        token       = 1*<any CHAR except CTLs or separators>
 *     separators     = "(" | ")" | "<" | ">" | "@"
 *                    | "," | ";" | ":" | "\" | <">
 *                    | "/" | "[" | "]" | "?" | "="
 *                    | "{" | "}" | SP | HT
 *
 * From Ryan Dahl's http_parser.c
 */
var TOKENS = [
/*   0 nul    1 soh    2 stx    3 etx    4 eot    5 enq    6 ack    7 bel  */
        0,       0,       0,       0,       0,       0,       0,       0,
/*   8 bs     9 ht    10 nl    11 vt    12 np    13 cr    14 so    15 si   */
        0,       0,       0,       0,       0,       0,       0,       0,
/*  16 dle   17 dc1   18 dc2   19 dc3   20 dc4   21 nak   22 syn   23 etb */
        0,       0,       0,       0,       0,       0,       0,       0,
/*  24 can   25 em    26 sub   27 esc   28 fs    29 gs    30 rs    31 us  */
        0,       0,       0,       0,       0,       0,       0,       0,
/*  32 sp    33  !    34  "    35  #    36  $    37  %    38  &    39  '  */
       ' ',      '!',     '"',     '#',     '$',     '%',     '&',    '\'',
/*  40  (    41  )    42  *    43  +    44  ,    45  -    46  .    47  /  */
        0,       0,      '*',     '+',      0,      '-',     '.',     '/',
/*  48  0    49  1    50  2    51  3    52  4    53  5    54  6    55  7  */
       '0',     '1',     '2',     '3',     '4',     '5',     '6',     '7',
/*  56  8    57  9    58  :    59  ;    60  <    61  =    62  >    63  ?  */
       '8',     '9',      0,       0,       0,       0,       0,       0,
/*  64  @    65  A    66  B    67  C    68  D    69  E    70  F    71  G  */
        0,      'a',     'b',     'c',     'd',     'e',     'f',     'g',
/*  72  H    73  I    74  J    75  K    76  L    77  M    78  N    79  O  */
       'h',     'i',     'j',     'k',     'l',     'm',     'n',     'o',
/*  80  P    81  Q    82  R    83  S    84  T    85  U    86  V    87  W  */
       'p',     'q',     'r',     's',     't',     'u',     'v',     'w',
/*  88  X    89  Y    90  Z    91  [    92  \    93  ]    94  ^    95  _  */
       'x',     'y',     'z',      0,       0,       0,      '^',     '_',
/*  96  `    97  a    98  b    99  c   100  d   101  e   102  f   103  g  */
       '`',     'a',     'b',     'c',     'd',     'e',     'f',     'g',
/* 104  h   105  i   106  j   107  k   108  l   109  m   110  n   111  o  */
       'h',     'i',     'j',     'k',     'l',     'm',     'n',     'o',
/* 112  p   113  q   114  r   115  s   116  t   117  u   118  v   119  w  */
       'p',     'q',     'r',     's',     't',     'u',     'v',     'w',
/* 120  x   121  y   122  z   123  {   124  |   125  }   126  ~   127 del */
       'x',     'y',     'z',      0,      '|',     '}',     '~',       0 ];

var LF       = 10;
var CR       = 13;
var COLON    = 58;
var DASH     = 45;
var CRLF     = new Buffer('\r\n');
var DASH_END = new Buffer('--\r\n');

module.exports = MultipartParser;
util.inherits(MultipartParser, EventEmitter);
function MultipartParser() {
  EventEmitter.call(this);

  this.writable            = false;
  this._state              = 'NO_BOUNDARY';
  this._boundary           = null;
  this._preamble           = true;
  this._counter            = 0;
  this._marker             = 0;
  this._offset             = 0;
  this._part               = null;
  this._headerBufferLimit  = 4 * 1024;
  this._headerBufferLength = 0;
  this._headerField        = '';
  this._headerValue        = '';
  this._boundaryChars      = {};
}

MultipartParser.create = function(boundary) {
  var instance = new this();
  instance.boundary(boundary);
  return instance;
};

MultipartParser.prototype.boundary = function(boundary) {
  // Last 3 bytes are used for lookbehind
  this._boundary = new Buffer('\r\n--' + boundary + '???');
  this._state    = 'PREAMBLE';
  this.writable = true;

  this._boundaryChars = {};
  for (var i = 0; i < this._boundary.length - 3; i++) {
    this._boundaryChars[this._boundary[i]] = true;
  }
};

MultipartParser.prototype.write = function(buffer) {
  if (!this.writable) {
    throw this._error('NotWritable', 'Bad state: ' + this._state);
  }

  var i    = 0;
  var byte = buffer[i];

  while (true) {
    switch (this._state) {
      case 'PREAMBLE':
        switch (byte) {
          case CR:
            this._state   = 'BOUNDARY';
            this._marker  = 0;
            break;
          case DASH:
            // As per RFC-1341 we have to accept this, but complying clients
            // should not generate this.
            this._state  = 'BOUNDARY';
            this._marker = 2;
            break;
        }
        break;
      case 'BOUNDARY':
        if (byte !== this._boundary[++this._marker]) {
          this._state  = 'BOUNDARY_MISMATCH';
          continue;
        }

        if (this._marker === this._boundary.length - 4) {
          this._state = 'BOUNDARY_END';
        }
        break;
      case 'BOUNDARY_END':
        this._counter                  = 0;
        this._boundary[++this._marker] = byte;

        switch (byte) {
          case CR:
            this._state = 'BOUNDARY_LINE_END';
            break;
          case DASH:
            if (this._preamble) {
              this._state = 'PREAMBLE';
              break;
            }

            this._state = 'BOUNDARY_DASH_END';
            break;
          default:
            this._state = 'BOUNDARY_MISMATCH';
            continue;
        }
        break;
      case 'BOUNDARY_LINE_END':
        switch (byte) {
          case LF:
            this._preamble = false;
            this._state    = 'HEADER_FIELD';
            this._counter  = 0;
            this._marker   = 0;
            this._part     = new Part();
            break
          default:
            this._state = 'BOUNDARY_MISMATCH';
            continue;
        }
        break;
      case 'BOUNDARY_DASH_END':
        if (byte !== DASH_END[++this._counter]) {
          this._state = 'BOUNDARY_MISMATCH';
          continue;
        }

        if (this._counter === DASH_END.length - 1) {
          this.emit('end');
          this._state = 'EPILOGUE';
          break;
        }

        this._boundary[++this._marker] = byte;
        break;
      case 'BOUNDARY_MISMATCH':
        if (this._preamble) {
          this._state = 'PREAMBLE';
          continue;
        }

        this._part.write(this._boundary, 0, this._marker);
        this._state = 'PART_BODY';
        continue;
      case 'HEADER_FIELD':
        switch (byte) {
          case COLON:
            this._state = 'HEADER_VALUE';
            break
          case CR:
            if (this._headerField) {
              this._emitError('InvalidHeaderFieldToken', byte);
              return;
            }

            this._state = 'HEADERS_END';
            break
          default:
            var character = TOKENS[byte];
            if (!character) {
              this._emitError('InvalidHeaderFieldToken', byte);
              return;
            }

            if (++this._headerBufferLength > this._headerBufferLimit) {
              this._state = 'HEADER_BUFFER_OVERFLOW';
              continue;
            }

            this._headerField += character;
            break;
        }
        break;
      case 'HEADERS_END':
        switch (byte) {
          case LF:
            this._marker = i;
            this._state  = 'PART_BODY';
            this.emit('part', this._part);
            break;
          default:
            this._emitError('InvalidHeaderFieldToken', byte);
            return;
            break;
        }
        break;
        break;
      case 'HEADER_VALUE':
        if (byte === CRLF[this._counter++]) {
          if (this._counter === CRLF.length) {
            this._part.addHeader(this._headerField, this._headerValue.trim());
            this._headerField = '';
            this._headerValue = '';
            this._counter     = 0;
            this._state       = 'HEADER_FIELD';
          }
          break;
        }

        this._counter = 0;

        if (++this._headerBufferLength > this._headerBufferLimit) {
          this._state = 'HEADER_BUFFER_OVERFLOW';
          continue;
        }

        this._headerValue += String.fromCharCode(byte);
        break;
      case 'PART_BODY':
        this._marker = i;

        var boundaryLength = this._boundary.length - 1;
        var bufferEnd      = buffer.length;
        var boundaryChars  = this._boundaryChars;

        do {
          i += boundaryLength;
        } while (i < bufferEnd && !(buffer[i] in boundaryChars))
        i -= boundaryLength;

        this._offset += i - this._marker;

        while(true) {
          if (byte === CR) {
            this._part.write(buffer, this._marker, i);
            this._marker = 0;
            this._state  = 'BOUNDARY';
            break;
          }

          this._offset++;

          if ((byte = buffer[++i]) === undefined) {
            this._part.write(buffer, this._marker, i);
            break;
          }
        }
        break;
      case 'EPILOGUE':
        return;
      case 'HEADER_BUFFER_OVERFLOW':
        this._emitError(
          'HeaderBufferOverflow',
          'Max buffer size: ' + this._headerBufferLimit + 'bytes'
        );
        return;
      case 'NO_BOUNDARY':
        this._emitError('NoBoundary', 'No boundary configured for parser.');
        return;
      default:
        this._emitError('InvalidParserState', 'Unknown state: ' + this._state);
        return;
    }

    if ((byte = buffer[++i]) === undefined) break;
    this._offset++;
  }
};

MultipartParser.prototype._error = function(type, reason) {
  if (typeof reason === 'number') {
    var byte      = reason;
    var character = String.fromCharCode(byte);

    reason =
      'Got byte: ' + byte + ' / ' + JSON.stringify(character) + ' ' +
      'at offset: ' + this._offset;
  }

  return new Error('MultipartParser.' + type + ': ' + reason);
};

MultipartParser.prototype._emitError = function(type, reason) {
  var err       = this._error(type, reason);
  this.writable = false;
  this.emit('error', err);
};

MultipartParser.prototype.end = function() {
  this.emit('end');
};
<|endoftext|>


<commit_before>/**
 * @file shapes.js
 * @author mengke01
 * @date
 * @description
 * 轮廓模式处理事件
 */

define(
    function(require) {

        var guid = require('render/util/guid');
        var ShapesGroup = require('../group/ShapesGroup');
        var lang = require('common/lang');
        var selectShape = require('render/util/selectShape');
        var commandList = require('../menu/commandList');
        var POS_CUSOR = require('./cursor');

        // 移动步频
        var stepMap = {
            'left': [-5, 0],
            'right': [5, 0],
            'up': [0, -5],
            'down': [0, 5]
        };

        /**
         * 处理右键菜单
         *
         * @param {string} command 命令
         */
        function onContextMenu(e) {
            if(this.currentGroup.shapes.length === 0) {
                return;
            }

            this.fire('command', e);
            if(e.returnValue === false) {
                return;
            }

            this.contextMenu.hide();

            var command = e.command;

            // 是否编辑器支持
            if(this.supportCommand(command)) {
                this.execCommand(command, e);
                return;
            }

            var shapes = this.currentGroup.shapes;
            var shape = shapes[0];

            if (command == 'remove') {
                this.execCommand('removeshapes', shapes);
                this.setMode();
            }
            else if (command == 'reverse_point') {
                this.execCommand('reversepoint', shape);
            }
            else if (command == 'top') {
                this.execCommand('topshape', shape);
            }
            else if (command == 'bottom') {
                this.execCommand('bottomshape', shape);
            }
            else if (command == 'up') {
                this.execCommand('upshape', shape);
            }
            else if (command == 'down') {
                this.execCommand('downshape', shape);
            }
            else if (command == 'rotate_left') {
                this.execCommand('rotateleft', shapes);
                this.currentGroup.refresh();
            }
            else if (command == 'rotate_right') {
                this.execCommand('rotateright', shapes);
                this.currentGroup.refresh();
            }
            else if (command == 'reverse_shapes') {
                this.execCommand('reverseshapes', shapes);
            }
            else if (command == 'mirror_shapes') {
                this.execCommand('mirrorshapes', shapes);
            }
            else if (command == 'add_referenceline') {
                var bound = this.currentGroup.getBound();
                if(bound) {
                    this.execCommand('addreferenceline', bound.x, bound.y);
                    this.execCommand(
                        'addreferenceline',
                        bound.x + bound.width, bound.y + bound.height
                    );
                }
            }
            else if (command == 'cut') {
                this.execCommand('cutshapes', shapes);
                this.setMode();
                this.fire('change');
            }
            else if (command == 'copy') {
                this.execCommand('copyshapes', shapes);
            }
            this.fire('change');
        }

        var mode = {

            /**
             * 按下事件
             */
            down: function(e) {
                var render = this.render;
                var result = render.getLayer('cover').getShapeIn(e);

                if(result) {
                    this.currentPoint = lang.clone(result[0]);
                }
                else {

                    this.currentPoint = null;

                    result = render.getLayer('font').getShapeIn(e);

                    if(result) {
                        var shape = result[0];
                        if (result.length > 1) {
                            shape = selectShape(result);
                        }

                        if(this.currentGroup.shapes.indexOf(shape) >=0) {
                            return;
                        }
                        else {
                            this.currentGroup.setShapes([shape]);
                            this.currentGroup.setMode('scale');
                            this.currentGroup.refresh();
                            this.clicked = false;
                            return;
                        }
                    }

                    // 框选模式
                    this.setMode('range');
                }
            },

            /**
             * 开始拖动
             */
            dragstart: function(e) {

                // 点拖动模式
                if (this.currentPoint) {
                    this.currentGroup.beginTransform(this.currentPoint, this.render.camera);
                }
                // 复制模式
                else if (e.ctrlKey && e.altKey) {
                    var shapes = lang.clone(this.currentGroup.shapes);
                    var fontLayer = this.fontLayer;
                    shapes.forEach(function(shape) {
                        shape.id = guid('shape');
                        fontLayer.addShape(shape);
                    });

                    this.currentGroup.setShapes(shapes);
                }
                else {
                    this.currentGroup.setMode('move');
                    this.currentGroup.beginTransform(this.currentPoint, this.render.camera);
                }

            },

            /**
             * 拖动事件
             */
            drag: function(e) {
                if(this.currentGroup) {
                    this.currentGroup.transform(this.currentPoint, this.render.camera);
                }
            },

            /**
             * 拖动结束事件
             */
            dragend: function(e) {

                if (this.currentPoint) {
                    this.currentGroup.finishTransform(this.currentPoint, this.render.camera);
                    this.currentPoint = null;
                }
                else if (this.currentGroup.mode == 'move') {
                    this.currentGroup.finishTransform(this.currentPoint, this.render.camera);
                    this.currentGroup.setMode('scale');
                }

                this.render.setCursor('default');
                this.fire('change');
            },

            /**
             * 移动
             */
            move: function(e) {

                var shapes = this.coverLayer.getShapeIn(e);
                var mode = this.currentGroup.mode;

                if(shapes && mode != 'move' ) {
                    this.render.setCursor(POS_CUSOR[this.currentGroup.mode][shapes[0].pos] || 'default');
                }
                else {
                    this.render.setCursor('default');
                }
            },

            /**
             * 右键
             */
            rightdown: function(e) {
                // 对单个shape进行操作
                this.contextMenu.onClick = lang.bind(onContextMenu, this);
                this.contextMenu.show(e,
                    this.currentGroup.shapes.length > 1
                    ? commandList.shapes
                    : commandList.shape
                );
            },

            click: function(e) {
                if (this.clicked) {
                    // 变换编辑模式
                    var mode = this.currentGroup.mode;
                    this.currentGroup.setMode(mode == 'scale' ? 'rotate' : 'scale');
                    this.currentGroup.refresh();
                }
                this.clicked = true;
            },

            /**
             * 按键
             */
            keyup: function(e) {
                // esc键，重置model
                if (e.key == 'delete') {
                    this.execCommand('removeshapes', this.currentGroup.shapes);
                    this.setMode();
                    this.fire('change');
                }
                // 全选
                else if(e.keyCode == 65 && e.ctrlKey) {
                    this.currentGroup.setShapes(this.fontLayer.shapes.slice());
                    this.currentGroup.refresh();
                }
                // 剪切
                else if (e.keyCode == 88 && e.ctrlKey) {
                    this.execCommand('cutshapes', this.currentGroup.shapes);
                    this.setMode();
                    this.fire('change');
                }
                // 复制
                else if (e.keyCode == 67 && e.ctrlKey) {
                    this.execCommand('copyshapes', this.currentGroup.shapes);
                }
                // 移动
                else if(stepMap[e.key]) {
                    this.fire('change');
                }
                else if (e.key == 'esc') {
                    this.setMode();
                }
            },

            /**
             * 按住
             */
            keydown: function(e) {
                // 移动
                if(stepMap[e.key]) {
                    this.currentGroup.move(stepMap[e.key][0], stepMap[e.key][1]);
                }
            },

            /**
             * 开始模式
             */
            begin: function(shapes) {
                this.currentGroup = new ShapesGroup(shapes, this);
                this.currentGroup.refresh();
                this.currentGroup.setMode('scale');
                this.clicked = false;
            },

            /**
             * 结束模式
             */
            end: function() {

                this.currentPoint = null;
                this.currentGroup.dispose();
                this.currentGroup = null;

                this.render.setCursor('default');
            }
        };

        return mode;
    }
);
<commit_msg>fix copy mode<commit_after>/**
 * @file shapes.js
 * @author mengke01
 * @date
 * @description
 * 轮廓模式处理事件
 */

define(
    function(require) {

        var guid = require('render/util/guid');
        var ShapesGroup = require('../group/ShapesGroup');
        var lang = require('common/lang');
        var selectShape = require('render/util/selectShape');
        var commandList = require('../menu/commandList');
        var POS_CUSOR = require('./cursor');

        // 移动步频
        var stepMap = {
            'left': [-5, 0],
            'right': [5, 0],
            'up': [0, -5],
            'down': [0, 5]
        };

        /**
         * 处理右键菜单
         *
         * @param {string} command 命令
         */
        function onContextMenu(e) {
            if(this.currentGroup.shapes.length === 0) {
                return;
            }

            this.fire('command', e);
            if(e.returnValue === false) {
                return;
            }

            this.contextMenu.hide();

            var command = e.command;

            // 是否编辑器支持
            if(this.supportCommand(command)) {
                this.execCommand(command, e);
                return;
            }

            var shapes = this.currentGroup.shapes;
            var shape = shapes[0];

            if (command == 'remove') {
                this.execCommand('removeshapes', shapes);
                this.setMode();
            }
            else if (command == 'reverse_point') {
                this.execCommand('reversepoint', shape);
            }
            else if (command == 'top') {
                this.execCommand('topshape', shape);
            }
            else if (command == 'bottom') {
                this.execCommand('bottomshape', shape);
            }
            else if (command == 'up') {
                this.execCommand('upshape', shape);
            }
            else if (command == 'down') {
                this.execCommand('downshape', shape);
            }
            else if (command == 'rotate_left') {
                this.execCommand('rotateleft', shapes);
                this.currentGroup.refresh();
            }
            else if (command == 'rotate_right') {
                this.execCommand('rotateright', shapes);
                this.currentGroup.refresh();
            }
            else if (command == 'reverse_shapes') {
                this.execCommand('reverseshapes', shapes);
            }
            else if (command == 'mirror_shapes') {
                this.execCommand('mirrorshapes', shapes);
            }
            else if (command == 'add_referenceline') {
                var bound = this.currentGroup.getBound();
                if(bound) {
                    this.execCommand('addreferenceline', bound.x, bound.y);
                    this.execCommand(
                        'addreferenceline',
                        bound.x + bound.width, bound.y + bound.height
                    );
                }
            }
            else if (command == 'cut') {
                this.execCommand('cutshapes', shapes);
                this.setMode();
                this.fire('change');
            }
            else if (command == 'copy') {
                this.execCommand('copyshapes', shapes);
            }
            this.fire('change');
        }

        var mode = {

            /**
             * 按下事件
             */
            down: function(e) {
                var render = this.render;
                var result = render.getLayer('cover').getShapeIn(e);

                if(result) {
                    this.currentPoint = lang.clone(result[0]);
                }
                else {

                    this.currentPoint = null;

                    result = render.getLayer('font').getShapeIn(e);

                    if(result) {
                        var shape = result[0];
                        if (result.length > 1) {
                            shape = selectShape(result);
                        }

                        if(this.currentGroup.shapes.indexOf(shape) >=0) {
                            return;
                        }
                        else {
                            this.currentGroup.setShapes([shape]);
                            this.currentGroup.setMode('scale');
                            this.currentGroup.refresh();
                            this.clicked = false;
                            return;
                        }
                    }

                    // 框选模式
                    this.setMode('range');
                }
            },

            /**
             * 开始拖动
             */
            dragstart: function(e) {

                // 点拖动模式
                if (this.currentPoint) {
                    this.currentGroup.beginTransform(this.currentPoint, this.render.camera);
                }
                else {
                    // 复制模式
                    if (e.ctrlKey && e.altKey) {
                        var shapes = lang.clone(this.currentGroup.shapes);
                        var fontLayer = this.fontLayer;
                        shapes.forEach(function(shape) {
                            shape.id = guid('shape');
                            fontLayer.addShape(shape);
                        });
                        this.currentGroup.setShapes(shapes);
                    }
                    // 移动
                    this.currentGroup.setMode('move');
                    this.currentGroup.beginTransform(this.currentPoint, this.render.camera);
                }

            },

            /**
             * 拖动事件
             */
            drag: function(e) {
                if(this.currentGroup) {
                    this.currentGroup.transform(this.currentPoint, this.render.camera);
                }
            },

            /**
             * 拖动结束事件
             */
            dragend: function(e) {

                if (this.currentPoint) {
                    this.currentGroup.finishTransform(this.currentPoint, this.render.camera);
                    this.currentPoint = null;
                }
                else if (this.currentGroup.mode == 'move') {
                    this.currentGroup.finishTransform(this.currentPoint, this.render.camera);
                    this.currentGroup.setMode('scale');
                }

                this.render.setCursor('default');
                this.fire('change');
            },

            /**
             * 移动
             */
            move: function(e) {

                var shapes = this.coverLayer.getShapeIn(e);
                var mode = this.currentGroup.mode;

                if(shapes && mode != 'move' ) {
                    this.render.setCursor(POS_CUSOR[this.currentGroup.mode][shapes[0].pos] || 'default');
                }
                else {
                    this.render.setCursor('default');
                }
            },

            /**
             * 右键
             */
            rightdown: function(e) {
                // 对单个shape进行操作
                this.contextMenu.onClick = lang.bind(onContextMenu, this);
                this.contextMenu.show(e,
                    this.currentGroup.shapes.length > 1
                    ? commandList.shapes
                    : commandList.shape
                );
            },

            click: function(e) {
                if (this.clicked) {
                    // 变换编辑模式
                    var mode = this.currentGroup.mode;
                    this.currentGroup.setMode(mode == 'scale' ? 'rotate' : 'scale');
                    this.currentGroup.refresh();
                }
                this.clicked = true;
            },

            /**
             * 按键
             */
            keyup: function(e) {
                // esc键，重置model
                if (e.key == 'delete') {
                    this.execCommand('removeshapes', this.currentGroup.shapes);
                    this.setMode();
                    this.fire('change');
                }
                // 全选
                else if(e.keyCode == 65 && e.ctrlKey) {
                    this.currentGroup.setShapes(this.fontLayer.shapes.slice());
                    this.currentGroup.refresh();
                }
                // 剪切
                else if (e.keyCode == 88 && e.ctrlKey) {
                    this.execCommand('cutshapes', this.currentGroup.shapes);
                    this.setMode();
                    this.fire('change');
                }
                // 复制
                else if (e.keyCode == 67 && e.ctrlKey) {
                    this.execCommand('copyshapes', this.currentGroup.shapes);
                }
                // 移动
                else if(stepMap[e.key]) {
                    this.fire('change');
                }
                else if (e.key == 'esc') {
                    this.setMode();
                }
            },

            /**
             * 按住
             */
            keydown: function(e) {
                // 移动
                if(stepMap[e.key]) {
                    this.currentGroup.move(stepMap[e.key][0], stepMap[e.key][1]);
                }
            },

            /**
             * 开始模式
             */
            begin: function(shapes) {
                this.currentGroup = new ShapesGroup(shapes, this);
                this.currentGroup.refresh();
                this.currentGroup.setMode('scale');
                this.clicked = false;
            },

            /**
             * 结束模式
             */
            end: function() {

                this.currentPoint = null;
                this.currentGroup.dispose();
                this.currentGroup = null;

                this.render.setCursor('default');
            }
        };

        return mode;
    }
);
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2013-2015 node-coap contributors.
 *
 * node-coap is licensed under an MIT +no-false-attribs license.
 * All rights not explicitly granted in the MIT license are reserved.
 * See the included LICENSE file for more details.
 */

// Generic toBinary and fromBinary definitions
var optionToBinaryFunctions = {}
var optionFromBinaryFunctions = {}

module.exports.toBinary = function(name, value) {
  if (Buffer.isBuffer(value))
    return value

  if (!optionToBinaryFunctions[name])
    throw new Error('Unknown string to Buffer converter for option: ' + name)

  return optionToBinaryFunctions[name](value)
}

module.exports.fromBinary = function(name, value) {
  var convert = optionFromBinaryFunctions[name]

  if (!convert)
    return value

  return convert(value)
}

var registerOption = function(name, toBinary, fromBinary) {
  optionFromBinaryFunctions[name] = fromBinary
  optionToBinaryFunctions[name] = toBinary
}

module.exports.registerOption = registerOption

// ETag option registration
var fromString = function(result) {
  return new Buffer(result)
}

var toString = function(value) {
  return value.toString()
}

registerOption('ETag', fromString, toString)
registerOption('Location-Path', fromString, toString)
registerOption('Location-Query', fromString, toString)
registerOption('Proxy-Uri', fromString, toString)

// Content-Format and Accept options registration
var formatsString = {}
var formatsBinaries = {}

var registerFormat = function(name, value) {
  formatsString[name] = new Buffer([value])
  formatsBinaries[value] = name
}
module.exports.registerFormat = registerFormat

registerFormat('text/plain', 0)
registerFormat('application/link-format', 40)
registerFormat('application/xml', 41)
registerFormat('application/octet-stream', 42)
registerFormat('application/exi', 47)
registerFormat('application/json', 50)

var contentFormatToBinary = function(result) {
  result = formatsString[result]
  if (!result)
    throw new Error('Unknown Content-Format: ' + value)

  return result
}

var contentFormatToString = function(value) {
  if (value.length === 0)
    return formatsBinaries[0]

  if (value.length === 1)
    value = value.readUInt8(0)
  else if (value.length === 2)
    value = value.readUInt16BE(0)
  else
    throw new Error('Content-Format option is too big')

  var result = formatsBinaries[value]

  if (!result)
    throw new Error('No matching format found')

  return result
}

registerOption('Content-Format', contentFormatToBinary, contentFormatToString)
registerOption('Accept', contentFormatToBinary, contentFormatToString)
registerOption('Observe', function(sequence) {
  var buf

  if (!sequence) {
    buf = new Buffer(0)
  } else if (sequence < 256) {
    buf = new Buffer(1)
    buf.writeUInt8(sequence, 0)
  } else if (sequence >= 256 & sequence < 65535) {
    buf = new Buffer(2)
    buf.writeUInt16BE(sequence, 0)
  } else {
    // it is three bytes long
    buf = new Buffer(3)
    buf.writeUInt8(Math.floor(sequence / 65535), 0)
    buf.writeUInt16BE(sequence % 65535, 1)
  }

  return buf
}, function(buf) {
  var result = 0

  if (buf.length === 1) {
    result = buf.readUInt8(0)
  } else if (buf.length === 2) {
    result = buf.readUInt16BE(0)
  } else if (buf.length === 3) {
    result += buf.readUInt8(0) * 65353
    result += buf.readUInt16BE(1)
  }

  return result
})

<commit_msg>Control the case when the option format is bigger than a byte<commit_after>/*
 * Copyright (c) 2013-2015 node-coap contributors.
 *
 * node-coap is licensed under an MIT +no-false-attribs license.
 * All rights not explicitly granted in the MIT license are reserved.
 * See the included LICENSE file for more details.
 */

// Generic toBinary and fromBinary definitions
var optionToBinaryFunctions = {}
var optionFromBinaryFunctions = {}

module.exports.toBinary = function(name, value) {
  if (Buffer.isBuffer(value))
    return value

  if (!optionToBinaryFunctions[name])
    throw new Error('Unknown string to Buffer converter for option: ' + name)

  return optionToBinaryFunctions[name](value)
}

module.exports.fromBinary = function(name, value) {
  var convert = optionFromBinaryFunctions[name]

  if (!convert)
    return value

  return convert(value)
}

var registerOption = function(name, toBinary, fromBinary) {
  optionFromBinaryFunctions[name] = fromBinary
  optionToBinaryFunctions[name] = toBinary
}

module.exports.registerOption = registerOption

// ETag option registration
var fromString = function(result) {
  return new Buffer(result)
}

var toString = function(value) {
  return value.toString()
}

registerOption('ETag', fromString, toString)
registerOption('Location-Path', fromString, toString)
registerOption('Location-Query', fromString, toString)
registerOption('Proxy-Uri', fromString, toString)

// Content-Format and Accept options registration
var formatsString = {}
var formatsBinaries = {}

var registerFormat = function(name, value) {
  var bytes;

  if (value > 255) {
    bytes = new Buffer(2);
    bytes.writeUInt16BE(value, 0);
  } else {
    bytes = new Buffer([value])
  }

  formatsString[name] = bytes
  formatsBinaries[value] = name
}

module.exports.registerFormat = registerFormat

registerFormat('text/plain', 0)
registerFormat('application/link-format', 40)
registerFormat('application/xml', 41)
registerFormat('application/octet-stream', 42)
registerFormat('application/exi', 47)
registerFormat('application/json', 50)

var contentFormatToBinary = function(result) {
  result = formatsString[result]
  if (!result)
    throw new Error('Unknown Content-Format: ' + value)

  return result
}

var contentFormatToString = function(value) {
  if (value.length === 0)
    return formatsBinaries[0]

  if (value.length === 1)
    value = value.readUInt8(0)
  else if (value.length === 2)
    value = value.readUInt16BE(0)
  else
    throw new Error('Content-Format option is too big')

  var result = formatsBinaries[value]

  if (!result)
    throw new Error('No matching format found')

  return result
}

registerOption('Content-Format', contentFormatToBinary, contentFormatToString)
registerOption('Accept', contentFormatToBinary, contentFormatToString)
registerOption('Observe', function(sequence) {
  var buf

  if (!sequence) {
    buf = new Buffer(0)
  } else if (sequence < 256) {
    buf = new Buffer(1)
    buf.writeUInt8(sequence, 0)
  } else if (sequence >= 256 & sequence < 65535) {
    buf = new Buffer(2)
    buf.writeUInt16BE(sequence, 0)
  } else {
    // it is three bytes long
    buf = new Buffer(3)
    buf.writeUInt8(Math.floor(sequence / 65535), 0)
    buf.writeUInt16BE(sequence % 65535, 1)
  }

  return buf
}, function(buf) {
  var result = 0

  if (buf.length === 1) {
    result = buf.readUInt8(0)
  } else if (buf.length === 2) {
    result = buf.readUInt16BE(0)
  } else if (buf.length === 3) {
    result += buf.readUInt8(0) * 65353
    result += buf.readUInt16BE(1)
  }

  return result
})

<|endoftext|>


<commit_before>var async         = require('async');
var cluster       = require('cluster');
var cookieParser  = require('cookie-parser')();
var fileServer    = require('node-static');
var forwarded     = require('forwarded-for');
var http          = require('http');
var microtime     = require('microtime-nodejs');
var multiparty    = require('multiparty');
var querystring   = require('querystring');
var url           = require('url');
var fServer       = new fileServer.Server('/', { cache: 7800, serverInfo: 'OrangeBox'});

var statics       = [];
var staticPath    = [];
var aliases       = {};
var vars          = {};
var engines       = {swig: require('swig')};

String.prototype.hCapitalize = function () {
	return this.split('-').map(
		function (el) {
			return el.charAt(0).toUpperCase() + el.substring(1);
		}
	).join('-');
};

function processRequest (request) {
	if (request.headers && typeof request.headers.host === 'string') {
		var host = request.headers.host.split(':');
		if (host[0]) request.host = host[0];
		if (host[1]) request.port = Number(host[1]) || 8080;
	} else {
		request.host = 'localhost';
		request.port = 8080;
	}

	var query = request.url.split('?');
	var path = query[0];
	query.splice(0,1);

	// request.ip = req.header('x-forwarded-for') || req.headers['X-Forwarded-For'] || req.client.remoteAddress;
	request.ip = forwarded(request, request.headers).ip;

	request.query = querystring.parse(query.join('?'));
	request.queryString = query.join('?');
	request.path = path;

	// aliases
	for (var a in aliases) {
		if (request.path.indexOf(a) === 0)  {
			request.originalUrl = request.url;
			request.url = request.path.replace(a, aliases[a]) + (request.queryString.length ? '?' + request.queryString : '');
		}
	}
}

function processPost (request, response, callback) {
	var queryData = '';
	if (typeof callback !== 'function') return;

	if (request.method === 'POST') {
		if (typeof request.headers["content-type"] === 'string' &&
		 request.headers["content-type"].indexOf('multipart/form-data') !== -1) {
		 	var options = {};
		 	var alss = {
				'multiparty maxfilessize' : 'maxFilesSize',
				'multiparty autofiles'    : 'autoFiles',
				'multiparty uploaddir'    : 'uploadDir'
		 	};
		 	for (var it in vars) {
		 		if (it in alss) options[alss[it]] = vars[it];
		 	}
			var form = new multiparty.Form(options);
			var _err = false, _cerr = false;

			form.on('error', function(err) {
				if (!vars['multiparty catcherror']) {
					_err = true;
					response.writeHead(err.statusCode || 500, {'Content-Type': 'text/plain'});
					response.end(err.message);
				} else {
					_cerr = err;
					_cerr.msg = err.message;
				}
			});

			form.parse(request, function(err, body, files) {
				for (var el in body) {
					if (Array.isArray(body[el]) && body[el].length === 1) {
						body[el] = body[el][0];
					}
				}
				if (!_err) {
					if (_cerr) {
						request.body = {
							'multiparty_error' : _cerr
						};
					} else {
						request.body = body;
						request.files = files;
					}
					callback();
				}
			});
		} else {
			request.on('data', function(data) {
				queryData += data;
				if(queryData.length > 1e6) {
					queryData = '';
					response.writeHead(413, {'Content-Type': 'text/plain'});
					response.end();
					request.connection.destroy();
				}
			});

			request.on('end', function() {
				request.body = querystring.parse(queryData);
				callback();
			});
		}
	} else {
		request.body = {};
		callback();
	}
}

if (!cluster.isMaster) {

	// Worker processes have a http server.
	var server = http.Server(function(request, response) {
		var saveStart = microtime.nowDouble();
		processRequest(request);

		function notFile(){
			processPost(request, response, function() {

				var reqId = Math.random().toString(36).substring(2,16);

				process.once('ok_' + reqId, function(ret) {
					ret._headers['x-time'] = (microtime.nowDouble() - saveStart).toFixed(6);
					if (204 === ret.statusCode || 304 === ret.statusCode) {
						if (ret._headers['content-type']) delete ret._headers['content-type'];
						if (ret._headers['content-length']) delete ret._headers['content-length'];
						if (ret._headers['transfer-encoding']) delete ret._headers['transfer-encoding'];
						ret.data = '';
					}

					var headers = ret.subHeaders || {};
					Object.keys(ret._headers).forEach(function(el) {
						headers[el.hCapitalize()] = ret._headers[el];
					});

					if (ret.sendFile && ret.filePath) {
						ret.data = '';
						var r = fServer.serveFile(ret.filePath, ret.statusCode, headers, request, response);
						fServer.root='/';
						r.on("error", function() {
							response.writeHead(404, {'Content-Type': 'text/html'});
							response.end('
404 Not Found
Requested resource could not be found.');
							r.removeAllListeners();
						}).on("success", function() {
							r.removeAllListeners();
						});
					} else {
						response.writeHead(ret.statusCode, headers);
						if (ret.Type === 'Buffer') {
							ret.data = new Buffer(ret.data, 'base64');
						}
						response.end(ret.data);
					}

				});

				cookieParser(request, response, function() {
					process.send({
						files            : request.files,
						headers          : request.headers,
						orangebox        : '0.3.0',
						url              : request.url,
						originalUrl      : request.url + '?' + request.queryString,
						host             : request.host,
						hostname         : request.host,
						port             : request.port,
						path             : request.path,
						baseUrl          : request.path,
						httpVersionMajor : request.httpVersionMajor,
						httpVersionMinor : request.httpVersionMinor,
						trailers         : request.trailers,
						httpVersion      : request.httpVersion,
						method           : request.method,
						body             : request.body,
						protocol         : request.protocol,
						secure           : 'https' === request.protocol,
						post             : request.post,
						query            : request.query,
						queryString      : request.queryString,
						ip               : request.ip,
						cookies          : request.cookies,
						signedCookies    : request.signedCookies,
						reqId            : reqId,
						pid              : process.pid,
						connection       : {
							encrypted     : request.connection.encrypted,
							readable      : request.connection.readable,
							writable      : request.connection.writable,
							destroyed     : request.connection.destroyed,
							allowHalfOpen : request.connection.allowHalfOpen,
							bytesRead     : request.connection.bytesRead
						}
					});
				});

			});
		}
		if (statics.length) {
			var pathname = url.parse(request.url).pathname;
			var aFlr = statics.filter(function(el,i){
				return pathname !== '' && pathname.indexOf(staticPath[i]) === 0;
			});
			if (!aFlr.length) {
				aFlr = statics.filter(function(){
					return pathname === '';
				});
			}
			async.eachSeries(aFlr, function(el, callback) {
				el.serve(request, response, function (e) {
					if (!e || e.status !== 404)
						callback(true);
					else
						callback(null);
				});
			}, function(err){
				if (!err) notFile();
			});
		} else {
			notFile();
		}
	});

	process.on('message', function(ret) {
		if (ret.cmd) {

			switch (ret.cmd) {

				case 'send':
					process.emit('ok_' + ret.response.reqId, ret.response);
					break;

				case 'sendFile':
					if (ret.options) {
						if (ret.options.root) {
							fServer.root = ret.options.root;
						}
						if (ret.options.headers) {
							ret.response.subHeaders = ret.options.headers;
						}
					}
					ret.response.sendFile = true;
					process.emit('ok_' + ret.response.reqId, ret.response);
					break;

				case 'sendRender':

					if (ret.engine === 'swig') {
						engines.swig.setDefaults({ autoescape: ret.response.variables['view autoescape'] });
						engines.swig.setDefaultTZOffset(ret.response.variables['view timezone']);
					}

					engines[ret.engine].renderFile(ret.viewsPath + '/' + ret.view + '.html' , ret.data || {}, function (err, output) {
						if (err) {
							console.error(err);
							ret.response.statusCode = 500;
						} else {
							ret.response.data = output;
						}
						process.emit('ok_' + ret.response.reqId, ret.response);
					});
					break;

				case 'setVar':
					vars[ret.name] = ret.value;
					break;

				case 'listen':
					var _listen = function() { process.send({'cmd': 'listen'}); };
					if (ret.listen.host) {
						server.listen(ret.listen.port, ret.listen.host, _listen);
					} else {
						server.listen(ret.listen.port, _listen);
					}
					break;

				case 'fileServer':
					statics.push(new fileServer.Server(ret.path, { cache: 7800, serverInfo: 'OrangeBox'}));
					staticPath.push(ret.routePath);
					break;

				case 'alias':
					aliases[ret.alias] = ret.path;
					break;

				default:
					console.warn('Undefined message' + ret);
					break;

			}
		}
	});

	var gracefulExit = function () {
		server.removeAllListeners();
	};
	process.once('SIGINT', gracefulExit).once('SIGTERM', gracefulExit);
}

<commit_msg>Fix overwriting `originalUrl`<commit_after>var async         = require('async');
var cluster       = require('cluster');
var cookieParser  = require('cookie-parser')();
var fileServer    = require('node-static');
var forwarded     = require('forwarded-for');
var http          = require('http');
var microtime     = require('microtime-nodejs');
var multiparty    = require('multiparty');
var querystring   = require('querystring');
var url           = require('url');
var fServer       = new fileServer.Server('/', { cache: 7800, serverInfo: 'OrangeBox'});

var statics       = [];
var staticPath    = [];
var aliases       = {};
var vars          = {};
var engines       = {swig: require('swig')};

String.prototype.hCapitalize = function () {
	return this.split('-').map(
		function (el) {
			return el.charAt(0).toUpperCase() + el.substring(1);
		}
	).join('-');
};

function processRequest (request) {
	if (request.headers && typeof request.headers.host === 'string') {
		var host = request.headers.host.split(':');
		if (host[0]) request.host = host[0];
		if (host[1]) request.port = Number(host[1]) || 8080;
	} else {
		request.host = 'localhost';
		request.port = 8080;
	}

	var query = request.url.split('?');
	var path = query[0];
	query.splice(0,1);

	// request.ip = req.header('x-forwarded-for') || req.headers['X-Forwarded-For'] || req.client.remoteAddress;
	request.ip = forwarded(request, request.headers).ip;

	request.query = querystring.parse(query.join('?'));
	request.queryString = query.join('?');
	request.path = path;

	// aliases
	for (var a in aliases) {
		if (request.path.indexOf(a) === 0)  {
			request.originalUrl = request.url;
			request.url = request.path.replace(a, aliases[a]) + (request.queryString.length ? '?' + request.queryString : '');
		}
	}
}

function processPost (request, response, callback) {
	var queryData = '';
	if (typeof callback !== 'function') return;

	if (request.method === 'POST') {
		if (typeof request.headers["content-type"] === 'string' &&
		 request.headers["content-type"].indexOf('multipart/form-data') !== -1) {
		 	var options = {};
		 	var alss = {
				'multiparty maxfilessize' : 'maxFilesSize',
				'multiparty autofiles'    : 'autoFiles',
				'multiparty uploaddir'    : 'uploadDir'
		 	};
		 	for (var it in vars) {
		 		if (it in alss) options[alss[it]] = vars[it];
		 	}
			var form = new multiparty.Form(options);
			var _err = false, _cerr = false;

			form.on('error', function(err) {
				if (!vars['multiparty catcherror']) {
					_err = true;
					response.writeHead(err.statusCode || 500, {'Content-Type': 'text/plain'});
					response.end(err.message);
				} else {
					_cerr = err;
					_cerr.msg = err.message;
				}
			});

			form.parse(request, function(err, body, files) {
				for (var el in body) {
					if (Array.isArray(body[el]) && body[el].length === 1) {
						body[el] = body[el][0];
					}
				}
				if (!_err) {
					if (_cerr) {
						request.body = {
							'multiparty_error' : _cerr
						};
					} else {
						request.body = body;
						request.files = files;
					}
					callback();
				}
			});
		} else {
			request.on('data', function(data) {
				queryData += data;
				if(queryData.length > 1e6) {
					queryData = '';
					response.writeHead(413, {'Content-Type': 'text/plain'});
					response.end();
					request.connection.destroy();
				}
			});

			request.on('end', function() {
				request.body = querystring.parse(queryData);
				callback();
			});
		}
	} else {
		request.body = {};
		callback();
	}
}

if (!cluster.isMaster) {

	// Worker processes have a http server.
	var server = http.Server(function(request, response) {
		var saveStart = microtime.nowDouble();
		processRequest(request);

		function notFile(){
			processPost(request, response, function() {

				var reqId = Math.random().toString(36).substring(2,16);

				process.once('ok_' + reqId, function(ret) {
					ret._headers['x-time'] = (microtime.nowDouble() - saveStart).toFixed(6);
					if (204 === ret.statusCode || 304 === ret.statusCode) {
						if (ret._headers['content-type']) delete ret._headers['content-type'];
						if (ret._headers['content-length']) delete ret._headers['content-length'];
						if (ret._headers['transfer-encoding']) delete ret._headers['transfer-encoding'];
						ret.data = '';
					}

					var headers = ret.subHeaders || {};
					Object.keys(ret._headers).forEach(function(el) {
						headers[el.hCapitalize()] = ret._headers[el];
					});

					if (ret.sendFile && ret.filePath) {
						ret.data = '';
						var r = fServer.serveFile(ret.filePath, ret.statusCode, headers, request, response);
						fServer.root='/';
						r.on("error", function() {
							response.writeHead(404, {'Content-Type': 'text/html'});
							response.end('
404 Not Found
Requested resource could not be found.');
							r.removeAllListeners();
						}).on("success", function() {
							r.removeAllListeners();
						});
					} else {
						response.writeHead(ret.statusCode, headers);
						if (ret.Type === 'Buffer') {
							ret.data = new Buffer(ret.data, 'base64');
						}
						response.end(ret.data);
					}

				});

				cookieParser(request, response, function() {
					process.send({
						files            : request.files,
						headers          : request.headers,
						orangebox        : '0.3.0',
						url              : request.url,
						host             : request.host,
						hostname         : request.host,
						port             : request.port,
						path             : request.path,
						baseUrl          : request.path,
						httpVersionMajor : request.httpVersionMajor,
						httpVersionMinor : request.httpVersionMinor,
						trailers         : request.trailers,
						httpVersion      : request.httpVersion,
						method           : request.method,
						body             : request.body,
						protocol         : request.protocol,
						secure           : 'https' === request.protocol,
						post             : request.post,
						query            : request.query,
						queryString      : request.queryString,
						ip               : request.ip,
						cookies          : request.cookies,
						signedCookies    : request.signedCookies,
						reqId            : reqId,
						pid              : process.pid,
						connection       : {
							encrypted     : request.connection.encrypted,
							readable      : request.connection.readable,
							writable      : request.connection.writable,
							destroyed     : request.connection.destroyed,
							allowHalfOpen : request.connection.allowHalfOpen,
							bytesRead     : request.connection.bytesRead
						}
					});
				});

			});
		}
		if (statics.length) {
			var pathname = url.parse(request.url).pathname;
			var aFlr = statics.filter(function(el,i){
				return pathname !== '' && pathname.indexOf(staticPath[i]) === 0;
			});
			if (!aFlr.length) {
				aFlr = statics.filter(function(){
					return pathname === '';
				});
			}
			async.eachSeries(aFlr, function(el, callback) {
				el.serve(request, response, function (e) {
					if (!e || e.status !== 404)
						callback(true);
					else
						callback(null);
				});
			}, function(err){
				if (!err) notFile();
			});
		} else {
			notFile();
		}
	});

	process.on('message', function(ret) {
		if (ret.cmd) {

			switch (ret.cmd) {

				case 'send':
					process.emit('ok_' + ret.response.reqId, ret.response);
					break;

				case 'sendFile':
					if (ret.options) {
						if (ret.options.root) {
							fServer.root = ret.options.root;
						}
						if (ret.options.headers) {
							ret.response.subHeaders = ret.options.headers;
						}
					}
					ret.response.sendFile = true;
					process.emit('ok_' + ret.response.reqId, ret.response);
					break;

				case 'sendRender':

					if (ret.engine === 'swig') {
						engines.swig.setDefaults({ autoescape: ret.response.variables['view autoescape'] });
						engines.swig.setDefaultTZOffset(ret.response.variables['view timezone']);
					}

					engines[ret.engine].renderFile(ret.viewsPath + '/' + ret.view + '.html' , ret.data || {}, function (err, output) {
						if (err) {
							console.error(err);
							ret.response.statusCode = 500;
						} else {
							ret.response.data = output;
						}
						process.emit('ok_' + ret.response.reqId, ret.response);
					});
					break;

				case 'setVar':
					vars[ret.name] = ret.value;
					break;

				case 'listen':
					var _listen = function() { process.send({'cmd': 'listen'}); };
					if (ret.listen.host) {
						server.listen(ret.listen.port, ret.listen.host, _listen);
					} else {
						server.listen(ret.listen.port, _listen);
					}
					break;

				case 'fileServer':
					statics.push(new fileServer.Server(ret.path, { cache: 7800, serverInfo: 'OrangeBox'}));
					staticPath.push(ret.routePath);
					break;

				case 'alias':
					aliases[ret.alias] = ret.path;
					break;

				default:
					console.warn('Undefined message' + ret);
					break;

			}
		}
	});

	var gracefulExit = function () {
		server.removeAllListeners();
	};
	process.once('SIGINT', gracefulExit).once('SIGTERM', gracefulExit);
}

<|endoftext|>


<commit_before>
        // lib code
var   state = require('../haxe-state')
        // node built in
    , path = require('path')

    // Match haxe compiler output info
var REGEX_HAXE_COMPILER_OUTPUT_LINE = /^\s*([^\:]+)\:([0-9]+)\:\s+(characters|lines)\s+([0-9]+)\-([0-9]+)\s+\:\s*(.*?)\s*$/;

module.exports = {

        // Parse haxe compiler output and extract info
    parse_output: function(output) {

        var info = [];
        var prev_info = null;
        var lines = output.split("\n");
        var cwd = state.hxml_cwd;
        var m, line, file_path, location, start, end, message;

        for (var i = 0; i < lines.length; i++) {
                // Reset the regex index
                // This is still better than creating a new regex each time.
            REGEX_HAXE_COMPILER_OUTPUT_LINE.lastIndex = -1;

            line = lines[i];

            if (info.length > 0) {
                prev_info = info[info.length - 1];
            }

            if (m = line.match(REGEX_HAXE_COMPILER_OUTPUT_LINE)) {
                file_path = m[1];
                line = parseInt(m[2], 10);
                location = m[3];
                start = parseInt(m[4], 10);
                end = parseInt(m[5], 10);
                message = m[6];

                    // Make file_path absolute if possible
                if (cwd != null && !path.isAbsolute(file_path)) {
                    file_path = path.join(cwd, file_path);
                }

                if (prev_info != null
                    && prev_info.file_path == file_path
                    && prev_info.location == location
                    && prev_info.line == line
                    && prev_info.start == start
                    && prev_info.end == end) {

                    prev_info.message += "\n" + message;
                }
                else {
                    info.push({
                        line: line,
                        file_path: file_path,
                        location: location,
                        start: start,
                        end: end,
                        message: message
                    });
                }
            }
        } //for lines

        return info;

    } //parse_compiler_output

}
<commit_msg>parsing; Micro change<commit_after>
        // lib code
var   state = require('../haxe-state')
        // node built in
    , path = require('path')

    // Match haxe compiler output info
var REGEX_HAXE_COMPILER_OUTPUT_LINE = /^\s*([^\:]+)\:([0-9]+)\:\s+(characters|lines)\s+([0-9]+)\-([0-9]+)\s+\:\s*(.*?)\s*$/;

module.exports = {

        // Parse haxe compiler output and extract info
    parse_output: function(output) {

        var info = [];
        var prev_info = null;
        var lines = output.split("\n");
        var cwd = state.hxml_cwd;
        var m, line, file_path, location, start, end, message;

        for (var i = 0; i < lines.length; i++) {
                // Reset the regex index
                // This is still better than creating a new regex each time.
            REGEX_HAXE_COMPILER_OUTPUT_LINE.lastIndex = -1;

            line = lines[i];

            if (info.length > 0) {
                prev_info = info[info.length - 1];
            }

            if (m = line.match(REGEX_HAXE_COMPILER_OUTPUT_LINE)) {
                file_path = m[1];
                line = parseInt(m[2], 10);
                location = m[3];
                start = parseInt(m[4], 10);
                end = parseInt(m[5], 10);
                message = m[6];

                    // Make file_path absolute if possible
                if (cwd != null && !path.isAbsolute(file_path)) {
                    file_path = path.join(cwd, file_path);
                }

                if (prev_info != null
                    && prev_info.file_path == file_path
                    && prev_info.location == location
                    && prev_info.line == line
                    && prev_info.start == start
                    && prev_info.end == end) {

                    prev_info.message += "\n" + message;
                }
                else {
                    info.push({
                        line: line,
                        file_path: file_path,
                        location: location,
                        start: start,
                        end: end,
                        message: message
                    });
                }
            }
        } //for lines

        return info;

    } //parse_output

}
<|endoftext|>


<commit_before>import React from 'react';
import ReactDOM from 'react-dom';
import {MemoryRouter} from 'react-router-dom';
import {shallow, mount} from 'enzyme';
import _ from 'lodash';
import fetchMock from 'fetch-mock/es5/client';
import {testTimeMoment, testContext, withDefaultNowContext} from '../testing/NowContainer';
import mockWithFixtures from './fixtures/mockWithFixtures';
import ClassListCreatorPage from './ClassListCreatorPage';
import class_list_json from './fixtures/class_list_json';
import {uri, sarah, laura} from '../testing/fixtures/currentEducator';

beforeEach(() => mockWithFixtures());

export function testProps(props) {
  return {
    currentEducator: sarah,
    disableHistory: true,
    disableSizing: true,
    ...props
  };
}

function mountWithContext(props) {
  const context = {...testContext(), districtKey: 'somerville'};
  return mount(<ClassListCreatorPage {...props} />, {context});
}

function anyServerCallsIncludePath(string) {
  return _.some(fetchMock.calls(), call => {
    const path = call[0];
    return path.indexOf(string) !== -1;
  });
}

function modifiedPrincipalStudentIdsByRoom() {
  const {studentIdsByRoom} = class_list_json.class_list.json;
  return {
    "room:unplaced": studentIdsByRoom['room:unplaced'].concat(studentIdsByRoom['room:0']),
    "room:0":[],
    "room:1":studentIdsByRoom['room:1']
  };
}

it('renders without crashing on entrypoint', () => {
  const props = testProps();
  const el = document.createElement('div');
  ReactDOM.render(
    <MemoryRouter initialEntries={['/classlists']}>
      {withDefaultNowContext(<ClassListCreatorPage {...props} />)}
    </MemoryRouter>
  , el);
});

it('renders without crashing with balanceId', () => {
  const props = testProps({defaultWorkspaceId: 'foo-id'});
  const el = document.createElement('div');
  ReactDOM.render(
    <MemoryRouter initialEntries={['/classlists/foo-id']}>
      {withDefaultNowContext(<ClassListCreatorPage {...props} />)}
    </MemoryRouter>
  , el);
});

it('integration test for state changes, server requests and autosave', done => {
  const props = testProps({ autoSaveIntervalMs: 200 });
  const wrapper = mountWithContext(props);
  wrapper.instance().onSchoolIdChanged(4);
  wrapper.instance().onGradeLevelNextYearChanged('6');
  wrapper.instance().onListTypeTextChanged('homeroom lists');
  wrapper.instance().onStepChanged(2);
  wrapper.instance().onClassroomsCountIncremented(2);

  // This should update component state, but also trigger server
  // requests and other state changes too.
  setTimeout(() => {
    expect(wrapper.state().schoolId).toEqual(4);
    expect(wrapper.state().gradeLevelNextYear).toEqual('6');
    expect(wrapper.state().listTypeText).toEqual('homeroom lists');
    expect(wrapper.state().stepIndex).toEqual(2);
    expect(wrapper.state().students.length).toEqual(7);
    expect(Object.keys(wrapper.state().studentIdsByRoom)).toEqual([
      'room:unplaced',
      'room:0',
      'room:1',
      'room:2',
      'room:3'
    ]);

    // Make a few more changes and verify workspace is dirty.
    wrapper.instance().onStepChanged(3);
    wrapper.instance().onFeedbackTextChanged('my feedback');
    expect(wrapper.instance().isDirty()).toEqual(true);

    // Wait for autosave to happen and then verify saving.
    setTimeout(() => {
      expect(wrapper.instance().isDirty()).toEqual(false);
      expect(wrapper.state().feedbackText).toEqual('my feedback');
      expect(wrapper.state().lastSavedSnapshot.feedbackText).toEqual('my feedback');
      expect(anyServerCallsIncludePath('teacher_updated_class_list_json')).toEqual(true);
      expect(anyServerCallsIncludePath('principal_revised_class_list_json')).toEqual(false);
      done();
    }, 300); // delay for autosave interval
  }, 10); // delay a tick for fetch requests
});

it('integration test for loading existing workspace', done => {
  const props = testProps({
    defaultWorkspaceId: 'foo-workspace-id',
    autoSaveIntervalMs: 200
  });
  const wrapper = mountWithContext(props);

  // Waiting for data to load
  setTimeout(() => {
    expect(wrapper.state().lastSavedSnapshot).not.toEqual(null);
    expect(wrapper.instance().isDirty()).toEqual(false);
    expect(wrapper.state().schoolId).toEqual(2);
    expect(wrapper.state().gradeLevelNextYear).toEqual('3');
    expect(wrapper.state().stepIndex).toEqual(0);
    expect(wrapper.state().students.length).toEqual(7);
    expect(Object.keys(wrapper.state().studentIdsByRoom)).toEqual([
      'room:unplaced',
      'room:0',
      'room:1'
    ]);
    expect(wrapper.state().feedbackText).toEqual('feedback!');
    expect(wrapper.state().principalNoteText).toEqual('principal!');
    expect(wrapper.state().planText).toEqual('plan!');

    // Type other feedback values
    wrapper.instance().onFeedbackTextChanged('other feedback');
    expect(wrapper.state().feedbackText).toEqual('other feedback');

    done();
  }, 10); // delay a tick for fetch requests
});

it('integration test for first-time principal revision', done => {
  // Update mock so that it's as if this class list were already submitted
  fetchMock.get('express:/api/class_lists/:workspace_id/class_list_json', {
    ...class_list_json,
    is_editable: false,
    class_list: {
      ...class_list_json.class_list,
      submitted: true
    }
  }, { overwriteRoutes: true });

  // This is the modifications we'll make as principal
  const principalStudentIdsByRoom = modifiedPrincipalStudentIdsByRoom();

  // Start the test
  const props = testProps({
    currentEducator: laura,
    defaultWorkspaceId: 'foo-workspace-id',
    autoSaveIntervalMs: 200
  });
  const wrapper = mountWithContext(props);

  // Waiting for data to load
  setTimeout(() => {
    expect(wrapper.state().lastSavedSnapshot).not.toEqual(null);
    expect(wrapper.instance().isDirty()).toEqual(false);

    // Make changes to the list as a principal and verify they update state correctly
    wrapper.instance().onStepChanged(4);
    wrapper.instance().onClassListsChangedByPrincipal(principalStudentIdsByRoom);
    expect(wrapper.state().principalStudentIdsByRoom).toEqual(principalStudentIdsByRoom);

    // Add teacher names
    wrapper.instance().onStepChanged(5);
    wrapper.instance().onPrincipalTeacherNamesByRoomChanged({
      "room:0":"Uri",
      "room:1":"Ale..."
    });
    expect(wrapper.state().principalTeacherNamesByRoom).toEqual({
      "room:0":"Uri",
      "room:1":"Ale..."
    });

    // Verify that changes get autosaved
    expect(wrapper.instance().isDirty()).toEqual(true);
    setTimeout(() => {
      expect(wrapper.instance().isDirty()).toEqual(false);
      expect(wrapper.state().lastSavedSnapshot.principalTeacherNamesByRoom).toEqual({
        "room:0":"Uri",
        "room:1":"Ale..."
      });
      expect(wrapper.state().lastSavedSnapshot.principalStudentIdsByRoom).toEqual(principalStudentIdsByRoom);
      expect(anyServerCallsIncludePath('teacher_updated_class_list_json')).toEqual(false);
      expect(anyServerCallsIncludePath('principal_revised_class_list_json')).toEqual(true);
      done();
    }, 300); // delay for autosave interval
  }, 10); // delay a tick for fetch requests
});

it('#onFetchedClassList loads principal revisions', () => {
  const props = testProps();
  const principalStudentIdsByRoom = modifiedPrincipalStudentIdsByRoom();
  const wrapper = mountWithContext(props);
  wrapper.instance().onFetchedClassList({
    ...class_list_json,
    class_list: {
      ...class_list_json.class_list,
      revised_by_principal_educator_id: laura.id,
      principal_revisions_json: {
        principalStudentIdsByRoom,
        "principalTeacherNamesByRoom":{
          "room:0":"Kevin",
          "room:1":"Alex",
        },
        "clientNowMs":testTimeMoment().unix()
      }
    }
  });
  expect(wrapper.state().principalStudentIdsByRoom).toEqual(principalStudentIdsByRoom);
  expect(wrapper.state().principalTeacherNamesByRoom).toEqual({
    "room:0":"Kevin",
    "room:1":"Alex"
  });
});

it('#isRevisable', () => {
  function isRevisableForEducator(currentEducator, state = {}) {
    const props = testProps({currentEducator});
    const wrapper = shallow(<ClassListCreatorPage {...props} />, {context: testContext()});
    wrapper.instance().setState(state);
    return wrapper.instance().isRevisable();
  }

  expect(isRevisableForEducator(laura, {schoolId: 2, isSubmitted: true })).toEqual(true);
  expect(isRevisableForEducator(laura, {schoolId: 4, isSubmitted: true })).toEqual(false);
  expect(isRevisableForEducator(laura, {schoolId: 2, isSubmitted: false })).toEqual(false);
  expect(isRevisableForEducator(sarah, {schoolId: 2, isSubmitted: true })).toEqual(false);
  expect(isRevisableForEducator(uri, {schoolId: 2, isSubmitted: true })).toEqual(false);
});
<commit_msg>Another districtKey wrapper<commit_after>import React from 'react';
import ReactDOM from 'react-dom';
import {MemoryRouter} from 'react-router-dom';
import {shallow, mount} from 'enzyme';
import _ from 'lodash';
import fetchMock from 'fetch-mock/es5/client';
import {testTimeMoment, testContext, withDefaultNowContext} from '../testing/NowContainer';
import PerDistrictContainer from '../components/PerDistrictContainer';
import mockWithFixtures from './fixtures/mockWithFixtures';
import ClassListCreatorPage from './ClassListCreatorPage';
import class_list_json from './fixtures/class_list_json';
import {uri, sarah, laura} from '../testing/fixtures/currentEducator';

beforeEach(() => mockWithFixtures());

export function testProps(props) {
  return {
    currentEducator: sarah,
    disableHistory: true,
    disableSizing: true,
    ...props
  };
}

function testEl(props) {
  return withDefaultNowContext(
    <PerDistrictContainer districtKey="somerville">
      <ClassListCreatorPage {...props} />
    </PerDistrictContainer>
  );
}

function mountWithContext(props) {
  const context = {...testContext(), districtKey: 'somerville'};
  return mount(<ClassListCreatorPage {...props} />, {context});
}

function anyServerCallsIncludePath(string) {
  return _.some(fetchMock.calls(), call => {
    const path = call[0];
    return path.indexOf(string) !== -1;
  });
}

function modifiedPrincipalStudentIdsByRoom() {
  const {studentIdsByRoom} = class_list_json.class_list.json;
  return {
    "room:unplaced": studentIdsByRoom['room:unplaced'].concat(studentIdsByRoom['room:0']),
    "room:0":[],
    "room:1":studentIdsByRoom['room:1']
  };
}

it('renders without crashing on entrypoint', () => {
  const props = testProps();
  const el = document.createElement('div');
  ReactDOM.render(
    <MemoryRouter initialEntries={['/classlists']}>
      {testEl(props)}
    </MemoryRouter>
  , el);
});

it('renders without crashing with balanceId', () => {
  const props = testProps({defaultWorkspaceId: 'foo-id'});
  const el = document.createElement('div');
  ReactDOM.render(
    <MemoryRouter initialEntries={['/classlists/foo-id']}>
      {testEl(props)}
    </MemoryRouter>
  , el);
});

it('integration test for state changes, server requests and autosave', done => {
  const props = testProps({ autoSaveIntervalMs: 200 });
  const wrapper = mountWithContext(props);
  wrapper.instance().onSchoolIdChanged(4);
  wrapper.instance().onGradeLevelNextYearChanged('6');
  wrapper.instance().onListTypeTextChanged('homeroom lists');
  wrapper.instance().onStepChanged(2);
  wrapper.instance().onClassroomsCountIncremented(2);

  // This should update component state, but also trigger server
  // requests and other state changes too.
  setTimeout(() => {
    expect(wrapper.state().schoolId).toEqual(4);
    expect(wrapper.state().gradeLevelNextYear).toEqual('6');
    expect(wrapper.state().listTypeText).toEqual('homeroom lists');
    expect(wrapper.state().stepIndex).toEqual(2);
    expect(wrapper.state().students.length).toEqual(7);
    expect(Object.keys(wrapper.state().studentIdsByRoom)).toEqual([
      'room:unplaced',
      'room:0',
      'room:1',
      'room:2',
      'room:3'
    ]);

    // Make a few more changes and verify workspace is dirty.
    wrapper.instance().onStepChanged(3);
    wrapper.instance().onFeedbackTextChanged('my feedback');
    expect(wrapper.instance().isDirty()).toEqual(true);

    // Wait for autosave to happen and then verify saving.
    setTimeout(() => {
      expect(wrapper.instance().isDirty()).toEqual(false);
      expect(wrapper.state().feedbackText).toEqual('my feedback');
      expect(wrapper.state().lastSavedSnapshot.feedbackText).toEqual('my feedback');
      expect(anyServerCallsIncludePath('teacher_updated_class_list_json')).toEqual(true);
      expect(anyServerCallsIncludePath('principal_revised_class_list_json')).toEqual(false);
      done();
    }, 300); // delay for autosave interval
  }, 10); // delay a tick for fetch requests
});

it('integration test for loading existing workspace', done => {
  const props = testProps({
    defaultWorkspaceId: 'foo-workspace-id',
    autoSaveIntervalMs: 200
  });
  const wrapper = mountWithContext(props);

  // Waiting for data to load
  setTimeout(() => {
    expect(wrapper.state().lastSavedSnapshot).not.toEqual(null);
    expect(wrapper.instance().isDirty()).toEqual(false);
    expect(wrapper.state().schoolId).toEqual(2);
    expect(wrapper.state().gradeLevelNextYear).toEqual('3');
    expect(wrapper.state().stepIndex).toEqual(0);
    expect(wrapper.state().students.length).toEqual(7);
    expect(Object.keys(wrapper.state().studentIdsByRoom)).toEqual([
      'room:unplaced',
      'room:0',
      'room:1'
    ]);
    expect(wrapper.state().feedbackText).toEqual('feedback!');
    expect(wrapper.state().principalNoteText).toEqual('principal!');
    expect(wrapper.state().planText).toEqual('plan!');

    // Type other feedback values
    wrapper.instance().onFeedbackTextChanged('other feedback');
    expect(wrapper.state().feedbackText).toEqual('other feedback');

    done();
  }, 10); // delay a tick for fetch requests
});

it('integration test for first-time principal revision', done => {
  // Update mock so that it's as if this class list were already submitted
  fetchMock.get('express:/api/class_lists/:workspace_id/class_list_json', {
    ...class_list_json,
    is_editable: false,
    class_list: {
      ...class_list_json.class_list,
      submitted: true
    }
  }, { overwriteRoutes: true });

  // This is the modifications we'll make as principal
  const principalStudentIdsByRoom = modifiedPrincipalStudentIdsByRoom();

  // Start the test
  const props = testProps({
    currentEducator: laura,
    defaultWorkspaceId: 'foo-workspace-id',
    autoSaveIntervalMs: 200
  });
  const wrapper = mountWithContext(props);

  // Waiting for data to load
  setTimeout(() => {
    expect(wrapper.state().lastSavedSnapshot).not.toEqual(null);
    expect(wrapper.instance().isDirty()).toEqual(false);

    // Make changes to the list as a principal and verify they update state correctly
    wrapper.instance().onStepChanged(4);
    wrapper.instance().onClassListsChangedByPrincipal(principalStudentIdsByRoom);
    expect(wrapper.state().principalStudentIdsByRoom).toEqual(principalStudentIdsByRoom);

    // Add teacher names
    wrapper.instance().onStepChanged(5);
    wrapper.instance().onPrincipalTeacherNamesByRoomChanged({
      "room:0":"Uri",
      "room:1":"Ale..."
    });
    expect(wrapper.state().principalTeacherNamesByRoom).toEqual({
      "room:0":"Uri",
      "room:1":"Ale..."
    });

    // Verify that changes get autosaved
    expect(wrapper.instance().isDirty()).toEqual(true);
    setTimeout(() => {
      expect(wrapper.instance().isDirty()).toEqual(false);
      expect(wrapper.state().lastSavedSnapshot.principalTeacherNamesByRoom).toEqual({
        "room:0":"Uri",
        "room:1":"Ale..."
      });
      expect(wrapper.state().lastSavedSnapshot.principalStudentIdsByRoom).toEqual(principalStudentIdsByRoom);
      expect(anyServerCallsIncludePath('teacher_updated_class_list_json')).toEqual(false);
      expect(anyServerCallsIncludePath('principal_revised_class_list_json')).toEqual(true);
      done();
    }, 300); // delay for autosave interval
  }, 10); // delay a tick for fetch requests
});

it('#onFetchedClassList loads principal revisions', () => {
  const props = testProps();
  const principalStudentIdsByRoom = modifiedPrincipalStudentIdsByRoom();
  const wrapper = mountWithContext(props);
  wrapper.instance().onFetchedClassList({
    ...class_list_json,
    class_list: {
      ...class_list_json.class_list,
      revised_by_principal_educator_id: laura.id,
      principal_revisions_json: {
        principalStudentIdsByRoom,
        "principalTeacherNamesByRoom":{
          "room:0":"Kevin",
          "room:1":"Alex",
        },
        "clientNowMs":testTimeMoment().unix()
      }
    }
  });
  expect(wrapper.state().principalStudentIdsByRoom).toEqual(principalStudentIdsByRoom);
  expect(wrapper.state().principalTeacherNamesByRoom).toEqual({
    "room:0":"Kevin",
    "room:1":"Alex"
  });
});

it('#isRevisable', () => {
  function isRevisableForEducator(currentEducator, state = {}) {
    const props = testProps({currentEducator});
    const wrapper = shallow(<ClassListCreatorPage {...props} />, {context: testContext()});
    wrapper.instance().setState(state);
    return wrapper.instance().isRevisable();
  }

  expect(isRevisableForEducator(laura, {schoolId: 2, isSubmitted: true })).toEqual(true);
  expect(isRevisableForEducator(laura, {schoolId: 4, isSubmitted: true })).toEqual(false);
  expect(isRevisableForEducator(laura, {schoolId: 2, isSubmitted: false })).toEqual(false);
  expect(isRevisableForEducator(sarah, {schoolId: 2, isSubmitted: true })).toEqual(false);
  expect(isRevisableForEducator(uri, {schoolId: 2, isSubmitted: true })).toEqual(false);
});
<|endoftext|>